Ruby-utilisation de la classe eval pour définir des méthodes
Je fais la classe Saas Stanford, en essayant de faire la partie 5 de cette affectation
J'ai vraiment du mal à saisir ce concept, c'est ce que j'ai essayé de faire:
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
Je fais probablement toutes sortes de choses mal, lisez le chapitre du Livre de Ruby sur la métaprogrammation et je ne comprends toujours pas, quelqu'un peut-il m'aider à comprendre cela?
3 réponses
C'était amusant!!!
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"
Notez que la seule raison pour laquelle vous devez utiliser des chaînes avec class_eval est que vous pouvez vous référer à la valeur de attr_name
lors de la définition du setter personnalisé. Sinon, on passerait normalement un bloc à class_eval
.
En ce qui concerne ce que vous avez fait, vous êtes en fait sur le point de la solution. C'est juste que #{attr_name}_history
n'existe pas dans votre code. Vous devrez créer une variable d'instance et la définir sur nil si elle n'existe pas. Ce que vous avez déjà devrait gérer pousser dans le tableau s'il existe.
Il y a plusieurs façons de le faire. Une façon est if defined? @#{attr_name}_history DoStuffHere
Vous devez remarquer que #{attr_name}_history
est une variable d'instance, utilisez donc @ before, comme @foo dans la classe ci-dessous
def #{attr_name}=value
, #{attr_name}=
est le nom de la méthode, value
est le paramètre, même en tant que def func parameter
def #{attr_name}=value
(!defined? @#{attr_name}_history) ? @#{attr_name}_history = [nil, value] : @#{attr_name}_history << value
end