En ruby, puedes hacer esto:
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
donde ahora f1 y f3 y público, f2 y f4 esprivado. ¿Qué sucede internamente que le permite invocar un método de clase que luego cambia la definición del método? ¿Cómo puedo implementar la misma funcionalidad (aparentemente para crear mis propias anotaciones de tipo java)?
por ejemplo...
class Thing
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
y fun and notfun cambiaría las siguientes definiciones de funciones.
Gracias
Respuestas
8 para la respuesta № 1A veces puedes meter a Ruby en una taza de café exprés. Veamos cómo.
Aquí hay un módulo 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
... que puedes usar para extender una clase ...
class Thing
extend FunNotFun
fun
def f1
puts "hey"
end
notfun
def f2
puts "hey"
end
end
... con este resultado:
Thing.new.f1
# => hey
# => That was fun
Thing.new.f2
# => hey
# => That was not fun
Pero ver debajo de la línea para una mejor manera.
Las anotaciones (ver la respuesta de normalocity) son menos problemáticas y, al ser un lenguaje común de Ruby, comunicará más fácilmente la intención de su código. Aquí está cómo hacerlo con anotaciones:
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
En uso, la anotación viene después de que se define el método, en lugar de antes:
class Thing
extend FunNotFun
def f1
puts "hey"
end
fun :f1
def f2
puts "hey"
end
notfun :f2
end
El resultado es el mismo:
Thing.new.f1
# => hey
# => That was fun
Thing.new.f2
# => hey
# => That was not fun
1 para la respuesta № 2
Parece que quieres escribir extensiones al lenguaje Ruby, lo cual es posible. No es algo que se pueda explicar brevemente, pero este enlace debería ayudarlo a comenzar:
http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html
Esta referencia, que tiene que ver con las anotaciones en Ruby, también podría ser útil / relevante:
http://martinfowler.com/bliki/RubyAnnotations.html
1 para la respuesta № 3
Aquí hay una solución de rubí puro para llevarte en la dirección correcta. Depende de method_added
. Tenga cuidado de evitar la recursión utilizando una cláusula de guarda.
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)?_labelZ/
@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