Ruby on Rails
HowToDoHasOneNestingInRails2.0 (Version #13)

Title:
How to Do has_one Nesting in Rails 2.0

Objective:

Clarify the differences between structuring nested has_many and nested has_one associations.

Background:

Google Groups and comments on Fabio Akita’s Rails 2.0 Tutorial
seem to indicate that this is not perfectly clear for many of us.

Prerequisites:

Rails 2.0.2

For our example

I am using the example of a travel arrangements database, where the app is mostly an admin interface to the tables. The application uses a typical scaffolding, since appearance isn’t an issue for this example.

a Trip
 -- has_one :plan
 -- has_one :expenditure
 -- has_one :reimbursement
and 
a Plan belongs_to :trip
an Expenditure (record) belongs_to :trip
a Reimbursement belongs_to :trip

In routes.rb


ActionController::Routing::Routes.draw do |map| map.resources :trips, :has_one =>[:plan, :expenditure, :reimbursement] # Install the default routes as the lowest priority. map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end

In expenditures_controller.rb


class ExpendituresController < ApplicationController

#given the expenditure’s trip_id, set @trip before_filter :load_trip, :except=>’index’ # GET /expenditures # GET /expenditures.xml def index @expenditures = Expenditure.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @expenditures } end end # GET trip/:trip_id/expenditure # GET trip/:trip_id/expenditure.xml def show @expenditure = @trip.expenditure respond_to do |format| format.html # show.html.erb format.xml { render :xml => @expenditure } end end # GET trip/:trip_id/expenditure/new # GET trip/:trip_id/expenditure/new.xml def new @expenditure = Expenditure.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @expenditure } end end # GET trip/:trip_id/expenditure/edit def edit @expenditure = @trip.expenditure end # POST trip/:trip_id/expenditure # POST trip/:trip_id/expenditure.xml def create #this is a minor gotcha compared to a has_many @expenditure = @trip.build_expenditure(params[:expenditure]) respond_to do |format| if @expenditure.save flash[:notice] = ‘Expenditure was successfully created.’ # and watch the pluralization here format.html { redirect_to trip_expenditure_path(@trip) } format.xml { render :xml => @expenditure, :status => :created, :location => @expenditure } else format.html { render :action => “new” } format.xml { render :xml => @expenditure.errors, :status => :unprocessable_entity } end end end # PUT trip/:trip_id/expenditure # PUT trip/:trip_id/expenditure.xml def update @expenditure = @trip.expenditure respond_to do |format| if @expenditure.update_attributes(params[:expenditure]) flash[:notice] = ‘Expenditure was successfully updated.’ format.html { redirect_to trip_expenditure_path(@trip) } format.xml { head :ok } else format.html { render :action => “edit” } format.xml { render :xml => @expenditure.errors, :status => :unprocessable_entity } end end end # DELETE trip/:trip_id/expenditure # DELETE trip/:trip_id/expenditure.xml def destroy @trip.expenditure.destroy respond_to do |format| format.html { redirect_to trip_path(@trip) } format.xml { head :ok } end end private def load_trip @trip = Trip.find(params[:trip_id]) end

end

DRY-ish forms
This took some futzing, and there may be a simpler way, but it works.

In /app/views/expenditures/new.html.erb


<h1>New expenditure

<= render :partial => @expenditure, :locals=>{:button_name=>’Add’}>

<%= link_to “Trip”, trip_path(@trip) %>

In /app/views/expenditures/edit.html.erb


<h1>Editing expenditure

<= render :partial => @expenditure, :locals=>{:button_name=>’Update’}>

<= link_to ‘Show’, trip_expenditure_path(@trip) %> |
<
= link_to “Trip”, trip_path(@trip) %>


In /app/views/expenditures/_expenditure.html.erb

This doesn’t look all that special, but it took some fooling around to get right.


<%= error_messages_for 'expenditure' %>

<%# I never got this to work without spelling out the url;%>
<%# I think the form_helper is broken, perhaps.%>
<%# In any event, watch what gets passed to form_for carefully%>

<% form_for([:trip,@expenditure],:url => trip_expenditure_path(@trip) ) do |f| %>

<!--[form:expenditure]-->
<p><label for="expenditure_departure_on">Departure on</label><br />
<%= f.datetime_select 'departure_on'  %></p>
<p><label for="expenditure_departure_at">Departure at</label><br />
<%= f.datetime_select 'departure_at'  %></p>
.
.
.
<p><label for="expenditure_intercity_train_usd">Intercity train usd</label><br />
<%= f.text_field 'intercity_train_usd'  %></p>
<p><label for="expenditure_intercity_bus_usd">Intercity bus usd</label><br />
<%= f.text_field 'intercity_bus_usd'  %></p>
  <%= f.submit button_name %>
<!--[eoform:expenditure]-->

<% end %>

Rails is pretty astute about picking up on whether you want :method=>:put or :method=>:post, but the :url seems to be needed.

Props and refs
A Google Group thread about this
Fabio Akita’s Rails 2.0 Tutorial

Title:
How to Do has_one Nesting in Rails 2.0

Objective:

Clarify the differences between structuring nested has_many and nested has_one associations.

Background:

Google Groups and comments on Fabio Akita’s Rails 2.0 Tutorial
seem to indicate that this is not perfectly clear for many of us.

Prerequisites:

Rails 2.0.2

For our example

I am using the example of a travel arrangements database, where the app is mostly an admin interface to the tables. The application uses a typical scaffolding, since appearance isn’t an issue for this example.

a Trip
 -- has_one :plan
 -- has_one :expenditure
 -- has_one :reimbursement
and 
a Plan belongs_to :trip
an Expenditure (record) belongs_to :trip
a Reimbursement belongs_to :trip

In routes.rb


ActionController::Routing::Routes.draw do |map| map.resources :trips, :has_one =>[:plan, :expenditure, :reimbursement] # Install the default routes as the lowest priority. map.connect ':controller/:action/:id' map.connect ':controller/:action/:id.:format' end

In expenditures_controller.rb


class ExpendituresController < ApplicationController

#given the expenditure’s trip_id, set @trip before_filter :load_trip, :except=>’index’ # GET /expenditures # GET /expenditures.xml def index @expenditures = Expenditure.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @expenditures } end end # GET trip/:trip_id/expenditure # GET trip/:trip_id/expenditure.xml def show @expenditure = @trip.expenditure respond_to do |format| format.html # show.html.erb format.xml { render :xml => @expenditure } end end # GET trip/:trip_id/expenditure/new # GET trip/:trip_id/expenditure/new.xml def new @expenditure = Expenditure.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @expenditure } end end # GET trip/:trip_id/expenditure/edit def edit @expenditure = @trip.expenditure end # POST trip/:trip_id/expenditure # POST trip/:trip_id/expenditure.xml def create #this is a minor gotcha compared to a has_many @expenditure = @trip.build_expenditure(params[:expenditure]) respond_to do |format| if @expenditure.save flash[:notice] = ‘Expenditure was successfully created.’ # and watch the pluralization here format.html { redirect_to trip_expenditure_path(@trip) } format.xml { render :xml => @expenditure, :status => :created, :location => @expenditure } else format.html { render :action => “new” } format.xml { render :xml => @expenditure.errors, :status => :unprocessable_entity } end end end # PUT trip/:trip_id/expenditure # PUT trip/:trip_id/expenditure.xml def update @expenditure = @trip.expenditure respond_to do |format| if @expenditure.update_attributes(params[:expenditure]) flash[:notice] = ‘Expenditure was successfully updated.’ format.html { redirect_to trip_expenditure_path(@trip) } format.xml { head :ok } else format.html { render :action => “edit” } format.xml { render :xml => @expenditure.errors, :status => :unprocessable_entity } end end end # DELETE trip/:trip_id/expenditure # DELETE trip/:trip_id/expenditure.xml def destroy @trip.expenditure.destroy respond_to do |format| format.html { redirect_to trip_path(@trip) } format.xml { head :ok } end end private def load_trip @trip = Trip.find(params[:trip_id]) end

end

DRY-ish forms
This took some futzing, and there may be a simpler way, but it works.

In /app/views/expenditures/new.html.erb


<h1>New expenditure

<= render :partial => @expenditure, :locals=>{:button_name=>’Add’}>

<%= link_to “Trip”, trip_path(@trip) %>

In /app/views/expenditures/edit.html.erb


<h1>Editing expenditure

<= render :partial => @expenditure, :locals=>{:button_name=>’Update’}>

<= link_to ‘Show’, trip_expenditure_path(@trip) %> |
<
= link_to “Trip”, trip_path(@trip) %>


In /app/views/expenditures/_expenditure.html.erb

This doesn’t look all that special, but it took some fooling around to get right.


<%= error_messages_for 'expenditure' %>

<%# I never got this to work without spelling out the url;%>
<%# I think the form_helper is broken, perhaps.%>
<%# In any event, watch what gets passed to form_for carefully%>

<% form_for([:trip,@expenditure],:url => trip_expenditure_path(@trip) ) do |f| %>

<!--[form:expenditure]-->
<p><label for="expenditure_departure_on">Departure on</label><br />
<%= f.datetime_select 'departure_on'  %></p>
<p><label for="expenditure_departure_at">Departure at</label><br />
<%= f.datetime_select 'departure_at'  %></p>
.
.
.
<p><label for="expenditure_intercity_train_usd">Intercity train usd</label><br />
<%= f.text_field 'intercity_train_usd'  %></p>
<p><label for="expenditure_intercity_bus_usd">Intercity bus usd</label><br />
<%= f.text_field 'intercity_bus_usd'  %></p>
  <%= f.submit button_name %>
<!--[eoform:expenditure]-->

<% end %>

Rails is pretty astute about picking up on whether you want :method=>:put or :method=>:post, but the :url seems to be needed.

Props and refs
A Google Group thread about this
Fabio Akita’s Rails 2.0 Tutorial