Ruby on Rails - Setting a Custom Route with Custom Action - ruby-on-rails

i'm a RoR beginner using rails 3.2.3.
I have a products resource and a nested Availability resource under it.
My routes are:
/products/:product_id/availabilities
which is working great.
However, my availability model allows to set the price of the product on an exact date, for e.g.: today the product 'x' will have 'y' availability (it is an integer) and 'z' price (price is also an attribute of the Availability Model).
My question is, I now want to add a page that allows the modification of prices (without creating a new Price Model), similar to the one I have for availability. I'm only using the index action, as it will show all the availabilities for that only selected product.
How can I create a route like:
/products/:product_id/availabilities/prices
I tried adding this to my routes rb:
match '/products/:id/availabilities/prices', :action => 'indexP', :via => [:get], :controller => 'prices'
match '/products/:id/availabilities/prices', :action => 'createP', :via => [:post], :controller => 'prices'
It is worth noting that I am using a prices controller to interact with the same availability model, but that shouldn't be a problem.
I also created in that prices controller the indexP and createP actions.
In my Availabilities View I created an indexP.html.erb to differentiate from the regular index.html.erb which contains the availabilities.
And when I run rake routes I get:
$ rake routes | grep prices
GET /products/:id/availabilities/prices(.:format)
POST /products/:id/availabilities/prices(.:format)
Which is great. However, when I access that URL, I get:
NoMethodError in Availabilities#show
{"product_id"=>"46",
"id"=>"prices"}
Why is it doing the show action instead of my custom match?
Note: I am not after something like: /products/:id/availabilities/:id/prices
just :/products/:id/availabilities/prices
I know this is probably a dumb question but I would appreciate any tips to help me understand what is going on.
Thanks in advance,
regards

How is your product model setup?
If it something like:
resources :products do
resources :availabilities
end
the first match the route does goes to the show action of the availabilities controller.
You have two options here. Limit the availabilities controller scope, with:
resources :products do
resources :availabilities, :only => [:index]
end
or to place your custom match before this block
match '/products/:id/availabilities/prices', :action => 'indexP', :via => [:get], :controller => 'prices'
match '/products/:id/availabilities/prices', :action => 'createP', :via => [:post], :controller => 'prices'
Note that this routes are not restfull. This may get you in trouble later :)
Hope this helps.

Related

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.

Rails: a singular resource nested on the index of another resource

I have an User(plural) and a Subscription(singular) controllers. I am wondering if I can set a route like the following:
/users/subscription/edit
which means editing the subscription of the current user. All examples I see are like /users/1/subscription/edit
This way any user can point to this same url and it will direct to their settings page.
Make the routes like :
match '/users/subscription/edit' => 'subscription#edit' : as => "subscription_edit"
OR
match '/users/subscription/edit', :controller => 'subscription', :action => 'edit' : as => "subscription_edit"

Routing: prefix item name before controller resources

I am trying to get the following routes to work in my Rails 3 app.
Scenario:
I have the following controllers in my app:
Practices
Doctors
Patients
Prescriptions
Putting up :resources for each of them in routes.rb gives me routes from
example.com/practices
example.com/doctors/1/edit etc
What I'd like to have however is the following resourceful routes, for example:
example.com/james_practice/docs that translates to the doctors controller
example.com/james_practice/awesome_prescriptions that routes to the prescriptions controller etc. etc.
both of these giving me access to :practice name and furthermore route the correct controller with all the helpers like edit_docs_path(doctor) etc.
How would I go about this? I've used
resources :prescriptions, :path => "/:practice/awesome_prescriptions"
but although it showed the correct routes in "rake routes", it still wouldn't work as expected.
I think this is the route you're looking for:
scope :path => ":practice" do
resources :docs, :controller => "doctors"
resources :awesome_prescriptions, :controller => "prescriptions"
end
By the way, you didn't give me the example of Patients, so I didn't put it there.
map.resources is just a pattern, more like a shortcut. If you want to have URLs like in your examples you should use "named routes". A good screencast for Rails 2.x: http://railscasts.com/episodes/34-named-routes
Edit: Read section 3.2 in Rails Tutorial: http://guides.rubyonrails.org/routing.html
p.s. Also you'll face a case where people use "." in their names and it'll cause problems.
Here's my route for "tags"
map.resources :tags, :requirements => { :id => %r([^/;,?]+) }

Handling ambiguous routes in Rails

Here's my dilemma: I have two types of routes which are semantically very different, and should go to different controllers.
ny/new-york/brooklyn/cleaners # should go to a list of cleaners for a neighborhood
ny/new-york/cleaners/mrclean # should go to an individual cleaner's page
Note that "brooklyn" and "cleaners" here are just examples. The app has many service types (e.g. "cleaner") and many neighborhoods, so it's impossible to hard-code a list of either into a regular expression and use that to distinguish the two routes.
Is it possible to involve an arbitrary method, which accesses ActiveRecord models, in the routing decision? I'm using Rails 2.3.8.
Edit : new answer with dynamic services
Looking at this blog entry it seems possible to use ActiveRecords in the routes.
Maybe you could do something like this :
service_names = Service.all.map(&:short_name) # assuming the property 'short_name' is the value used in urls
service_names.each do |service_name|
map.connect ':state/:city/#{service_name}/:company' :controller => ‘company’, :action => ‘show’ # to show the company's page
map.connect ':state/:city/:neighborhood/#{service_name}_finder' :controller => ‘company_finder’, :action => ‘find’ # to list the companies for the given service in a neighborhood
end
That should still prevent conflicts since the routes for a certain service is before a route for a neighborhood
Old bad answer
Can't you use the two following routes ?
map.connect ':state/:city/cleaners/:cleaner' :controller => ‘cleaners’, :action => ‘show’ # to show the cleaner's page
map.connect ':state/:city/:neighborhood/cleaners' :controller => ‘cleaner_finder’, :action => ‘find’ # to list the cleaners of a neighborhood
In your controller, you should be able to retrieve :state, :city and others value using params[:state], params[:city], etc.
Putting the :state/:city/cleaners/:cleaner on the first line should prevent ambiguity.

Resources