Routing with regex condition in Rails 2 - ruby-on-rails

In my routes.rb file, I've defined these routes:
map.with_options(:controller => "review") do |review|
review.review_index "/review/:page", :action => "index", :defaults => {:page => nil}, :requirements => {:page => /[0-9]./}
review.review_provider "/review/:category_name/:page", :action => "provider", :defaults => {:page => nil}
end
However, it only match with the second routes.
For example,
/review/1
must match with first rule but in fact it is matched with the second rule.
How can I config it so that:
/review/1 will match with the first rule
/review/a_category/1 will match with the second rule

Your regular expression in your first route is bad. Period matches any single character.
/[0-9]/ means "any number, followed by any other single character".
So, that would match /review/1a, /review/70, /review/7?, etc,
If you want to match one or more digits, change your regular expression to: /[0-9]+/

Related

Route match to dynamic actions in rails

Can I write one row route for below two rows?
match "/article/:id/" => "articles#redirect"
match "/articles/:id/" => "articles#redirect"
I tried something like
match "/:article_redirect/:id/" => "articles#redirect", :constraints => {:article_redirect => /[article|articles]/}
But it didn't work.
I think your regexp id not good: try
match "/:article_redirect/:id/" => "articles#redirect", :constraints => {:article_redirect => /(article|articles)/}
that is (article|articles) instead of [article|articles]

Accepting single dynamic, but unused, element in route

I want to accept URLs for my objects that contain an element that is not used, but which would be nice for SEO reasons. For example, I want to accept an url like:
http://localhost:3000/people/USA/123-joe-schmoe
.. where the "/USA" bit has no significance at all, other than signaling to users and search engines that this person resides in the USA.
I've tried setting up my rule using globbing, as well as for example doing something like
match "people/:whatever/:id" => "people#show", :constraints => {:id => /\d+-.*/}, :as => "person"
But this results in an attempt to route to :controller => people, :action => "show", :whatever => #<Person id: 123 ...> which fails.
Is it possible to have the routing ignore the :whatever part and pass in my :id in stead?
You can try a widcard match instead
match "people/*/:id => "doctors#show", :constraints=> {:id=>/\d+=.*/}, :as=>"person"

How to Detect an Integer in Rails 3 Routes?

I would like to do just a little bit of extra logic in rotues.rb, that probably doesn't belong there, but it seems to make the most sense to me.
I have two conflicting routes. To be primitive:
match '/videos/:browseby' => 'videos#browse', :as => "browse_by"
Where :browseby is looking for a string, such as "Tags", to browse videos by tags.
However, (and most probably saw this coming) I also have my basic show resource (again in primitive form):
match '/videos/:id' => 'videos#show', :as => "video"
Where :id is looking for the integer for the video ID.
Is there a way to add a small bit of logic such as...
match '/videos/:id' => 'videos#show', :as => "video", :format(:id) => :integer
(Which is my hypothetical rails syntax, to help show what I'm looking for.)
I know I can munch this in the Controller level, but it makes more sense to me to handle it at the route level.
You could try using :constraints and a regex:
match '/videos/:id' => 'videos#show', :as => "video", :constraints => { :id => /\d/ }
match '/videos/:browseby' => 'videos#browse', :as => "browse_by"
You'll also want to make sure the looser :browseby version comes after the :id version. Note that regex constraints are implicitly anchored at the beginning so that would work as long as your :browseby values didn't start with a number.
If you have tags that do start with numbers then you could use an object for the constraint and then you could include anchors in your regex:
class VideoIdsOnly
def matches?(request)
request.path =~ %r{\A/videos/\d+\z}
end
end
match '/videos/:id' => 'video#show', :as => "video", :constraints => VideoIdsOnly.new
match '/videos/:browseby' => 'videos#browse', :as => "browse_by"

routing to page name

is there a way i could route - http://localhost:3000/pages/1 to http://localhost:3000/home and all the other pages in my site i.e. - http://localhost:3000/pages/4 to http://localhost:3000/contact-us automatically?
I can do this the other way around using -
match "/home" => 'pages#show', :id => 1
match "/cars-for-sale" => 'pages#show', :id => 1
match "/contact-us" => 'pages#show', :id => 4
but need to do this in revers and automatically if possible.
Perhaps what you really need is a redirect:
match "/pages/1", :to => redirect("/home")
match "/pages/:id", :to => redirect("/contact-us")
Note, that the order is significant - "Rails routes are matched in the order they are specified" (see http://edgeguides.rubyonrails.org/routing.html)

Routing more than one action to the same controller and action

I am trying to get something like this working on my Rails app:
match '/:language', :to => 'posts#search_result'
match '/:tag', :to => 'posts#search_result'
match '/:language/:tag', :to => 'posts#search_result'
I am using this search_result action to filter some posts depending of the language and the tag.
The problem is that sometimes :tag will be nil or :language will be nil; so i have these 3 possibilities when calling the action:
<%=link_to "Spanish", {:controller => 'posts', :action => 'search_result', :language => "spanish"} %>
<%= link_to "Spanish", {:controller => 'posts', :action => 'search_result', :language => "spanish", :tag => #tag} %>
<%=link_to "#{tag.name}", {:controller => 'posts', :action => 'search_result', :tag => #tag} %>
And I am expection to have URLs like:
/spanish (for the first case)
/spanish/rails (where rails is a tag, for the second case)
/rails (for the third case)
But right now i am getting the rigth thing for the first and third case, but for the second case i am getting:
/spanish?tag=rails
or again /spanish (depending on if i had selected a tag first or a language first).
I hope i explained myself right. Any idea??. thanks!.
The router cannot tell the difference between a :language and a :tag.
Just because your routes say "language" and "tag" when you are constructing your code in the view.. remember that in the html this has been translated into just plain ole URLs eg /spanish or /rails
the route then has to be figured out from this URL.
Now as I said, the router can't tell that a particular word is a language or a tag... and the plain-ole-URL doesn't have the word "tag" or "language" in it anymore... so your two routes here:
match '/:language', :to => 'posts#search_result'
match '/:tag', :to => 'posts#search_result'
are both the same kind of URL
Just a single token after the slash. Here are some examples that will match that route:
/greek
/spanish
/rails
/urdu
/whatever
They will all match the first route that matches on "a single token after a slash"... which means your router will match all of them to the "language" route and will never ever match the "/:tag" route, because it's already matched on the route above.
he he: it's all greek to the router ;)
Edit:
Hi, this is helping me a lot to understand how routing works.. but still i can't see it clear. I understand what you said, and so basically i understand i should do something like match '/tags/:tag to at least only route to posts#search_result the URLS starting by /tag .. what would be a solution??
yes, "/tags/:tag" would be clear and unambiguous, but if you want it to truly flexible in tag vs language you would be better served by the simple:
match '/posts/search', :to => 'posts#search_result'
which can use any of your link_to examples above to generate eg:
/posts/search?tag=rails
/posts/search?language=spanish
/posts/search?language=spanish&tag=rails
It's also far more clear what is being passed and why.
The description of the third URL is "I'm searching for a set of posts which have language = spanish and tag = rails"
Your URL should reflect the resource (which in this case is a set of posts) everything else is better done as query params.
Instead of defining /:language and /:language/:tag separately, define them together, with /:tag as an optional URI element.
match '/:language(/:tag)', :to => 'posts#search_result'
I believe routes are matched (and URIs generated from them) in the order that the routes are defined. You defined /:lang before you defined /:lang/:tag, so it matched /:lang and made :tag a GET parameter. I suppose you could optimize the ordering of your definitions, but I believe using the above syntax is the preferred method.

Resources