Adding Requirements to Globbed Routes in Rails - ruby-on-rails

I have a question regarding regex matching on a globbed route in rails 2.3. This ticket explains exactly the same problem I'm having:
Rails routes with wildcard and requirements
The problem is that I cannot get the regex for :property_types to match on anything except the first instance of a property types, and that somehow breaks the match on :location_b.
So the following only works if the property_types requirement is removed:
/for-sale/us/ny/apartment/loft/brooklyn/
map.location ":transaction/:country_code/:location_a/*property_types/:location_b",
:controller => "search",
:action => "location",
:transaction => /(for-sale|for-rent|auction|new_development)/i,
:country_code => /\w\w/i,
:location_a => /\w\w/i,
:property_types => /(apartment|loft|home|condo|multi-home|town-house)/i
Hoping someone could explain how I can get the regex for property_types working as I do not understand how it's supposed to work across a glob bed route, which is an array. The ticket I linked to seems to suggest that it's possible to have the regex match across each type, but I was unable to get anything working.
Example url:
http://localhost:3000/for-sale/us/ny/apartment/loft/brooklyn

Related

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.

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.

ruby on rails - routes.rb - match file extension when multiple periods exist in filename

I have created a route plus controller for doing dynamic css in ruby on rails as per the instructions here:
http://www.misuse.org/science/2006/09/26/dynamic-css-in-ruby-on-rails/
It took some changing to account for a newer version of ruby on rails, but the problem comes in with the routes.rb entry. The original entry was this:
# dynamic CSS (stylesheets)
map.connect 'rcss/:rcssfile',
:controller => 'rcss',
:action => 'rcss'
This did not work with a newer version of RoR, and I found this solution to work:
# dynamic CSS (stylesheets)
map.connect 'rcss/:rcssfile.css',
:controller => 'rcss',
:action => 'rcss'
However, now I was bummed that I couldn't get a catch-all filetype extension handler. The request had to have the .css extension. Playing around with it further I came up with this:
# dynamic CSS (stylesheets)
map.connect 'rcss/:rcssfile.:format',
:controller => 'rcss',
:action => 'rcss'
So this is much better. Now I could potentially request a file that ended in .foobar or whatever and match it with a handler. Not that I would necessarily, but it's more about understanding everything.
So then I tried creating a file that looked something like "foo.net.rcss" . Now it would seem that the first dot messes everything up. "no routes match rcss/foo.net.css". My questions are as follows:
How can I match any filename and any extension regardless of how many dots are in the filename?
Why does the first example not work in later RoR versions?
Why do multiple dots screw up the match?
Thanks in advance for any help.
------- update -------
I am using Rails 3.0.5 . As per some more research I can shorten the syntax to:
match 'rcss/:rcssfile', :to => 'rcss#rcss'
This is the equivalent of the first example that did not seem to work, however using this syntax it works just as expected.
match 'rcss/:rcssfile:.:format', :to => 'rcss#rcss'
This also works just like my previous example #3, however it still has the problem of not matching a file with multiple periods.
It would seem that labeling a standard ":paramater" takes special consideration for the period character. ":parameter" will match a path with up to one period, ":parameter.:extension" will match a path with up to two periods, but the :extension will be only what's between the two periods, etc.
A way around this is to use what is called "Route Globbing", which uses an asterisk instead of a colon:
match 'rcss/*rcssfile', :to => 'rcss#rcss'
The only caveat is that this will match ANYTHING after the asterisk, including subdirectories. As such, you want to make sure that this does not accidentally expose any secure files or accidentally render things unintentionally.
I used this for a general case when you don't know the extension:
get '/uploads/:basename.:extension', to: 'controller#action', basename: /.*(?=\.[\w\d]+$)/
Use a regex to match the filename?
map.connect 'rcss/:rcssfile',
:controller => 'rcss',
:action => 'rcss',
:requirements => {:rcssfile => /.+\.rcss/ }
This would match (anything).rcss - you could adjust the regex for various suffixes.

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.

Rails routing with requirements

with the following routes I try to achive the goal, that I can present static resources like terms of use, imprint and so on in different languages using different urls.
I defined two example routes for my imprint like that:
map.imprint ':lang/impressum', :controller => "statics", :action => "imprint", :requirements => {:lang => /de/}
map.imprint ':lang/imprint', :controller => "statics", :action => "imprint", :requirements => {:lang => /en/}
Now in my view I try to use the path/url helper like that:
<%= link_to(t(statics.imprint.linkname), imprint_url(:lang => session[language])) %>
where there session[:language] is "de" or "en".
Thats results in a working link for the de route. But the english one fails. If I change the order of the routes, it's vice versa, and the english one works, while the german one fails.
The error always reads like that:
imprint_url failed to generate from {:controller=>"statics", :lang=>"de", :action=>"imprint"}, expected: {:controller=>"statics", :action=>"imprint"}, diff: {:lang=>"de"}
Can anyone help out with this?
Thanks.
Jason
As far as I know, you cannot map two routes to the same name like that.
You would need to rename one of them, ie
map.impressum
map.imprint
When Rails looks up the route, it will stop at the first one that it finds, that's why your 'de' links are working.

Resources