My RailsConf Proposal

March 17, 2010

Though it’s probably a 1 in 1000 shot, I decided to throw my hat in the ring and pitch a proposal for speaking at RailsConf 2010. While I wouldn’t dare compare my career and achievements with the amazing speakers that are already lined up, I do think that I have some ideas worth sharing. I’ve posted my proposal below, what do you think? Would anyone be interested in hearing about this at one of the Chicago Ruby events?


Creating user-extensible Web applications is the next frontier for Rails developers. While Web 2.0 was all about user-generated data, the next generation of Web apps (Web 3.0?) will be all about user-generated functionality.

By enabling your power-users to extend your application’s functionality, you position your app as a platform upon which unique and meaningful experiences can be continually built.

Many Rails apps are already doing this successfully. Shopify was able to build itself as a platform by letting users use the Liquid templating system to build their own view layer. Apps that need to make data-level layers extensible (ex. user defined business rules) can give their users a custom DSL with which to configure the app.

In the work I have done with my current company I have done both, creating an extensible email marketing system that uses DSLs to define custom business logic and Liquid for building custom email templates. Through building this app, I have discovered a few significant pitfalls:

  1. DSLs can be difficult to build, maintain and keep user-friendly.
  2. Templating systems like Liquid allow for an extensible view, but relies on a strict set of pre-defined data nodes, tags and filters.
  3. System security is inherently at risk when introducing user extensions.
  4. Templating systems and custom DSLs both use their own distinct syntaxes and rules, creating significant barrier to entry for users.

I am currently building a hosting/CMS system that allows for user application extension at the model, controller AND view layers, and I am successfully solving all four of these problems with a single tool: user-generated, Ruby-executed Javascript.

  1. Javascript acts as a pre-built DSL. Users can use the familiar Javascript syntax to modify and extend the Rails app’s behavior.
  2. Javascript-powered templating allows the user to do more than simply use predefined tags and filters; they can even define and use their own custom Javascript helper functions to generate their views.
  3. Using a tool like the Johnson gem, user-generated Javascript is securely executed within an isolated runtime, and the Rails developer has full control over what data actually affects the stable Ruby environment.
  4. While the user does need to be able to understand and write Javascript, I believe this is both easier and more fruitful than asking users to learn custom DSLs and templating languages that are specific to the single application.

My goal with this talk would be to identify the benefits and challenges of allowing user-generated functionality, introduce Javascript as a viable solution and show specific code implementations using tools like Johnson and Erubis::Ejavascript for solving these problems.


Tracking Your Rdoc Coverage with RdocMetric

September 10, 2009

I was surprised when I didn’t find a Ruby gem that would analyze your code and give you a metric for how thorough your Rdoc documentation was. Since I want to keep myself accountable to keeping code documented, I whipped up the rdoc_metric gem. So far it is very simple, giving you percentages of how many of your class, module, method, attribute and constant declarations are documented using Rdoc.

Usage is simple, just cd into your application directory and use the rdoc_metric command:

cd ~/apps/my_app
rdoc_metric

Or, if you want to get specific and only get metrics for a specific directory of your app (say, your models):

cd ~/apps/my_app
rdoc_metric app/models

Download it here on GitHub


Delayed Method Chaining

July 14, 2009

Usually alias_method_chain does a great job when you need to monkey patch some code. One limitation, however, is that both the original method and the hack method must be defined before using alias_method_chain. This is rarely an issue, but it does come into play if you ever want to chain methods to an ActiveRecord column getter/setter. These methods are added on the fly, so you have no access to them within your model class.

Now you can simply use delayed_method_chain:

# has a :price column
class Product < ActiveRecord::Base
  require 'delayed_method_chain'
  delayed_method_chain :price=, :formatting
  # quick formatting
  def price_with_formatting=(amt)
    price_without_formatting = amt.to_s.gsub(/[^0-9\.]/, '').to_f.round(2)
  end
end

You can download the code, or alter the gist on GitHub if you have ideas on how to improve it!


Ruby Gems vs. Rails Plugins

May 24, 2009

A point of some confusion to a developer learning Ruby on Rails is the difference between using gems and plugins. They both offer out-of-the-box, third party functionality, and yet they have remarkable differences. Thanks to GitHub it’s very easy to write, host and share your own gems and plugins, so here is a quick review of their differences.

Rails Plugins

The Ruby on Rails framework comes with a specific architecture for loading plugins that can change and augment your application and the Rails framework itself. When your Rails app is loaded, this is the order in which code is loaded:

  1. The Rails framework is loaded (either from vendor/rails or the Rails gems).
  2. All plugins are loaded from vendor/plugins.
  3. Your application code in app and lib is loaded.

Because of this order, plugins can use code in the Rails framework, it can override the framework, and then it can be used within your application code.

Plugins must live in vendors/plugins, so the installed code will be localized in your code repository. The good news is that should you need to alter the plugin in any way, you can go in and hack away at the code without affecting your other apps. Unfortunately, your favorite plugin will have to be installed each time you create a new app (unless you’re using the new Rails Templates).

Rails plugins have the flexibility to hook into every part of Rails, including generators, rake tasks and tests/specs. They are specific to Rails, however, and could not be (directly) used with another Ruby framework (Merb, Sinatra, etc.). Personally, I think that any functionality that solely extends the Rails framework should always be a plugin rather than a gem, simply because that gem couldn’t be used outside of Rails anyways.

Ruby Gems

Gems are a facility of the Ruby language and have no dedicated connection to the Rails framework. Ruby gems are highly portable chunks of Ruby code that can be used inside any Ruby script or application. Thanks to its distributed architecture, one quick ‘gem install’ command can search RubyForge, GitHub and other repositories at the same time so that you don’t need to know where that gem lives to install it.

Gems are saved on your machine once and then included into applications using ‘require’. Naming conventions can be tricky, however, when gems are stored on GitHub or when the author did not stick to regular conventions. For example, this will install my iTermWindow gem from GitHub:

sudo gem install chrisjpowers-iterm_window

To use the gem in code, however, it looks like this:

require 'rubygems'
require 'iterm_window'

Regardless, RubyGems make it very easy to make functionality available in all your Ruby apps and scripts. It is important, though, to make sure that you have all the correct gems installed on all your development machines and staging/production servers, and that they all are using the same versions of the gems.

To avoid the problem of missing dependencies, Rails gives you a rake task to copy the gem files locally in your project in vendor/gems. Your gems will be saved in your repository and distributed along with your app when you use this task:

rake gem:unpack

Please note that this will not work for all gems — some gems have more complex C bindings and native extensions that cannot be unpacked into vendor.

Different Tools for Different Jobs

While Ruby gems and Rails plugins have similar effects in your application, they obviously vary significantly in their architecture. Ultimately, each will prove more or less useful in a given scenario. As I mentioned before, my rule of thumb is to make Rails-specific functionality a plugin while making more general Ruby libraries into gems.

So now that you know the differences, get onto GitHub and start sharing some code!


DRY Up Forms with Custom Form Builders

November 21, 2008

Revisiting Our Form

Continuing with the example from my last article, I have this simple form:

<% form_for :product, :url => products_path do |f| %>

  <div class="form_item text_field">
    <label for="product[name]">Name:</label>
    <%= f.text_field :name %>

  </div>
  <div class="form_item text_field">
    <label for="product[price]">Price:</label>
    <%= f.text_field :price %>

  </div>
  <div class="form_item text_field">
    <label for="product[features]">Features (1 per line):</label>
    <%= f.text_area :features %>

  </div>
  <div class="form_item submit_button">
    <%= f.submit "Create Product" %>
  </div>

<% end %>

Note: I changed the original form’s wrapper tags from <p> to <div>. The reason is that, if you’re using the built-in Rails error message helpers, by default it will wrap erroneous fields in <div> tags. Since <div> tags can’t nest within <p> tags, this can cause serious layout-breaking problems.

There’s a lot of duplication here in terms of the wrapper code. Each field is wrapped in a <div> with a class of form_item as well as a descriptor of what kind of field it contains. I use this information for CSS styling of specific kinds of inputs. The labels are also fairly repetitive as well, and these repetitions would become more obnoxious were this a longer form.

Removing Repetition

What if we could generate this same form code while removing the repetition? It’s possible, and Rails gives us a great hook for just this scenario by allowing us to build custom Form Builders. A custom Form Builder is simply a subclass of ActionView::Helpers::FormBuilder that can alter and extend the abilities of the regular Form Builder. In our current form, the f block variable is an instance of FormBuilder, so methods like text_field and submit are all instance methods of the FormBuilder class. Let’s override these methods to not only output the field markup, but to also output the wrapper div:

# in /helpers/application_helper.rb
class WrapperFormBuilder < ActionView::Helpers::FormBuilder
  METHODS_TO_OVERRIDE = %w{text_field text_area password_field file_field date_select datetime_select submit}

  METHODS_TO_OVERRIDE.each do |method_name|
    src =<<END_SRC

      def #{method_name}_with_wrapper(field, options={})
        # allow explicit setting of label text with options[:label]
        field_label = if '#{method_name}' == 'submit'
          '' # no label for submit inputs
        elsif options[:label]
          label(field, options.delete(:label))
        else
          label(field) + ":" # Adds colon as default
        end

        # get unwrapped field
        field_markup = #{method_name}_without_wrapper(field, options)

        # return wrapped field (@template gives us access to helper methods in this class)
        @template.content_tag(:div, field_label + field_markup, :class => "form_item #{method_name}")
      end
    END_SRC

    class_eval src, __FILE__, __LINE__
    alias_method_chain method_name.to_sym, :wrapper

  end

end

For our form, we can now implement our new class like this:

<% form_for :product, :url => products_path, :builder => WrapperFormBuilder do |f| %>

Another possibility is to create a new method to replace form_for:

# in helpers/application_helper.rb
def wrapper_form_for(name, object=nil, options={}, &proc)

  form_for(name, object, options.merge(:builder => WrapperFormBuilder), &proc)

end

Using this new helper method, our form now looks like this:

<% wrapper_form_for :product, :url => products_path do |f| %>

  <%= f.text_field :name %>
  <%= f.text_field :price %>

  <%= f.text_area :features, :label => "Features (1 per line):" %>

  <%= f.submit "Create Product" %>
<% end %>

If you need to create a form field without a wrapper (perhaps to use a non-conforming wrapper), you can still access the original methods like f.text_field_without_wrapper or f.submit_without_wrapper.

I have found custom Form Builders to be powerful tools for speeding up form development, DRYing up code and keeping consistency between forms and developers. This is a fairly basic example, but the sky is the limit for what you can implement using these techniques.


Pretty Data, Pretty Code

November 14, 2008

In my last article I wrote about using data modeling to clean up form-related code and to take advantage of powerful helpers like form_for and error_messages_for. This solves the significant problem of isolating business logic into a model class, but another problem remains — how can we make our form data pretty without trashing our model’s code with view logic?

Beautifying the Data

I have a simple Product model with rows for name, price and features. Setting and displaying the price field is tricky because I need to remove currency formatting before storing it in the database as a decimal, and I want to reformat it when displaying the current price in an ‘Edit’ form. The features field is also tricky. I want the user to enter each product feature (“Slices and Dices”, “Purees Anything”, etc.) on a separate line of the textarea and for that to be split into a serialized array that I will store in the database.

My first instinct is to create virtual attributes in my model to handle the logic of deformatting/reformatting this data. Here’s how it looks:

class Product < ActiveRecord::Base
  serialize :features, Array

  validates_presence_of :name
  validates_numericality_of :price

  # need this for formatting
  def helpers

    ActionController::Base.helpers
  end

  def price_field=(p)

    # expecting p to be something like "$4,000.00", set price to 4000.00
    p.gsub!(/[^0-9.]/, '')
    self.price = p.to_f

  end

  def price_field
    helpers.number_to_currency(self.price)

  end

  def features_field=(str)
    # expecting string with features separated by newlines
    self.features = str.split("n").collect {|f| f.strip }

  end

  def features_field
    self.features.join("n")

  end
end
<%# the product form %>
<% form_for :product, url => products_path do |f| %>

  <p class="form_item text_field">
    <label for="product[name]">Name:</label>
    <%= f.text_field :name %>

  </p>
  <p class="form_item text_field">
    <label for="product[price_field]">Price:</label>
    <%= f.text_field :price_field %>

  </p>
  <p class="form_item text_field">
    <label for="product[features_field]">Features (1 per line):</label>
    <%= f.text_area :features_field %>

  </p>
  <p class="form_item submit_button">
    <%= f.submit "Create Product" %>
  </p>

<% end %>

The good news is that we have a very simple, straightforward looking view with no logic stuffed in it. Our controller is also completely vanilla, so I didn’t bother to even show it.

Our model, however, is getting cluttered. What once was a haven for business logic is now filled with both business and view logic. I’m also a little concerned about having to use the #price_field and #features_field methods, since it adds complexity to what should be a simple object API. Will this cause confusion with my fellow programmers?

Beautifying the Code

What if we extracted the view logic into its own object? By using a presenter object as an adapter between our Product model and our form we can isolate the view logic from the business logic. Here’s how it looks:

class Product < ActiveRecord::Base

  serialize :features, Array

  validates_presence_of :name
  validates_numericality_of :price

end
class ProductPresenter
  attr_reader :product

  def initialize(product)

    @product = product
  end

  # need this for formatting
  def helpers

    ActionController::Base.helpers
  end

  # for mass assignment
  def attributes=(hash)

    hash.each_pair do |key, val|
      self.send("#{key}=".to_sym, val)

    end
  end

  def price=(p)
    # expecting p to be something like "$4,000.00", set price to 4000.00

    p.gsub!(/[^0-9.]/, '')
    @product.price = p.to_f

  end

  def price
    helpers.number_to_currency(@product.price)

  end

  def features=(str)
    # expecting string with features separated by newlines
    @product.features = str.split("n").collect {|f| f.strip }

  end

  def features
    @product.features.join("n")

  end

  # proxy all other methods to @product
  def method_missing(method_name, *args, &block)

    @product.send(method_name, *args, &block)
  end

end
class ProductsController < ApplicationController
  def new
    @product = ProductPresenter.new(Product.new)

  end

  def edit
    product = Product.find(params[:id])

    @product = ProductPresenter.new(product)
  end

  def create

    @product = ProductPresenter.new(Product.new)
    @product.attributes = params[:product]

    if @product.save
      # success
    else
      render :action => 'new'

    end
  end

  def update
    p = Product.find(params[:id])

    @product = ProductPresenter.new(p)
    @product.attributes = params[:product]

    if @product.save
      # success
    else
      render :action => 'new'

    end
  end
end
<%# the product form %>
<% form_for :product, url => products_path do |f| %>

  <p class="form_item text_field">
    <label for="product[name]">Name:</label>
    <%= f.text_field :name %>

  </p>
  <p class="form_item text_field">
    <label for="product[price]">Price:</label>
    <%= f.text_field :price %>

  </p>
  <p class="form_item text_field">
    <label for="product[features]">Features (1 per line):</label>
    <%= f.text_area :features %>

  </p>
  <p class="form_item submit_button">
    <%= f.submit "Create Product" %>
  </p>

<% end %>

Our business and view logic have been effectively separated, our form code is clearer and the controller is only slightly more complicated. Because the ProductPresenter is acting as a proxy object to its Product object, we can simply treat it like a product thanks to ‘duck typing’. While this example is a little contrived, using presenter classes can make or break your code base as you create complex model objects with equally complex visual representations.


Modeling All Form Data

November 7, 2008

Forms of Every Shape and Color

As the Web continues to develop as an interactive, read/write medium, web forms are more useful and necessary than ever. Historically speaking, forms tend to be difficult for developers — validation, error handling, field population, redirection, data modeling and storage, these all play a part in even the simplest forms.

In the Rails world, our lives were made significantly easier thanks to the form_for helper. With this helper method, the Rails developer gets a lot for free thanks to its interaction with the ActiveRecord object that is being manipulated. Between ActiveRecord validation and the intelligent field population of form_for, creating forms becomes nearly trivial.

But what about the other forms, those that are not directly manipulating an ActiveRecord object? Forms like ‘Log In’ and ‘Search’ forms? If the developer is not using the form to CRUD an ActiveRecord model instance, he suddenly finds himself back to using low level helpers like text_field_tag and enforcing validation and other business logic within the controller.

But There’s a Better Way!

All data that we receive from forms should be modeled, but we don’t always have to use an ActiveRecord model class to do it. For data that needs to be modeled but not stored in the database, we can simply use a plain ol’ Ruby class. Let’s say that we want to create a login form – it will have fields for email and password, and a checkbox for “Remember Me”. We can model this data like so:

<!–

# app/models/login.rb
class Login
  attr_reader :email, :password, :remember_me
  def initialize(params = {})
    @email = params[:email]
    @password = params[:password]
    @remember_me = params[:remember_me]
  end

  def authenticate
    # tie into User model for authentication
    user = User.authenticate(self.email, self.password)
    return user
  end
end

–>

# app/models/login.rb
class Login
  attr_reader :email, :password, :remember_me

  def initialize(params = {})
    @email = params[:email]

    @password = params[:password]
    @remember_me = params[:remember_me]

  end

  def authenticate
    # tie into User model for authentication
    user = User.authenticate(self.email, self.password)

    return user
  end
end

<!–

# app/controllers/sessions_controller.rb
class SessionsController  'new' # unsuccessful login
    end
  end
end

–>

# app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
  def new
    @login = Login.new

  end

  def create
    @login = Login.new(params[:login])

    if current_user = @login.authenticate
      redirect_to successful_login_path
    else

      render :action => 'new' # unsuccessful login
    end
  end

end

<!–

<%# app/views/sessions/new %>
<% form_for :login, :url => sessions_path do |f| %>
  <p class="form_item">
    <label for="login[email]">Email</label>
    <%= f.text_field :email %>
  </p>
  <p class="form_item">
    <label for="login[password]">Password</label>
    <%= f.password_field :password %>
  </p>
  <p class="form_item">
    <label for="login[remember_me]">Remember Me</label>
    <%= f.check_box :remember_me %>
  </p>
  <p class="form_item submit_button">
    <%= f.submit "Login" %>
  </p>
<% end %>

–>

<%# app/views/sessions/new %>
<% form_for :login, :url => sessions_path do |f| %>

  <p class="form_item">
    <label for="login[email]">Email</label>
    <%= f.text_field :email %>

  </p>
  <p class="form_item">
    <label for="login[password]">Password</label>
    <%= f.password_field :password %>

  </p>
  <p class="form_item">
    <label for="login[remember_me]">Remember Me</label>
    <%= f.check_box :remember_me %>

  </p>
  <p class="form_item submit_button">
    <%= f.submit "Login" %>
  </p>

<% end %>

A Dash of Validation

The good news is that we have modeled the login data and now have a place other than the controller for login-specific logic. We are still missing validation, however. If our Login class doesn’t inherit from ActiveRecord, does that mean we need to roll our own validation methods?

Thankfully Jay Fields already did the work for us by creating the Validatable Ruby gem (sudo gem install validatable). The Validatable module can be mixed into any Ruby class and provides the same validation interface that we are accustomed to using with ActiveRecord. Now we can require both the email and password to be filled out before we even try attempt a login. Here’s our updated code:

<!–

# app/models/login.rb
class Login
  include Validatable

  validates_presence_of :email, :password

  attr_reader :email, :password, :remember_me
  def initialize(params = {})
    @email = params[:email]
    @password = params[:password]
    @remember_me = params[:remember_me]
  end

  def authenticate
    # tie into User model for authentication
    unless user = User.authenticate(self.email, self.password)
      self.errors.add(:base, "No user was found with that email and password.")
    end
    return user
  end
end

–>

# app/models/login.rb
class Login
  include Validatable

  validates_presence_of :email, :password

  attr_reader :email, :password, :remember_me
  def initialize(params = {})

    @email = params[:email]
    @password = params[:password]

    @remember_me = params[:remember_me]
  end

  def authenticate

    # tie into User model for authentication
    unless user = User.authenticate(self.email, self.password)

      self.errors.add(:base, "No user was found with that email and password.")
    end
    return user

  end
end

<!–

# app/controllers/sessions_controller.rb
class SessionsController  'new' # unsuccessful login
    end
  end
end

–>

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController

  def new
    @login = Login.new
  end

  def create

    @login = Login.new(params[:login])
    if @login.valid? && current_user = @login.authenticate

      redirect_to successful_login_path
    else
      render :action => 'new' # unsuccessful login

    end
  end
end

<!–

<%# app/views/sessions/new %>
<%= error_messages_for :login %>
<% form_for :login, :url => sessions_path do |f| %>
  <p class="form_item">
    <label for="login[email]">Email</label>
    <%= f.text_field :email %>
  </p>
  <p class="form_item">
    <label for="login[password]">Password</label>
    <%= f.password_field :password %>
  </p>
  <p class="form_item">
    <label for="login[remember_me]">Remember Me</label>
    <%= f.check_box :remember_me %>
  </p>
  <p class="form_item submit_button">
    <%= f.submit "Login" %>
  </p>
<% end %>

–>

<%# app/views/sessions/new %>

<%= error_messages_for :login %>
<% form_for :login, :url => sessions_path do |f| %>

  <p class="form_item">
    <label for="login[email]">Email</label>
    <%= f.text_field :email %>

  </p>
  <p class="form_item">
    <label for="login[password]">Password</label>
    <%= f.password_field :password %>

  </p>
  <p class="form_item">
    <label for="login[remember_me]">Remember Me</label>
    <%= f.check_box :remember_me %>

  </p>
  <p class="form_item submit_button">
    <%= f.submit "Login" %>
  </p>

<% end %>

Our Login class now can use the familiar validates_* class methods, the valid? instance method and all the facilities that come with having the errors object (like using error_messages_for).

It also becomes very easy to set default values for the login form. For example, if we wanted to ‘check’ the Remember Me box by default, we could simply change our SessionsController#new action to this:

def new
  @login = Login.new(:remember_me => true)

end

Modeling our form data in this way is a win-win scenario — we’re using good coding practices by keeping business logic out of our controller, and we’re also inheriting all the facilities that Rails provides by leveraging form_for, error_messages_for and validations.


Monkey Patching 101 – Proxy Objects

August 8, 2008

I think the most significant strength of the Ruby language is its impressive power and flexibility in metaprogramming. Affectionately dubbed “Monkey Patching” by Ruby developers, metaprogramming makes it easy to ‘hack’ existing code and frameworks like Rails for infinite customization, while making it easy to keep these hacks legible, organized and maintainable.

A powerful programming concept that Ruby makes easy to implement is the use of Proxy Objects. Since Ruby implements duck typing, a proxy object can easily step in to take the place of a regular object so long as the two share a similar object interface.

The following tutorial is the extraction of a “real world” problem that was solved with the simple implementation of a few proxy objects.

The Problem

Let’s say that we are displaying information about a collection of people in our system, and we want to display min, max and averages for the attributes of the members of that collection. An immediate solution would be to clutter up my template with code to find the statistic values, but this gets messy and puts too much logic in my view. Using some kind of helper methods would help extract the logic from the view, which would be better.

That’s still not quite what I want, though. What I would really like is to have an object that acts just like a Person, but that gives me values based on a collection of Person objects rather than just one. A nice implementation would be:

@john.age #=> 22
@jim.age #=> 48

@susan.age #=> 20
avg = AveragePerson.new([@john, @jim, @susan])

avg.age #=> 30

This approach is very strong if, for example, you’re creating a table by iterating through a collection of people. Thanks to duck typing, an AveragePerson could be slipped into a collection of Person objects and the table would be none the wiser — the average data would be displayed just the same as a single person’s data.

Creating Some Models

First, let’s whip up some quick model classes. I will simply use new Ruby classes, but the following techniques can be used with ActiveRecord classes in Rails just as easily.

class Person
  attr_accessor :name, :age, :inches, :weight

  def initialize(n, a, i, w)

    @name = n
    @age = a
    @inches = i

    @weight = w
  end
end

class AveragePerson
  def initialize(col)

    @collection = col
  end
end

Writing Some Tests

Let’s write some RSpec tests to set some goals for our AveragePerson class:

require 'rubygems'
require 'spec'

describe "People Statistics" do
  before(:each) do
    @james = Person.new("James", 23, 74, 210)

    @cheryl = Person.new("Cheryl", 47, 63, 115)

    @timmy = Person.new("Timmy", 12, 55, 87)

    @people = [@james, @cheryl, @timmy]
  end

  describe "AveragePerson" do
    before(:each) do
      @average = AveragePerson.new(@people)

    end

    it "should give the average age" do
      @average.age.should eql((@james.age + @timmy.age + @cheryl.age) / 3)

    end

    it "should give the average weight" do
      @average.weight.should eql((@james.weight + @timmy.weight + @cheryl.weight) / 3)

    end

    it "should give the average inches" do
      @average.inches.should eql((@james.inches + @timmy.inches + @cheryl.inches) / 3)

    end

    it "should be named 'Average'" do
      @average.name.should eql("Average")

    end
  end
end

Now let’s get these to pass.

A Little Proxy Magic

Per our specs, we need to add #age, #weight, #inches and #name methods to AveragePerson. This would do the trick, but it’s not DRY:

class AveragePerson
  def name
    "Average"
  end  

  def age

    total = @collection.inject(0){ |sum, person| sum += person.age }

    total / @collection.length
  end

  def weight

    total = @collection.inject(0){ |sum, person| sum += person.weight }

    total / @collection.length
  end

  def inches

    total = @collection.inject(0){ |sum, person| sum += person.inches }

    total / @collection.length
  end
end

The tests should pass now, but we have some serious repetition and need to DRY this up. It’s also not extensible — any time a method is added to Person, another would need to be added to AveragePerson.

All we’re really doing here is proxying the method that AveragePerson receives to each member of the collection, so let’s use method_missing to do this more concisely and extensibly.

class AveragePerson
  def name

    "Average"
  end  

  # proxy methods to collection, return the average of results
  def method_missing(method_name, *args, &block)

    total = @collection.inject(0){ |sum, person| sum += person.send(method_name, *args, &block) }

    total / @collection.length
  end
end

Now AveragePerson will proxy any methods it doesn’t have to its collection, add up the results and return the average. The tests still pass and we have much cleaner code.

Adding in Height

It isn’t very helpful to just tell a user how many inches tall a person is, it would be much more useful to return a string like “5ft 8in”. Let’s integrate that into the Person model and write a test for AveragePerson:

class Person
  def height

    inches_to_height(self.inches)
  end

  private

  def inches_to_height(_inches)

    ft = _inches / 12
    ins = _inches % 12

    "#{ft}ft #{ins}in"
  end
end

###

describe "AveragePerson" do

  it "should give the average height" do
    @average.height.should eql("5ft 4in")

  end
end

Though our AveragePerson object is proxying the #height method to the collection, this test crashes and burns because our #method_missing assumes that each member will return a number, not a string like “6ft 0in”. To fix this, let’s “override” AveragePerson#height.

class AveragePerson
  def height
    ft = self.inches / 12

    ins = self.inches % 12
    "#{ft}ft #{ins}in"

  end
end

The tests pass, but we still have some refactoring to do. I’ve duplicated the height display logic from Person into AveragePerson, so let’s extract this out into a module and include it in both classes:

module PersonHelper

  def height
    inches_to_height(self.inches)
  end

  def inches_to_height(_inches)

    ft = _inches / 12
    ins = _inches % 12

    "#{ft}ft #{ins}in"
  end
end

class Person
  include PersonHelper

end

class AveragePerson
  include PersonHelper
end

I can also include PersonHelper into my RSpec tests so that I can make them more legible using the #inches_to_height helper method:

describe "AveragePerson" do
  include PersonHelper
  it "should give the average height" do

    # @average.height.should eql("5ft 4in")
    @average.height.should eql(inches_to_height((@james.inches + @timmy.inches + @cheryl.inches) / 3))

  end
end

Creating Max and Min Classes

Using the same proxy techniques, we can easily build out MaxPerson and MinPerson classes. First the tests:

describe "MaxPerson" do

  before(:each) do
    @max = MaxPerson.new(@people)

  end

  it "should give the max age" do
    @max.age.should eql(@cheryl.age)

  end

  it "should give the max weight" do
    @max.weight.should eql(@james.weight)

  end

  it "should give the max inches" do
    @max.inches.should eql(@james.inches)

  end

  it "should be named 'Max'" do
    @max.name.should eql("Max")

  end

  it "should give the max height" do
    @max.height.should eql(inches_to_height(@james.inches))

  end

end

describe "MinPerson" do

  before(:each) do

    @min = MinPerson.new(@people)
  end

  it "should give the min age" do

    @min.age.should eql(@timmy.age)
  end

  it "should give the min weight" do
    @min.weight.should eql(@timmy.weight)

  end

  it "should give the min inches" do
    @min.inches.should eql(@timmy.inches)

  end

  it "should be named 'Min'" do
    pending("Needs to be implemented") do

      @min.name.should eql("Min")
    end
  end

  it "should give the min height" do
    @min.height.should eql(inches_to_height(@timmy.inches))

  end
end

And here are the classes:

class MaxPerson
  include PersonHelper

  def initialize(col)
    @collection = col
  end

  def name
    "Max"
  end

  def method_missing(method_name, *args, &block)

    @collection.inject(0) do |highest, person|
      val = person.send(method_name, *args, &block)

      highest = (val > highest) ? val : highest

    end
  end
end

class MinPerson
  include PersonHelper

  def initialize(col)
    @collection = col
  end

  def name
    "Min"
  end

  def method_missing(method_name, *args, &block)

    # assumes at least one item in the collection will return a value less than 1000000
    @collection.inject(1000000) do |lowest, person|
      val = person.send(method_name, *args, &block)

      lowest = (val < lowest) ? val : lowest

    end
  end
end

More Options with Extend

Our proxy objects are working nicely, but what if we want to access our objects a different way? Maybe it would be slicker if the collection itself had methods to instantiate these objects rather than simply instantiating them explicitly. Let’s do something like this:

@people = [@john, @jim, @susan]
@people.min_person #=> MinPerson object

@people.max_person #=> MaxPerson object
@people.avg_person #=> AveragePerson object

Here is a new describe block of tests:

describe "PeopleCollection" do
  it "should return a MinPerson" do

    @people.min_person.class.should eql(MinPerson)
  end

  it "should return a MaxPerson" do
    @people.max_person.class.should eql(MaxPerson)

  end

  it "should return an AveragePerson" do
    @people.avg_person.class.should eql(AveragePerson)

  end
end

Let’s create a module that will define these collection methods:

module PeopleCollection
  def min_person

    MinPerson.new(self)
  end

  def max_person
    MaxPerson.new(self)

  end

  def avg_person
    AveragePerson.new(self)
  end

end

One way to apply this module so that our collection will have access to these methods is to open up the Array class and include the module:

class Array

  include PeopleCollection
end

# or you can use the send hack:
# Array.send :include, PeopleCollection

I’m not sure this is what I want, though. The tests passs, but I don’t really need (or want) every single array in my app to have these methods — at best it’s sloppy, at worst I could run into conflicts. I really only want my @people collection to have this functionality, so instead I will extend that instance with my module only when I need it. Here’s the updated describe test block:

describe "PeopleCollection" do
  before(:each) do
    @people.extend PeopleCollection

  end

  it "should return a MinPerson" do
    @people.min_person.class.should eql(MinPerson)

  end

  it "should return a MaxPerson" do
    @people.max_person.class.should eql(MaxPerson)

  end

  it "should return an AveragePerson" do
    @people.avg_person.class.should eql(AveragePerson)

  end
end

I have found this to be a helpful technique when you just need to add a little extra functionality to a single object and want to keep it lightweight.

Here is the final version of the script.


Best Practices for Partials

March 18, 2008

A Problem with Partials

Partials are incredibly powerful and flexible tools for a Rails developer; they help to DRY up view code, contextualize distinct presentational ideas and modularize markup for use in HTML or Javascript. It’s been my experience, however, that each developer seems to use partials in a different way and, while I appreciate the value of personal coding style, these variations can cause confusion between developers.

The following techniques have proven to be ‘best partial practices’ for my fellow developers and myself. Adhering to these ideas helps us to keep our code consistent, legible and portable.

Keeping Organized

Organization and naming conventions are two initial hurdles in developing effective partials. It is an easy mistake to start arbitrarily organizing and naming partial files without thinking through their roles within the application.

I think that one pitfall is using (or at least over-using) a /views/shared directory for storing partials that will be used by many controllers. I used to use the /shared folder after seeing it used in the prominent Rails books and tutorials, and initially the logic of such a directory makes sense (especially before REST became the norm). That logic breaks down, however, when virtually ALL of your partials exist in that folder! If partials are meant to be reusable and flexible, shouldn’t they all be in the shared folder? This is what would happen to me, and suddenly my file names lost a lot of meaning. What does the partial shared/thumbnail show? What about shared/favorite?

Certainly you can simply use more concise naming (user_thumbnail, favorite_video) but I have found that it’s best to place these partials in the view folder of the related resource. There’s nothing magical about the /shared directory, it’s just as easy to use :partial => 'users/thumbnail' as it is to use :partial => 'shared/user_thumbnail'. I find storing the partial in /views/users, /views/videos, etc. helps to clarify the partial’s function and keeps my files in order.

Another good practice is utilizing singular and plural naming conventions. In a recent project I was using two partials with poor names: shared/user_block and shared/user_thumb. The user_block partial accepted an array of Users as the :object and passed it on as :collection to the user_thumb partial, placing it all in a wrapper. I found that a name like user_block doesn’t help me remember what the partial does – does it display a block of users, or one user in a block? By renaming my partials as users/thumbs and user/thumb, respectively, I was able to clarify their purposes and make it obvious which would display a single thumb versus a collection of them.

One other pitfall is mixing up where wrapper HTML is stored. A sidebar, for example, may have a series of modules that all use a common wrapper and header markup. Some of these modules are stored in their own partials, so should the wrapper markup go inside the partial? Or should that markup stay in the view file and wrap the render method call? Either might be appropriate given any set of circumstances, but the most important thing to remember is to keep it consistent! Placing the wrapper in one partial but leaving it out of another can only lead to confusion down the road.

No Partial is an Island… or is it?

In software development modular design is king. The ability to create portable, flexible and self-sufficient chunks of code significantly helps speed up development and code reuse. So why not apply the same principles to partials? Partials, like any other code, can become much more robust and useful when given good defaults, error handling and documentation.

If a partial were a method then :object, :collection and :locals would be the arguments. Unlike arguments, however, there is no built-in way to ensure that these passed variables are valid or even present. It is simple, however, to mimic this functionality within the RHTML template. For example, the following partial displays a series of thumbnails for recently added videos (the partial name is videos/recent_thumbs):

  <% limit ||= 10 %>
  <% videos = recent_thumbs || Video.find_recent_videos(limit) %>

  <div id="recent_video_thumbs">
    <%= render :partial => 'videos/thumb', :collection => videos %>

  </div>

Default values are supplied for each variable that can be passed in to the render method. The number of videos displayed (limit) can be passed in through the :locals hash, otherwise it defaults to 10. The :object parameter, which reads as recent_thumbs within the partial, will be used if given; otherwise, the partial uses the Video class to find the most recent videos. This partial can be passed some, all or none of those parameters and will still work correctly.

But wait! Isn’t this placing application logic in the view?! Isn’t this strictly taboo?! Though it may approach that line, I believe this is still clean code. The actual logic is still stored within the Video#find_recent_videos method and this behavior is only the fallback behavior, not the primary functionality. In this situation it works, but that may not always be the case. If a fallback collection would not be available, proper error handling should be used instead:

<% raise ArgumentError, "The videos/recent_thumbs partial requires an array
of Video objects passed as :object." unless recent_thumbs %>

Using these techniques achieves two goals. First, the developers are given more flexibility and less trouble with partials that can 'take care of themselves.' These partials can be easily dropped into a view and rely on defaults that can later be easily overridden. The developers are also given some 'documentation' about how the partial works. It may not be rDoc, but these variable 'declarations' at the top of a partial make it very clear what :locals it takes and whether it takes an :object and/or a :collection.

With a Little Help From My Friends

When passing a collection to a partial, I have often found the "collection_name_counter" variable to be very helpful (ie the thumb_counter in :partial => 'thumb'). This dynamic variable, built-in by Rails, gives the current index within the passed collection. As helpful as that can be, I usually find it to be incomplete without having access to the collection itself. Without the collection array I have no idea if the current index is the last item, the first item or any in between!

For a while I simply passed in the collection both as :collection as well as :locals => {:collection => @collection}. It worked, but it wasn't very DRY. To fix this I created a simple monkey patch module called PartialEnhancer. This hacks the partial code so that you have access to two more dynamic variables. If the partial were named "thumb" the variables would be thumbs and thumbs_count. The thumb_count variable simply stores the length of the collection for comparison with thumb_counter while the thumbs plural variable accesses the collection array directly. It's now very simple to find where a given object is within the overall collection within a partial.

Another partial-related problem struck me when I began working on a project that used a great number of nested partials within nested layouts. Whenever I had to change some view markup it was often very difficult to figure out where that markup was! Which stacked partial, layout or view did it belong to? To help me find my way I created another monkey patch module called PartialExposure. This module hacks the render method so that every layout and partial is wrapped in an HTML comment. Each comment displays the filename and path of each partial file and reveals exactly where it starts and stops. Thanks to that module I can now instantly figure out where display code exists, even on projects that I am not familiar with. Just make sure you only use it in Development!

Download the PartialEnhancer and PartialExposure files.


Follow

Get every new post delivered to your Inbox.