Passing values between actions

I've seen numerous discussions similar to this, but none that are
exactly the same.

I am building a simple support ticket system where a ticket has_many
entries.

I want to have a view that displays all the entries associated with a
certain ticket and also have a link a user can click to add an entry
to that ticket.

I have an 'edit' action in my Entry controller that is used to add/
edit entries.

I'm trying to figure out a good way to tell the 'edit' action which
ticket the new entry belongs to.

I saw that I can use the link_to helper in my ticket template to add a
link to the 'edit' action of the Entry controller. If I add :ticket
=> 1 to the list of options to the link_to helper then I can retrieve
that value as params[:ticket] in the edit action.

Is this the accepted way to do this sort of thing?

I'm new to RoR so I'm trying to pick up any best practices I can...

Serengeti wrote:

I saw that I can use the link_to helper in my ticket template to add a
link to the 'edit' action of the Entry controller. If I add :ticket
=> 1 to the list of options to the link_to helper then I can retrieve
that value as params[:ticket] in the edit action.

Is this the accepted way to do this sort of thing?

Yes. You can also add a route to make the URL nicer.
Or get the route for free by instead using params[:id].

the somehow official way is using the Flash!

flash[:notice] = "Thank you for your comment"

you can access the data in the flash like session data and use it in
another controller.

Thanks for the response.

I was thinking of using params[:id], but I thought I might need that
for the case where somebody is editing an existing entry (params[:id]
would refer to the entry id).

I think what you are saying is I could set up a route
like :controller/:action/:id/:ticket. Is that right?

I see where that would make for a cleaner looking url as opposed to
having ?ticket=1 in the url.

I guess this would be a good place to ask about the second part of
this problem:

I need to keep that ticket value handy through the next request (where
the user submits the form with the fields for the entry).

I used form_for to connect my form to the Entry object.

I then used a hidden_field_tag with the name "ticket" to store the
value my controller got from params[:ticket] before displaying the
form. My controller then loaded up the Ticket object using find_by_id
and the value passed from the hidden field in my form.

Is this the "right" way to do this?

It seemed like the only obvious solution, but it reminds too much of
what I do in all my PHP apps -- I thought perhaps there was a cleaner
solution I had missed.

I considered trying to use the flash or session, but wasn't sure how
to tie the value to the current request.

When a user clicks my link, I need to display the entry form and know
that this entry belongs to the ticket that was being displayed when
the user clicked the link.

I thought the flash/session could get confused easily. Say, for
example, the user has two windows open to two different tickets. How
would the entry 'edit' action know which ticket the new entry was for?

Serengeti wrote:

Thanks for the response.

I was thinking of using params[:id], but I thought I might need that
for the case where somebody is editing an existing entry (params[:id]
would refer to the entry id).

I think what you are saying is I could set up a route
like :controller/:action/:id/:ticket. Is that right?

:controller/:action/:ticket/:id would be better because that's
the hierarchy -- the entry id is optional.

I need to keep that ticket value handy through the next request (where
the user submits the form with the fields for the entry).

I used form_for to connect my form to the Entry object.

I then used a hidden_field_tag with the name "ticket" to store the
value my controller got from params[:ticket] before displaying the
form. My controller then loaded up the Ticket object using find_by_id
and the value passed from the hidden field in my form.

Is this the "right" way to do this?

No need for the hidden field, the post can be made to come from url
:controller/:action/:ticket, so params[:ticket] will already
be there.

I set up the route :controller/:action/:ticket/:id so I would get nice-
looking urls on my 'edit entry' links.

I'm not sure I understand how to have the post come from a url of this
form.

My template has:

<% form_for :entry do |form| %>
...

Which results in a form like this:

<form action="/support/entry/edit" method="post">

Could you elaborate on how to have the url include the ticket?

Thanks,
Errol

Serengeti wrote:

I set up the route :controller/:action/:ticket/:id so I would get nice-
looking urls on my 'edit entry' links.

I'm not sure I understand how to have the post come from a url of this
form.

My template has:

<% form_for :entry do |form| %>
...

Which results in a form like this:

<form action="/support/entry/edit" method="post">

Could you elaborate on how to have the url include the ticket?

The default POSTing url for a Rails form is the url of the page
the form's on, so you just have to make sure the user is sent to
the /support/entry/edit/<ticket#> url to create new entries for
a ticket, and to /support/entry/edit/<ticket#>/<entry#> for entry
edits.

An alternative is to add the required parameters for the POST
url as options to form_for, as described in another post in this
thread.

Argh. I just posted a reply and it seems to have gotten lost. I
apologize in advance if this ends up on here twice.

I seem to have taken one step forward and two steps back.

I am trying to set up a route as suggested
for :controller/:action/:ticket/:id.

Because :id is optional (for creating a new entry) it is not possible
for Rails to tell the difference between this and the default routing
of :controller/:action/:id.

Everything I have tried (taken from examples in Agile Web Development
With Rails) causes a Status: 500 Internal Server Error. The error
message gives no more detail so I don't know where to begin trouble-
shooting.

I first tried adding this route to the top of the list:

map.connect 'support/entry/edit/:ticket/:id'

where support/entry is the controller and edit is the action.

Why would this cause an Internal Server Error.

I have tried 3 or 4 other variations and they all cause an Internal
Server Error.

Suggestions?

Thanks,
Errol

Serengeti wrote:

I first tried adding this route to the top of the list:

map.connect 'support/entry/edit/:ticket/:id'

where support/entry is the controller and edit is the action.

Why would this cause an Internal Server Error.

I have tried 3 or 4 other variations and they all cause an Internal
Server Error.

I'm not a routing expert, but try

  map.connect 'support/entry/edit/:ticket/:id',
              :controller => 'support', :action => 'entry'

  map.connect 'support/entry/edit/:ticket',
              :controller => 'support', :action => 'entry'

I tried that (or something close to it -- the controller is support/
entry and the action is edit):

   map.connect 'support/entry/edit/:ticket/:id',
               :controller => 'support/entry', :action => 'edit'

   map.connect 'support/entry/edit/:ticket',
              :controller => 'support/entry', :action => 'edit'

Same result -- Internal Server Error.

I appreciate any additional assistance.

Very strange. I stopped the server, rolled back to a pre-route-
experiment version, and added these routes:

  map.connect 'support/entry/edit/:ticket',
    :controller => 'support/entry',
    :action => 'edit'

  map.connect 'support/entry/edit/:ticket/:id',
    :controller => 'support/entry',
    :action => 'edit'

I then started the server I no longer got the Internal Server Error.

This is strange because I had tried this exact series of steps earlier
and got different results. I suppose I must have missed some subtle
difference.

At any rate, I am closer now -- but not all the way there.

Adding new entries works fine (where :id is left off the route).

Editing existing entries does not work.

For example, a url of support/entry/edit/1/2 causes a form to be
displayed where the fields are populated with the data from entry #2,
but the form tag looks like this:

<form action="/support/entry/edit/1" method="post">

Submitting the form causes a new entry to be created.

Based on what Mark posted earlier, shouldn't the form post back to /
support/entry/edit/1/2?

Here is the complete code for the edit.rhtml template:

<% form_for :entry do |form| %>
  <p>
    <label for="entry_body">Post:</label>
    <%= form.text_area :body, :cols => '60', :rows => '20' %>
  </p>
  <%= submit_tag "Submit" %>
<% end %>

Serengeti wrote:

Editing existing entries does not work.

For example, a url of support/entry/edit/1/2 causes a form to be
displayed where the fields are populated with the data from entry #2,
but the form tag looks like this:

<form action="/support/entry/edit/1" method="post">

Submitting the form causes a new entry to be created.

Based on what Mark posted earlier, shouldn't the form post back to /
support/entry/edit/1/2?

Someone more familiar with routes may be able to help,
but you could try switching the order of the two related routes.

That helps in some ways, hurts in others.

Having the routes in this order:

  map.connect 'support/entry/edit/:ticket/:id',
    :controller => 'support/entry',
    :action => 'edit'
  map.connect 'support/entry/edit/:ticket',
    :controller => 'support/entry',
    :action => 'edit'

Makes it so that this form:

<% form_for :entry do |form| %>
  <p>
    <label for="entry_body">Post:</label>
    <%= form.text_area :body, :cols => '60', :rows => '20' %>
  </p>
  <%= submit_tag "Submit" %>
<% end %>

results in this html:

<form action="/support/entry/edit/1/1" method="post">
  <p>
    <label for="entry_body">Post:</label>
    <textarea cols="60" id="entry_body" name="entry[body]" rows="20"></

  </p>
  <input name="commit" type="submit" value="Submit" />
</form>

The problem is that the second route is NOT used when I think it
should be.

<%= link_to "Post new reply to this ticket", :action => "edit",
                           :controller => "entry",
                           :ticket => @ticket %>

links to: http://localhost:3000/support/entry/edit/1/1

I assume :id is populated because this link appears on the page that
lists all the entries for a particular ticket. In this case :id
refers to the ticket id, but it causes link_to to show a link that
would edit the entry with that id.

How can I have Rails use the right route for the circumstances?

Here's some new info:

1. It appears that the second route will never be called, so I
eliminated that.

2. According to Agile Web Development With Rails I should be able to
do something like this to un-set the id parameter that I don't want
passed:

<%= arrow_link "Post new reply to this ticket", :action => "edit",
                           :controller => "entry",
                           :id => nil,
                           :ticket => @ticket %>

However, this does not work for me. The value for id is still added
to the url in the link.

Just for fun, I tried setting id to an empty string instead of nil,
like this:

<%= arrow_link "Post new reply to this ticket", :action => "edit",
                           :controller => "entry",
                           :id => '',
                           :ticket => @ticket %>

It works. It's a little ugly because it includes the trailing slash
in the url:

<a href="/support/entry/edit/1/">Post new reply to this ticket</a>

Does anybody know why using :id => nil would not work as in the
examples?