Как составить модули, содержащие method_missing в ruby - PullRequest
9 голосов
/ 16 июня 2011

У меня есть пара модулей, которые расширяют метод, отсутствующий:

module SaysHello
    def respond_to?(method)
        super.respond_to?(method) || !!(method.to_s =~ /^hello/)
    end
    def method_missing(method, *args, &block)
        if (method.to_s =~ /^hello/)
            puts "Hello, #{method}"
        else
            super.method_missing(method, *args, &block)
        end
    end
end

module SaysGoodbye
    def respond_to?(method)
        super.respond_to?(method) || !!(method.to_s =~ /^goodbye/)
    end
    def method_missing(method, *args, &block)
        if (method.to_s =~ /^goodbye/)
            puts "Goodbye, #{method}"
        else
            super.method_missing(method, *args, &block)
        end
    end
end

class ObjectA
    include SaysHello
end

class ObjectB
    include SaysGoodbye
end

Это все работает хорошо, например, ObjectA.new.hello_there вывод "Hello, hello_there". Аналогично, ObjectB.new.goodbye_xxx выводит "Goodbye, xxx". respond_to? также работает, например, ObjectA.new.respond_to? :hello_there возвращает true.

Однако, это не очень хорошо работает, если вы хотите использовать SaysHello и SaysGoodbye:

class ObjectC
    include SaysHello
    include SaysGoodbye
end

Хотя ObjectC.new.goodbye_aaa работает правильно, ObjectC.new.hello_a ведет себя странно:

> ObjectC.new.hello_aaa
Hello, hello_aaa
NoMethodError: private method `method_missing' called for nil:NilClass
    from test.rb:22:in `method_missing' (line 22 was the super.method_missing line in the SaysGoodbye module)

Выводит правильно, затем выдает ошибку. Также respond_to? не правильно, ObjectC.new.respond_to? :hello_a возвращает false.

Наконец, добавив этот класс:

class ObjectD
    include SaysHello
    include SaysGoodbye

    def respond_to?(method)
        super.respond_to?(method) || !!(method.to_s =~ /^lol/)
    end


    def method_missing(method, *args, &block)
        if (method.to_s =~ /^lol/)
            puts "Haha, #{method}"
        else
            super.method_missing(method, *args, &block)
        end
    end
end

Также действует странно. ObjectD.new.lol_zzz работает, однако ObjectD.new.hello_a and ObjectD.new.goodbye_t оба выдают исключение имени после вывода правильной строки. respond_to? также не работает для методов hello и goodbye.

Есть ли способ заставить все это работать правильно? Объяснение того, как method_missing, Модули и super взаимодействуют, также было бы очень полезно.

РЕДАКТИРОВАТЬ: coreyward решил проблему, если я использую super вместо super.<method-name>(args...) во всех определяемых мной методах, программа работает правильно. Я не понимаю, почему это так, поэтому я задал еще один вопрос по этому поводу на Что делает super. в ruby?

Ответы [ 2 ]

5 голосов
/ 16 июня 2011

Когда вы переопределяете метод, вы переопределяете метод;period.

То, что вы делаете, когда включаете второй модуль с определением метода method_missing, переопределяет ранее определенное значение method_missing.Вы можете сохранить его, добавив псевдонимы, прежде чем переопределять его, но вы можете быть осторожны с этим.

Кроме того, я не знаю, почему вы звоните super.method_missing.Как только ваше определение method_missing вышло из уловок, вы должны сообщить Ruby, что оно может продолжить цепочку в поисках способа обработки вызова, просто вызвав super (не нужно передавать аргументы или указывать имя метода).

О Super (обновление)

При вызове super Ruby продолжает цепочку наследования в поисках следующего определения вызываемого метода и, если этонаходит тот, который вызывает, и возвращает ответ.Когда вы вызываете super.method_missing, вы вызываете метод method_missing для ответа на super().

. Возьмите этот (несколько глупый) пример:

class Sauce
  def flavor
    "Teriyaki"
  end
end

# yes, noodles inherit from sauce:
#   warmth, texture, flavor, and more! ;)
class Noodle < Sauce
  def flavor
    sauce_flavor = super
    "Noodles with #{sauce_flavor} sauce"
  end
end

dinner = Noodle.new
puts dinner.flavor     #=> "Noodles with Teriyaki sauce"

Вы можете видеть, что суперметод, как и любой другой, он просто делает магию за кулисами.Если вы позвоните super.class здесь, вы увидите String, так как "Терияки" - это строка.

Имеет смысл сейчас?

2 голосов
/ 10 декабря 2013

http://www.perfectline.ee/blog/activerecord-method-missing-with-multiple-inheritance

В этой статье объясняется, как именно он работает: каждый новый модуль не перезаписывает и не заменяет методы - вместо этого его методы вставляются в цепочку наследования.Вот почему вызов super из каждого метода method_missing в конечном итоге вызывает все методы method_missing.

Класс остается самым низким в цепочке наследования, а последний добавленный модуль находится рядом с классом.

Итак:

class Foo
  include A
  include B
end

приводит к ядру -> A -> B -> Foo

...