Friday, February 26, 2010

Rails Source Code Walkthrough #1: the ActiveModel Module

I was taking a look into the fresh ActiveModel module of Rails 3 as of github revision #100644. Here is what I learned:

  1. Learned about new feature called autoload. Rails Inside has a small yet useful autoload example here. It is a mechanism to lazy load your modules, so delaying the load unless the module methods are actually called. A call to autoload simply marks it as a potential include, but the actual include takes place only when you first use something on that module. I has a syntax like the following: autoload :Callbacks
  2. Using autoload, the ActiveModel module loads a number of other modules such as: AttributeMethods, Callbacks, Dirty, Observer, Observing etc.
  3. Also, it initializes the I18n Internationalization with the default en locale.
What's inside AttributeMethods?
This module defines class methods that lets you define attribute methods for the objects. I learned that you can have prefix, suffix and affix appended to your attributes that go to to a default method. For example, consider the following example right from the code:

# class Person
      #
      # include ActiveModel::AttributeMethods
      # attr_accessor :name
      # attribute_method_suffix '_short?'
      # define_attribute_methods [:name]
      #
      # private
      #
      # def attribute_short?(attr)
      # send(attr).length < 5
      # end
      # end
      #
      # person = Person.new
      # person.name = "Bob"
      # person.name # => "Bob"
      # person.name_short? # => true
Isn't this amazing?
What's inside callbacks?
Callbacks module is responsible for firing your before, after and around callbacks on ActiveModel models.
What's inside Conversion?
Conversion module only has 3 methods, to_model, to_key and to_param . These methods can be overriden to allow custom conversion of the ActiveModel objects.
What's inside Dirty?
The dirty module is all about dirty tracking methods of your ActiveModels. It has methods like changes, changed?, changed, reset_attr! etc. that you can use to track changes of your model objects. Thinks like history tracking or audition on changes can be done using this module methods.
What's inside Errors module?
This module has everything that deals with generating errors on ActiveModel validations. One ruby feature I learned from this module that I didn't know before. Its something like an indexer in C#. Here is an example from the source code:

# When passed a symbol or a name of a method, returns an array of errors for the method.
    #
    # p.errors[:name] #=> ["can not be nil"]
    # p.errors['name'] #=> ["can not be nil"]
    def [](attribute)
      if errors = get(attribute.to_sym)
        errors
      else
        set(attribute.to_sym, [])
      end
    end
    # Adds to the supplied attribute the supplied error message.
    #
    # p.errors[:name] = "must be set"
    # p.errors[:name] #=> ['must be set']
    def []=(attribute, error)
      self[attribute.to_sym] << error
    end
What's inside the Naming module?
Naming module is all about singular, plural, human and such names for your models! This methods are used to define the routes as well as in views. You can override such methods to provide a custom pluralized name for your model.
What's inside observing?
Rails Observers provide you a clean implementation of the Observer design pattern. It extends on top of the default observer module from Ruby. These observers are often use for implementing Aspect oriented programming as well as the code that are neither part of models or controllers, rather fall in between the two. Email notification is an useful example from Rails Guides.
What's inside the Railtie module?
Well, not much! But it glues up ActiveModel with Rails! Just two lines of code as folllows:

require "active_model"
require "rails"
What's up with Serialization?
It has only one method that generates a hash based on the serializable attributes with :only and :except filter!
What's inside Serializers?
It has two serializers, one for json and another for xml. This two works great out of the box. However, if you need to tweak it, it should be very simply done by subclassing this classes.
What's inside translation?
It translates your model attribute names to match your locale using I18n. The default skim looks for the following naming in your local yml file when called through the method human_attribute_name
activemodel.attributes.underscore_model_name.attribute_name
Whats inside Validations?
This is a freshly renovated module for Rails 3, as it merged the validates_presence like methods into a single method validate that lets you keep all your validations for a model with a single call. Also you can easily reuse custom validators through out your models.
But this forked a few more modules, one per kind of validation. All these validations and your potential custom validators will probably be descendent of the Validator class that has the following method:

# Override this method in subclasses with validation logic, adding errors
    # to the records +errors+ array where necessary.
    def validate(record)
      raise NotImplementedError
    end
Some in-built implementation of this validator are acceptance, confirmation, exclusion etc. I learned something new here, the NotImplementedError exception.
So, what's the big learning here?
  1. I will use Modules to modularize my ruby code. This perfectly makes sense and also this is how one can come up with plugins out of their code base. Having the small bits in a module also facilitates reusability inside a project.
  2. The source of ActiveModel is very simple and completely ignorant of its underlying database. This is the big change of liberating Rails from ActiveRecord, which I think is not a matter for most rails developer anyway! But its a good lesson learned.
  3. Once I see some time, I would like to jump in to the development of Rails, at least make some initial contribution.
Stay tuned for more posts on Rails 3 source code.