Enough with concepts, ideas and diagrams: it's time to start coding something. Everyone knows what's the first step when creating a Rails applications, but anyhow, here it is:
rails italysimply
Then I create a new development database, load it up with the schema I previously prepared and modify the
config/database.yml
to be able to connect to it. Nothing new here.
I actually had to modify the schema a little bit:
- I changed all the names for the foreign keys to something more evocative than “has_many” or “has_one”
- I added a level column to the states, availabilities and conditions table
- I removed the description column from the categories table
Great, but… hang on: now some of the database tables look awfully similar with each other:
- statuses
- states
- roles
- types
- tags
- conditions
- availabilities
- categories
They all have a name column, some of them have a name column as well, they'll hold only a relative small number
of records which will hardly ever be deleted. In fact, I was tempted to use Enums for some of those
things…
Anyhow, I'll still have to add and modify data in those tables, so it looks like I kinda need to create 8
controllers, 8 models and about four views for each one of them. No way. Fair enough for the controllers and models,
but I'm not going to create 32 views which all look exactly the same. Rails should be smarter than that!And it
is, luckily. Derek Sivers & C. came out with an interesting Shared Controller concept, which could be just
what I'm looking for in this case. Actually I need something really simple in this case:
- Put all the CRUD logic into one controller
- Create only one set of views
Here's the controller:
app/controllers/admin/lite_controller.rb
class Admin::LiteController < ApplicationController</p> layout ‘admin' before_filter :prepare def prepare @item_name = model.to_s end def index list end verify :method => :post, :only => [ :destroy, :create, :update ], :redirect_to => { :action => :list } def list ordering = model.column_names.include?(‘level') ? ‘level <span class="caps">ASC</span>' : ‘name <span class="caps">ASC</span>' @items = model.find(:all, :order => ordering) render(‘lite/list') end def show @item = model.find(params[:id]) render(‘lite/show') end def new @item = model.new render(‘lite/new') end def create <code>item = model.new(params[:"#{</code>item_name.downcase}"]) if @item.save flash[:notice] = @item_name+' was successfully created.' redirect_to :action => ‘list' else render(‘lite/new') end end def edit @item = model.find(params[:id]) render(‘lite/edit') end def update @item = model.find(params[:id]) if <code>item.update_attributes(params[:"#{</code>item_name.downcase}"]) flash[:notice] = @item_name+' was successfully updated.' redirect_to :action => ‘list' else render(‘lite/edit') end end <p>end
Then all I need to do is create eight controllers with just a few lines of code in each:
app/controllers/admin/statuses_controller.rb
class Admin::StatusesController < Admin::LiteController def model Status end end
Basically, I just need to specify which model the specific controller takes care of, Ruby's inheritance does the rest. The model name will be passed to the views like this:
app/controllers/admin/lite_controller.rb
def prepare @item_name = model.to_s end
And each method uses the
model
method to access the model, like this:app/controllers/admin/lite_controller.rb
def create @item = model.new(params[:"#{@item_name.downcase}"]) if @item.save flash[:notice] = @item_name+' was successfully created.' redirect_to :action => 'list' else render('lite/new') end end
Note how the params are collected:
@item = model.new(params[:"#{@item_name.downcase}"])
params[:"#{
item_name.downcase}"]@ at runtime becomesparams[:status]
orparams[:role]
etc. etc., depending on which controller is called. Sweet.The views? Modified accordingly:
app/views/lite/edit.rb
<h1>Editing <br /> <div class='ruby'><pre><code><h1>Editing <%= @item_name %></h1></p> <p><% form_tag :action => ‘update', :id => @item do <span>><br /> <</span>= render :partial => ‘lite/form' <span>><br /> <</span>= submit_tag ‘Edit' <span>><br /> <</span> end %></p> <p><%= link_to ‘Show', :action => ‘show', :id => @item <span>> |<br /> <</span>= link_to ‘Back', :action => ‘list' %>
app/views/lite/_form.rb
<%= error_messages_for 'item' %> <!--[form:lite]--> <p><label for="<%= @item_name.downcase %>_name">Name: </label> <%= text_field @item_name.downcase, 'name', {:value => @item.name} %></p> <% if @item.methods.include?('level') then %> <p><label for="<%= @item_name.downcase %>_level">Level: </label> <%= text_field @item_name.downcase, 'level', {:value => @item.level} %></p> <% end %> <!--[eoform:lite]-->