Invalid route name for GET and POST - ruby-on-rails

I'm working through an older tutorial that was done for Rails 3. I'm using Rails 4.1.4.
One of the instructions is to change the routes file to include the following:
get '/boards/:board_id/conversations/:id/reply' => "conversations#reply", :as => :reply_board_conversation
post '/boards/:board_id/conversations/:id/reply' => "conversations#save_reply", :as => :reply_board_conversation
Obviously that gives me an error:
Invalid route name, already in use: 'reply_board_conversation'
It seems to me that the route is somehow trying to replicate the behaviour of a new and create action. Get for new and Post for create with a single route.
The problem is I can't figure out how to rewrite the route so it works. I've googled for solutions but can't seem to find anything. If anyone could point me in the right direction it would be hugely appreciated.

It looks like the only issue is with the duplicated "named route" name reply_board_conversation. So you could simply change one. I'd probably rename the save version to save_reply_board_conversation. Then it should work. Just remember to refer to the route this way in the future. This would primarily be used in a form tag. So, for exmaple:
<= form_tag :url => save_reply_board_conversation_path do %>
Note the use of save_reply_board_conversation_path instead of reply_board_conversation_path given that the form would be submitting a POST request instead of a GET request.

The names for these routes should be different although since the composition of the URL is the same so you really only need a name for the first one.
The trick with named routes is they generate the URL only, they do not set the HTTP request method. That has to be done independently.
That means you can call the same named route two different ways:
<%= link_to('View', board_path(#board)) %>
<%= link_to('Delete', board_path(#board), method: :delete) %>
These actually render as the same URL but one will hit the GET route, the other the DELETE one.

Related

ruby on rails- non-existent route

I have a controller file,
abc_controller.rb.
I have defined the show method inside it.
I have a view file,
show.html.haml
inside app/views/abc/
In my routes.rb file, I am giving the following command
resources :abc
I have a button
= link_to 'abc', abc_path, class: 'btn btn-default'
But when I click on the button, its not going to the new page.
I am getting non-existent route error.
Since I am a newbie to rails, I am not able to figure what the problem is.
You are getting a error because there is no such path as abc_path. Run rake routes and you'll see the routes that Rails understands. In you example, resources :abc produces the following routes.
abc_index GET /abc(.:format) abc#index
POST /abc(.:format) abc#create
new_abc GET /abc/new(.:format) abc#new
edit_abc GET /abc/:id/edit(.:format) abc#edit
abc GET /abc/:id(.:format) abc#show
PATCH /abc/:id(.:format) abc#update
PUT /abc/:id(.:format) abc#update
DELETE /abc/:id(.:format) abc#destroy
The first column are the named routes. So in order to get to the index action of abc_controller, the route is named abc_index_path. There is an abc_path but it needs an id params which means that you need to pass something to it. In your case, there's no definitive value to pass to this method so as a trial just use abc_path(1) which will redirect you to /abc/1. This goes to the show action with params[:id] set to 1.
If you do resources (plural) the resulting route for show requires an id: /abc/:id(.:format), so abc_path requires that you pass that :id or a object. If you are dealing with a singular abc (resource :abc), the resulting path doesn't require that :id, so you code should work (this is less common, but hard to tell with your "abc" example.

Why is link_to with an absolute url considered technically superior to targetting controller action?

New to rails, so if this is discussed somewhere, just link me off: I had a good search but all I could find were people trying to figure out how to use link_to, not any discussion of this comment:
link_to "Profile", profile_path(#profile)
# => Profile
in place of the older more verbose, non-resource-oriented
link_to "Profile", :controller => "profiles", :action => "show", :id => #profile
# => Profile
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
I get that the latter is more verbose, and thus undesirable, but the former seems like a strange thing to be recommending.
If I have an action at say: /blah/add and I link to it using:
link_to "Link", link_add_path
Then I'm linking to mysite.com/link/add. This is a hard coded url.
If I change the route that this maps to, I have to change every instance of link_to in my code base to point to the new absolute url. This seems crazy.
However, if I link to it using:
link_to "Link", :controller => "thing", :action => "add"
Then the url is dynamically determined. If I have to change the path all I do is edit config/routes.rb and not touch any of my code. This seems like much lower maintenance.
I appreciate it's slightly more complex than that, the blah_path variable is not actually a static route, and actually contains some smarts like the application base url and prevents you from linking to urls that don't exist, but it seems like a step backwards to facilitate a fractionally less verbose syntax.
So, what's up with that?
What technical reason would you choose the former link_to syntax over the latter?
"You're doing it wrong" :P
Seriously though: use named resources, and here's why that's cool:
Example:
you've got this in your routes file:
resources :user_orders
And you are using "user_orders_path" everywhere. Then you do a refactor and decide (because the orders are now generic) that you want the controller to be called "orders" but you don't want to break all your old code. you can do this:
resources :user_orders, controller: "orders"
And your existing links will continue to work! (plus you can add a "orders" resource to move things over to the new scheme)
There's also neat things like named links:
match 'exit' => 'sessions#destroy', :as => :logout
I'd also like to add, if you needed to refactor your controller using the old link syntax - you'd still have to change a pile of controller links!
Then I'm linking to mysite.com/link/add. This is a hard coded url.
No, it's not. link_add_path is a method generated by Rails that points to a specific route in your config/routes.rb. You can see this by running
rake routes | grep link_add
If I change the route that this maps to, I have to change every instance of link_to in my code base to point to the new absolute url. This seems crazy.
No, you don't. Take the following example
get "link/add", as: :link_add, controller: :links, action: :add
If I run the above
rake routes | grep link_add
I get
link_add GET /link/add(.:format) links#add
But what if I change the name of my controller to UrisController? Just change the route in config/routes.rb
get "link/add", as: :link_add, controller: :uris, action: :add
and now you have
link_add GET /link/add(.:format) uris#add
The link_to's don't have to change because the link_add_path method is still mapped to the newly modified line in my config/routes.rb because the route name is the same. With your more explicit way of specifying controllers/actions for every link_to, you have to go through every link and update it manually to reflect the new controller: :uris controller.
Read about Naming Routes in the rails guide.

Passing id in form_for action

I have a form to update an attribute of a model - I don't want it to go to the standard update action I want it to go to a different action.
<% for car in #cars %>
<%= form_for car, :url => { :action => "custom_action/#{car.id}" } do |f| -%>
This is giving the following error -
No route matches {:action=>"custom_action/1", :controller=>"cars"}
However if I visit the url - custom_action/1 - I don't get the routing error.
Any idea why I can't do this?
In addition to what Frederick Cheung said about GET vs POST vs PUT, I think your code might be wrong in general.
To do exactly what you want, try:
form_for car, :url => {:action => "custom_action", :id => car, :controller => "cars"} do |f|
I don't think this is a good idea, and will probably cause you pain. I suggest taking a look at the Ruby on Rails Routing Guide, to understand how to do this properly. (Routing is a topic where I always have to consult the manual.)
With correct routes your code should look something like this:
form_for car, custom_action_car_path(car) do |f|
Which will be easier to change in the future if/when you refactor your app.
If you go to the url by typing it into the url bar, you're making a GET request, whereas forms emit POST requests by default, and in this case would probably emit a PUT request (since you're acting on an existing record).
It sounds like your route is only routing GET requests. Given that you say your custom action does update the record, it sounds like you should update your routes to accept PUT requests instead.

Set Up Route for Accessing Private S3 Content

I've been following
https://github.com/thoughtbot/paperclip/wiki/Restricting-Access-to-Objects-Stored-on-Amazon-S3
and
Rails 3, paperclip + S3 - Howto Store for an Instance and Protect Access to try and get Paperclip's expiring links to work. I believe most of what I'm running into is one of the routing variety.
In my pieces_controller I put a method in like this
def download
redirect_to #asset.asset.expiring_url(1000)
end
And then in my routes, I put this:
match "pieces/download"
Then in my view I have:
<%= link_to download_asset_path(piece)%>
It would seem to be far from working, and I'm not sure what is messed up. I know I'm getting routing errors for one, but it's also telling me that my download_asset_path is undefined, which is likely also routing related... I feel like I'm doing everything all wrong.
Tearing my hair out. Thanks!
Try modifying your routes file to:
match 'pieces/download' => 'pieces#download', :as => 'download_asset'
Your match needs to tell which controller#action to go to, and the as option will allow you to name the route download_asset_path.
If your pieces controller is for a Piece resource it could be cleaner like:
resources :pieces do
member do
get :download
end
end
But then you would want to change the link to:
link_to 'Link text', download_piece_path(piece)
For further reading: http://guides.rubyonrails.org/routing.html

renaming routes (map, link_to, to_param) in rails

I'm having a little issue...I setup a rails application that is to serve a german website. To make use of Rails' internal pluralization features, I kept all my models in english (e.g. the model "JobDescription").
Now, if I call "http://mysite.com/job_descriptions/", I get all my job_descriptions....so far, so good. Because I didn't want the english term "job_descriptions" in my url, I put the following into my routes.rb
map.german_term '/german_term', :controller => 'job_descriptions', :action => 'index'
map.german_term '/german_term/:id', :controller => 'job_descriptions', :action => 'show'
If I call "http://mysite.com/german_term/" or "http://mysite.com/german_term/283" I get all my job_descriptions, which is fine.
However, to make the URL more SEO friendly, I'd like to exchange the id for a more userfriendly slug in the URL. Thus, I put the following in my job_description.rb:
def to_param
"#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
end
which, whenever I use "job_description_path" in any link_to method, renders my URLs out to something like "http://mysite/job_descriptions/13-my-job-description-title".
However, and this is where I'm stuck, I'd like to get "http://mysite/german_term/13-my-job-description-title". I already tried to exchange the "job_description_path" with "german_term_path" in the link_to code, but that only generates "http://mysite/german_term/13". Obviously, to_param isn't called.
One workaround I found is to build the link with:
<%= link_to job_description.name, german_term_path(job_description.to_param) %>
But that's rather tedious to change all the link_to calls in my code. What I want is to replace "job_description" by "german_term" whenever it occurs in a URL.
Any thoughts?!?
Regards,
Sebastian
I think you're going to need to use the restful route helpers to get what you want.
In that case, it wouldn't take much re-factoring (assuming you've mapped JobDescriptions as a resource). Leave your to_param as is and change your JobDescriptions route to something like the following:
map.resources :job_descriptions, :as => 'german_term'
Hope this helps!
Rails only utilizes the
def to_params
end
URL builder when you are using a restful route/link helper. The only way I am aware of is to do it similar to how you did, unless you are willing to just scrap your english language links and do it all in German. In that case, just get rid of the named route lines and change the to_params to use the correct name field from the database. At that point, the REST routes should behave correctly.

Resources