I’ve been banging my head against the wall for almost a week with a Rails application. This post is not a plea for help – I’d use a forum for that – just a record of the problem. That said, feel free to comment, especially if you have a similar problem.
This is all using Rails 2.3.2, Mongrel 1.1.5, installed as gems on Ubuntu 9.04.
The basic issue: 2 models, 2 controllers, 2 sets of views. Identical in almost every respect, little more than basic CRUD (index, create, update, destroy). (1) works, (2) does not.
Update – thanks for your comments, here and elsewhere. In the end I rebuilt from scratch with scaffolding and it’s all good so far. Guess there was something rogue in my hand-crafted code.
The details:
1. The models
Model Organism:
class Organism < ActiveRecord::Base has_many :chromosomes has_many :platforms validates_presence_of :name,:binomial,:taxid validates_numericality_of :taxid validates_uniqueness_of :name,:binomial,:taxid end
Model Chromosome:
class Chromosome < ActiveRecord::Base belongs_to :organism has_many :features validates_presence_of :name, :length validates_numericality_of :length validates_uniqueness_of :name, :scope => :organism_id end
2. The routes
File routes.rb contains (just the relevant lines):
ActionController::Routing::Routes.draw do |map| map.resources :chromosomes map.resources :organisms end
3. Controller for Chromosome (this works)
Here’s the controller for Chromosome. It’s just your basic CRUD.
chromosomes_controller.rb
class ChromosomesController < ApplicationController
def index
@chromosomes = Chromosome.all
end
def edit
@chromosome = Chromosome.find(params[:id])
end
def update
@chromosome = Chromosome.find(params[:id])
if @chromosome.update_attributes(params[:chromosome])
flash[:notice] = "Chromosome updated"
redirect_to :action => :index
else
render :action => :new
end
end
def new
@chromosome = Chromosome.new
end
def create
@chromosome = Chromosome.new(params[:chromosome])
if @chromosome.save
flash[:notice] = "Chromosome added"
redirect_to :action => "index"
else
render :action => "new"
end
end
def destroy
@chromosome = Chromosome.find(params[:id])
# don't delete chromosome if linked to features
if Feature.all(:conditions => {:chromosome_id => @chromosome.id}).length > 0
flash[:notice] = "Cannot delete chromosome: it has features"
redirect_to :action => "index"
else
@chromosome.destroy
flash[:notice] = "Chromosome deleted"
redirect_to :action => "index"
end
end
end
3. Views for Chromosome (these work too)
Now the views: index, edit, new and a partial named “_form.html.erb”.
index.html.erb:
<h1>Chromosomes</h1>
<div class="info">Add a <%= link_to 'new chromosome', new_chromosome_path %> here.</div>
<div class="info">Or click a 'last update' table entry to view/edit/delete.</div>
<table>
<tr>
<th>Name</th>
<th>Length</th>
<th>Organism</th>
<th>Last Updated</th>
</tr>
<% @chromosomes.each do |@chrom| %>
<tr class="data">
<td><%=h @chrom.name %></td>
<td><%=h @chrom.length %></td>
<td><%=h @chrom.organism.name %></td>
<td><%= link_to @chrom.updated_at, edit_chromosome_path(@chrom) %></td>
</tr>
<% end %>
</table>
new.html.erb and edit.html.erb – are essentially identical except for wording:
new.html.erb
<h1>New Sequence</h1> <div class="info">Enter sequence details and click 'Save'.</div> <%= render :partial => "form" %>
Here’s the partial ERB file, _form.html.erb:
<% form_for(@chromosome) do |f| %>
<%= f.error_messages %>
<table>
<tr>
<td><%= f.label :name %></td>
<td><%= f.text_field :name %></td>
</tr>
<tr>
<td><%= f.label :length %></td>
<td><%= f.text_field :length %></td>
</tr>
<tr>
<td><%= f.label :organism %></td>
<td>
<%= render :partial => "organisms/select", :locals => {:organisms => @organisms} %>
</td>
</tr>
<tr>
<td colspan="2" class="right"><%= f.submit 'Save' %></td>
</tr>
</table>
<% end %>
<% if @chromosome.id %>
<div class="info">Or <%= link_to 'delete', @chromosome,
:confirm => "Are you sure?",
:method => :delete %> this chromosome permanently.</div>
<% end %>
The partial app/views/organisms/_select.html.erb is simply:
<%= collection_select(:chromosome, :organism_id, Organism.all, :id, :name) %>
4. Controller and views for Organism (these don’t work)
The controller organisms_controller.rb is identical to that for chromosomes, EXCEPT that @chromosome(s) is replaced by @organism(s) throughout and the destroy method is:
def destroy
@organism = Organism.find(params[:id])
# don't delete organism if linked to chromosomes
if Chromosome.all(:conditions => {:organism_id => @organism.id}).length > 0
flash[:notice] = "Cannot delete organism: it has chromosomes"
redirect_to :action => "index"
else
@organism.destroy
flash[:notice] = "Organism deleted"
redirect_to :action => "index"
end
end
And index.html.erb, new.html.erb, edit.html.erb are also identical, except that @chrom(osome) is replaced by @org(anism) throughout and the fields are those for Organism. For example in _form.html.erb:
<% form_for(@organism) do |f| %>
<%= f.error_messages %>
<table>
<tr>
<td><%= f.label :name %></td>
<td><%= f.text_field :name %></td>
</tr>
<tr>
<td><%= f.label :binomial %></td>
<td><%= f.text_field :binomial %></td>
</tr>
<tr>
<td><%= f.label :taxid %></td>
<td><%= f.text_field :taxid %></td>
</tr>
<tr>
<td colspan="2" class="right"><%= f.submit 'Save' %></td>
</tr>
</table>
<% end %>
<% if @organism.id %>
<div class="info">Or <%= link_to 'delete', @organism,
:confirm => "Are you sure?",
:method => :delete %> this organism permanently.</div>
<% end %>
5. The problem
Chromosome works just fine. Index shows a list of chromosomes. New adds a new chromosome. Edit fills in the form with chromosome parameters and allows update or destroy.
Organism does not work just fine. The new method works and adds a new organism. But edit gives this error (I removed the ERB/HTML tags as they mess up blog formatting):
NoMethodError in Organisms#edit
Showing app/views/organisms/_form.html.erb where line #1 raised:
undefined method `organism__path' for #ActionView::Base:0xb6a81fe0
Extracted source (around line #1):
1: form_for(@organism) do |f|
2: f.error_messages
Trace of template inclusion: app/views/organisms/edit.html.erb
RAILS_ROOT: /bioinfo/neil/projects/microarray_mining/code/Rails/ArrayMiner
Application Trace | Framework Trace | Full Trace
/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller/polymorphic_routes.rb:109:in `__send__'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller/polymorphic_routes.rb:109:in `polymorphic_url'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller/polymorphic_routes.rb:116:in `polymorphic_path'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_view/helpers/form_helper.rb:298:in `apply_form_for_options!'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_view/helpers/form_helper.rb:277:in `form_for'
code/Rails/ArrayMiner/app/views/organisms/_form.html.erb:1:in `_run_erb_app47views47organisms47_form46html46erb_locals_form_object'
code/Rails/ArrayMiner/app/views/organisms/edit.html.erb:4:in `_run_erb_app47views47organisms47edit46html46erb'
The problem?
There seems to be some issue with routes. For some reason, form_for(@organism) works fine with the new method, but not with the edit method. The error “undefined method ‘organism__path’” (note the 2 underscores) is perplexing.
Other observations – (1) moving lines around in routes.rb then restarting Mongrel has sporadically fixed the problem, but then errors return. (2) I have similar code for a model named Technology which misbehaves in the same way, but code for 3 other models in addition to Chromosome works fine. As before, all the code is more or less identical, just with different variable names.


