Voici un code simple qui, pour chaque argument spécifié, ajoutera des méthodes get/set spécifiques nommées après cet argument. Si vous écrivez attr_option :foo, :bar
, vous verrez #foo/foo=
et #bar/bar=
méthodes d'instance sur Config
:Comment écrire un test RSpec pour tester ce code de métaprogrammation intéressant?
module Configurator
class Config
def initialize()
@options = {}
end
def self.attr_option(*args)
args.each do |a|
if not self.method_defined?(a)
define_method "#{a}" do
@options[:"#{a}"] ||= {}
end
define_method "#{a}=" do |v|
@options[:"#{a}"] = v
end
else
throw Exception.new("already have attr_option for #{a}")
end
end
end
end
end
Jusqu'à présent, si bon. Je veux écrire des tests RSpec pour vérifier que ce code est en train de faire ce qu'il est censé faire. Mais il y a un problème! Si j'appelle attr_option :foo
dans l'une des méthodes de test, cette méthode est maintenant définie pour toujours dans Config. Donc, un test ultérieur échouera quand il ne devrait pas, parce que foo
est déjà défini:
it "should support a specified option" do
c = Configurator::Config
c.attr_option :foo
# ...
end
it "should support multiple options" do
c = Configurator::Config
c.attr_option :foo, :bar, :baz # Error! :foo already defined
# by a previous test.
# ...
end
Est-il possible que je peux donner à chaque essai, un « clone » anonyme de la classe Config
qui est indépendant des autres?
+1 pour des conseils solides et une approche novatrice; Je n'aurais pas pensé créer des classes anonymes et je pensais à créer un module anonyme avec la classe et à le détruire ensuite. C'est clairement une meilleure solution. –