Rails named_scope наследование? - PullRequest
1 голос
/ 05 мая 2011

Я пытаюсь обобщить некоторые из моих моделей, предоставляя общую базовую модель для наследования, которая содержит несколько взаимных объявлений named_scope и метод фильтра, который активирует этот поиск для более простых запросов на стороне контроллера. Кажется, это работает, когда я запускаю его в консоли, но не работает, когда в контроллере:

# in the base model
class GenericModel < ActiveRecord::Base

  named_scope :by_name, lambda { |name|
    ( name.blank? ) ? {} : { :conditions => [ "#{self.table_name}.name like ?", "%#{name}%" ] }
  }

  def filter(params)
    res = []
    res = self.by_name( (params[:name] or '') ) if params[:name]
    return res
  end

end

class MyModel < GenericModel
  set_table_name 'my_models'
end

# works in in console!
>> params = { :name => 'jimmy' }
>> MyModel.filter(params)
=> [ <#MyModel ...>, ... ]
nil

# fails in controller
@model = MyModel.filter(params)

# ActiveRecord::StatementInvalid (Mysql::Error Unknown column 'generic_models.name' in where clause...)

Очевидно, родительский класс named_scope вызывается в rails, но отлично работает в консоли rails. Есть идеи, как это исправить? спасибо.

1 Ответ

5 голосов
/ 05 мая 2011

Это что-то вроде крушения поезда из-за того, как ActiveRecord пытается интерпретировать то, что вы говорите. Как правило, первый класс, производный от ActiveRecord :: Base, используется для определения имени базовой таблицы и подклассов, которые по умолчанию определены для использования в наследовании одной таблицы (STI). Вы работаете над этим, используя set_table_name, но, как это часто бывает, хотя в Rails можно пойти против зернистости, все часто становится беспорядочным.

Вы должны быть в состоянии сделать это намного более аккуратно, используя миксин, как предложено Берлингтоном.

module ByNameExtension
  def self.extended(base)
    # This method is called when a class extends with this module

    base.send(:scope, :by_name, lambda { |name|
      name.blank? ? nil : where("#{self.table_name}.name LIKE ?", "%#{name}%")
    })
  end

  def filter(params)
    params[:name].present? ? self.by_name(params[:name]) : [ ]
  end
end

class MyModel < ActiveRecord::Base
  # Load in class-level methods from module ByNameExtension
  extend ByNameExtension
end

Вы должны иметь возможность хранить свои расширения, содержащиеся в этом модуле. Если вы хотите убрать это еще дальше, напишите инициализатор, который определяет метод, подобный scoped_by_name для ActiveRecord :: Base, который запускает это поведение:

class ActiveRecord::Base
  def scoped_by_name
    extend ByNameExtension
  end
end

Затем вы можете пометить все классы, которые требуют этого:

class MyModel < ActiveRecord::Base
  scoped_by_name
end
...