Making Rails Resource and Custom Routes Conflict Work - ruby-on-rails

I am new to rails and was wondering how I can make this work. I want a URL to look like this:
http://localhost:3000/businesses/coldfire-gundam
using this route:
match "/businesses/:permalink", :to => "businesses#show", :as => :business_permalink
however when I place this route before this:
resources :businesses
any call to /businesses/1 (1 as param[:id]) does not work anymore, obviously because it is caught by the permalink declaration
how can I make it work then?

You need a way to differentiate /businesses/:id and /businesses/:permalink. The :id should always be numeric (unless of course you're using MongoDB) so if you can force your :permalink to always contain something non-numeric then a simple :constraints should do the trick:
match '/businesses/:permalink', :to => 'businesses#show`, :constraints => { :permalink => /.*\D/ }, :as => :business_permalink
The /.*\D/ forces the route to only match if :permalink contains at least one non-numeric character. You need the .* because route regexes are implicitly anchored at the beginning.
If you happen to be using MongoDB then your :id will probably be a hex BSON ID so you'd want to use /.*\H/ as your constraint and you'd want some way to ensure that your :permalink always contains at least one non-hex character.
Once all that's in place you can put your match "/businesses/:permalink" before your resources :businesses in routes.rb and everything should work fine. And routes are checked in the same order that they appear in routes.rb so you will want your match before your resources.

I would suggest using the friendly_id gem for creating permalink routes. This will handle most of the 'magic' for you in an easily reusable way.
Resources for the gem and railscast:
https://github.com/norman/friendly_id
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid

Related

How can I rename a Rails controller with a route?

I have a controller in a Rails 3 app named "my_store." I would like to be able to use this controller as is, except replacing "my_store" in all the URL's with another name. I do not want to rename the controller file, and all the references to it. Is there a clean way to do this with just a routing statement?
If you use RESTful routes:
resources :another_name, :controller => "my_store"
Otherwise:
match "another_name" => "my_store"
If your routes are RESTful, this is pretty easy.
resources :photos, :controller => "images"
You can see how to do this and other helpful Rails routing information in the Rails routing guide.
Update, the other guys are correct, to replace all references you would change the resources name and corresponding controller in routes.rb! My answer is only good to set a specific route.
Yup, you would do this in your routes.rb using the :as option to specify
example:
match 'exit' => 'sessions#destroy', :as => :logout
source

Implicit creation of helpers - routes.rb and 'match' statements

I am reading Obie Fernandez' "The Rails 3 Way", and there is a bit of it that I am not sure I understand correctly. I am new to rails, and want to make sure I understand it correctly. I have some experience with vanilla Ruby. Not much, but some.
The text in question is as follows: (regarding routing and the config/routes.rb file)
"...
By creating a route like
match 'auctions/:id' => "auction#show", :as => 'auction'
you gain the ability to use nice helper methods in situations like
link_to item.description, auction_path(item.auction)
..."
My question is, specifically what part of match 'auctions/:id' => "auction#show", :as => 'auction' creates the helper functions? (such as link_to auction and auction_path() ) Is it the :as => 'auction' part? Would any helpers be created without appending :as => 'auction'?
My confusion stems from other guides I have seen where this is omitted, and yet helpers seem to be created regardless. What specifically does rails use in match statements in the routes.rb file to create helpers? If it isn't the :as => 'auction' part, then what is the specific purpose of appending this to the match statement?
I know this seems like a super basic question, but this detail seems to get glossed over in the texts I have read thus far. Thanks in advance for any light you can shed on this.
I just tried this:
match "alfa/beta", to: 'users#new'
In this case, even without an :as => 'named_route', I got for free the following helper
alfa_beta_path
which, as expected, points to users#new.
So, it seems that helpers are also automagically generated by parsing the route's string, in case there is no :as specification.
Yes, it is the :as => 'named_route' part that creates the named route (which in turn creates the helpers). As for leaving it off, are you referring to instances of resources :something in routes.rb? The resources method generates a set of URL helpers based on the name of the resource automagically.

url_for and route defaults in Rails 3

I have a rails route set up like:
match ':controller/:id/:action'
# match 'teams/:id' => "teams#show" # doesn't have any additional effect, which makes sense to me
resources :teams, :only => [:index, :show]
That way I can say /teams/cleveland-indians and it will call teams#show with :id => 'cleveland-indians'. Works great. My issue is that url_for doesn't quite do what I want. In my views/teams/index view, I get this behavior:
url_for(:id => "cleveland-indians") # => /teams/cleveland-indians/index
url_for(:id => "cleveland-indians", :action => :show) # => /teams/cleveland-indians/show
Of course that second one behaves the way I want, but I'd like to get rid of the unnecessary /show at the end. I don't know much about how these helpers work, but I'd have guessed it would know that show was the default action for a GET with a specified id, same as the routing engine does. Anyway, what's the best way for me to take care of this? Or am I just doing it all wrong?
'resources' line should already provide you with the routes you probably want so you can just remove first 'match' line.
Note that you can also use 'teams_path', 'team_path("cleveland-indians")' instead of 'url_for'.

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.

word constraints in routing

match '/posts/:id/:title' => 'posts#show', :as => :slug
resources :posts
I don't want slug_path to match some words as title parameter.
For example:
posts/5/edit
"edit" is making trouble. I want to restrict this word.
If you're only worried about the standard routes interfering (like edit), simply put your match statement after your resources :posts. That way, the match statement will only catch anything that the resources statement didn't know how to handle.
You can also use a regular expression as a constraint to limit what :title can match. Another option would also be to make your URL more explicit - this would also avoid confusion with the default restful actions:
match '/posts/:id/title/:title' => 'posts#show', :as => :slug

Resources