Rails 2.3 - 使用mixin实现动态named_scope

发布于 2024-12-04 19:26:59 字数 1573 浏览 1 评论 0原文

我使用以下 method_missing 实现为某个模型提供可适应的 name_scope 过滤:

class Product < ActiveRecord::Base
  def self.method_missing(method_id, *args)

    # only respond to methods that begin with 'by_'
    if method_id.to_s =~ /^(by\_){1}\w*/i

      # extract column name from called method
      column = method_id.to_s.split('by_').last

      # if a valid column, create a dynamic named_scope
      # for it. So basically, I can now run
      # >>> Product.by_name('jellybeans')
      # >>> Product.by_vendor('Cyberdine')
      if self.respond_to?( column.to_sym )
        self.send(:named_scope, method_id, lambda {|val|
          if val.present?
            # (this is simplified, I know about ActiveRecord::Base#find_by_..)
            { :conditions => ["#{base.table_name}.#{column} = ?", val]}
          else
            {}
          end
        })
      else
        super(method_id, args)
      end
    end
  end
end

我知道 ActiveRecord::Base 已经使用 find_by_ 提供了这一点,但我正在尝试做一点除了我给出的示例之外,还提供了一些针对我的应用程序定制的自定义过滤。我希望将其提供给选定的模型,而无需将此代码段粘贴到每个模型类中。我想过使用一个模块,然后将其混合到选择的模型中 - 我只是对语法有点模糊。

当错误开始堆积时,我已经做到了这一点(我这样做对吗?):

module GenericFilter
  def self.extended(base)

    base.send(:method_missing, method_id, *args, lambda { |method_id, args|
    # ?..
    })

  end
end

然后我希望能够像这样使用它:

def Product < ActiveRecord::Base
  include GenericFilter
end

def Vendor < ActiveRecord::Base
  include GenericFilter
end

# etc..

任何帮助都会很棒 - 谢谢。

I use the following method_missing implementation to give a certain model an adaptable named_scope filtering:

class Product < ActiveRecord::Base
  def self.method_missing(method_id, *args)

    # only respond to methods that begin with 'by_'
    if method_id.to_s =~ /^(by\_){1}\w*/i

      # extract column name from called method
      column = method_id.to_s.split('by_').last

      # if a valid column, create a dynamic named_scope
      # for it. So basically, I can now run
      # >>> Product.by_name('jellybeans')
      # >>> Product.by_vendor('Cyberdine')
      if self.respond_to?( column.to_sym )
        self.send(:named_scope, method_id, lambda {|val|
          if val.present?
            # (this is simplified, I know about ActiveRecord::Base#find_by_..)
            { :conditions => ["#{base.table_name}.#{column} = ?", val]}
          else
            {}
          end
        })
      else
        super(method_id, args)
      end
    end
  end
end

I know this is already provided by ActiveRecord::Base using find_by_<X>, but I'm trying to go a little bit beyond the example I've given and provide some custom filtering taylored to my application. I'd like to make it available to selected models w/o having to paste this snippet in every model class. I thought of using a module and then mixing it in the models of choice - I'm just a bit vague on the syntax.

I've gotten as far as this when the errors started piling up (am I doing this right?):

module GenericFilter
  def self.extended(base)

    base.send(:method_missing, method_id, *args, lambda { |method_id, args|
    # ?..
    })

  end
end

Then I hope to be able to use it like so:

def Product < ActiveRecord::Base
  include GenericFilter
end

def Vendor < ActiveRecord::Base
  include GenericFilter
end

# etc..

Any help will be great - thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

谁的年少不轻狂 2024-12-11 19:26:59

有两种方法可以实现这一目标

 module GenericModule
   def self.included(base)
     base.extend ClassMethods
   end

   module ClassMethods
     def methods_missing
       #....
     end
   end
 end

 class YourModel 
   include GenericModule
   .. 
 end

,或者

  module GenericModule
    def method_missing
      #...
    end
  end

  class MyModel
    extend GenericModule
  end

我建议使用第一种方法,它对我来说似乎更干净。作为一般建议,我会避免覆盖 method_missing :)。

希望这有帮助。

Two ways of achieving this

 module GenericModule
   def self.included(base)
     base.extend ClassMethods
   end

   module ClassMethods
     def methods_missing
       #....
     end
   end
 end

 class YourModel 
   include GenericModule
   .. 
 end

or

  module GenericModule
    def method_missing
      #...
    end
  end

  class MyModel
    extend GenericModule
  end

I would suggest using the first one, its seems cleaner to me. And as general advise, I'd avoid overriding method_missing :).

Hope this helps.

瑶笙 2024-12-11 19:26:59

您需要在包含 mixin 的类的上下文中定义范围。将您的范围包装在include_class.class_eval中,并且self将被正确设置为include_class。

module Mixin
  def self.included(klass)
    klass.class_eval do
      scope :scope_name, lambda {|*args| ... }
    end
  end
end

class MyModel
  include Mixin
end

You need to define the scope within the context of the class that is including your mixin. Wrap your scopes in including_class.class_eval and self will be correctly set to the including_class.

module Mixin
  def self.included(klass)
    klass.class_eval do
      scope :scope_name, lambda {|*args| ... }
    end
  end
end

class MyModel
  include Mixin
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文