Confusing routing error - ruby-on-rails

I know this is a newbie question, but I haven't seen an explanation and I'd like one.
What exactly does it mean when Rails issues a routing error like this:
Routing Error
No route matches {:action=>"show", :controller=>"library_imports", :library_id=>#<Library id: 1, ...
What puzzles me is that the message itself shows that my request is being routed to the show action of the library_imports controller. How does that happen if the request URL didn't match any routes?
For the sake of completeness, the URL I'm hitting that results in this error is:
http://localhost:3000/libraries/2/library_imports
which should map to the "index" action, not "show".
The relevant part of config/routes.rb is:
Import::Application.routes.draw do
resources :libraries do
resources :library_imports
end
And the pertinent portion of rake routes output is:
library_library_imports GET /libraries/:library_id/library_imports(.:format) {:action=>"index", :controller=>"library_imports"}
POST /libraries/:library_id/library_imports(.:format) {:action=>"create", :controller=>"library_imports"}
new_library_library_import GET /libraries/:library_id/library_imports/new(.:format) {:action=>"new", :controller=>"library_imports"}
edit_library_library_import GET /libraries/:library_id/library_imports/:id/edit(.:format) {:action=>"edit", :controller=>"library_imports"}
library_library_import GET /libraries/:library_id/library_imports/:id(.:format) {:action=>"show", :controller=>"library_imports"}
PUT /libraries/:library_id/library_imports/:id(.:format) {:action=>"update", :controller=>"library_imports"}
DELETE /libraries/:library_id/library_imports/:id(.:format) {:action=>"destroy", :controller=>"library_imports"}

The error message is misleading for what it doesn't say, rather than what it says. There is indeed no route that can be constructed from :controller => :library_imports, :action => :show, :library_id => #library, because that hash omits the required :id parameter.
It might be less confusing if there were some hint, like (did you leave out a required parameter?).
It's also useful to note that this error is generated (I believe) in the url_for helper, not in the dispatcher. So the request is not actually being routed at all.

Related

How to get url_for working with a module?

I have mounted FullcalendarEngine in my routes.rb:
mount FullcalendarEngine::Engine , at: "/fullcalendar_engine"
Unfortunately, even though I have this in routes.rb:
resources :events, module: 'fullcalendar_engine'
And the generated routes:
fullcalendar_engine_path /fullcalendar_engine FullcalendarEngine::Engine
events_path GET /events/index(.:format) fullcalendar_engine/events#index
event_path GET /events/:id(.:format) fullcalendar_engine/events#show
events_path GET /events(.:format) fullcalendar_engine/events#index
POST /events(.:format) fullcalendar_engine/events#create
new_event_path GET /events/new(.:format) fullcalendar_engine/events#new
edit_event_path GET /events/:id/edit(.:format) fullcalendar_engine/events#edit
event_path GET /events/:id(.:format) fullcalendar_engine/events#show
PATCH /events/:id(.:format) fullcalendar_engine/events#update
PUT /events/:id(.:format) fullcalendar_engine/events#update
DELETE /events/:id(.:format) fullcalendar_engine/events#destroy
I still cannot use url_for with it (which I need to do in order to get it working with will_paginate):
Error:
RailsDevise::Application.routes.url_for({ controller: 'events', action: 'index'})
=> ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"events"}
FullcalendarEngine::Engine.routes.url_for({ controller: 'events', action: 'index'})
=> ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"events"}
When I inspect the module's routes:
FullcalendarEngine::Engine.routes.routes.collect {|journey| journey.defaults }
=> [{:controller=>"fullcalendar_engine/events", :action=>"index"}, {:action=>"get_events", :controller=>"fullcalendar_engine/events"}, {:action=>"move", :controller=>"fullcalendar_engine/events"}, {:action=>"resize", :controller=>"fullcalendar_engine/events"}, {:action=>"index", :controller=>"fullcalendar_engine/events"}, {:action=>"create", :controller=>"fullcalendar_engine/events"}, {:action=>"new", :controller=>"fullcalendar_engine/events"}, {:action=>"edit", :controller=>"fullcalendar_engine/events"}, {:action=>"show", :controller=>"fullcalendar_engine/events"}, {:action=>"update", :controller=>"fullcalendar_engine/events"}, {:action=>"update", :controller=>"fullcalendar_engine/events"}, {:action=>"destroy", :controller=>"fullcalendar_engine/events"}]
Notice it has this:
#defaults={:controller=>"fullcalendar_engine/events", :action=>"index"}
Notice the namespace for the controller value. This does not work:
FullcalendarEngine::Engine.routes.url_for({:controller=>"events", :action=>"index"})
=> ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"events"}
But if I try to namespace it, it gives this error:
FullcalendarEngine::Engine.routes.url_for({:controller=>"fullcalendar_engine/events", :action=>"index"})
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
Even adding a host generates the wrong url:
FullcalendarEngine::Engine.routes.url_for({:controller=>"fullcalendar_engine/events", :action=>"index", host: "localhost"})
=> "http://localhost/fullcalendar_engine/"
How can I get url_for to recognize the route?
Your routes expect an id parameter, so you need to give it one:
url_for(controller: 'events', action: 'show', id: 5) # < Notice the `id`
Additionally, the routes you posted do not include a route for the index, so it may not exist. If it did, it probably wouldn't require an id
For anyone else that encounters this. I stepped through the Rails code and this is what fixed it for me:
FullcalendarEngine::Engine.routes.url_for({:action=>"index", host: "localhost"}, 'fullcalendar_engine_path')
It looks that a question asked 5 years ago may still be pretty relevant. I'm hoping that you have already found a solution. :)
I have been hit by similar problem in Rails 6.0.3 (a little old, isn't it?), where the url_for was exploding in a very similar manner for a controller from a non-isolated engine, inheriting from ::ApplicationController
I stumbled upon this chain of issues and comments:
https://github.com/rails/rails/pull/37927#issuecomment-695934757
https://github.com/rails/rails/pull/40263
https://github.com/rails/rails/pull/40264 (closed as stale)
https://github.com/rails/rails/pull/41463 (merged, backported to Rails 6.1)
Long story short, this ugly hack may save your hide for a while, until you upgrade to newer Rails:
class Foo::BarController < ::ApplicationController
# your normal stuff
private
def _routes # <- this overrides an inherited method, so keep the name!
#_routes || my_engine_routes
end
def my_engine_routes # <- and this you can name as you want
#_my_engine_routes ||= Foo::Engine.routes
end
end
Just be sure to carefully test whether it works well for your case, and do not forget to remove it once you upgrade. It may cause nasty conflicts with some deep magic in Rails!

Routing resource does not handle "new" as expected

I have a simple rails application in which I'm trying to add a very simple type of record ("client_types") to a database.
I have a route in routes.rb which reads:
resources :client_types
And as I understand it, should be a proxy to all of the conventional routes for my client_types resource.
So when I browse to the following URL http://localhost:3000/client_types/new, I get the following routing error at runtime:
No route matches {:action=>"show", :controller=>"client_types"}
Notice the action in question here is show, not new (and I have a method for both of these in my controller).
So... I added the following route below the resources route above, and viola, it works:
match 'client_types/new' => 'client_types#new', :as => :client_type
So what am I missing? My assumption was that resources :client_types in my routing file would have added a route matching the one I explicitly added later.
rake routes reveals the following:
client_types GET /client_types(.:format) {:action=>"index", :controller=>"client_types"}
POST /client_types(.:format) {:action=>"create", :controller=>"client_types"}
new_client_type GET /client_types/new(.:format) {:action=>"new", :controller=>"client_types"}
edit_client_type GET /client_types/:id/edit(.:format) {:action=>"edit", :controller=>"client_types"}
client_type GET /client_types/:id(.:format) {:action=>"show", :controller=>"client_types"}
PUT /client_types/:id(.:format) {:action=>"update", :controller=>"client_types"}
DELETE /client_types/:id(.:format) {:action=>"destroy", :controller=>"client_types"}
client_type /client_types/new(.:format) {:controller=>"client_types", :action=>"new"}
This is now working. I had an issue in one of my views and this error message was a red herring.

Understanding rake routes output

I'm confused by my rake routes output. For an example (trimmed):
profil GET /profil/:id(.:format) {:action=>"show", :controller=>"profil"}
PUT /profil/:id(.:format) {:action=>"update", :controller=>"profil"}
login GET /login(.:format) {:action=>"new", :controller=>"sessions"}
POST /login(.:format) {:action=>"create", :controller=>"sessions"}
logout GET /logout(.:format) {:action=>"destroy", :controller=>"sessions"}
I've always thought:
Line 2: Route can be accessed using profil_path with PUT method.
Line 4: Route can be accessed using login_path with POST method.
Conclusion: Lines with the first column empty (line 2 and 4) would follow the one above it.
However, I've been experimenting with adding parameter to the url. So, I added these codes in my routes.rb:
namespace :admin do
resources :pengguna_bulk, :only => [:new, :create]
resources :pengguna do
collection do
get 'index/:page', :action => :index
end
end
end
New rake routes output (trimmed):
admin_pengguna_bulk_index POST /admin/pengguna_bulk(.:format) {:action=>"create", :controller=>"admin/pengguna_bulk"}
new_admin_pengguna_bulk GET /admin/pengguna_bulk/new(.:format) {:action=>"new", :controller=>"admin/pengguna_bulk"}
GET /admin/pengguna/index/:page(.:format) {:action=>"index", :controller=>"admin/pengguna"}
admin_pengguna_index GET /admin/pengguna(.:format) {:action=>"index", :controller=>"admin/pengguna"}
POST /admin/pengguna(.:format) {:action=>"create", :controller=>"admin/pengguna"}
new_admin_pengguna GET /admin/pengguna/new(.:format) {:action=>"new", :controller=>"admin/pengguna"}
edit_admin_pengguna GET /admin/pengguna/:id/edit(.:format) {:action=>"edit", :controller=>"admin/pengguna"}
admin_pengguna GET /admin/pengguna/:id(.:format) {:action=>"show", :controller=>"admin/pengguna"}
PUT /admin/pengguna/:id(.:format) {:action=>"update", :controller=>"admin/pengguna"}
DELETE /admin/pengguna/:id(.:format) {:action=>"destroy", :controller=>"admin/pengguna"}
My question is, why is the 3rd route looks like it's under the 2nd route? Is it empty because Rails do not know what to name it and I'd have to use get 'index/:page', :action => :index, :as => :page to name it?
So, this means, route with an empty first column doesn't always follow the above path?
I've always thought:
Line 2: Route can be accessed using profil_path with PUT method.
Line 4: Route can be accessed using login_path with POST method.
Conclusion: Lines with the first column empty (line 2 and 4) would
follow the one above it.
Everything's correct except the conclusion. profil_path expands to /profil/:id(.:format). If it is called with method GET it responds to your first route, if its called with method PUT it responds to your second route.
But same doesn't hold true for second set of routes. You don't have any named helper for /admin/pengguna/index/:page(.:format). If you want a named helper, you should define the route like:
get 'index/:page', :action => :index, :as => :what_ever_named_helper_you_want

Rails 3 routing and namespaces

I want to have a namespaced controller called 'portal'.
in this will be nested resources such as companies and products.
I'd like routes like :
/portal/:company_id/product/:id to work
I can get
/portal/company/:company_id/product/:id to work but would like to eliminate the 'company' in the url
Hope that is clear. Please keep in mind that I need the namespaced module portal to exist.
I think you could use scope to achieve what you want. Perhaps like this:
namespace "portal" do
scope ":company_id" do
resources :products
end
end
That will generate the following routes:
portal_products GET /portal/:company_id/products(.:format) {:action=>"index", :controller=>"portal/products"}
POST /portal/:company_id/products(.:format) {:action=>"create", :controller=>"portal/products"}
new_portal_product GET /portal/:company_id/products/new(.:format) {:action=>"new", :controller=>"portal/products"}
edit_portal_product GET /portal/:company_id/products/:id/edit(.:format) {:action=>"edit", :controller=>"portal/products"}
portal_product GET /portal/:company_id/products/:id(.:format) {:action=>"show", :controller=>"portal/products"}
PUT /portal/:company_id/products/:id(.:format) {:action=>"update", :controller=>"portal/products"}
DELETE /portal/:company_id/products/:id(.:format) {:action=>"destroy", :controller=>"portal/products"}
Edit: Accidentally used resource instead of resources. Fixed now.
You can customize the routes to nearly whatever you want if you spell them out directly, like this:
match '/portal/:company_id/product/:id', :to => 'companies_products#show'
The :to part specifies the controller and action to use, something that should match what you have in your routes now. If you're not sure what that is, rake routes will tell you its specific interpretation.

Rails 3 restful route problem

The following link works in my app:
<%= link_to "invitation", :controller => :invitations, :action => :index %>
To follow restful conventions i changed the link to:
<%= link_to "invitation", index_invitation_path %>
The error that i get is:
undefined local variable or method `index_invitation_path'
Rake routes yields:
invitations GET /invitations(.:format) {:controller=>"invitations", :action=>"index"}
The page name is index.html.erb. The model is invitation.rb. The controller is invitation_controller.rb. Routes has resources :invitations. What am i missing?
Thanks!
Assuming you have the routing correct:
resources :invitations
Then the correct helper for the index action (with the url /invitations.html) is
invitations_path
You can see more information by running rake routes. It will display text like the following:
lists GET /lists(.:format)
{:action=>"index", :controller=>"lists"}
POST /lists(.:format)
{:action=>"create", :controller=>"lists"}
new_list GET /lists/new(.:format)
{:action=>"new", :controller=>"lists"}
edit_list GET /lists/:id/edit(.:format)
{:action=>"edit", :controller=>"lists"}
list GET /lists/:id(.:format)
{:action=>"show", :controller=>"lists"}
PUT /lists/:id(.:format)
{:action=>"update", :controller=>"lists"}
DELETE /lists/:id(.:format)
{:action=>"destroy", :controller=>"lists"}
root /(.:format)
{:controller=>"lists", :action=>"index"}
The above was from a route of my own (for a model called List). The route helper method is shown immediately before the HTTP method. You have to remember to append the _path to each helper method. For example the helper methods I could use are:
list_path(list)
edit_list_path(list)
new_list_path
lists_path
You'll need a route in your routes.rb file that defines a mapping to the invitations controller and the index action.
Typically this is created with a resources call
resources :invitations
Which creates several default routes, which you can see by running rake routes.
For single resources, you can also define it using a match call
match "invitations/:id" => "invitations#index", :as => index_invitation
The rails site has a great resource on routing that provides all the details: Routing from the Outside In
Update: Based on your updated question, your route includes an invitaions (notice the trailing 's') route - nothing with index or invitation. The index_ prefix is generated by the resources call when it creates the default routes for :invitations.
It looks like you've defined a custom get mapping for an invitation. While this may technically work, if you're aim is to support restful routes, use the resources method. And have a read of the Routing guide from rails it's very easy to follow and quite detailed.
type rake routes in your console and look at listing of available routes. Seems to be there is no such route index_invitation_path? maybe it named differently
I think you need "invitations_controller.rb" to contain InvitationsController. Plural.

Resources