在具有变量名称的模块中定义类方法

时间:2023-01-15 18:52:02

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