I created a module which is included in a class. In the module, I am trying to define a method that is the downcased version of a class name without Filter. So ShowFilter would have a method called show that returns the class Show. I get "NoMethodError: undefined method `show' for ShowFilter:Class"
我创建了一个包含在类中的模块。在模块中,我试图定义一个方法,它是没有Filter的类名的下层版本。因此,ShowFilter将有一个名为show的方法,它返回Show类。我得到“NoMethodError:未定义的方法`show for ShowFilter:Class”
module Filters
module Base
module ClassMethods
@@filters = {}
def filter name, &block
@@filters[name] = block
end
def run query = {}
query.each do |name, value|
@@filters[name.to_sym].call(value) unless @@filters[name.to_sym].nil?
end
self
end
def self.extended(base)
name = base.class.name.gsub(/filter/i, '')
define_method(name.downcase.to_sym) { Kernel.const_get name }
end
end
def self.included base
base.extend ClassMethods
end
end
end
class ShowFilter
include Filters::Base
filter :name do |name|
self.show.where(:name => name)
end
end
EDIT: Example of use
编辑:使用示例
class ShowController < ApplicationController
def index
ShowFilter.run params[:query]
end
end
1 个解决方案
#1
3
When you define Filters::Base::ClassMethods
, it evaluates self in that context so the method you'll end up defining is ClassMethods.classmethods
(since the gsub won't do anything).
当您定义Filters :: Base :: ClassMethods时,它会在该上下文中计算self,因此您最终定义的方法是ClassMethods.classmethods(因为gsub不会执行任何操作)。
Like the included hook you tapped into in Base, you want to use extended in ClassMethods:
就像你在Base中使用的包含钩子一样,你想在ClassMethods中使用扩展:
module Filters
module Base
module ClassMethods
@@filters = {}
def filter name, &block
@@filters[name] = block
end
def run query = {}
query.each do |name, value|
@@filters[name.to_sym].call(value) unless @@filters[name.to_sym].nil?
end
Object.const_get(self.to_s.gsub('Filter', ''))
end
def self.extended(base)
define_method(base.to_s.downcase.gsub('filter', '').to_sym) do
Object.const_get(self.to_s.gsub('Filter', ''))
end
end
end
def self.included base
base.extend ClassMethods
end
end
end
class ShowFilter
include Filters::Base
filter :title do |title|
self.show.where(:title => title)
end
end
#1
3
When you define Filters::Base::ClassMethods
, it evaluates self in that context so the method you'll end up defining is ClassMethods.classmethods
(since the gsub won't do anything).
当您定义Filters :: Base :: ClassMethods时,它会在该上下文中计算self,因此您最终定义的方法是ClassMethods.classmethods(因为gsub不会执行任何操作)。
Like the included hook you tapped into in Base, you want to use extended in ClassMethods:
就像你在Base中使用的包含钩子一样,你想在ClassMethods中使用扩展:
module Filters
module Base
module ClassMethods
@@filters = {}
def filter name, &block
@@filters[name] = block
end
def run query = {}
query.each do |name, value|
@@filters[name.to_sym].call(value) unless @@filters[name.to_sym].nil?
end
Object.const_get(self.to_s.gsub('Filter', ''))
end
def self.extended(base)
define_method(base.to_s.downcase.gsub('filter', '').to_sym) do
Object.const_get(self.to_s.gsub('Filter', ''))
end
end
end
def self.included base
base.extend ClassMethods
end
end
end
class ShowFilter
include Filters::Base
filter :title do |title|
self.show.where(:title => title)
end
end