2016-12-27 2 views
1

Предположим, у нас есть множество методов с типичными префиксами.Динамически генерировать префиксы для имен методов

def pref_foo 
    # code 
end 

def pref_bar 
    # code 
end 

Я хочу, чтобы узнать, как автоматически предварять эти префиксы к моим именам методов (например, как это делается в Rails: Model.find_by_smth).

Другими словами я хочу создать некоторые возможности pref_, который принимает методы и pref_ добавлять в начало их имен, так что мой метод foo становится доступен как pref_foo.


module Bar 
    # definition of some wrapper `pref_` 
end 

class Foo 
    include Bar 

    <some wrapper from Bar> do 
    def foo 
     puts 'What does a computer scientist wear on Halloween?' 
    end 

    def bar 
     puts 'A bit-mask.' 
    end 
    end 
end 

foo = Foo.new 

foo.pref_foo # => "What does a computer scientist wear on Halloween?" 
foo.pref_bar # => "A bit-mask." 
+0

проверить ['method_missing'] (https://ruby-doc.org/core-2.3.3/BasicObject.html#method-i-method_missing) :) –

ответ

5

Попробуйте это,

class Module 
    def with_prefix(prefix, &block) 
    m = Module.new 
    m.instance_eval(&block) 
    m.methods(false).each do |name| 
     define_method "#{prefix}_#{name}", &m.method(name) 
     module_function "#{prefix}_#{name}" unless self.is_a?(Class) 
    end 
    end 
end 

class A 
    with_prefix :pref do 
    with_prefix :and do 
     def foo 
     puts "foo called" 
     end 

     def bar 
     puts "bar called" 
     end 
    end 
    end 
end 

A.new.pref_and_foo 
A.new.pref_and_bar 

Как это работает?

  • Определим новую функцию with_prefix на суперкласс всех классов
  • Эта функция принимает имя и блок
  • Evaluate блок в контексте анонимного модуля.
  • Это выполняет def заявления на анонимном модуле, а не класс
  • Enumerate над всеми функциями этого модуля
  • Создать Префиксальные методы для каждого из этих функций
+0

Удивительный! Еще один вопрос: как сделать 'with_prefix' вложенным? – Viktor

+1

@ viktordanilov обновлен! – akuhn

+0

Этот ответ полностью заслуживает награду – Viktor

1

Вы можете использовать метод обратного вызова Module#included и методы класса Method#instance_methods, Method#alias_method, Module#remove_method и Object#send следующим образом.

module Bar 
    def self.included(klass) 
    klass.instance_methods(false).each do |m| 
     klass.send :alias_method, "pref_#{m.to_s}".to_sym, m 
     klass.send :remove_method, m 
    end 
    end 
end 

class Foo 
    def foo 
    puts 'hi' 
    end 

    def bar 
    puts 'ho' 
    end 

    include Bar 
end 

Foo.instance_methods.select { |m| [:foo, :bar, :pref_foo, :pref_bar].include?(m) } 
    #=> [:pref_foo, :pref_bar] 

Foo.new.pref_foo 
    #=> "hi" 

Foo.new.foo 
    #=> NoMethodError: undefined method `foo' for #<Foo:0x007fdecb0633d8> 

send должны быть использованы, поскольку alias_method и remove_method методы частного класса. Утверждение include Bar, очевидно, должно следовать определениям методов экземпляра в Foo.

В качестве вопроса (sic): «Я хочу автоматически добавить префикс к именам методов экземпляра ...», префикс должен быть жестким.

Смежные вопросы