How do you test route constraints using RSpec - ruby-on-rails

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/ }

Related

how to pass get variables via request specs

I'd like to pass a named variable in my rspec call to match
this is the route:
match '/api/get-pairings/:global_id' => 'api#get_pairings', :as => :get_pairings
this is what I have but doesn't work:
it "should get pairings for a specific id" do
{:get => get_pairings_path, :global_id => 1000 }.should route_to(:controller => "api", :action => "get_pairings")
{:get => get_pairings_path, :params => { :global_id => 1000 } }.should route_to(:controller => "api", :action => "get_pairings")
end
Any ideas?
thx in advance
You need to pass the variable to the _path method for the specs to work:
it "should get pairings for a specific id" do
{:get => get_pairings_path(:global_id => 1000) }.
should route_to(:controller => "api",
:action => "get_pairings",
:global_id => "1000")
end

how url_for can check constraints from my routes.rb

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.

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

Rails routes issue with controller testing

I'm at a loss as to what I'm doing wrong here. Anyone have any insight?
Here's my routes.rb
resources :accounts do
collection do
get "search/:term/:offset/:limit.:format", :action => "search", :constraints => { :offset => /\d+/, :limit => /\d+/ }
end
end
Here's my rake routes output...
GET /accounts/search/:term/:offset/:limit.:format {:offset=>/\d+/, :action=>"search", :controller=>"accounts", :limit=>/\d+/}
Here's my test line...
get :search, :term => "Test", :offset => 0, :limit => 2
Here's my error...
ActionController::RoutingError: No route matches {:term=>"Test", :action=>"search", :controller=>"accounts", :offset=>0, :limit=>2}
Any ideas?
Thanks in advance!
I found the issues...
1) It is expecting to match on strings so instead of
:offset => 0, :limit => 2
it should be
:offset => '0', :limit => '2'
2) :format was not optional. I chose to make it an optional param, but if you encounter this you will have to pass format along if you don't make it optional.

Rails routes with date

My question it's about use a filter by date in the route of a rails application, at the moment I all ready have the rule that match the pattern of the date in the routes.rb file, it's looks like this:
match "itineraries/:day/:month/:year" => "itineraries#index",
:constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }
match "itineraries/new/:day/:month/:year" => "itineraries#new",
:constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }
resources :itineraries
match '/:controller(/:action(/:id))'
root :to => "itineraries#index"
That match for example /itineraries/01/01/2011, the problem comes when I generate a route from the resource, for example, itineraries_path(:year=>2011,:month=>1,:day=>1) generate:
/itineraries?day=1&month=1&year2011
instead of
/itineraries/01/01/2011
Is there a way to put the match rule inside the resource mapping?
well, short answer is your route does exists, but it's not named yet (check rake routes to convince yourself) and that's why you can't call it just like that (using itineraries_path)
so, the correct way to do it would be for example:
match "itineraries/:day/:month/:year" => "itineraries#index",
:constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ },
:as => "itineraries_date"
(notice the :as part)
now, if you call itineraries_date_path(11,12,1998) it will give you itineraries/11/12/1998

Resources