How do I make part of my route optional? - ruby-on-rails

I have this route, which overrides a resource-generated route:
match "make_tiles(/:tile_type,(:finish))" => "tiles#new", :as => :make_tiles
This allows for nice URLs like /make_tiles/two_two,matte
But I'd like the option to use: /make_tiles/two_two also. Currently only /make_tiles/two_two, works.
How can I get rid of the trailing comma requirement?

You can't use a comma to separate fields, and I'm not sure why you'd want to. A comma is not a very good field separator for routes. If you really insist on doing it this way, have the options go into one parameter and separate them manually:
match "make_tiles(/:tile_type_and_finish)" => "tiles#new", :as => :make_tiles
Then in your controller
(tile_type,finish) = params[:tile_type_and_finish].split(",") if params[:tile_type_and_finish].present?
The reason your way isn't allowed is that rails defines the parameter separator as a constant in ActionDispatch::Routing:
SEPARATORS = %w( / . ? )
Otherwise
match "make_tiles(/:tile_type(/:finish))" => "tiles#new", :as => :make_tiles
should work fine.

Related

Rails routes constraint - unicode regex not matching

So I have a route with a constraint that matches paths such as the following:
/jobs-in-London
get ':id' => 'search#show', :constraints => { :id => /jobs-in-[A-Z].*/ }
This works great, but I need to match to locations written in other character sets, such as Japanese. I am happy for the "jobs-in-" to remain in english, since the valid urls are generated from a list of locations uploaded by the user.
This means I would be expecting to match /jobs-in-東京, which would then search in Tokyo once it hit the controller. Geolocation is done later, and works without translation. I just need to get the request to the right controller.
I have tried this:
get ':id' => 'search#show', :constraints => { :id => /jobs-in-\p{L}*/ }
which I constructed using Rubular, and it appears to match correctly as seen here. I am aware this doesn't have the capital letter constraint, but that doesn't matter as some languages don't seem to have true capital letters anyway.
However the routes don't match it, and instead spit it out to the wildcard catch all at the bottom.
Am I falling foul of how Rails implements regex for routes? Does it not support the unicode selectors?
I cannot have this:
get ':id' => 'search#show', :constraints => { :id => /jobs-in-.*/ }
as this matches /jobs-in-$£# which is undesired, I want to constrain it to "letter" characters.
I wanted to match routes like /foo/a and /foo/ä, where that last bit could be non-ASCII letters like "ä".
On Rails 4.2, I had no luck with
get "foo/:letter" => "foos#bar", letter: %r{[[:alpha:]]}
or other variations of the regex that I tried, but this worked fine:
get "foo/:letter" => "foos#bar",
constraints: ->(request) {
# Matching with `letter: some_regex` did not work with åäö.
letter = request.path_parameters[:letter]
letter.match(/\A[[:alpha:]]\z/u)
}

Making Rails Resource and Custom Routes Conflict Work

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

Rails routes match full hostname with multiple period in between

I'm trying to match a URL something like
http://example.com/this.is.my.full.hostname/something/else
apparently when I pass the param in the routes.rb file it doesn't recognize this parameter
my code says the following
match '/:computer_hostname/somethingelse' => 'test#info'
any ideas what's the right way to achieve the URL I wanted above ? Or is it even possible ? I know period is allowed character in URL but does it allow more than one ?
I think the constraints method/option will help you out. Try something like the following:
match ':hostname/something/else' => 'test#info',
:constraints => {:hostname => /[A-Za-z0-9\._\-]+/}
If you're doing multiple matches all with the same :hostname segment, then you can wrap them in a constraints method call:
constraints(:hostname => /[A-Za-z0-9\._\-]+/) do
match ':hostname/something/else' => 'test#info'
match ':hostname/foo/bar' => 'test#foo'
end

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

Rails Routing Conditional with multiple value options

I have a rails route that is based on two conditions, that the domain and subdomain are a specific value. The problem is that there are multiple possible values for subdomain to work, but I can't seem to be able to pass them as an array or hash.
map.with_options(:conditions => {:domain => AppConfig['base_domain'], :subdomain => 'www'..'www3'}) do |signup|
signup.plans '/signup', :controller => 'accounts', :action => 'plans'
...[truncated]...
end
The above example works as accepting www, www1, www2 & www3 as a value for the subdomain. However, that doesn't really solve my needs. I need to be able to accept a value of '' (nothing), 'www' and 'www2' so I tried something to the extend of:
map.with_options(:conditions => {:domain => AppConfig['base_domain'], :subdomain => ['','www','www2']}) do |signup|
That's similar to how you would set it up in ActiveRecord but it doesn't seem to be the same for routes.
Does anybody know now I can specify three values that aren't sequential?
If you can render it as a regular expression, you can use it as a condition. Converting an array to a regular expression is quite easy:
:subdomain => Regexp.new(%w[ www www3 ].collect { |p| Regexp.escape(p) }.join('|'))
Since you're just dealing with a simple pattern anyway, why not express it as this?
:subdomain => /www\d*/
It is important to note that the regular expressions used by routes are not supposed to be anchored using ^ or $ like you usually would. They must match completely to be valid, and partial matches are ignored.

Resources