Ruby-Usando class_eval para definir métodos
metaprogramming class-eval (3)
¡¡¡Esto fue divertido!!!
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s # make sure it''s a string
attr_reader attr_name
attr_reader attr_name+"_history"
class_eval %Q"
def #{attr_name}=(value)
if !defined? @#{attr_name}_history
@#{attr_name}_history = [@#{attr_name}]
end
@#{attr_name} = value
@#{attr_name}_history << value
end
"
end
end
class Foo
attr_accessor_with_history :bar
end
class Foo2
attr_accessor_with_history :bar
def initialize()
@bar = ''init''
end
end
f = Foo.new
f.bar = 1
f.bar = nil
f.bar = ''2''
f.bar = [1,nil,''2'',:three]
f.bar = :three
puts "First bar:", f.bar.inspect, f.bar_history.inspect
puts "Correct?", f.bar_history == [f.class.new.bar, 1, nil, ''2'', [1,nil,''2'',:three], :three] ? "yes" : "no"
old_bar_history = f.bar_history.inspect
f2 = Foo2.new
f2.bar = ''baz''
f2.bar = f2
puts "/nSecond bar:", f2.bar.inspect, f2.bar_history.inspect
puts "Correct?", f2.bar_history == [f2.class.new.bar, ''baz'', f2] ? "yes" : "no"
puts "/nIs the old f.bar intact?", f.bar_history.inspect == old_bar_history ? "yes" : "no"
Tenga en cuenta que la única razón por la que necesita usar cadenas con class_eval es para poder consultar el valor de attr_name
al definir el attr_name
personalizado. De lo contrario, normalmente se pasaría un bloque a class_eval
.
Estoy haciendo la clase de SaaS Stanford, tratando de hacer la Parte 5 de esta tarea
Me está costando mucho entender este concepto, esto es lo que he intentado hacer:
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s
attr_reader attr_name
attr_reader attr_name + ''_history''
class_eval %Q''{def #{attr_name}(a);#{attr_name}_history.push(a) ; end;}''
end
end
Probablemente estoy haciendo todo tipo de cosas mal, lea el capítulo sobre la metaprogramación del Libro de Ruby y todavía no lo entiendo, ¿puede alguien ayudarme a comprender esto?
Con respecto a lo que has hecho, realmente estás en la cúspide de la solución. Es solo que #{attr_name}_history
no existe en tu código. Tendrá que crear una variable de instancia y establecerla en nil si no existe. Lo que ya debe manejar debe empujar dentro de la matriz si existe.
Hay varias maneras de hacer eso. Una forma es if defined? @#{attr_name}_history DoStuffHere
if defined? @#{attr_name}_history DoStuffHere
Debes notar que #{attr_name}_history
es una variable de instancia, así que usa @ antes, como @foo en la clase a continuación
def #{attr_name}=value
, #{attr_name}=
es el nombre del método, el value
es parámetro, igual que el def func parameter
def #{attr_name}=value
(!defined? @#{attr_name}_history) ? @#{attr_name}_history = [nil, value] : @#{attr_name}_history << value
end