Showing posts with label Ruby on Rails. Show all posts
Showing posts with label Ruby on Rails. Show all posts

Thursday, February 05, 2009

The Fat Controller must die!

As most parents with little boys know the Fat Controller is a key figure in the Thomas the Tank Engine TV series (and books, toys, clothing, linen, bags, crockery, snacks, etc.). he is a cheery fellow, unfortunately prone to angrily shouting at train engines, but equally kindly and caring about his beloved vehicles (especially steam powered ones like Elizabeth the lorry). For the sake of my children I must point out that I have no problem with this jolly fellow.

fat controller

There is another domain where fat controllers exist, and that is the world of the MVC (Model-View-Controller) pattern. The pattern has experienced a renaissance recently in web applications, particularly because of Ruby on Rails, and now Microsoft's ASP.NET MVC offering.

Now these Ruby on Rails guys have been doing this for a little while longer than the ASP.NET guys, and one key paradigm became clear fairly early on:

"Try to keep your controller actions and views as slim as possible."

This was most clearly explained by Jamis Buck in his excellent Skinny Controller, Fat Model post back in 2006, which is still worth reading even if you don't use Ruby on Rails. More recently Ian Cooper has pointed out this is the same problem webforms had:

"This is the good old problem of domain-logic in code-behind that we had in ASP.NET webforms. Indeed Webforms are just another type of controller, a page controller, and switching to an application controller model does not remove the need for us to watch for domain logic creeping into the controller. There can be a temptation to believe that just because the controller is easier to test it is now safe to put domain logic in there. Do not fall into that trap."

In his book, Domain-Driven Design, Eric Evans identifies this as the Smart UI Anti-Pattern.

“Put all the business logic into the user interface. Chop the application into small functions and implement them as separate user interfaces, embedding the business rules into them. Use a relational database as a shared repository of the data. Use the most automated UI building and visual programming tools available.”

SmartUI
Image from David Hayden's blog

Eric is kind (and pragmatic) enough to point out when it is useful.

“A project needs to deliver simple functionality, dominated by data entry and display, with few business rules. Staff is not composed of advanced object modelers.”

Clearly ASP.NET's webforms model encouraged this sort of practice (it is just far too easy to do), but surely a controller is a safe place for this code? After all, a controller can use many views, so it is more clearly separated than code behind. The problem is that whilst application logic ("Which view do I show next?", "What domain object is handling this request?") makes perfect sense in a controller, there are too many examples of them being used as stores for business/domain logic - which is the province of the domain model.

Hang on, we probably have a (ubiquitous) language problem here. You see, the "Model" in MVC != the "domain model" in Domain-Driven Design (DDD). In fact we need to map the DDD concept of layered architecture to MVC in order to see what we truly have.

MvcMapToDdd

Instead of giving the Model layer a fully fleshed out domain and supporting infrastructure (repository and services), many ASP.NET developers seem to want to treat the Model as pure database infrastructure and DAL (like the one in the Smart UI graphic above). This leads to the problem of where to put the business logic, and the natural assumption is that it belongs in the controller ... which is like making the navigator the captain of the ship.

Ian and Jamis have much more (technical) stuff to say on this subject so I'd advise you to check their posts out if you're into .NET or Rails, respectively, but do take this message with you, the Fat Controller must die!

Tuesday, November 20, 2007

Visual Studio 2008 and .NET 3.5 released!

Microsoft have given us developers an early Christmas present by releasing Visual Studio 2008 and .NET Framework v3.5 today! See Scott Guthrie's post about the release for full details.

The bits I'm waiting for the most are the VB.NET improvements, particularly Lambda Expressions which might give us the ability to use some of the niftier BDD test framework's features in VB rather than needing to write executable specs in C#. The new web designer interface and CSS/JavaScript improvements will be welcome ones too. Then there are other features that look nice, but I am less excited about, LINQ being foremost of those.

The piece I am most eagerly awaiting is not yet released, that is the ASP.NET MVC framework, which will give us .NET developers the same sort of simple and clean structure Ruby on Rails developers have been enjoying.

Thursday, July 05, 2007

New .NET Role

I'm about to start a new permanent role at a .NET web company in Sydney. I've been lamenting the fact that I won't get to play with Ruby on Rails at work, mainly because I love the simplicity of the Model-View-Controller architecture it forces on you. Then, lo and behold, what should I stumble across but MonoRail, a project to bring the MVC web framework of ActionPack (what drives Rails) to .NET (1.1, 2.0 and Mono).

So I may end up escaping WebForms horror of one-form to a page, abstracted abstractions, and the cross-browser nightmares that follow.

Well, OK, a guy can dream can't he? ;D

[UPDATE: That sounds like I'm not excited about the new role, the fact is I am, especially because we'll be using Scrum, and the guys seem friendly and smart. It's just that I was coding dynamic web pages before WebForms came along, and I hope to be doing so well after they're relegated to the scrapheap.]

Sunday, May 27, 2007

RubyWorks announced by ThoughtWorks Studios

ThoughtWorks Studios have announced the July 2007 release of RubyWorks, which is intended to be “a new products and support service for Ruby ... to help enterprises implement and use Ruby more effectively”.

It looks like the bundling of some material they've already used in-house (like JRuby) with Mingle, with products they've worked extensively on themselves, such as CruiseControl.rb.

Of course the payoff is the consulting services they hope to sell from this, and increased willingness by larger enterprises to adopt Ruby and Ruby on Rails.

Which OS for Ruby on Rails development?

I recently acquired a second hand laptop to allow me to play more with Ruby on Rails without ignoring the family - I spend over an hour a day on trains, so it makes sense to do more than just read during that time.

“My first instinct was to whip out the spare Windows XP Pro CD”
My first instinct was to whip out the spare Windows XP Pro CD I've got at home, but then I figured I should really poke around and see what others had to say about developing on Linux. ThoughtWorker Jake Scruggs has an interesting post comparing Windows vs OSX vs Ubuntu for Ruby/Rails Development. He makes some good points and basically kiboshed the idea of using Windows:
“I’m not going to mince words here -- try to avoid developing in Windows if at all possible ...”
Now most Rails developers I know of use Apple Macs running OSX; for example that was by far the most common laptop at the Ruby on Rails Oceania meeting I attended recently. So it was unsurprising to have Jake sing its praises, especially using TextMate, however I was surprised by this summation:
“Really the choice between OSX and Linux is mostly the choice between TextMate and IntelliJ (or Eclipse/RadRails). And, for now, I think TextMate has a slight edge. TextMate’s auto complete is a joy to work with while IntelliJ’s indexing makes for crazy fast searching.”
I've been using RadRails for Windows, so this got me interested. A couple of weeks later and I've settled in to using RadRails on Xubuntu 7.0.4 (Feisty Fawn).

Xubuntu is my first real attempt at personally using a Linux distribution as a primary desktop OS. I've had experience using Linux servers (no GUIs), so I figured I could always drop back to the command line if something broke and I've had to once or twice during installations. But overall I have been pleasantly surprised at how civil an experience it has been.

“But overall I have been pleasantly surprised at how civil an experience it has been.”
My main hurdle was getting the new Aptana + Rails IDE setup. That proved to be difficult, with few helpful instructions online. At the end of the day I fell back to the tried and true RadRails 0.7.2 release.

One of the nice things is that with Xubuntu I can run MySQL server, a WebBRICK web server, RadRails, a PDF viewer (with AWDWR open) and Firefox without using more than half of my 512 MB RAM and only occasionally pushing the 1.8 GHz CPU above 25%.

The hardest thing to get used to is the compartmentalisation of functionality between applications. Sometimes that's a minor delay as I find where I need to do something, like the hassle I go through to change my desktop, screensaver and power settings. It's one application in Windows, but three (or four!) in Linux. Other times it's a royal pain, for example when I find that I haven't (yet) installed the right application to do something - but it's not clear what I should install to fix the issue.

My recommendation? The Windows experience is a great one if you have background in it, the Linux one is smoother in some ways (installing gems, finding online help) but in other ways (application interoperability) is more jarring. I am enjoying the experience of Xubuntu on my little old laptop, but I would hesitate before setting up my main work machine the same way.

I have no opinion on the OSX option, but my experiences with Apple Macs have not been universally wonderful - others have compared OSX and Windows XP more graphically and with greater detail than I ever will ...

Friday, May 25, 2007

Currency formatting stuck in Rails' Views

I've been re-doing the Depot application tutorial from the excellent Agile Web Development with Rails book as I've updated it for the latest version of Rails in order to get a better handle on REST and other changes in Rails 1.2.3. I'm mostly doing it on the train in the morning and evening.

Anyway, as I go I'm challenging the approach given in the book to validate my own Rails/OO design knowledge. One area where I thought I could see a better way of doing things was with the formatting of the prices in the store view. Previously I had used sprintf() to give me a display_price facade attribute in the Product model that I could use for pretty (i.e. currency specific) versions of prices.

The number_to_currency helper method seemed to offer a great way of doing the currency formatting, especially given the helpful options it has for setting all sorts of specific details like currency symbol, separators for thousands etc. The tutorial showed it being added into the Store view, which is nice, but potentially could leave me with a whole lot of places to make changes if I wanted to internationalise the application (although the elements could be stored in constants). However when I plugged it into my Product model it threw a nasty error:
undefined method 'number_to_currency'
It turns out that this helper method is part of ActionView and so can't be used in models or controllers (the documentation I link to above has comments about that under it). The workaround is to use sprintf() within models or controllers. At least the database column is now automatically detected as a BigDecimal rather than a Float so my previous hack is no longer required.

So is there a problem with this? On the one hand, I can see that number_to_currency is all about creating a string representation of a number, and thus sits squarely within the world of views. On the other hand the DRY principle militates that we should have a single authoritative source of this information, without creating larger issues. In theory this means the model layer, although it is unclear whether you would restrict yourself to the Rails models or the underlying database objects.

My rule of thumb is to KISS it and use the Rails model when you know everything you write will be in Rails. If you expected to have non-Rails applications accessing the data (e.g. legacy .NET or Java code) then I would look at creating a view (less flexible currency styling) or stored procedure to act as the database source object for my Rails and .NET models.

Thursday, May 17, 2007

Rails Envy: Java meets Rails

The Rails Envy guys ("It's not the size of the app that matters, it's how you code it") have got some great mock ads on Rails vs Java and Rails vs PHP in the same vein as those Apple ads. Here is the first of their offerings:

Wednesday, May 09, 2007

Agile and Ruby on Rails thinking

I attended the Ruby on Rails Oceania meetup tonight and learned some interesting things about RDoc (PDF) from Tim Lucas, got some good pointers on how to involve customers in Agile projects (PDF) from Max Muerrman and lastly some great tips on geocoding in Rails (PDF) from Cameron Barrie.

[UPDATE: Got more details (like full names) from Tim's blog. Also Tim's got his slides up on SlideShare.]

I've been playing with Ruby on Rails to broaden my understanding of Agile development concepts and practices, and it's going well. The principles of the Agile Manifesto sounded good when I first read them over a year ago, but since listening to Jason Yip from ThoughtWorks talk about them they've come to life for me, so I will be doing a series of posts about them and how they relate to my experience in over 13 years of IT consulting and contracting.

It's also been nice to read about how Rails is finally getting some traction in the Enterprise arena, firstly through Thoughtworks delivering their project management application, Mingle on JRuby, which basically re-writes Ruby in Java. This means that we now have applications written in Ruby on Rails, but deployed on Java in the enterprise. Very nice.

Secondly we have Microsoft announcing the Dynamic Language Runtime, which allows projects like their IronRuby (a different beastie than Wilco Bauwer's). In an interview with eWeek one of the developers behind the DLR, Jim Hugunin explains how they developed the DLR:
“Hugunin: Where this all came from is I joined Microsoft with IronPython, but I joined to make the CLR [Common Language Runtime] a better platform for dynamic languages. In some ways this is what we've been working on for the past two and a half years. The way that we did that is we started by building one real high-quality production implementation of a language, which was IronPython, and getting a lot of users to beat on it and make sure that we could do it well. Now that we have that one quality implementation we started saying, 'Well, how can we make more languages run well on the platform and make them run even better?'”

Friday, May 04, 2007

Rails slideshows

There are some great Rails slideshows on SlideShare, here are some of my favourites:

Scaling Twitter:
“It’s Easy, Really.
  1. Realize Your Site is Slow
  2. Optimize the Database
  3. Cache the Hell out of Everything
  4. Scale Messaging
  5. Deal With Abuse”


Introduction to Active Record:
“I have never seen an Active Record implementation as complete or as useful as rails.”
Martin Fowler



Ruby on Rails Resources (RESTful development):
“Constraints are liberating (a straight jacket for your mind)”

Monday, April 30, 2007

Authentication in Ruby on Rails

I've been getting sporadic time fragments to spend on Ruby on Rails, hardly throwing myself into it, but the best I can manage with my work and family commitments. One of the key items I'm wrestling with is user authentication.

This is an important area to me as any web app I am likely to write in Rails will need to authenticate users, at the very least site admins, but more likely regular users that have private data/functions they need to access.

Allow me to explain a crucial point here; when people look at application security there are two related, but different, concepts that are often confused. These are authentication and authorisation.
Authentication
This is how you prove that you are who you claim to be. An authenticated user is one who we accept the identity of.

Authorisation
This is how we determine what you are allowed to do. The first step is always to authenticate that you are really who you claim to be, the next is to determine what authorisation you have been granted within our application.
I actually intend to implement both concepts in my breakable toy apps, but unless I can find a good answer to the first one, I won't be able to do the second.

Fortunately authentication systems can be easily shared, for example I could try to implement Google account authentication, which would allow users to login to my application using their Google account. The acts_as_google_account plugin might give me exactly what I need in this area. That places the burden of user administration on Google and leaves me just worrying about authorisation.

It is much harder to take on board someone else's authorisation system, as it is by necessity interwoven with the object model we are using, and may not meet anyone else's expectations about how to control user access. A truly orthogonal authorisation system may be possible, but I have yet to see one.

Anyway, my problem at the moment is that acts_as_google_account has minimal documentation, no test cases and a module called "Foo" ... hardly something that instils confidence in the code. I could look at the Acts as Authenticated plugin, which at least is documented (almost well documented) and widely used, and someone has already worked out how to install the plugins as generators (which is more what they are).

ModelSecurity is very nice as it implements at the model level, avoiding the potential loopholes that controller-based authorisation schemes provide (e.g. a new controller is built that exposes the models without implementing the right security scheme). However there are some concerns with its integration with auto-completing text fields. But at least some of the issues have been worked out for me.

I had read someone complain that ModelSecurity implements a single level of security, which would be less than useful for me, however a read of the ModelSecurity class reference shows that the security scheme allows you to specify any test you like, so you could implement user security groups or other funkiness as you will (e.g. allow access for users whose firstname begins with "A" and a date of birth in April for any even-numbered year).

Other possibilities are to look at ActiveRBAC (RBAC = Role-Based Access Control) and the Authorization plugin.

Of them all, ModelSecurity appeals to me the most as it secures at the lowest level possible. Hopefully the next time I write about this I will have implemented it in one of my toy apps.

Wednesday, April 18, 2007

Ruby on Rails embraces BigDecimal

I am just getting back into Ruby on Rails after ignoring it for far too long. One of the first things I did was buy some shiny new books. The first one I picked up was Designing the Obvious, by Robert Hoekman. The second was The Pragmatic Programmer by Andrew Hunt and Dave Thomas. Funnily enough, neither of these books is explicitly about Ruby on Rails - but both deal with ideals (simple, yet effective design and pragmatic programming) that lie behind Rails' appeal to me.


I also checked out Dave Thomas's blog and in one of his recent posts he points out that Rails now embraces decimal database fields:
“In the Rails trunk, numeric and decimal database columns with a scale factor are now converted into Ruby BigDecimal objects. If the scale factor is zero, they instead become integers.

Migrations now support decimal columns too, with the addition of two new attributes, precision and scale.
   add_column :orders, :price,
:decimal, :precision => 8, :scale => 2
I just spent a day reworking all the Depot chapters to use this, and it seems to work great.”
That is great news, and I'm really looking forward to not needing to use the workaround I previously found.

Wednesday, February 14, 2007

Obie's software opinions

Ruby author Obie Fernandez has some interesting opinions about software development. I like this one:
“When it comes to delivering a successful project, developer motivation and productivity trump other factors.”

Sunday, April 09, 2006

Playing with Ruby on Rails

Recently I decided to play with Ruby on Rails and see what all the fuss was about. I've been around a while and taught myself a bunch of stuff along the way from Omnis7 to Java to T-SQL to .NET, and so Ruby on Rails shouldn't be too hard ... right?

Well, it wasn't, once I invested in Programming Ruby and Agile Web Development with Rails. Those two books helped me considerably, especially the follow-on tutorials in the Rails specific one. Without those I would have been left floundering.

However, there were two problems that neither book really helped me with (although the first one was properly shown in the Programming Ruby book, it just didn't bother to point out the weirdness of it).

Elsif

You might not have realised it, but that heading above is missing an "e" (and a space, but most programming languages ignore that). Unlike any other language I have learnt, Ruby's If ... Elseif ... End construct spells the "Elseif" without the second "e", as in "Elsif". Perhaps it was because Matz is Japanese, but this really threw me - I even ignored the way the Ruby book showed me it was.

Decimals

Rails' ActiveRecord is very nice for doing lots of hard work for you. Thank you very much David Heinemeier Hansson. Unfortunately it maps decimal database columns to float variables ... which if you have ever tried using floating numbers in financial calculations you would know means major problems!

Frankly this is a show-stopper for me. I can't imagine ever doing something real with Rails that would not involve dollars, and I certainly do not want nasty rounding errors creeping in because there are floats mapped to my decimal table columns. It turns out that the solution in Ruby is to use the BigDecimal data type. It took a little while to find the API documentation for this datatype, but eventually I did.

That didn't help me work out how to implement it. The Rails community is full of talk about modifying ActiveRecord to do the job properly - but it turns out that ends up involving a lot of database-specific code, and whilst ProgresSQL and SQL Server have been done, good old MySQL is left wanting, and that is what I am using to play around with. There is a little blurb in the book about overriding the accessor/mutator attributes for a model (don't worry if that makes no sense to you, it didn't to me either until about 8 hours into the tutorial). The online API documentation for ActiveRecord seems to hint that the _before_type_cast accessor could help here.

After too much mucking about, I finally got an answer that is elegant and works very nicely. I even have some sample code for those other newbies following the Rails tutorials. At the beginning of our Product model we need to add a couple of lines:

# modified this to get the price as a decimal

require "bigdecimal"
require "bigdecimal/util.rb"

Then later, we can override the default price accessor/mutator with a simple call to BigDecimal:

def price
    # we want to make Price a BigDecimal, the format
    # will look weird if we don't do anything to it
    # but we are using sprintf() in our fmt_currency
    # method, which handles this nicely
    BigDecimal(price_before_type_cast, 2)
end

As I've noted, the main problem with BigDecimal is that the native display of it is ugly. A value of 20.00 is displayed as 0.2E2, which is nasty. Fortunately the formatting seems to work out in text fields OK, and we end up formatting price everywhere we show it as a label so that it shows with dollar signs. The formatting function sprintf() has no problem with BigDecimal so that works fine!

So far (and I have all of the cart code working) this does not seem to lead to any issues with the Depot tutorial, and it gives an elegant solution (provided you are willing to create a formatting method for displaying these numbers wherever they are outside form fields).

[EDIT: The year-old enhancement request relating to the decimal issue is an interesting read.]

Submit to Reddit