Ben Orozco
Ben Orozco

Reputation: 4381

Route alias, is it possible?

I have a Vehicle model:

Routes:

map.resources :vehicles, :has_many => :suppliers

Everything works great, but Vehicle has a boolean attribute is_truck. I want to make an Alias so I can get the same resources filtering only trucks, I tried with:

Routes:

map.trucks '/trucks', :controller => :vehicles, :action => :index, :is_truck => true
map.trucks '/trucks/by_supplier/:supplier', :controller => :vehicles, :action => :index, :is_truck => true

The first one works well, but when I search within a Form the second doesn't work and searches all suppliers.

Controller:

class VehiclesController
   def index
     if params[:supplier]
       @vehicles = Vehicle.all :conditions => { :is_truck => params[:is_truck] }
     else
       @vehicles = Vehicle.all
     end
   end
   ...
end

Search Form:

<% form_for :truck, :url => {:controller => :trucks, :action => :index}, :html => {:method => :get} do |f| %>
  <% f.text_field :search %>
  <% f.submit 'Search Trucks' %>
<% end %>

Is it possible to map.resources as an alias ?

Upvotes: 5

Views: 2477

Answers (5)

hurikhan77
hurikhan77

Reputation: 5930

Just amend your routes in the following way:

map.resources :vehicles, :has_many => :suppliers,
                         :collection => { :trucks => :get }

And check rake routes for the routes this generates. It will allow you to list vehicles which are trucks:

trucks_vehicles GET /vehicles/trucks(.:format)
                    {:controller=>"vehicles", :action=>"trucks"}

So you now just need to add a new action called "trucks" which works similar to "index". Forms should keep track on it's own (via form fields) if you create a truck or another vehicle. Don't try to trick around with rails routing, which usually would mean your app design is flawed which will get you into trouble later.

You may take a look at STI (single table inheritance: one table stores multiple classes of vehicles). Another way would be to create a trucks controller which inherits from the vehicles controllers and overwrites just some methods like so:

class TrucksController < VehiclesController
  def new
    @is_truck = true
    super
  end
  ...
end

or

class TrucksController < VehiclesController
  before_filter :this_is_a_truck
  ...

  private

  def this_is_a_truck
    @is_truck = true
    super
  end
end

Update: Here's another one (given you have a is_truck column):

class TrucksController < VehiclesController
  around_filter :with_truck_scope
  ...

  private

  # Scope every active record access with an is_truck condition
  # you may want to put this directly into the model to get rid of the .send
  # method and directly access "Vehicle.with_truck_scope &block" here
  def with_truck_scope(&block)
    Vehicle.send :with_scope, :find => { :conditions => "is_truck = 1" },
                              :create => { :is_truck => 1 }, &block
  end
end

But I recommend really first to try out going with the :collection and :member parameters of Rails' routing.

Upvotes: 1

Ben Orozco
Ben Orozco

Reputation: 4381

I found a cleaner way to do it, but Search is still broken under a specific supplier:

# Show all vehicles
map.connect '/vehicles/supplier/:supplier', :controller => :vehicles, :action => :index
map.resources :vehicles

# Only show trucks
map.connect '/trucks/supplier/:supplier', :controller => :vehicles, :action => :index, :is_truck => true
map.resources :vehicles, :as => 'trucks', :requirements => { :is_truck => true }

Resource: http://api.rubyonrails.org/classes/ActionController/Resources.html

Upvotes: 1

makevoid
makevoid

Reputation: 3287

I recommend you to use another resource, just adding:

map.resources :vehicles, :as => :trucks, :has_many => :suppliers

Then handle it in your controller with something like:

def index 
  conds = {}
  conds = { ... } if request.uri =~ /trucks/ # you can be more specific about the regexp if you need to
  @vehicles = Vehicle.all :conditions => conds      
end

What do you think about it?

Upvotes: 0

fantactuka
fantactuka

Reputation: 3334

Not sure if it's possible to make an alias but at least you can try to swap your routes:

map.trucks '/trucks/by_supplier/:supplier', :controller => :vehicles, :action => :index, :is_truck => true
map.trucks '/trucks', :controller => :vehicles, :action => :index, :is_truck => true

Since routes works like 'first added - has higher priority' your solution could fail since map.trucks '/trucks' catch '/trucks/by_supplier/:supplier' as well. Also I'd recommed to refactor it a bit:

map.with_options :controller => :vehicles, :action => :index, :is_truck => true do |v|
  v.trucks '/trucks/by_supplier/:supplier'
  v.trucks '/trucks'
end

Upvotes: 0

zed_0xff
zed_0xff

Reputation: 33227

are you processing it in your controller?

smth like:

class VehiclesController
   def index
     if params[:supplier]
       @vehicles = Vehicle.all :conditions => { :supplier_id => params[:supplier] }
     else
       @vehicles = Vehicle.all
     end
   end
end

Upvotes: 0

Related Questions