Pass local variables to another controller

I’ve been beating by noob head against a wall, I’d appreciate any help
that can be offered.

I have a view listing items with a link to append a description to
each item. I believe I have the associations in the Models defined
correctly (item has_many :descriptions, description belongs_to :item)
to be RESTful and such…

My item view includes the following code in the <% for item in @items
%>:

<%= link_to ‘Describe’, :controller => :discriptions, :action =>
‘new’, :locals => {:item_id => item.id} %>

and in the development.log file I see

Parameters: {“locals”=>{“item_id”=>“2”}, “action”=>“new”,
“controller”=>“descriptions”}

But I can’t seem to apply the item_id from the items/index view to the
object being created in the descriptions/new view.

The DescriptionsController reads

def new
@description = Description.new
@description.item_id = ‘item_id’ (I know this doesn’t work, it’s
just the last thing I tried before calling it a day…)

BTW there is an item_id field in the Descriptions table… I must be
missing something in plain sight. Thanks for any guidance.

SH

Try it like this:

Item.find(params[:locals][:item_id]).descriptions << Description.new

Or if you really want to, you can do it your way but use:
@description.item_id = params[:locals][:item_id]
but it’s two lines versus one, and you still need a save. The << way
will create the description, save it, and add it to the Item’s
has_many association (i.e. set item_id on the Description object)

Really, though, you can leave off the locals too:

<%= link_to ‘Describe’, :controller => :descriptions, :action =>
‘new’, :item => item %>

And then:

Item.find(params[:item]).descriptions << Description.new

(when using an object as a hash value, i.e. in the find, Rails will
pull out the ID for you automatically… I think that’s a relatively
new feature… well, I don’t think Rails 1.1 was that way at least)

:slight_smile:

-Danimal

Danimal,

Thanks for the reply… I edited the DescriptionsController to read

def new
Item.find(params[:item]).descriptions << Description.new
end

and changed the link_to code in the view… I got an error but saw
that rails created a Description object with that item_id in the
database before the error was triggered. I think I’m missing something
in the DescriptionsControllers ‘new’ code?

Switching gears and using @description.item_id = params[:locals]
[:item_id] instead, I got the correct id passed to the Description
object, but if the text_field for the item_id was removed from the
view it was saved as nil for that object! Without the text_field in
the view I see in the development.log:

Parameters: {“commit”=>“Create”, “description”=>{“author”=>“joe”,
“topic”=>“wine”}

… with the text_field in the view I see in the development.log:

Parameters: {“commit”=>“Create”, “description”=>{“item_id”=>“3”,
“author”=>“joe”, “topic”=>"wine

Ugh. Any way to get the item_id saved without that field available for
the user to change? Or perhaps figure out what I’m doing wrong to
create the error following your suggestion Thanks again for any help.

SH

Hi Danimal,
If passing the item id from the view without an text field is what you
need, just put a hidden_field in the view.

I use a very simple and transparent method (in this app, a scenario
has_many unit tests):

In the show view for a Scenario, the link for adding a new Unittest just
passes the current scenario’s ID in the params (if this looks strange, I
use haml for my views):

%td.ctl= link_to ‘New Unit Test’, new_unittest_path(:scenario =>
scenario_id)

Then in the Unittest controller, see if that param is populated, if so,
set the new Unittest’s value:

def new
@unittest = Unittest.new
@unittest.scenario_id = params[:scenario] if params[:scenario]
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @unittest }
end
end

@AndyV

Thank you for the explanation… you’re right that my understanding of
what’s going on is confused, I’m new to coding and get myself stuck in
rabbit holes then find myself trying to use :locals to solve problems,
for example.

Your description of how the routes.rb maps the relationship between
models by named routes is clear and intuitive. After my day job I will
apply these changes to my project…

Thanks for the help,

SH

@SH

It’s important to think about the process that’s going on here. The
language you’re using to describe the problem is a little confused and
it might help a lot to get a bird’s eye view of what you’re doing. In
the simplest scenario the controller instance is a very short-lived
object that springs to life only to answer a question posed by the
user. It’s job is to gather enough information to answer the question
and reply in a way that the user understands… and may eventually ask
another question. In a sense it’s part of a conversation with the
user. In this regard you are not ‘passing local variables to another
controller’, you are answering a user request and the user is making a
completely new request to some other controller. It’s an important
distinction to make because it will help keep you from thinking in
terms of sending messages to another controller; controllers respond
to browsers.

Specifically here the main question to ask is whether the Descriptions
will always be attached to Items. If so then you can do a few
things that will really simplify your coding.

  1. Modify your routes.rb file, nesting the descriptions resource
    inside the items resource:

map.resources :items do |item|
item.resources :descriptions
end

By doing this you explicitly state to the Routing system that
descriptions belong to items. The bonus is that you get named routes
to help you with your main issue.

  1. Use the named route in your link_to
    <%= link_to ‘Describe’, new_item_description_path(@item) %>

This will build a url for you that will look something like /items/15/
descriptions/new. The advantage is that the route is created by the
Routing system so you can’t miss. Better still, the intention of the
code is much clearer. (As a side note, I am assuming that you got
the :locals reference by reading some about partials… it’s not
necessary here).

  1. Code the expectation for an Item into the Description model.
    The main argument for the nested route (#1) is that the nested item
    only makes sense in the context of it’s parent. In this situation,
    the Description is only meaningful with its Item. You can/should make
    that explicit in your controller. When it responds to the request to
    make a new Description for an Item it can say so:

class DescriptionsController < ApplicationController

def new
@item = Item.find params[:item_id] # Note: item_id is created by
the named route
@description = @item.descriptions.build
end

def create
@item = Item.find params[:item_id]
@description = @item.descriptions.build params[:description]

respond_to do |format|
  if @description.save
  ...
  end
end

end
end

  1. Also let your new.html.erb take advantage of the named route
    <% form_for :item, :url=>item_descriptions_path, :method=>:post do |f|
    %>

    <% end %>

Here you’re going to post a new entry into the Item.descriptions
collection (you can clean this up further if it’s a singular
resource). The create method (above) will receive the parameters. As
with #new, it already understands that it’s scoped to an Item and the
named route has made sure that you got the item_id.
@item.descriptions.build params[:description] will build a new
Description object using the values that the user keyed and
automatically pop the scoping item_id in as well (note – item_id was
not available to the user!).

@AndyV

Perfect - I got it to work using the named routes. A thing of
beauty… Thanks

SH

AndyV,

Great response. One question – what if Descriptions are not always
attached to Items?

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs