views:

222

answers:

1

I have a custom finder defined below:

class ContainerGateIn << ActiveRecord::Base
  ...
  def self.search(text)
    result = if text
      text.split(' ').inject(self) do |result, criteria|
        case criteria
          when /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/
            result.search_by_date(criteria.to_date)
          else
            result.search_by_text(criteria)
        end
      end
    else
      self
    end
  end
  ...
end

Where search_by_date and search_by_text are named scopes, and eager is an eager-loading named scope defined as:

named_scope :eager, :include => [{:container_inventory => {:container => [:size_type, :grade]}}, :company, :truck, :hauler]

The association is setup via a HMT (has_many :through):

class ContainerDepot << ActiveRecord::Base
  has_many :container_inventories
  has_many :container_gate_ins, :through => :container_inventories do
end

The problem is if the finder is invoked via association nesting from ContainerDepot, it fails with an ActiveRecord::Statement::Invalid, saying that the table has been specified more than once.

ContainerDepot.first.container_gate_ins.eager.search(text)
=> ActiveRecord::StatementInvalid: PGError: ERROR:  table name "container_inventories" specified more than once

I could correct it by copying the whole custom finder as an association extension:

class ContainerDepot << ActiveRecord::Base
  ...
  has_many :container_gate_ins, :through => :container_inventories do
    def search(text)
      ... custom finder code from ContainerGateIn ...
    end
  end
  ...
end

It is not very DRY though and introduces a very unnecessary and potentially problematic redundancy, as the custom finder will have to be changed from time to time to accomodate additional search logic.

Any ideas on how this could be done better?

A: 

You can use named_scope with a lambda, allowing you to pass in parameters to the scoping method:

named_scope :eager, lambda {|text| {:conditions=>{:name=>text}, :include => [{:container_inventory => {:container => [:size_type, :grade]}}, :company, :truck, :hauler]}}

This should allow you to include and filter with only one named scope.

LWille