Piotr Sarnacki home

I'm writing a book about Ember.js. Sign up here for a notification email if you are interested.

Rails3 modularity

Since I spend a lot more time with Rails 3 codebase, I am more and more amazed by its modularity. Rails 3 is much more extensible than I would have ever thought. As a part of my rubysoc project I wanted to port merb-parts to Rails 3. Some of you may never heard of merb parts and ask what the hell are parts? Do I need it? Parts are a bit like controllers, but much more lightweight and without overhead of handling requests. I like to think about parts as powerful partials. If you have a partial and you would like to add a bit more logic to it or fetch something from database, you could probably benefit from using parts. Let’s go with a simple example:

# app/parts/articles_part.rb
class ArticlesPart < Parts::Base
  def index
    @articles = Article.limit(params[:limit] || 10).order("created_at DESC")
  end
end
# app/parts/views/articles_part/index.html.erb
<ul>
  <% @articles.each do |article| %>
    <li><%= article.title %></li>
  <% end %>
</ul>

This simple part will fetch last params[:limit] articles or 10 if limit is not provided. Let’s use it in our view:

  <%= part(ArticlesPart => :index, :limit => 5) %>

Such call will render list of last 5 articles.

The question is: how much lines of code does it take to implement something like that with ability to render views, layouts, :inline, use helpers, filters and much more? Over 100 lines of code including part() helper and a railtie (railtie is used to plug it into rails) – if you don’t believe me grab the repo and check it yourself.

How is it possible? Let’s look at the implementation of Parts::Base:

require 'parts/default_layout'

module Parts
  class Base < AbstractController::Base
    attr_reader :params

    include AbstractController::Layouts
    include AbstractController::Translation
    include ActionController::Helpers
    include AbstractController::Rendering
    include ActionController::ImplicitRender
    include DefaultLayout
    include AbstractController::Callbacks

    def initialize(controller, params)
      @params = controller.params.dup
      @params.merge!(params) unless params.empty?
      self.formats = controller.formats
    end

    def self.inherited(klass)
      super
      klass.helper :all
    end
  end
end

That’s all… ? Yes!

As you cans see Parts::Base inherits from AbstractController::Base, which gives it really basic functionality. Additionaly a few helpers are included to add a bit more behavior. The only mixin that I needed to create myself is Parts::DefaultLayouts which ensures that layout with the name of the part is rendered by default, unless :layout => false or there is no such layout in layouts directory.

Ok, that all is nice and dandy, but how does it work internally?

The implementation of such pattern can be demonstrated with such code:

class Foo
  def foo
    puts 'foo'
  end
end

module Bar
  def foo
    puts 'bar'
    super
  end
end

module Baz
  def foo
    puts 'baz'
    super
  end
end

class Omg < Foo
  include Bar
  include Baz

  def foo
    puts 'omg'
    super
  end
end

Omg.new.foo #=> omg
            #   baz
            #   bar
            #   foo

There are 2 modules (Bar and Baz) and 2 classes (Foo and Omg) implementing foo method here. Omg class inherits from Foo and additionaly modules Bar and Baz are included in Omg. This code takes advantage of ruby object model. How does it work?

When you instantinate Omg object with Omg.new and call method foo on it, it looks for foo method in current class and its superclasses. So first method that will be actually called is Omg#foo. This method is calling super, so ruby will look for method foo also in Omg’s superclass. At first you could think that it’s Foo, but internally Ruby treats modules as superclasses. That said, the next superclass will be the last included module, which is Baz. After that Bar’s and Foo’s methods will be invoked.

This one of the best patterns I have seen in Ruby so far. Not only it allows to extend objects easily, but also to reuse small chunks of code making the whole thing more modular. If Rails would stop at standard inheritance that we know from Java and other similar OO languages, creating something like parts would be much harder. In Parts::Base I used a few modules from AbstractController, but also a module from ActionController. With implementation without mixins, I would need to take all or nothing approach.

If you’re a Ruby developer, please think about that pattern while writing your next library. It can really help to make your code easier to extend and reuse. I was amazed how easy it was to implement all that stuff and I love the way that Rails is going!


If you liked this post consider following me on twitter.
blog comments powered by Disqus
Fork me on GitHub