how url_for can check constraints from my routes.rb - ruby-on-rails

I try define some translate routes on my rails application. The route change with the subdomain define. So I want this result :
describe "url_for" do
context 'with en' do
it 'brand translate' do
url_for(
:controller => 'boats',
:action => 'index',
:subdomain => :en,
:brand => 'hello',
:only_path => true
).should == '/yacht-charter/brand-hello'
end
end
context 'with fr' do
it 'brand translate' do
url_for(
:controller => 'boats',
:action => 'index',
:subdomain => :fr,
:brand => 'hello',
:only_path => true
).should == '/location-bateau/marque-hello'
end
end
end
Like you can see the only change between both url_for params is the subdomain. I try :
constraints => :subdomain => :en do
match '/yacht-charter/brand-:brand' => 'boats#index', :as => 'en_brand_search'
end
constraints :subdomain => :fr do
match '/location-bateau/marque-:brand' => 'boats#index', :as => 'fr_brand_search'
end
But all the time it's the first route define it use. the second is never define.
How Can i do that. It's a rails bug or not ?

Not sure about it being a rails bug or not, but have a look at https://github.com/francesc/rails-translate-routes it works well and may help achieving this.

Related

No route matches even though defined in rails 3.2

I am trying to use url_for in mailer view for a defined route:
<%= url_for(
:controller => 'scribe_requests',
:action => 'accept',
:id => #match.acceptance_token,
:only_path => false) %>
I have defined the route in my routes.rb:
resources :scribe_requests do
member do
match 'accept' => 'scribe_requests#accept', :as => :accept
end
end
And my controller:
class ScribeRequestsController < ApplicationController
respond_to :html
def accept
..
end
..
end
I am not sure what is going wrong here? My delayed job is failing with the exception
"{No route matches {:controller=>"scribe_requests", :action=>"accept",
:id=>"nv4Nl8wWXLX2zFDm3s3t7w"}
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb:532:in
raise_routing_error'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb:528:in
rescue in generate'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb:520:in
generate'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb:561:in
generate'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb:586:in
url_for'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_dispatch/routing/url_for.rb:148:in
url_for'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_view/helpers/url_helper.rb:107:in url_for'
/home/syed/work/projects/mapunity/retina-india/app/views/notifier/scribe_service_needed_email.html.erb:47:in
_app_views_notifier_scribe_service_needed_email_html_erb___981003510_106966530'
/home/syed/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.2.5/lib/action_view/template.rb:145:in
`block in render'
Had to add :via option to the route definition and it worked. Weirdly, I had done that with no affect. May be some caching issue. So after the change, my route definition became,
resources :scribe_requests do
member do
match 'accept' => 'scribe_requests#accept', :via => :get, :as => :accept
end
end
Test run:
ruby-1.9.2-p0 > app.url_for :controller => 'scribe_requests', :action => 'accept', :id => 'test'
=> "http://www.example.com/scribe_requests/test/accept"
Thanks for the help.

Empty route globs in Rails 3

I'm upgrading from Rails 2 to 3.1, and having some problems with route globs. We had working globbed routes in Rails 2 that were like this:
map.connect '/foo/*bar', :controller => 'foo', :action => 'index'
A GET to '/foo/one/two' would give the following parameters at the controller side:
{
:controller => 'foo',
:action => 'index',
:bar => ['one, 'two']
}
Empty globs were fine, so we could also do GET '/foo' and get:
{
:controller => 'foo',
:action => 'index',
:bar => []
}
Upgrading to Rails 3.1, there are a few differences:
match '/foo/*bar', :to => 'foo#index'
The globbed route segment comes through as a string instead of an array, but I've dealt with that in my code. No problem there. A GET '/foo/one/two' gives:
{
:controller => 'foo',
:action => 'index',
:bar => 'one/two'
}
The problem comes when I have nothing in the glob segment. GET '/foo' is no longer routable, and if I try to generate the URL from parameters, it cannot generate. Basically, if params[:bar] is an empty string, it just completely fails to route.
This is awkward in my app, as it depends on the Rails 2.3 behaviour to produce sensible URLs. We also have instances where the glob is mid-path:
match '/foo/*bar/show', :to => 'foo#show'
This means that we had paths like:
/foo/one/two/show
/foo//show
Is there any way to get the routing engine to handle the empty strings as it used to in Rails 2.3? I've tried adding extra routes for the empty-glob version, which works in the simple trailing-glob case, but in the case where the glob is mid-path, it doesn't.
I get tests passing with these routes:
Your::Application.routes.draw do
match '/foo/', :to => 'foo#index', :bar => ''
match '/foo//show', :to => 'foo#show', :bar => ''
match '/foo/*bar/show', :to => 'foo#show'
match '/foo/*bar', :to => 'foo#index'
end
Sequence is important!
Ambiguity: Is /foo/show meant to "show foo-index", or do "index for bar=show"?
Test:
require_relative '../test_helper'
class FooRoutingTest < ActionController::TestCase
test 'root index' do
assert_recognizes({:controller => 'foo', :action => 'index', :bar => ''}, '/foo')
assert_recognizes({:controller => 'foo', :action => 'index', :bar => ''}, '/foo/')
end
test 'root show' do
assert_recognizes({:controller => 'foo', :action => 'show', :bar => ''}, '/foo/show')
assert_recognizes({:controller => 'foo', :action => 'show', :bar => ''}, '/foo//show')
end
test 'wildcard index' do
assert_recognizes({:controller => 'foo', :action => 'index', :bar => 'hello'}, '/foo/hello')
assert_recognizes({:controller => 'foo', :action => 'index', :bar => 'hello/more'},'/foo/hello/more')
end
test 'wildcard show' do
assert_recognizes({:controller => 'foo', :action => 'show', :bar => 'wow'}, '/foo/wow/show')
assert_recognizes({:controller => 'foo', :action => 'show', :bar => 'wow/more'}, '/foo/wow/more/show')
end
end

How do you test route constraints using RSpec

Given a couple of cities in the DB:
City.first.attributes => {:id => 1, :name => 'nyc'}
City.last.attributes => {:id => 2, :name => 'boston'}
And a route like:
match '/:city/*dest' => 'cities#do_something', :constraints => {:city => /#{City.all.map{|c| c.name}.join('|'}/}
(so the constraints should evaluate to: /nyc|boston/)
And a spec:
it "recognizes and generates a route for city specific paths" do
{ :put => '/bad-city/some/path' }.should route_to({:controller => "cities", :action => "do_something", :dest => 'some/path', :city => 'bad-city'})
end
I would expect a failure. But it passes.
Likewise:
it "doesn't route bad city names" do
{ :put => '/some-bad-city/some/path' }.should_not be_routable
end
Here I expect it to pass, but it fails.
It seems the constraint is being ignored in the specs, since the matching cities have the same behavior as the bad ones.
Is this a known issue, or am I missing something that I need to do?
This approach works:
In routes.rb
match '/:city/*destination' => 'cities#myaction', :constraints => {:city => /#{City.all.map{|c|c.slug}.join('|')}/}
In the spec:
describe "routing" do
before(:each) do
#mock_city = mock_model(City, :id => 42, :slug => 'some-city')
City.stub!(:find_by_slug => #mock_city, :all => [#mock_city])
MyApp::Application.reload_routes!
end
it "recognizes and generates a route for city specific paths" do
{ :get => '/some-city/some/path' }.should route_to({:controller => "cities", :action => "myaction", :destination => 'some/path', :city => 'some-city'})
end
it "rejects city paths for cities that don't exist in the DB" do
{ :get => '/some-bad-city/some/path' }.should_not be_routable
end
end
Lastly, I added an observer to reload routes whenever the cities table changes.
When you specify constraints, you must include the parameter to constrain:
match '/:city/*dest' => 'cities#do_something', :constraints => { :city => /nyc|boston|philly/ }

Rails, routing many named routes to one action

Is there a simpler way of writing this:
map.old_site_cusom_packages '/customs_packages_options.html', :controller => :public, :action => :redirect_to_home
map.old_corporate '/corporate.html', :controller => :public, :action => :redirect_to_home
map.old_track '/track.html', :controller => :public, :action => :redirect_to_home
map.old_links '/links.html', :controller => :public, :action => :redirect_to_home
map.old_contact '/contact.html', :controller => :public, :action => :redirect_to_home
I want to send many named routes to one action on one controller, I'm making sure url's left over from an old site redirect to the correct pages.
Cheers.
Use the with_options method:
map.with_options :controller => :public, :action => :redirect_to_home do |p|
p.old_site_custom_packages '/customs_packages_options.html'
p.old_corporate '/corporate.html'
p.old_track '/track.html'
p.old_links '/links.html'
p.old_contact '/contact.html'
end
You can always write a multi-purpose route with a regular expression to capture the details:
old_content_names_regexp = Regexp.new(%w[
customs_packages_options
corporate
track
links
contact
].join('|'))
map.old_content '/:page_name.html',
:controller => :public,
:action => :redirect_to_home,
:requirements => {
:page_name => old_content_names_regexp
}
That should capture specific pages and redirect them accordingly. A more robust solution is to have some kind of lookup table in a database that is checked before serving any content or 404-type pages.
Edit: For named routes, it's an easy alteration:
%w[
customs_packages_options
corporate
track
links
contact
].each do |old_path|
map.send(:"old_#{old_path}",
"/#{old_path}.html",
:controller => :public,
:action => :redirect_to_home,
)
end
In most cases the old routes can be rewritten using the singular legacy route listed first. It's also best to keep the routing table as trim as possible. The second method is more of a crutch to try and bridge the old routes.

Rails will_paginate routing caching duplicate pages

I use the will_paginate plug-in.
In oder to generate routes that I can cache ( /posts/index/2 instead of /posts?page=2) I added the following to my routes.rb:
map.connect '/posts/index/1', :controller => 'redirect', :url => '/posts/'
map.connect 'posts/index/:page',
:controller => 'posts',
:action => 'index',
:requirements => {:page => /\d+/ },
:page => nil
The first line redirects /posts/index/1 to /posts/ using a redirect controller, to avoid having a duplicate page.
Is there something wrong with the way I set up the 'posts/index/:page' rule?
I thought adding :requirements => {:page => /\d+/ } would ensure that /post/index/ without a :page parameter should not work, but /posts/index.html is getting cached.
How can I redirect /posts/index/ to /posts/ to avoid having both /posts.html and /posts/index.html ?
Thanks
UPDATE
I simply added
map.connect '/posts/index/', :controller => 'redirect', :url => '/posts/'
And I'm not getting duplicate pages anymore.
However, I still don't uderstand why I was getting /posts/index.html. Any explanations or suggestions on how to make this rule more succinct are welcome ;)!
map.connect '/posts/index/1', :controller => 'redirect', :url => '/posts/'
map.connect '/posts/index/', :controller => 'redirect', :url => '/posts/'
map.connect 'posts/index/:page',
:controller => 'posts',
:action => 'index',
:requirements => {:page => /\d+/ },
:page => nil
Here I found possible answer to your question.
I think that adding :page => nil can override previous condition. So maybe when you remove this part, it will work as you expected.

Resources