views:

145

answers:

4

I'd like to add descriptions to ActiveRecord model fields to serve as basic instructions / examples for each of the fields. Basically model metadata. I can then display these in the UI (next to the fields on a form etc.)

The way I'm planning to do it is simply create a static hashtable inside the model with the field name as the key and description as the value. I.e.

FIELD_DESCRIPTIONS = {
  'category' => 'Select the category it should appear within.',
  'title' => 'The title should be a short but descriptive summary.',
  'description' => 'Please enter a full description.'
}

etc.

Then I would create a a basic form helper that would wrap these explanations inside of a span (initially hidden and shown via jQuery) so they could be instatiated via f.field_description(:title) or something along those lines.

Anyone have any better ideas? I'd like to keep this field metadata in the model since many views could use the same information, and I also think it's nice to have descriptions within the model when you're going back to look at the code (like how DataMapper can be used right within the model to specify fields).

To give you a little more detail on what I've already done (and it works fine) here's the code. I think there has to be a prettier way of expressing these descriptions in the model, so let me know if you have any ideas.

In model:

FIELD_DESCRIPTIONS = {
  'category' => 'Select the category it should appear within.',
  'title' => 'The title should be a short but descriptive summary.',
  'description' => 'Please enter a full description.'
}

def self.describe_field(field)
  FIELD_DESCRIPTIONS[field]
end

In application_helper.rb

def field_helper(form, field)
  "<span class='field_helper'>#{form.object.class.describe_field(field)}</span>"
end

In view:

<%= field_helper(f, 'title') %>

This will produce the desired output:

<span class='field_helper'>The title should be a short but descriptive summary.</span>


UPDATE:

Ok So this is the final code I'm using based on the accepted answer.

File: /config/initializers/describe_attr.rb

if defined?(ActiveRecord)

  # let's us add attribute descriptions to each AR model
  class ActiveRecord::Base
    def self.describe_attr(*params)
      attrs = params.shift
      unless attrs.nil?
        case attrs
          when Hash
            @@attr_descriptions = attrs
          when Symbol
            return @@attr_descriptions[attrs]
        end
      end 
      @@attr_descriptions ||= {}
    end
  end

end

File: /app/models/project.rb

describe_attr(
    :category => 'Select the category the project should appear within.',
    :title => 'The title should be a short but descriptive summary of the project.',
    :description => 'Describe the project in detail.',
    :image => 'Upload an image for the project.'
)

File: /app/helpers/application_helper.rb

# assumes you have a style defined for attr_description
def describe_attr(form, attribute)
    "<span class='attr_description'>#{form.object.class.describe_attr(attribute)}</span>"
end

File: /app/views/projects/_form.html.erb

<%= describe_attr(f, :title) %>
+1  A: 

You can mix your solution with the label helper itself:

f.label :title, f.describe(:title)

And in your model:

FIELD_DESCRIPTIONS = {
  :category => 'Select the category it should appear within.',
  :title => 'The title should be a short but descriptive summary.',
  :description => 'Please enter a full description.'
}

def describe(:field)
  self.class::FIELD_DESCRIPTIONS[field]
end
khelll
That's a good idea, but I actually want the label and the description to be separate. The description is only going to show up once the user clicks a help icon link (via jquery). I'll just be writing a helper to handle this. Anyways, what I was really asking is whether anyone had a better idea that using a hashtable in the model to attach descriptions to it's properties. I.e. dynamically add object.field_description methods or something similar.
Dave Rapin
+4  A: 

The hash is a reasonable simple solution, but if you're on rails 2.2 or higher you might want to try the internationalization api to do this. This would also put you in a good place if you ever wanted to add translations.

Check out the i18n guide for details, but basically you would create a config/locales/en.yml that includes your column names like:

en:
  labels:
    category: Select the category it should appear within.

Then in your view:

<%= t('labels.category') %>

The namespace is your call of course. Also check out section 4.1.4 for a neat way to separate translations based on your current view template.

matschaffer
Unless it doesn't meet your needs for some reason (and I can't imagine why not), this is definitely the way to go. Plugins and gems that need to use these descriptions will look here.
James A. Rosen
This is a really slick solution. The only problem I have with this though, is that it removes the descriptions from the context of the model. Field descriptions can also serve as comments for the data that a model holds, and it would be much nicer to have them in the model itself. However, if I did ever need to support internationalization this is definitely the best way to solve it.
Dave Rapin
I feel your pain Dave, but I think something like http://agilewebdevelopment.com/plugins/annotate_models would be a better solution for an inline model attribute reference.
matschaffer
+1  A: 

If you want to patch ActiveRecord, then you can do something like:

# Add this at the bottom of enviroment.rb
class ActiveRecord::Base
  def self.field_description(*params)
    attrs = params.shift
    unless attrs.nil?
      case attrs
        when Hash
          @@field_description = attrs
        when Symbol
          return @@field_description[attrs]
        end
      end 
      @@field_description ||= {}
    end
end

And inside your model you can add this line like a macro:

class Product < ActiveRecord::Base

  field_description  :category => 'Select the category it should appear within.',:title => 'The title should be a short but descriptive summary.',:description => 'Please enter a full description.'

end

To get the value

Product.field_description : title
khelll
You nailed it. I really like this way of handling it. Feels cleaner than the hashtable constant in the model I was doing.
Dave Rapin
+1  A: 

Be sure to check out formtastic, which includes support for internationalized (or not) field labels as per matschaffer's answer.

http://github.com/justinfrench/formtastic/

sheldonh
This looks great. I'm going to play around with it. The *hint* attrib is basically what I was building.
Dave Rapin