rails3 routes issue - ruby-on-rails

I have following routes.
pota.resources :on_k,
:as => ':klass',
:path_prefix => 'pota/klass',
:controller => 'main'
When I do rake routes this is what I get for show method:
pota_on_k GET /pota/klass/:klass/:id(.:format)
{:action=>"show", :controller=>"pota/main"}
Above code works fine in rails 2.x . However if I am using rails3 then I get following error
ActionController::RoutingError: No route matches
{:action=>"show", :controller=>"pota/main", :klass=>"vehicle/door", :id=>1}
Notice that I am passing 'vehicle/door' as :klass. If I pass a standard model like :klass => 'pet' then it works fine. However if I pass a nested model name like :klass => 'vehicle/door' then I get route error in rails3.
I guess that is because I have '/' in the value . I can solve that by having a regex but I might also pass :klass which is not nested.
On a class like Vehicle::Car I do
Vehicle::Car.underscore #=> vehicle/car
"vehicle/car".camelize.constantize #=> Vehicle::Car
This underscore and camelize/constantize on the other side makes it easier to pass nested class name.
Any idea on how to go about fixing it for rails3?

STOP!
Think about what you're doing here - you should not be calling constantize on url parameters. Assuming that you're likely to be calling find on the result you're basically giving a hacker a way to query every ActiveRecord model in your application.
A better way is to use meta programming to dynamically build static routes that can't be hacked, e.g:
%w[pet vehicle/car vehicle/bike].each do |klass|
resources :pota,
:path => "pota/#{klass.pluralize}",
:as => "pota_#{klass.tr('/','_').pluralize}",
:defaults => { :klass => klass }
end
Then you can add a helper method that calls the appropriate named route helper to generate urls based upon a passed model instance.

Related

Rails 4: You should not use the `match` method in your router without specifying an HTTP method

Okay, so I've upgraded to Rails 4 (kind of unplanned with my 10.9 server update) and have been able to get everything running on my photo gallery app except for the routes. For some reason I've always had trouble understanding routes since rails 3. Here was my previous working code under Rails 3
root :to => "gallery#index", :as => "gallery"
get 'gallery' => 'gallery#index'
resources :galleries
match 'gallery_:id' => 'gallery#show', :as => 'gallery'
I understand that match has been depreciated, but if I try to use GET, I'm getting the following error:
Invalid route name, already in use: 'gallery' You may have defined two routes with the same name using the :as option, or you may be overriding a route already defined by a resource with the same naming.
Basically, I want the root (index) to load as "/photos/gallery" as it does, and my show action to load, for example, record id 435 as: "/photos/gallery_435" which is how I previously had it working. Sorry for what is probably a simple question, I just cannot seem to grasp the rails routing.
Try this
match 'gallery_:id' => 'gallery#show', :via => [:get], :as => 'gallery_show'
You can then refer to this path as gallery_show_path in your helpers and views.
Changing the 'as' removes the conflict.

Route override doubles records

I'd like to override a default path of an Spree/Rails extension.
The extension spree_contact_us defines default route in it's config/routes.rb this way:
Spree::Core::Engine.routes.draw do
resources :contacts,
:controller => 'contact_us/contacts',
:only => [:new, :create]
match 'contact-us' => 'contact_us/contacts#new', :as => :contact_us
end
In the routes table there is just one record for route named contact-us:
contact_us /contact-us(.:format) spree/contact_us/contacts#new
If I pass following override in main application's config/routes.rb to routes.prepend method
Spree::Core::Engine.routes.prepend do
match 'napiste-nam' => 'contact_us/contacts#new', :as => :contact_us
end
rake routes displays routes to a new named path twice, when passed to routes.append even three times:
contact_us /napiste-nam(.:format) spree/contact_us/contacts#new
contact_us /napiste-nam(.:format) spree/contact_us/contacts#new
Can anybody explain this behaviour ?
The problem here is that you will be creating an ambiguous named route :contact_us which when referenced by contact_us_path will return the path for the last entry in routes because you are redefining it.
The duplication does seem strange but I have not looked at how spree handles these things.
In order to avoid this you could rename the secondary route such as
Spree::Core::Engine.routes.append do
match 'napiste-nam' => 'contact_us/contacts#new', :as => :contact_us_czech
end
This should create 2 routes in which you could use contact_us_path and contact_us_czech_path which will both lead to the same place. then create a method to determine which to use.
Or just add the new route directly into the spree routing tables as (PROBABLY NOT VALID DUE TO CALL TO routes_reloader in Spree Core.
match 'napiste-nam' => 'contact_us/contacts#new', :as => :contact_us
match 'contact_us' => 'contact_us/contacts#new', :as => :contact_us
Just remember that this means that contact_us_path with always reference the second route.
Edit
It seems Spree builds the default routes and then reloads them after initializing as is stated in the code
# We need to reload the routes here due to how Spree sets them up.
# The different facets of Spree (backend, frontend, etc.) append/prepend
# routes to Core *after* Core has been loaded.
#
# So we wait until after initialization is complete to do one final reload.
# This then makes the appended/prepended routes available to the application.
config.after_initialize do
Rails.application.routes_reloader.reload!
end
I believe this is causing the named route :contact_us to be routed to it's defined route meaning that you defined it as contact_us and then redefined it as napiste-nam and since a variable can have only 1 value it held on to the second one on reload!. Due to this fact I am not sure you can do this directly through Spree.
Using
Spree::Core::Engine.routes.draw
instead of
Spree::Core::Engine.routes.prepend
solved the routes duplication problem for me.

Getting Routing Error(Route doesn't match)

I am trying to update multi-select list on change but I am getting a routing error.
I call this with an onchange event $.post("/levels/category_lists_for_level"
I have an action called category_lists_for_level in a controller called level.
My routes file looks like this.
match '/levels/category_lists_for_level/:id' => 'levels#category_lists_for_level'
resources :levels
resources :levels , :collection => {:category_lists_for_level => :get}
What am I doing wrong here? I never had any problem in Rails 2, all I used to add the collection.
It is a bit hard to say exactly what you need since as others have said you are missing some info, but you have a few apparent things going on here:
You are duplicating routes
You have the route set on a collection and a member
You are allowing multiple requests types (get and post) to access this route.
If you would like to have this operate on a collection you just need:
resources :levels do
post "category_lists_for_level", :on => :collection
end
or on a member:
resources :level do
get "category_lists_for_level", :on => :member
end
This will reduce your routes. Just use rake routes | grep level to get the routes for this controller.
Take a look at this for some more info.

Do you have to define controller helpers to get paths for new routes in rails?

I have a Customer model and I want his controller to repond to a find method
I added this in my routes.rb file:
match 'customers/find/:name' => 'mymodel#find' resources :customers
In my controller I have something like this:
def find
#customers = Customer.fin_all_by_name(params[:name])
end
in my views, when I need to create a link for that resource I'm using this:
= link_to 'Find By Name', :controller => "customers", :action => "find", :name => #customer.name
now, I'm trying integration tests with cucumber and I have a problem: I have to create a step definition in my customer_step.rb file for customers having same name:
when /^customers having same name as "(.*)"/
url_encode('/customers/find/' + $1)
now that line doesn't work, it says undefined method `url_encode'
I need to encode that string because if the name contains a space I get obvious errors.
I'm new to ruby and rails and I think I'm missing something here.
Am I following the right pattern to accomplish this search?
Should I define an helper method in my controller to generate search urls?
Is it correct that line I have in my _step.rb file?
I don't want urls to be like this: customers/find?name=test
but: customers/find/test
I just sorted it out, I slightly modified my match expression and added the :as parameter
and this gave me the possibility to call find_path() helper method
match 'customers/find/:name' => 'customers#find', :as => :find
Is this correct?
Using :as should indeed create a route helper for you. If you want to get a list of your matched routes, to which controller/action they route, and the name of the route helper, use rake routes in console.

make pretty url with routes.rb

I would like to do something to this effect, I believe:
map.connect 'show/:company_name/:id',
:controller => 'companies',
:action => 'show'
Basically, each time the show action is called, I would like it to take the company_name param and place it into the url as such (show/:company_name/:id)
However, it seems I am using old (rails 2.x routing api) and cannot use map.connect without getting an error. How can I upgrade this?
Is there some way to do this with "match"?
Thanks!
===================
This is the error I see when I try to use map.connect:
undefined local variable or method `map' for #<ActionDispatch::Routing::Mapper:0x103757458>
I think your routes lack a "/" symbol in the first line.
Try this:
match '/show/:company_name/:id' => 'companies#show'
You can check your routes path with command rake routes.
--
Besides, the show action is the default RESTful method in Rails. I'll suggest you change a equivalent word, and reserve "show" action for future or other situation.
In Rails convention, you can write resources :companies, and the path will be /companies/:id using show action.
Some adjustment, in app/models/company.rb
def to_param
self.name
end
So your url will look like http://yourdoamin.com/companies/37signals.
In app/controllers/companies_controller.rb
#company = Company.find_by_name(params[:id])
If I'm understanding your goal, try
match 'companies/show/:company_name/:id' => 'companies#show'

Resources