Sunday, June 22, 2008

Object-Oriented Programming with Rails ActiveRecord

Have you seen this before?

@posts = Post.find :all, :conditions => { :user_id => @user.id }

This happens to new Rails developers quite a bit. This code retrieves correctly an array of @posts and send them to the view for rendering. But what about it?

The key thing here is that this can also be done in a better way:

@posts = @user.posts

When you use the former method to retrieve @posts, you are actually thinking in database terms along the lines of "in the database table 'posts' and therefore ActiveRecord model 'Post', retrieve the rows whose foreign key column 'user_id' has the value @user.id." ActiveRecord is a pattern for accessing your data from objects, but you also have to combine it with the power of the object-oriented-ness of Ruby to create the chimes of beautiful code. Using Rails and Ruby does not make anyone a better programmer automagically. One can still write code in Java/C# in the same procedural style as if you were writing C. It is how to leverage the best of all worlds makes you a better problem solver.

Therefore, think like an object bigot. Think in objects, and not in database foreign key column values. Whenever you see any Rails code in the pattern of this:

:model_id => @model.id

Just stop. "Objects on Rails" sounds a lot better than "Ruby on Foreign Keys."

Wednesday, June 18, 2008

Learning encapsulation should come before learning Rails

Do you see anything wrong in this one line of code?

puts 'Drink up your milk!' if @milk.expiration_date < 2.days.from_now


I think there is. In particular, the programmer can do better. It is about a programming concept that we have all heard and should be familiar with: encapsulation. If you are into testing, which you ought to be, you can probably identify such encapsulation violations by how smelly your tests are, as I believe in Test Driven Design. Going back to the line of code. What's wrong with it?

The pattern of this code is, you have code somewhere that yanks states (expiration_state) out of an object (@milk) and then interrogate against those states (< 2.days.from_now). By no means this is a rule, as exceptions do exist. But, when this happens, your programming siren in your head should go off as if the CI build is broken: can this be a method on the object @milk itself?

Ruby is a powerful programming language. It allows you to do all sorts of fancy programming: dynamically altering classes, add methods to only the selected few object instances, while being duck-typed and object-oriented all at the same time. There are examples galore in Rails itself. But to enable all such magic, all of your ActiveRecord domain models have getters and setters on all of their states (i.e. data). While that is convenient to access their states, sometimes you have to be careful. With great power comes great responsibility, and that responsibility comes down to you.

You might not think this applies to you. But have you ever written code like this in Ruby?

MyMailer.deliver_product_returned_notification if @product.state == State::Returned

total = @line_items.sum { |line_item| line_item.price * line_item.quantity }

if [:admin, :superuser].include?(@user.role)
...
end


Now, let's look at these rewritten examples:

puts 'Drink up your milk!' if @milk.expiring?

MyMailer.deliver_product_returned_notification if @product.returned?

total = @line_items.sum(&:subtotal)

if @user.superuser?
...
end


Forget about the loops, the blocks, the hype, and everything about Ruby for a sec. Code is code. Not only does the code become more readable, when you try to enrich your domain by naming things correctly, you could also very well be opening up new business concepts that wasn't previously clear or accurate. If you have a domain model, you are modeling it against domain concepts so that your app can interact with. You'd better be sure it is right, or else lots of time will go wasted on code that solves only half of the business problems all the time.

Monday, June 09, 2008

Launching apps in command line (Mac)

In most cases, to launch an application on a Mac is just a matter of using the awesome QuickSilver keyboard shortcut and type in what you want to open. But, sometimes it is useful to be able to launch/open an application in command line or terminal in a scripting context.

So, instead of going through all the typing $ /Application/MyFavouriteApp/Contents/MacOS/MyFavouriteApp in your terminal, you can do:

$ open -a MyFavouriteApp

This will open any applications in your /Applications folder by name.

Wednesday, May 21, 2008

Rails composed_of validation

ActiveRecord allows you to declaratively write validations (e.g. validates_presence_of) in any ActiveRecord::Base models. In addition, the errors will be stored in the @model#errors collection and be used by various view helpers and methods like FormBuilder, error_messages_for, and error_message_on, wrapping a nice error CSS around the offending html form elements. But when you have a normal domain model object that is non-ActiveRecord::Base such as a composed_of type Value Object, you will not have access to these declarative validation magic methods. Today let me try to elaborate on how to use ActiveRecord::Validation methods in your composed_of objects in Rails 2.0.


(The following entry revolves around the assumption that you are using f.fields_for to create and assign value object domain models onto your ActiveRecord::Base models. Using this approach eliminates most value object creation code in your controllers, achieving "Skinny Controllers". To learn about, visit, check out the Rails composed_of &block conversion blog entry.)


Using the same example in the previous entry, let's include our ActiveRecord::Validation module into our Money class like such:

class Money
include ActiveRecord:Validations

attr_reader :balance, :currency

validates_presence_of :currency
validates_inclusion_of :currency, :in => ['USD', 'EUR'], :if => :currency?
validates_presence_of :balance
validates_numericality_of :balance, :if => :balance?

def initialize(balance, currency)
@balance, @currency = balance, currency
@errors = ActiveRecord::Errors.new self
end

def new_record?
true
end

def currency?
!@currency.blank?
end

def balance?
!@balance.blank?
end

def self.human_attribute_name(attr)
attr.humanize
end

def balance_before_type_cast
@balance
end
end


You will notice a couple things. One, I have to define the #new_record? method. This method is defined on all ActiveRecord::Base objects, but since our PORO object is not a record per se, we just stub it out. Also, we need to store a collection @errors of type ActiveRecord::Errors.

Depending on what validation routine you will end up using in Money, you may have to stub out different methods. For example, I am showing error messages with error_messages_for (more on this later), and it requires stubbing out self.human_attribute_name (as of Rails 2.0, but no longer needed in future Rails). Using validates_numericality_of requires me to stub out balance_before_type_cast. Also, the validation :if conditions requires me to add the question-mark methods balance? and currency?. Remember, this approach does not give you all the validation magic. For example, validates_uniqueness_of will not work because it assumes too much about your object being a normal AR model and needs access to a database. But in practice, your Value Objects should not need such validations, and in most cases they contain only simple one-off validations, and provide simple functionalities such as formatting like this and this.

After all these, let's see our ActiveRecord Book class.

class Book < ActiveRecord::Bases
composed_of :balance, :class_name => 'Money', :mapping => [%w(balance balance), %w(currency currency)] do |params|
Money.new params[:balance], params[:currency]
end
validates_presence_of :name
validates_associated :balance
end


The composed_of conversion block remains. You will notice the validates_associated :balance line as well. This tells your book instances that they should not be persisted should there be any balance validations failing, just like any normal validations you would write. By default, any failing balance validation will add an error message 'Balance is invalid' in your @book#errors collection. If you want to suppress that message from showing up, you can pass in option :message => nil.

So, to put it all together, here is the view and the controller code:

<h1>New book</h1>

<%= error_messages_for :object => [@book, @book.balance] %>

<% form_for(@book) do |f| %>
<p>Name: <%= f.text_field :name %></p>

<% f.fields_for :balance, @book.balance do |ff| %>
<p>Balance: <%= ff.text_field :balance %></p>
<p>Currency: <%= ff.text_field :currency %></p>
<% end %>

<p>
<%= f.submit "Create" %>
</p>
<% end %>

<%= link_to 'Back', books_path %>


To show error messages from multiple objects on the view, I am using the view helper method error_messages_for(*args). The :object option actually allows you to pass an array of objects (c'mon, you should know this trick about ActiveRecord by now. If not, check it out here and here).

def create
@book = Book.new params[:book]

if @book.save
flash[:notice] = 'Book was successfully created.'
redirect_to @book
else
render :action => "new"
end
end


Again, a skinny, thin, sexy-looking controller action.

Tuesday, May 06, 2008

A normal Rails un-RESTful page as a resource... or can it?

An index page comes with Rails scaffolding. It is used to show a list of the "thing" you are CRUD-ing on. However, all too often we are tasked to show some pages that we aren't doing any CRUD operations on. Just a few examples:

  • Dashboard page after user logs in

  • The "Forgot your password?" page

  • Tabs or subtabs of multiple lists

  • List page showing multiple lists of different entities

A normal dashboard page isn't something you would normally CRUD on. It is more like a place holder page that a user sees the first thing after s/he logs in, showing many items of all sorts valuable to a user. In many Rails app, depending on what those items are, the dashboard page will end up being rendered by one of the items' index action. Worse yet, next time when you actually need that index page by that model, you have to call it something else, maybe 'list', and then play with the routes to get it wired up correctly.

class CustomersController < ApplicationController

# The dashboard page
def index
@customers = Customer.find :all
@products = Product.find :all
@tasks_of_the_day = Task.find :all
end

# A list of customers
def list
@customers = Customer.find :all
end

end

ActionController::Routing::Routes.draw do |map|

map.resources :customers, :products, :tasks
map.with_options :controller => 'customer' do |r|
r.dashboard '/customers/list', :action => 'list'
end

end


Here's a suggestion: how about put it in views/dashboards/show.html.erb, and, while you are at it, give it a DashboardsController? Then, put it in your routes.rb as a (singular) map.resource :dashboard, like such:

class DashboardsController < ApplicationController

def show
@customers = Customer.find :all
@products = Product.find :all
@tasks_of_the_day = Task.find :all
end

end

ActionController::Routing::Routes.draw do |map|

map.resources :customers, :products, tasks
map.resource :dashboard

end


By rendering the dashboard page in a completely different controller, you now have a very readable GET dashboard_path named route (GET: http://localhost:3000/dashboards), and you will not contaminate the index action of your other models' controllers with instance variables of all kinds. You also have a more readable routes.rb file.

One of the examples above for non-RESTful pages is the "Forgot My Password" page. Can you think of a good way to do it the Rails REST-ful way? Please go to Seeing Rails Resources Clearly to share some of your thoughts.

Saturday, April 05, 2008

Pragmatic Rake tasks organization

Do you have a lot of rake tasks on your Rails projects? I am sure you do. Rake tasks are handy, and developing your next-big-idea Rails app is a lot less attractive without it. Most of the time you drop a new foo.rake file in your lib/tasks folder, and you write code in it and get out. Over the course of development, that folder is likely to look like your favorite Halloween party house (ahem, Neal's...), except on an every day basis.

If you want your rake tasks to be more manageable, here are a few tips:

1) Namespace your rake tasks.

Namespaces in rake is nice. They make sure all tasks in your namespace are cohesive as a area of work. Still, your job is to keep your rake tasks not too crowded in a single namespace. For example, the db namespace that Rails provides very easily become crowded with tasks of all sorts, because, well, on a Rails application what you do almost always is related to your database: drop/create/migrate your database, importing reference/application data, database integration points with other applications, db related background tasks, backups... Phew! How about create a new namespace for import, integration, background, and backup for each of them?

2) Name your rake files by their namespaces.

Even if you namespace all your rake tasks, still, you are likely to end up with a bunch of .rake files in lib/tasks. But, now that you have your tasks grouped by their meaningful and not-so-crowded namespaces, you should name your files by your namespace. For example, db.rake, data.rake, import.rake, integration.rake, and backup.rake. Can you guess where that rake db:awesome is implemented? You certainly don't know what it does, but you got that right, in lib/tasks/db.rake.

3) Try not to nest more than 2 namespaces.

This tip is not a rule, the point being don't over do it. Sometimes, that third-level namespace really makes sense, For example, db:create:all from Rails creates databases for all environments. It isn't for everything, though. For example, I prefer rake data:load better than rake db:data:load. Not only is it shorter, but also the namespace db is simply implied. Short nested-ness forces you to name your namespaces more descriptively, and thus easier to navigate to their implementation.

4) Move your rake file and its external ruby files into a sub-folder, named by that namespace.

When your rake tasks are complicated enough, you obviously will use objects to do the job for you. When that happens, instead of having lib/tasks/integration.rake, you will instead want to have a sub-folder lib/tasks/integration/, and put files like integration.rake and integration_awesome_klass.rb in it. There really is no need to contaminate your lib folder with rake specific files.

Shameless Plug: Still using NAnt? Check out how to organize your NAnt scripts as well, on this blog post.

Thursday, March 27, 2008

params[:fu] #5 ) Update multiple models in update action atomically.

Updating multiple models is hard? It sounds complicated, but with Rails it actually isn't, if you know how to take advantage of it. Knowing what you know about Rails params, let's take a look at today's topic: the update action.

<% form_for @reader do |f| %>
<%= f.text_field :name %
>

<% for @subscription in @subscriptions %>
<p
>
<% fields_for @subscription do |ff| %>
<%= ff.collection_select :magazine_id, Magazine.find(:all), :id, :name, {}, :index =
> @subscription.id %>
<% end %
>
</p>
<% end %>

<%= f.submit 'Save' %>
<% end %>


generates:

<p>
<select id="subscription_4_magazine_id" name="subscription[4][magazine_id]">
<option value="101">PC Magazine</option>
<option value="102">IT Pro<
/option>
<option value="103" selected="selected">WIRED</option>
<
/select>
</p>

<p>
<select id="subscription_5_magazine_id" name="subscription[5][magazine_id]">
<option value="101">PC Magazine<
/option>
<option value="102">IT Pro</option>
<option value="103" selected="selected">WIRED<
/option>
</select>
<
/p>

... (and more)


Processing ReadersController#update (for 127.0.0.1 at 2008-01-14 21:12:56) [PUT]
Parameters: { "commit" => "Update",
"reader" => { "name" => "stephen chu" },
"subscriptions" => { "4" => { "magazine_id" => "101" },
"6" => { "magazine_id" => "102" },
"7" => { "magazine_id" => "103" } },
"authenticity_token" => "238ba79b8282882ba01d840352616c2cc79280f0",
"action" => "create",
"controller" => "readers" }


See the pattern? The POST-ed parameters are of the same structure as in one of our last example, hash of hashes. The sub-hashes are keying off of the subscription id, because this time around we are updating existing subscriptions. So last time we used params[:subscriptions].values, what would it look like this time? Let take a look.

def update
@reader = Reader.find params[:id]
@reader.attributes = params[:reader]
@reader.subscriptions.update params[:subscription].keys, params[:subscription].values

if @reader.save
flash[:notice] = 'Reader was successfully updated.'
redirect_to @reader
else
flash[:notice] = 'Failed.'
end
end


Again, another ActiveRecord model method utilizes the array of hashes pattern! The update method source code on RDoc looks like it is just updating one at a time. But a peek at the source code says otherwise:

def update(id, attributes)
if id.is_a?(Array)
idx = -1
id.collect { |id| idx += 1; update(id, attributes[idx]) }
else
object = find(id)
object.update_attributes(attributes)
object
end
end


Again, the update method recognizes array! So, where to get the arrays that we will use in our controller action? They come from .keys and .values of course:

params[:subscriptions].keys    # => [ "4", "6", "7" ]
params[:subscriptions].values # => [ { "magazine_id" => "101" }, { "magazine_id" => "102" }, ... ]


So in essence, our controller code is free from all those ugly params-munging activities. Remember, controller actions should not shuffle around their params, or otherwise it fails to abide the "Skinny Controller, Fat Model" principle, and they will stink.

Now, if you are thinking about by using update, we run the risk of not atomically saving all of our models should any of our models fail validation, you are correct. This is where rescue_from in controller saves the day. Just transact our update action using AR transaction, and re-render the edit page should it catches ActiveRecord::RecordInvalid error, you should be able to make your update action atomic. Given how lean our controller action looks like, having a transaction block that wraps around our code is not so much a nuisance anymore.

This also wraps up our params[:fu] series. Remember, how you assemble your views form elements have a lot to do with how thin and skinny your controllers look like. Thanks for reading!