Ruby: добавить метод в класс входного параметра - PullRequest
1 голос
/ 23 декабря 2010

Я просто изучаю ruby ​​и размышлял о теоретической возможности добавления метода к классу объекта.Например, определите метод, который принимает параметр и добавит метод к классу этого параметра (а не только к самому объекту параметра).Пример примерно такой:

class SomeClass
end

class AnotherClass
end

alpha = SomeClass.new
beta = AnotherClass.new

def AddHelloMethodTo param

 # This is where I'm trying to
 # add a method to the class of the parameter
 def param.class.Hello 
  "Hello"
 end

end

AddHelloMethodTo alpha
AddHelloMethodTo beta

gamma = AnotherClass.new

alpha.Hello
beta.Hello
gamma.Hello

(Простите, если у меня есть синтаксические ошибки / опечатки, я ДЕЙСТВИТЕЛЬНО новичок в этом!)
Обратите внимание, как я не вызываю AddHelloMethodTo на gamma но я ожидаю, что Hello будет определен, потому что я добавил его в класс.
Возможно ли это?

Ответы [ 2 ]

7 голосов
/ 23 декабря 2010

Это самое близкое к тому, что у тебя было.Я позволил себе сменить его на стандартный стиль кодирования Ruby, но учтите, что единственное реальное изменение - это первая строка add_hello_method_to_class_of:

class SomeClass; end
class AnotherClass; end

alpha = SomeClass.new
beta = AnotherClass.new

def add_hello_method_to_class_of(obj)
  obj.class.send(:define_method, :hello) do
    'Hello'
  end
end

add_hello_method_to_class_of(alpha)
add_hello_method_to_class_of(beta)

gamma = AnotherClass.new

alpha.hello
beta.hello
gamma.hello

Первоначально у вас было

def obj.class.hello

Это сработает, но не так, как вы думаете.Это добавит одноэлементный метод к самому объекту класса , но, похоже, вы предполагаете, что он добавит экземплярный метод .Если вы хотите добавить метод экземпляра , вам нужно использовать Module#define_method следующим образом:

obj.class.define_method(:hello)

За исключением того, что Module#define_method равно private, поэтому вам нужно использовать отражениечтобы обойти это ограничение доступа:

obj.class.send(:define_method, :hello)

Обратите внимание, что я также изменил название метода с add_hello_method_to на add_hello_method_to_class_of, поскольку, не добавляет *Метод 1031 * к аргументу, он добавляет его к классу аргумента .

Однако, если вы делаете патч обезьяны, как это, обычно считается хорошей практикой вместо этого использовать миксины, с тех пормиксин обнаруживается в цепочке наследования объекта, что дает любому отладчику этого кода как минимум реальный шанс выяснить, откуда, черт возьми, этот загадочный метод hello:

# file: hello_extension.rb
module HelloExtension
  def hello
    'Hello'
  end
end

def add_hello_method_to_class_of(obj)
  obj.class.send(:include, HelloExtension)
end

# some other file
require 'hello_extension'

class SomeClass; end
class AnotherClass; end

alpha = SomeClass.new
beta = AnotherClass.new

add_hello_method_to_class_of(alpha)
add_hello_method_to_class_of(beta)

gamma = AnotherClass.new

alpha.hello
beta.hello
gamma.hello

Теперь выможет легко отладить этот код:

gamma.class.ancestors
# => [AnotherClass, HelloExtension, Object, Kernel, BasicObject]

Если кто-то задается вопросом, откуда взялся метод hello, то не нужно много времени, чтобы выяснить, что миксину под названием HelloExtension, вероятно, есть чем занятьсяс этим.И, следуя стандартным правилам именования Ruby, они даже знают, что искать в файле с именем hello_extension.rb

Вы даже можете сделать это:

gamma.method(:hello).source_location
# => ['hello_extension.rb', 3]
1 голос
/ 23 декабря 2010

Ваш пример (как только вы исправите небольшую синтаксическую ошибку) добавляет метод класса к классу вместо метода экземпляра.

Чтобы определить метод экземпляра, вы можете использовать class_eval и определить метод, как если бы вы были в середине определения класса:

def AddHelloMethodTo param
  param.class.class_eval do
    def Hello
      "Hello"
    end
  end
end

Теперь все предыдущие и будущие экземпляры класса объекта будут иметь доступ к методу Hello.

...