Rails magic form helper – create field based on type

2009 March 21
tags: ,
by Nick

The following is a helper that adds onto the regular form_for helper within rails. The script below adds methods onto the default form_for to generate fields automatically based on the type of the field. If you have a really basic model you can also generate the entire form based on the data type of each column.

# put this in lib/magic_form.rb
class MagicForm < ActionView::Helpers::FormBuilder
  def input_fields(opts = {})
    object = @object.class
    columns = object.columns.delete_if {|key,value| opts[:exclude] && opts[:exclude].include?(key.name) || key.name == "id" }
    columns.collect {|o| input_for(o.name)}
  end
 
  def input_for(field, opts = {})
    field = field.to_s
    field_label = self.label(field, opts[:label])
    type = @object.class.columns_hash[field].type.to_s
 
    if field.include?("_id")
      field = self.collection_select field, field.gsub("_id","").camelize.constantize.all, opts[:option_id] ? opts[:options_id] : :id, opts[:option_label] ? opts[:option_label] : :name
    else
      field = case type
        when "string", "integer", "float"
          self.text_field field, opts
        when "boolean"
          self.check_box field, opts
        when "datetime"
          self.datetime_select field, opts
        when "date"
          self.date_select field, opts
        when "time"
          self.time_select field, opts
        when "text"
          self.text_area field, opts
        when "binary"
          self.file_field field, opts
        eles
          self.text_field field, opts
        end
    end
 
    if type == "boolean"
      "<p>\n\t#{field}\n\t#{field_label}\n</p>\n"
    else
      "<p>\n\t#{field_label}\n\t<br />\n\t#{field}\n</p>\n"
    end
  end
end
 
# Example usage
# app/admin/views/listings/new.html.haml
%h2 New listing
= render :partial => "form", :locals =&gt; {:listing => @listing}
 
# app/models/listing.rb
class Listing < ActiveRecord::Base
  has_one :status
end
 
# app/models/status.rb
class Status < ActiveRecord::Base
  validates_presence_of :name
  belongs_to :listing
end
 
# app/admin/views/llistings/_form.html.haml
# single field usage
# f.input_for :price prints out the label along with the proper field. integer, string, float is a
# text field, text is a text area, boolean is a checkbox, date field is a date selector, datetime
# is date time selector, etc.
# f.input_for :status_id makes a label with a select field and populates the select input
# with all of the options from the status table.  The way that the helper determines the
# associated model's data could probably be improved upon, I'm sure there's a better way.
- form_for ([:admin, @listing]), :builder =&gt; MagicForm  do |f|
  = f.input_for :price
  = f.input_for :status_id
  = submit_tag "Create new listing"
 
# app/admin/views/listings/_form.html.haml
# complete form maker
# f.input_fields will make the entire form for you from Model.columns (excluding id, didn't
# want to use content_columns since that strips out all fields that have _id).  This probably
# won't be used much but it's here for really basic models where nothing too complicated is going on.
- form_for ([:admin, @listing]), :builder => MagicForm  do |f|
  = f.input_fields, :exclude => ['created_at']
  = submit_tag "Create new listing"
 
# the helper doesn't support has_many associations just has_one associations.
# This is meant to be used as a replacement for most of the simple cases for
# administering content, not for complex forms or associations.

This form builder was inspired by Merb’s control_for method which I can no longer find documentation on. Hopefully something similar will make it into Rails 3.

Let me know if you have tips on how to add on or improve the script or if you find it useful.

4 Responses leave one →
  1. August 13, 2009

    I was thinking about doing something similar but a little bit more complex. Now I have decided to change my strategy and keeping it much more simple and go your way. I have also non-model fields that I want to integrate so I guess I have to make some changes

  2. September 7, 2009

    You should lookup Formtastic. It does this and a whole lot more

  3. September 7, 2009

    Nice to know how it works though. I might need it :)

  4. Nick permalink*
    September 18, 2009

    @PhilT I just needed a simple solution, I’ve seen that plugin it’s pretty slick.

    Eventually I’ll get around to packaging this one up as a plugin.

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS