Polymorphic Routes in Rails 4.1.4 - ruby-on-rails

I have a polymorphic association between Posts and Postables, right now with Projects being my only Postables. In my routes, I have:
resources :projects do
...
member do
resources :posts
end
end
I know how to retrieve the right ids from the parameters, and all of my controller specs pass just fine, but when I try to write links in my views, they don't work. Running rake routes, I see a little weirdness:
...
post SHOW /projects/:id/posts/:id(.:format) posts#edit
...
And so on for the rest. If I'm not mistaken, the path should be 'new_project_post', and the first parameter should be :project_id.
Now, in my show view for Projects, I have the index of posts for that particular project. But the links don't work. Lets assume I have a project with an ID of 2, and a post for that project with an ID of 1.
If I try link_to 'Edit', edit_post_path(#project, post), the link comes out as .../projects/1/posts/1/edit, so both :ids get the post's id. If I swap post and #project, both :ids will be the project's id.
If I try passing them as an array, link_to 'Edit', post_path([post, #project]), the resulting link will be .../projects/1%2F2/posts/1%2F2/edit. %2F is a URL-encoded slash character, so I'm not sure what the hell Rails is trying to do here.
If I try using polymorphic_path([#project, post]) for my links, all it does is spit out paths that don't exist: undefined method 'project_post_path'
I've tried several other combinations of parameters without success. So if anyone could point me in the right direction, I'd be extremely grateful.

The appropriate syntax for nested resources in Rails is:
resources :projects do
resources :posts
end
In member block you could only declare additional actions to work with project instances.

You are using nested resource inside member and it is incorrect. Read more about this here: http://thelazylog.com/posts/polymorphic-routes-in-rails

Related

Error using alias for routes in Ruby on Rails

I have a route with a namespace
namespace :publishers do
resources :authors
get 'books' :to => 'books'
get 'books/custom_report/:id', :to => "curriculos#custom_report"
end
Often I will have to make links in my application and I know than it`s possible to use a alias for routing like this:
<%= link_to "Books", publishers_books_path %>
I call that publishers_books_path a alias route, does this is the correct name?
Furthermore, I still not able to understand the logic with this alias naming because i can`t use for a new or a custom action like this
link_to 'Show the report', publishers_books_custom_report_path(params[:id])
I'm always get a error of undefined_method for publishers_books_custom_report_path
So there`s some questions
First of all whats it`s the correct name of this feature in RoR?
How I can use the custom_report as aliases to link_to? And also if i need to use some basic operations like new, update, insert?
Can someone give me the link to the documentation to really understant that feature?
First of all whats it`s the correct name of this feature in RoR?
The docs use "path helper" and "named route helpers" interchangeably.
How I can use the custom_report as aliases to link_to?
Use rails route or visit /rails/info/routes in your dev server to get a list of all your routes, their helpers, and controller actions.
Apparently it is publishers_path which doesn't seem right. You can fix this with an as.
get 'books/custom_report/:id', to: "curriculos#custom_report", as: :books_custom_report
And also if i need to use some basic operations like new, update,
insert?
A get declares just that one specific route. If you need all the operations on a model, declare it as a resource.
namespace :publishers do
resource :authors
resource :books
get 'books/custom_report/:id', to: "curriculos#custom_report", as: :books_custom_report
end
Can someone give me the link to the documentation to really understand that feature?
Rails Routing From The Outside In.

Rails uncountable model name, no route matches get name_index

I have a model with uncountable name - class Equipment and in this article (https://markembling.info/2011/06/uncountable-nouns-rails-3-resource-routing) I found that in such cases we get into problems while trying to get model's index path. So article provides tips how to use inflection rules. However, I believe word 'Equipment', just like 'person' is already understood by Rails and I dont even need to define inflection rule, since I still get this path:
equipment_index GET /equipment(.:format) equipment#index
But, for some reason, after I navigate to localhost:3000/equipment_index, I get
No route matches [GET] "/equipment_index"
All other paths works (like localhost:3000/equipment).
Any ideas whats going on..?
p.s. please do not write how to add a custom path. I hope to solve this in the Rails way - convention over configuration. Thanks.
routes:
equipment_index GET /equipment(.:format) equipment#index
POST /equipment(.:format) equipment#create
new_equipment GET /equipment/new(.:format) equipment#new
edit_equipment GET /equipment/:id/edit(.:format) equipment#edit
equipment GET /equipment/:id(.:format) equipment#show
PATCH /equipment/:id(.:format) equipment#update
PUT /equipment/:id(.:format) equipment#update
DELETE /equipment/:id(.:format) equipment#destroy
routes.rb:
resources :users do
member do
get 'generate_raport'
end
end
resources :client_users
resources :clients
devise_for :users, skip: [:registrations]
resources :equipment
root to: 'static#homepage'
equipment_index is a named route, not a url string. The url string that corresponds to this named route is in this part:
GET /equipment(.:format)
When you say:
equipment_index GET /equipment(.:format) equipment#index
you are really saying that equipment_index is a named route (an alias so to say) for the actual url route localhost:3000/equipment. The last part that says:
equipment#index
just says that your request will be routed through the equipment controller and the corresponding index action.
Solution
You can simply navigate to localhost:3000/equipment to get to the index page for your equipment controller.
For example, you would link to this page using a rails link_to helper and the named route discussed above like this:
link_to "My index path", equipment_index_path
Follow up on comments
change add the following line to your routes.rb file directly after the line that contains resources :equipment. It would now look like:
resources :equipment
get 'equipment', to: 'equipment#index', as: 'equipment'
This is convention over configuration!
You're simply reading the output of rake routes wrong or have the wrong expectations about how its supposed to work. The first column is just the name of the route which is primarily used for creating path helpers. The actual paths are in the third column*.
equipment_index_path() # /equipment
equipment_path(1) # /equipment/1
equipment_path() # error due to missing id param
Since equipment is an uncountable noun Rails cleverly avoids an issue where the generated path helpers would be ambiguous - equipment_path could potentially lead to either the index action or the show action. Regular countable nouns don't have this issue so the _index postfix is not usually needed.
# no ambiguity
cats_path() # /cats
cat_path(1) # /cats/1
While you could argue that rails in that case should use the presence of the id param to differentiate that is not how its built and could mask bugs where you pass nil instead of a record.

Nested routes in rails

I am someone who has always liked sinatra better than rails, and has never had to do a large enough scale project that rails was required (all the sources I have read say that rails is better for larger scale projects) and now I do have to do a large scale project. I have gotten very confused with the url structure of rails. What I am trying to do is the rails equivalent of this:
get "/" do
erb :index
end
get "/home" do
erb :dashboard
end
get "/home/profile" do
erb :profile
end
get "/home/friends" do
erb :friends
end
In the first one I understand that I should put in app/routes.rb I should put root home#index and in the home controller I should put def index end.
In the second one, I understand that I should do the same except replacing index with home.
But for the third and forth ones I have no idea what to do.
Also, is the a RESTful way to do the first two?
You probably want something like this
root 'home#index'
get 'home' => 'home#dashboard'
get 'home/profile' => 'home#profile'
get 'home/friends' => 'home#friends'
remember to use the command rake routes to see all your routes, where they lead and what their names are (if they have any)
I never understood what RESTful means, so someone else will have to answer that part of your question.
K M Rakibul Islam has shown you what can be called a "resourceful" way to do routes (because it uses the keyword resources) but it looks like you're just doing the static pages at this stage, so it's not necessary.
The simplest way to do routes is with this formula:
method url => controller::action, as: route_name
where method can be get, post, patch or delete so you can have different actions linked to the same URL depending on the method the request uses.
Putting a name on the route is optional, but it gives you a clean way to use the route in your views (route_name_path)
When you start making models then you'll find that using the resources keyword comes in handy. Read about it.
You can have this:
resources :home do
collection do
get :profile
end
collection do
get :friends
end
end
end
This will give you routes like this:
profile_home_index GET /home/profile(.:format) home#profile
friends_home_index GET /home/friends(.:format) home#friends
The standard way of declaring the root path:
root 'home#index'
And for the 2nd one, you have to do:
get 'home' => 'home#dashboard'
which will give you this route:
GET /home(.:format) home#dashboard
One route can be defined in many ways that works. But, Rails has conventions that should be followed while defining routes in your Rails app.
I would highly recommend you to take a look at the Rails Routing Guide

Including attributes in custom Rails routes

I hope the title is not to misleading, as I don't know a better title for the problem I'm working on:
I have a doctor which belongs to location and specialty. I'd like to route to show action of the doc controller like this:
/dentist/berlin/7
I defined my routes like this:
get ':specialty/:location/:id', to: 'docs#show'
And in my views create the following url to link to the show action of the doc controller:
<%= link_to doc.name, "#{doc.specialty.name}/#{doc.location.name}/#{doc.id}" %>
Is this a good solution to the problem? If not, is there a cleaner way to construct urls like this possibly using resources? What the heck is the name for a this problem?
Thank your very much for your help in advance.
For references, you should have a look at this page (especially the end of section 2.6)
If it is only for a single route, it's okay as you did. But then if you want to have more than one route (like /dentist/berlin/7, /dentist/berlin/7/make_appointment, etc.) you might want to structure a bit more your routes so as to take advantage of rails resources.
For example, instead of
get ':specialty/:location/:id', to: 'doctors#show'
get ':specialty/:location/:id/appointment', to: 'doctors#new_appointment'
post ':specialty/:location/:id/appointment', to: 'doctors#post_appointment'
You could have something like this (the code is almost equivalent, see explanation below)
resources :doctors, path: '/:specialty/:location', only: [:show] do
member do
get 'new_appointment'
post 'create_appointment'
end
end
Explanation
resources will generate the RESTful routes (index, show, edit, new, create, destroy) for the specified controller (doctors_controller I assume)
The 'only' means you don't want to add all the RESTful routes, just the ones specified
Then you want to add member actions, ie. actions that can be executed on a particular item of the collection. You can chose different syntaxes
resources :doctors do
member do
# Everything here will have the prefix /:id so the action applies to a particular item
end
end
# OR
resources :doctors do
get 'new_appointement', on: :member
end
By default, the controller action is the same as the path name you give, but you can also override it
member do
get 'appointment', action: 'new_appointment'
post 'appointment', action: 'post_appointment'
end
Rails has some wonderful helpers when it comes to routing !
The correct approach is to give your route a name, like this:
get ':specialty/:location/:id', to: 'docs#show', as: 'docs_show'
Then you can use it like this:
<%= link_to doc.name, docs_show_path(doc.specialty.name, doc.location.name, doc.id) %>
Note 1:
Rails appends _path at the end of the route names you define.
Note 2:
You can see all the available named routes by executing rake routes.

Routing error with Rails 3 with members

I have the following route in rails 3:
resources :jobs do
member do
post :seller_job_submitted
end
end
And the following form
=form_for job, :url=>seller_job_submitted_job_path(job), :remote=>true do |f|
I know it's not very restful, but it's kind of a stop gap for now. In any case, I keep getting this error when submitting the form
Started POST "/jobs/74/seller_job_submitted" for 127.0.0.1
ActionController::RoutingError (No route matches "/jobs/74/seller_job_submitted"):
but when I run rake routes | grep seller_job_submitted, I think the correct results come up:
seller_job_submitted_job POST /jobs/:id/seller_job_submitted(.:format) {:action=>"seller_job_submitted", :controller=>"jobs"}
Any ideas about what might be going on?
Thanks!
Assuming you have defined method seller_job_submitted in model and controller.
Replace your code with
resources :jobs
match "jobs/:id/seller_job_submitted" => "jobs#seller_job_submitted", :as => "seller_job_submitted"
Then in form_for tag use :url=>seller_job_submitted_path
This should fix your problem: you did not define seller_job_submitted_job_path explicitly.
Perhaps use put instead of post? Or use :post as the method in the submit form.
You can tell if this is the issue by looking at what the REST method is for the generated form (look for the hidden field in the page source).
So in short, maybe Rails is somehow expecting a POST on that URL but it's receiving a PUT.
Yes, this is a regression bug with Rails 3.
It turns out you need to be careful about using POST in your routes.rb.
resources :jobs do
member do
post :seller_job_submitted # will not work
put :seller_job_submitted # will just work
end
This is even though the FORM method says POST.

Resources