crud

Posted on October 10, 2008 to Rails and crud  

Some rails projects have components that are little more than glorified CRUD interfaces to a database.

In this particular case, I’m working on a backend administration tool, bolting onto some legacy data.

Parts of the app need to be custom, with a nice GUI using in place editing, etc. But other parts are just a dumb listing of the data, with editing ability.

ActiveRecord::Base has a class method ‘columns’ which returns all of the metadata for the class’ database columns.

Here are a few helpers that you can put in a file, e.g. ‘extensions.rb’ in your config/initializers directory (Rails 2.1):

module ColumnHelperExtensions
  
  def field_columns
    columns.select {|c| [:string, :integer].include?(c.type) && !c.primary && !c.name.include?('id') }
  end

  def date_columns
    columns.select {|c| :date == c.type }
  end

  def boolean_columns
    columns.select {|c| :boolean == c.type }
  end

end

class ActiveRecord::Base
  class << self
    include ColumnHelperExtensions
  end
end

Your controllers will behave just as normal CRUD (e.g. scaffolded-style) rails controllers.

Here is an example edit.html.erb:

<h2>Editing <%= @foo.name %></h2>

<% form_for @foo do |f| %>
  <%= error_messages_for 'foo' %>
  <table class="standard_form">
    <%= render :partial => 'shared/generic_form', :locals => {:f => f, :klass => SparqUser} %>
  </table>
  <%= submit_tag "Update Foo &raquo;" %>
<% end %>

Then in ‘shared/_generic_form.html.erb’ we tie it all together:

<% klass.field_columns.each do |field| %>
  <tr>
    <td class="first"><%= f.label(field.name) %></td>
    <td><%= f.text_field(field.name) %></td>
  </tr>
<% end %>

<% klass.date_columns.each do |field| %>
  <tr>
    <td class="first"><%= f.label(field.name) %></td>
    <td><%= f.date_select(field.name) %></td>
  </tr>
<% end %>

<% klass.boolean_columns.each do |field| %>
  <tr>
    <td class="first"><%= f.label(field.name) %></td>
    <td><%= f.check_box(field.name) %></td>
  </tr>
<% end %>

When facing the task of maintaining views for a table with 20+ columns, I find this approach, at the very least, a good way to bootstrap a CRUD interface without lots of code bloat.

Note: for this to be effective, your columns have to be named at least somewhat sensibly. If you have a column titled ‘event_date’, this gets labeled ‘Event date’. Obviously a column named ‘trlf_widget_gzy’ will not make much sense to end users when humanized.