Как реализуется метод" public/protected/private " и как я могу его эмулировать?
В ruby вы можете сделать следующее:
class Thing
public
def f1
puts "f1"
end
private
def f2
puts "f2"
end
public
def f3
puts "f3"
end
private
def f4
puts "f4"
end
end
Где теперь f1 и f3 и public, f2 и f4-частные. Что происходит внутри, что позволяет вызвать метод класса, который затем изменяет определение метода? Как я могу реализовать ту же функциональность (якобы для создания собственных Java-подобных аннотаций)
Например...
class Thing
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
И fun и notfun изменили бы следующие определения функций.
Спасибо
3 ответа:
Иногда вы можете засунуть Рубин в чашку эспрессо. Давайте посмотрим, как это сделать.
Вот модуль FunNotFun...
module FunNotFun def fun @method_type = 'fun' end def notfun @method_type = 'not fun' end def method_added(id) return unless @method_type return if @bypass_method_added_hook orig_method = instance_method(id) @bypass_method_added_hook = true method_type = @method_type define_method(id) do |*args| orig_method.bind(self).call(*args).tap do puts "That was #{method_type}" end end @bypass_method_added_hook = false end end
... который можно использовать для расширения класса ...
class Thing extend FunNotFun fun def f1 puts "hey" end notfun def f2 puts "hey" end end
... с таким результатом:
Но смотрите ниже строки для лучшего способа.Thing.new.f1 # => hey # => That was fun Thing.new.f2 # => hey # => That was not fun
Аннотации (см. ответ normalocity) менее сложны и, будучи обычной идиомой Ruby, легче передадут намерение вашего кода. Вот как это сделать с аннотациями:
module FunNotFun def fun(method_id) wrap_method(method_id, "fun") end def notfun(method_id) wrap_method(method_id, "not fun") end def wrap_method(method_id, type_of_method) orig_method = instance_method(method_id) define_method(method_id) do |*args| orig_method.bind(self).call(*args).tap do puts "That was #{type_of_method}" end end end end
В использовании, аннотация появляется после определения метода, а не раньше:
class Thing extend FunNotFun def f1 puts "hey" end fun :f1 def f2 puts "hey" end notfun :f2 end
Результат тот же:
Thing.new.f1 # => hey # => That was fun Thing.new.f2 # => hey # => That was not fun
Похоже, что вы хотите написать расширения для самого языка Ruby, что вполне возможно. Это не то, что можно объяснить кратко, но эта ссылка должна помочь вам начать:
Http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html
Эта ссылка, имеющая отношение к аннотациям в Ruby, также может быть полезной / актуальной:
Вот чисто рубиновое решение, которое поможет вам двигаться в правильном направлении. Он зависит от
method_added
. Будьте осторожны, чтобы избежать рекурсии с помощью предложения guard.module Annotations def fun @state = :fun end def not_fun @state = :not_fun end def inject_label(method_name) state = @state define_method(:"#{method_name}_with_label") do |*args, &block| puts "Invoking #{method_name} in state #{state}" send(:"#{method_name}_without_label", *args, &block) end alias_method :"#{method_name}_without_label", :"#{method_name}" alias_method :"#{method_name}", :"#{method_name}_with_label" end def self.extended(base) base.instance_eval do def self.method_added(method_name) return if method_name.to_s =~ /_with(out)?_label\Z/ @seen ||= {} unless @seen[method_name] @seen[method_name] = true inject_label(method_name) end end end end end class Foo extend Annotations fun def something puts "I'm something" end not_fun def other puts "I'm the other" end end