ROR route with parentheses in constraint - ruby-on-rails

I'm trying to create a rails route for movies (on the root path) that has parentheses containing the movie's year in it.
E.g. Men in black => "/men-in-black-(1997)"
My route is:
resources :movies,
path:'/',
only:[ :index, :list, :show ],
constraints: { id: /[A-Za-z0-9-]+\(\d{4}\)/ }
When I use this route (movie_path(Movie.first)), I get
"ActionController::RoutingError: No route matches: ..."
When I change the route constraint to:
constraints: { id: /[A-Za-z0-9-]+\\\(\d{4}\\\)/ }
the route works when using the url routing helper. However, the route doesn't work for the reverse mapping (e.g. taking "/men-in-black-(1997)" and routing it to the correct action/controller). When I run (from console):
Rails.application.routes.recognize_path("/men-in-black-(1997)")
I get the same routing error:
ActionController::RoutingError: No route matches
The problem seems to be associated to how rails escapes regex's in routing. For escaping with \( the object-to-route map fails, but url-to-route works. But when escaping with \\\( it is the opposite.
Anyone have any tips or experience with this?

As a workaround hack you could try:
constraints: { id: /[A-Za-z0-9-]+(\\\(\d{4}\\\)|\(\d{4}\))/ }
That is, make the constraint accept either, if it accepts one in one case and the other in the other case.
Which is to say: that's weird, I have no idea why Rails would do that or how to fix it ;)

Well I don't have a lot of experience writing regex constraints, but you could always do a wildcard route and then sanitize in the controller.
match 'movies/*movie' => 'movie#action'
This will give you access to a :movie param with all the characters input

Related

Ruby - A request route with a wildcard doesn't work

Could someone suggest why these 2 routes aren't the same:
get('/:id/' => 'outlets/play#show', :as => :listen, constraints: { id: /thetrack-a123-bay7623/ } )
get('/:id/' => 'outlets/play#show', :as => :listen, constraints: { id: /thetrack-.*/ } )
What I'm trying to achieve is only want that route outlets/play#show to be used when there is an :id that begins with thetrack.
I've found that if I explicitly use those characters its fine i.e. without thetrack in the route it doesnt use that route. However if I use thetrack-.* it still goes into the outlets/play#show route despite thetrack not being present in my request.
Any ideas?
I've tried other regex patterns e.g.
thetrack-.+
thetrack-.+-.+
thetrack-.*-.*
with no luck
If what you are trying to do is route any request /:id/ with ID starting with thetrack- to outlets/play#show, then your configured route should work:
get '/:id/', to: 'outlets/play#show', as: :listen, constraints: { id: /thetrack-.*/ }
Here are some example paths that will route to outlets/play#show using this wildcard:
/thetrack-
/thetrack-a123
/thetrack-a123-bay7623
/:id/ is quite broad reaching. Check that you have not got any other conflicting routes. i.e. another route at root level /:something/ that could be catching the other requests where thetrack- is not specified.

How can I route a subdomain to rails app in routes.rb?

I have a url https://www.openhub.net which is my rails 4.2.7 app. I have a separate subdomain on another system which is https://code.openhub.net/. If you clink on the second link, this will reroute you to a discontinued page. What's hosting the code.openhub.net url is going to be decomissioned and will be rerouted to openhub.net. So what I'm trying to do is that when someone tries to go to code.openhub.net my rails app that has the openhub.net will serve a static page. The problem is that I can't conceptualize how this will work. For example, here is a snippet of rails documentation under routing.
3.9 Request-Based Constraints
You can also constrain a route based on any method on the Request object that returns a String.
You specify a request-based constraint the same way that you specify a
segment constraint:
get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' }
You can also specify constraints in a block form:
namespace :admin do
constraints subdomain: 'admin' do
resources :photos
end
end
Request constraints work by calling a method on the Request object
with the same name as the hash key and then compare the return value
with the hash value. Therefore, constraint values should match the
corresponding Request object method return type. For example:
constraints: { subdomain: 'api' } will match an api subdomain as
expected, however using a symbol constraints: { subdomain: :api } will
not, because request.subdomain returns 'api' as a String.
I understand what the document is saying but when I try to put this code into practice, I'm stumped. Here is my routing.rb file:
get '???', to: '???', constraints: { subdomain: 'code' }
I understand that the subdomain constraint portion of the url will be code but will the get and to be? How do I know will go under get? Do I simply substitute get code.openhub.net which will route to a CodeController#index? Has anyone tried to do something like this before? From the documentation I see that the request object has an original_url method that I can call. Is there perhaps a way I can make use of this? Any help would be greatly appreciated.
As a sidenote, I noticed that subdomain is not a property of the request object outlined here Rails Request Object
This should work:
get '/', to: 'code#index', constraints: { subdomain: 'code' }
That will route requests to https://code.openhub.net/ to the the index method in your CodeController.
However, the URL will still say https://code.openhub.net/. If you don't want that, you might try a redirect, like this:
get '/', to: redirect('https://www.openhub.net/'), constraints: { subdomain: 'code' }

Rails Route URL segment with + (plus) sign character in id is not parsed correctly

I have a TagsController class, with a route like the following (in routes.rb)
resources :tags
Now, when I go to
http://localhost:3000/tags/test
It works correctly.
However, when I go to
http://localhost:3000/tags/c++
Rails seems to be parsing the "c++" as "c ", so this results in a "404 Not Found"
Could anyone give me any instructions on how I could fix this?
For rails 4 rails provides constrain options to determine regex parameter in routes:
This is constrains for allowing you to access param with /photos/1 or /photos/RR27
resources :tags, constraints: { id: /[A-Z][A-Z][0-9]+/ }
For advance constraint
By default the :id parameter doesn't accept dots - this is because the
dot is used as a separator for formatted routes. If you need to use a
dot within an :id add a constraint which overrides this - for example
id: /[^/]+/ allows anything except a slash.
resources :tags, constraints: { id: /[^\/]+/ }
These is the details of specifying constraint routes. I hope it can help you.

Rails routing: Treating hardcoded path as a parameter?

I'm new to Rails, and after reading some of the Rails Routing documentation, I still can't figure out why my path is being treated as such:
With the following examples, Rails complains that it is unable to find a Movie whose id is equal to "inception", even though I am trying to pass along an id parameter.
(The controller#action to which I am routing is of the resources-type show).
routes.rb
1: get 'movies/inception' => "movies#show", :id => 6
2: get 'movies/inception' => 'movies#show', defaults: {id: 6}
(These two lines are obviously tried separately.)
Error
1:Couldn't find Movie with id=inception
2: app/controllers/movies_controller.rb:16:inshow'
3: {"id"=>"inception"}
Why is the inception-part of the matched url treated as a variable even when I'm not prefixing it with : and how do I make a hardcoded url point to a object#show-action with a hardcoded id?
Looks like you taked into issue with routes priority:
Not all routes are created equally. Routes have priority defined by the order of appearance of the routes in the config/routes.rb file. The priority goes from top to bottom. The last route in that file is at the lowest priority and will be applied last. If no route matches, 404 is returned.
So, I suppose, that you had defined
resources :movies
before
get 'movies/inception' => "movies#show", :id => 6
get 'movies/inception' => 'movies#show', defaults: {id: 6}
And your routes were not overwritted.
Just see your last question. Uses this route form, instead of yours:
get 'movies/inception/(:id)' => 'movies#show', defaults: {id: 6}
My guess is you have a line something like
resources :movies
If this is the case, then it is creating the standard get show route of movies/:id
If this line is above your hard-coded route then it would take precedence. The lines at the top of the routes file take precedence over anything below it
A couple of things that may help you on your rails journey. From the command line, run
rake routes
That will spit out all available routes
Something you may want to consider if you are looking to do this:
Check out the friendly_id gem
"It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models." such as /movies/inception

Rails route if no route matched

Is there a solution to redirect the user to a specific controller#action if no route matched? I had a "last rule" like this:
match ":rest" => "application#notfound", :constraints => { :rest => /.*/ }
to redirect all "other" stuff to application#notfound. The problem is that plugins with own routes where set below shis rule and never gets called.
Make a catch-all route and put it as the last rule in your routes.rb file.
match "*rest" => "application#notfound"
you also get whatever the path was as a parameter thru params[:rest] in your controller.
You are definitely not the only person having this problem. I know it's frustrating, but give it some time - I'm confident the Rails team will put together something simple and elegant. I'm in a similar position, only I need to know the incorrect url resource the user entered.
A quick Google search and I found this article demonstrating a full solution to the Rails 3.0 rescue_from ActionController::RoutingError problem. The solution involves Route Globbing and solves my need to both handle incorrect urls and capture the url entered. Hope this helps!
Put this rule last.. Routing matches from top to bottom so if it did find a match then it stops there.
Maybe it's not the best approach, but it will works while you don't find better solution
for rails 2.3, at the application controller, catch the exception like
rescue_from(ActionController::RoutingError) { redirect_to "/xpto" #add your code here }
Rails 3
At initializer put a code like:
module ActionDispatch
class ShowExceptions
def render_exception(env, exception)
if exception.kind_of? ActionController::RoutingError
render(500, 'it was routing error')
else
render(500, "some other error")
end
end
end
end
or something more clean like:
https://gist.github.com/522944
https://gist.github.com/522924
or:
match '/:anything', :to => "home#routing_error", :constraints => { :anything => /.*/ }
but maybe this last match won't be good for you as it'll override all routes from plugins/engines. I think that best solution is to use custom rack middleware for handling special cases.

Resources