web-dev-qa-db-fra.com

Ruby - Utilisation de class_eval pour définir des méthodes

Je fais la SaaS Stanford, en essayant de faire la partie 5 de cette affectation

J'ai vraiment du mal à saisir ce concept, voici ce que j'ai tenté 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 mauvaises, lisez le chapitre du livre de Ruby sur la métaprogrammation et je ne comprends toujours pas, est-ce que quelqu'un peut m'aider à comprendre cela?

22
8vius

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 au value de attr_name lors de la définition du programme de définition personnalisé. Sinon, on passerait normalement un bloc à class_eval.

41
Irfy

En ce qui concerne ce que vous avez fait, vous êtes sur le point de connaître 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 l’introduction 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

6
Angel Marquez

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, identique à def func parameter

def #{attr_name}=value
  (!defined? @#{attr_name}_history) ? @#{attr_name}_history = [nil, value] : @#{attr_name}_history << value
end
0
duykhoa