How to get up and running with the model_formatter plugin for Rails.

I’m in the middle of a very large project which I can’t talk too much about until it’s done, suffice to say we’ve got lots of database columns that need to be formatted/un-formatted. View helpers are great for formatting things on the way out, but what happens when users need to enter that data in the same format, or in an auto-detected format?

To begin, take our Widget example, with a column called weight. Weight is just an integer and we want it formatted all pretty, so we use a helper. But later we decide the user needs to enter data using the same format, so the controller accepts the input and can strip it, or we can throw that in the model too. Eventually though, we end up with a lot of the same code, un-formatting things in a model or controller that we’ve formatted already in the view. Seems a little un DRY to me…

Ideally, we’d have this code formatting stuff on the way into and out of a model, as with something like the code below:


class Widget < ActiveRecord::Base
  include ActionView::Helpers::NumberHelper

  def formatted_weight
    number_with_delimiter weight
  end

  def formatted_weight=(value)
    weight = value.nil? ? nil : value.gsub(/,/, '').to_i
  end
end

Ok, not so bad – we’ve got some basic stuff in place to format an attribute in the model and then something to accept formatted text, un-format it and set the weight attribute. But this is ruby, so we should be able to simplify this even further.

First, install the model_formatter:


script/plugin install -x \
  https://hattenschwetter.com/svn/hattenschwetter/rails_plugins/model_formatter/trunk/

Then we can change our Widget to the following:


class Widget < ActiveRecord::Base
  format_column :weight, :as => :integer
end

Cool, so we’re on our way to getting a much simpler way to stack up formats on all these columns in our Widget. Now, what happens when we want to change the way weight is formatted? Let’s pass some options.


format_column :weight, :as => :integer, :delimiter => '.'

Now, instead of formatting our weight with something like 1,425,321 we return 1.425.321. You can pass options like this to all the formatters (and custom formatters too, as you’ll see further down). For example, the :decimal formatter takes :precision and the :currency formatter just passes options on the number_to_currency – so all your freedom is preserved.

Each formatter is just a small class which extends Formatters::Format. The super class contains a very simple set of methods: initialize, from, and to. It names the formatters, by default, with the ‘formatted_’ prefix, but we can change that to something much shorter – like ‘fmt_’.


format_column :weight, :as => :integer, :prefix => 'fmt_'

Now we’ve got a fmt_weight reader and a fmt_weight= writer.

So far, all we’ve used are the included formatters in the Formatters module, which are conveniently resolved with the symbolized shortcuts. So for example, :as => :integer expands to Formatters::FormatInteger. Fortunately, we can provide any Formatters::Format code to model_formatter in the same way as the shortcuts:


format_column :weight,
  :as => Formatters::FormatInteger.new(:delimiter => '.')

So there it is, the simple way to format things coming out of and going back into the model. We can customize the model_formatter even more though. What if we wanted to provide our own formatting functionality? Easy – we can do this several different ways. Either provide a class like the above, or you can provide a Proc directly:


format_column :weight,
  :from => Proc.new {|value, options| number_with_delimiter(value) + 'lbs.'},
  :to => Proc.new {|str, option| str.gsub(/,/, '')}

Of course, we can always do the same thing with a block.


format_column :weight do
  def from(value, options = {})
    number_to_delimiter value
  end
  def to(str, options = {})
    str.gsub(/,/, '')
  end
end

This is only the start though – there are a few more methods that are generated from the methods above (besides the accessors) that can be of great help when accessing attributes.

More on this in Part 2.

1 Response to “Formatting data in Rails Models - Part 1”

  1. pendJeofe Says:

    Excellent forum with fantastic references and reading…. well done indeed… http://srubibablo.com Wow! Good resources here, Enjoyed the visit!

Leave a Reply