List Presenter

In a typical Rails app, we have a lot of “index” action in almost every controllers. And in most cases, an index is a list of same type of objects. There are a lot of similar features we need to present these lists. So it makes sense to have a concept as ListPresenter and each individual presenter who presents a list should extend from it.

Of course, in Ruby, we don’t have to actually have a class called ListPresenter and do all the stuff that are not necessarily related to each other. We can have as many modules as we need. Each module does one particular task. and all presenters for list can mixin those modules.

Why it is good to do things this way? Let’s take an example: If all lists are HTML tables, and all tables have table headers with title of columns. Now we decide to use title case (such as ‘Column Name’) for these headers, we could manually type in these texts into your HTML template. The approach is straightforward, but bearing three potential problems.

  1. Since developers generally do not know how to type, one could easily have typos
  2. Two developers may develop two lists and one may think, wrongly, that title should be humanized (such as “Column name”). Then we end up with inconsistent UI. This inconsistency could also happen when a developer produced lists before the team decided title case. So he used humanized words back then. He had to come back fix them one by one, even with the best IDE ever – Windows notepad’s help, it still a tedious and boring task.
  3. Later on, the business doesn’t like title case, they want humanized case. Well, 4 boring hours, again!

In this case, a simple method can help, all it takes is a module which provides a html_table_columns method, as the following:

module HtmlTableColumns
  def html_table_columns(*args)
    self.send :define_method, :columns do
      returning([]) do |columns_hash|
        args.each do |arg|
          case arg
            when String, Symbol
              columns_hash << arg.to_s.titlecase})
            when Hash
              columns_hash <<
              raise"The params must be Hash, String or Symbols")

This method defines a <i>columns</i> for class who calls this method. A call like:

  table_columns('name', "last_connected"
  {'user_login' => "User"}, 'status')

will generate a method like this (in RSpec):

      presenter.columns.should ==
      [{'name' => 'Name'},
       {"last_connected"=>"Last Connected"},
        {'user_login' => "User"}, {'status' => "Status"}]

As long as every developer uses this method, there will be no typo, no inconsistency. And if people ever want to change from title case to humanized words, there is only one place that needs to be changed and I believe readers know where.

This is just an example how group similar features into one place is nice. In general I believe if I can find a feature that is across more than one places, it’s worth my effort to group them into one place, use whatever pattern that is applicable. So after all, I guess this post is not about List Presenter, it is about a case study of DRY.

~ by Yi Wen on October 28, 2008.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: