How does path_prefix and requirements in Rails routes work? - ruby-on-rails

Taking the 2 examples below - how do they work?
map.resources :api_developers, :path_prefix => '-'
map.connect '-/:controller/:action/:id', :requirements => { :controller => /metrics|labs/ }

The :path_prefix option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route:
map.resources :photos, :path_prefix => '/photographers/:photographer_id'
Routes recognized by this entry would include:
/photographers/1/photos/2
/photographers/1/photos
So yours samples
first one
/-/api_developers/
/-/api_developers/1
/-/api_developers/1/edit
etc
second one
/-/metrics/:action/:id
/-/labs/:action/:id
since there is no requirements on :action and :id they can be any string like
/-/metrics/first_string/second_string

Related

Ruby on Rails - Setting a Custom Route with Custom Action

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.

Github like routes in Rails

Using github like chain routes in rails
I have URLs similar to this:
'localhost:3000/document_managers/[:module_name]'
'localhost:3000/document_managers/[:module_name]/1/2/3/.' # can be any level deep
Here is the route definition for them:
map.connect '/document_managers/:module',
:controller => "document_managers",
:action => :new_tree,
:module => ["A","B","C"]
map.connect '/docuemnt_managers/:module/*path',
:controller => "document_managers",
:action => "new_tree",
:module => ["A","B","C"]
Here is the problem:
The idea that module name value can't be anything except from the
given above array i.e("A","B","C") like at any time the URL must be something like
localhost:3000/document_managers/A/1 or
localhost:3000/document_managers/B/221/1 or
localhost:3000/document_managers/C/121/1
but that not the case even though
localhost:3000/document_managers/D/121/1 is treated as valid url
and module is set to D even though the "D" is not in listed array
above
I want the the URL localhost:3000/document_managers/A to
also redirect to same action i.e new_tree if the extra parameter isn't
provided as in the URL contain extra parameters
localhost:3000/document_managers/C/121/1 then the URL is redirected
appropriately to the desired controller and action but if the URL only
contain the path until the module name the Rails return a routes
ActionController::UnknownAction I don't know why as I have already
defined the controller and action.
In Rails 3.1, you can do this in your routes file to get what you want:
match '/document_managers/:module',
:controller => "document_managers",
:action => :new_tree,
:constraints => {:module => /[ABC]/}

Rails REST routing: dots in the resource item ID

I have following in my routes.rb:
resources :users, :except => [:new, :create] do
get 'friends', :as => :friends, :on => :member, :to => "users#friends"
end
and following in my user.rb:
def to_param
self.login
end
And when, for example, user with dots in login (for example 'any.thing') comes from facebook, rails gives routing error (no route found, I suppose that's because it recognises anything after dot as a format or because of route constraints). How can I come over this error?
The following constrain definition permit the dot in id as well as any character except slash.
Supported formats must be explicitly defined (here .html and .json) to not to be taken by id.
resources :foobars,
:constraints => { :id => /[^\/]+(?=\.html\z|\.json\z)|[^\/]+/ }
That constrain definition is worked with Rails 3.1
For earlier Rails versions you may need to backport look-ahead support in regin gem (it is vendored in rack-mount gem)
You could replace periods with another character:
def to_param
login.gsub(/\./,"-") # note: 'self' is not needed here
end
user = User.find_by_login("bart.simpson")
user_path(user) # => "/users/bart-simpson"
EDIT
You're right, this fails to deal with unique logins that map to the same value. Maybe a better way is to use segment constraints in the route:
match 'users/(:id)' => 'users#show',
:constraints => { :id => /[0-9A-Za-z\-\.]+/ }
This should allow "/users/bart-simpson" and /users/bart.simpson" to generate :id => "bart-simpson" and :id => "bart.simpson" respectively. You'd have to alter the regex to add all the acceptable characters for the URL.
Note that this is mentioned in the Rails Routing Guide, section 3.2:
By default dynamic segments don’t accept dots – this is because the
dot is used as a separator for formatted routes. If you need to use a
dot within a dynamic segment add a constraint which overrides this –
for example :id => /[^\/]+/ allows anything except a slash.
To allow the :id segment to contain any character except '/':
match 'users/(:id)' => 'users#show', :constraints => {:id => /[^\/]+/}
It's written elsewhere in one of the answers, but this is IMO the simplest way.

problem in routes

i want to change the default route in RoR to what i want:
consider the following example...
:controller/:action/:id
which will give you the route in the browser as:
http://localhost:3000/controller/action/id
now i want to change it to...
http://localhost:3000/this-is-what-i-want/id
we can get an alias for the controller and the action as well like...
resources :controller, :as => "my-custom-name"
and if you want to have the alias for the action, then
resources :controller, :path_names => { :action => 'my-custome-name-1', :action => 'my-custome-name-2' }
BUT i want to change the controller and the action at once... if u noticed the above http://localhost:3000/this-is-what-i-want/id path in the question...
need help...
thanks in advance...
You need a named route.
In Rails2:
map.a_name 'this-is-what-i-want/:id', :controller => 'controller_name', :action => 'action_name'
In Rails3:
match 'this-is-what-i-want/:id' => 'controller_name#action_name'
You want to be using Rest routes, rather than controller/action
I'm going to use "balls" instead of "this-is-what-i-want"
resources :balls
Then, when you link to a ball, do link_to(ball.name, ball).
This will give you a link of http://localhost:3000/balls/45
This rails rest cheatsheet is a good start.

Ruby on Rails: Routing for a tree hierarchy of places

So we've got a legacy system that tracks places with IDs like "Europe/France/Paris", and I'm building a Rails facade to turn this into URLs like http:// foobar/places/Europe/France/Paris. This requirement is not negotiable, the number of possible levels in unlimited, and we can't escape the slashes.
Setting up routes.rb for http://foobar/places/Europe is trivial:
map.resources :places
...but http:// foobar/places/Europe/France complains "No action responded to Europe". I tried:
map.connect '/places/:id', :controller => 'places', :action => 'show'
...but this gives the same result, as apparently the :id ends at the first '/'. How do I make the ID cover anything and everything after the "places"?
Have a look at the Routing Guide for full documentation:
http://guides.rubyonrails.org/routing.html
Specifically section "4.9 Route Globbing".
But I think what you really want to do is declare your route like:
map.connect '/places/*id', :controller => 'places', :action => 'index'
Called with a URL like
/places/foo/bar/1
Yields a params[:id] => ["foo", "bar", "1"]
Which you could easily (re)join with "/" to yield the full string you want "foo/bar/1" (you will probably have to re-insert the leading slash manually.
That should get you going.
I tweaked Cody's answer above slightly to come up with this:
map.place '/places/*id', :controller => 'places', :action => 'show'
map.connect '/places/*id.:format', :controller => 'places', :action => 'show'
By using map.place instead of map.connect, Rails knows what resource we're dealing with and generated place_url, place_path etc helpers correctly.
Now, the 2nd line should work but doesn't thanks to the bug above, so here's a workaround for places_controller.rb that manually splits the ID and sets the format, defaulting to XML:
id, suffix = params[:id].join('/').split('.')
params[:format] = suffix ? suffix : "xml"

Resources