Определите метод класса в модуле с именем переменной

Я создал модуль, который включен в класс. В модуле я пытаюсь определить метод, который представляет собой сокращенную версию имени класса без фильтра. Таким образом, ShowFilter будет иметь метод show, который возвращает класс Show. Я получаю "NoMethodError: неопределенный метод `show' для 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: пример использования

class ShowController < ApplicationController
  def index
    ShowFilter.run params[:query]
  end
end

person chris    schedule 24.07.2012    source источник
comment
Да, а где метод show?   -  person Sergio Tulentsev    schedule 25.07.2012
comment
define_method(self.name.downcase.gsub('filter', '').to_sym) {self}   -  person chris    schedule 25.07.2012


Ответы (1)


Когда вы определяете Filters::Base::ClassMethods, он оценивает себя в этом контексте, поэтому метод, который вы в конечном итоге определите, будет ClassMethods.classmethods (поскольку gsub ничего не сделает).

Подобно включенному хуку, который вы использовали в 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
person Chris    schedule 25.07.2012
comment
Я попробовал то, что вы предложили, новый код находится в исходном вопросе. Я все еще получаю сообщение об ошибке. Я пытаюсь определить фильтр DSL для моделей монгоидов. Я хочу иметь возможность включить базовый модуль фильтра в несколько классов фильтров и получить доступ к модели для этого фильтра по его имени, а не к чему-то общему, например модели. - person chris; 25.07.2012
comment
Хорошо, я обновил свой код. Я все еще не уверен, что он должен вернуть. Это self.filter? Или, вероятно, это будет модель под названием self.downcase.gsub('filter', '') - person Chris; 25.07.2012
comment
извините, я, вероятно, отредактировал свой код, пока вы работали, взгляните еще раз на то, что он возвращает, и он должен быть более ясным. Спасибо, что посмотрели это! В основном ShowFilter должен иметь метод класса show, который возвращает класс Show (не экземпляр, а сам класс). А у класса BugsFilter будет метод bugs, возвращающий класс Bugs. - person chris; 25.07.2012
comment
Хорошо! Ну вот. Итак, учитывая WalrusFilter, вы можете сделать WalrusFilter.walrus, и это вернет класс Walrus. - person Chris; 25.07.2012
comment
Также не забудьте удалить определение класса Show (я удалю отсюда). В противном случае я получал ошибки об этом. - person Chris; 25.07.2012
comment
Это работает для вас? Я все еще получаю сообщение об ошибке при запуске rspec - person chris; 25.07.2012
comment
давайте продолжим это обсуждение в чате - person Chris; 25.07.2012