2017-05-22 1 views
0

J'ai une application Rails avec un besoin récurrent de définition d'attributs par défaut. Parfois, l'utilisateur fournira des valeurs pour les attributs qui seront respectés, mais dans d'autres circonstances, le modèle ou l'utilisateur peut souhaiter que ces attributs soient remplacés avec des valeurs par défaut en ne tenant pas compte des valeurs d'origine. J'ai supposé que ce problème nécessitait une méthode de configuration par défaut (!) et non-frappée pour définir des valeurs par défaut permettant à l'utilisateur et au programme de passer à l'état approprié. Le setter non affecté ne définira les valeurs par défaut que lorsqu'elles ne sont pas nulles alors que la version banged écrasera toujours les attributs par défaut. La différence est mineure:Création d'accesseurs d'attribut avec des options par défaut

class BangDiBang 
    attr_accessor :value 

    def set_default 
    self.value ||= do_some_suff_to_determine_default_value 
    end 

    def set_default! 
    self.value = do_some_suff_to_determine_default_value 
    end 

    ... 
end 

Le problème avec ce code est que si j'avais un tas de variables à définir, je finirais en répétant le même code deux fois pour chaque variable.

Ma question est de savoir comment sortir ce code? Sauvegarde de la logique dans une méthode et ayant deux méthodes set_value et set_value! appelant la logique centrale avec les différents opérateurs d'affectation.

J'ai invoqué une solution: écrire la logique centrale en tant que texte, remplacer l'opération d'affectation par les méthodes de réglage et évaluer (mais cela ne semble pas correct). Comment ne pas me répéter?

+0

Que diriez-vous de créer une préoccupation avec elle et de les inclure partout où vous en avez besoin? – Severin

Répondre

0

Votre façon de procéder ne fonctionnera pas pour plusieurs paramètres puisque vous appelez set_default mais sans spécifier de variable. Vous voudrez définir le comportement de chaque variable, idéalement. Il existe des bibliothèques qui gèrent ce genre de chose, mais vous pouvez rouler votre propre assez facilement:

class Example 
    def self.default_param(name, default_value) 
    define_method("default_#{name}") { default_value } 

    define_method(name) do 
     instance_variable_get("@#{name}") || default_value 
    end 

    attr_writer name 
    end 

    default_param :foo, 'foo default' 
end 

ex = Example.new 
ex.foo #=> "foo default" 
ex.foo = 'bar' 
ex.foo #=> "bar" 
ex.default_foo #=> "foo default" 

J'ai retitré set_default et set_default! être plus clair: pour chaque variable avec une valeur par défaut, trois méthodes sont créées (par exemple en utilisant foo comme nom de variable):

  1. foo - retourne la valeur de @foo si elle est truthy et default_foo autrement
  2. foo= - définit la valeur de @foo
  3. default_foo - retourne la valeur par défaut

Vous pouvez compartimenter et sécher une partie du code ci-dessus précisées, la création d'une méthode default_params (au pluriel) de prendre un hachage, l'extraction de la macro de classe à une préoccupation:

module DefaultParams 
    def default_param(name, default_value) 
    define_method("default_#{name}") { default_value } 

    define_method(name) do 
     instance_variable_get("@#{name}") || default_value 
    end 

    attr_writer name 
    end 

    def default_params(params) 
    params.each { |default| default_param(*default) } 
    end 
end 

class Example 
    extend DefaultParams 
    default_params foo: 'default foo', bar: 'my favorite bar' 
end 
+0

J'avais besoin d'un petit indice auquel je n'avais tout simplement pas pensé: avoir une méthode default_value pour chaque attribut qui devrait avoir une valeur par défaut. Merci. –

0

J'ai implémenté une solution avec beaucoup d'inspiration de coreyward. Au début, je n'avais pas réalisé que plutôt que d'avoir deux méthodes pour définir un tableau de valeurs par défaut, j'avais besoin que les valeurs par défaut soient séparées en méthodes simples. Cela donne une grande flexibilité à la conception de l'application. Alors merci beaucoup pour la réponse.

Pour la configuration des rails, je l'ai ajouté à application_record.rb

def set_attr_from_defaults 
    default_attrs.each do |atr| 
    eval("self.#{atr[0..atr.length-9]} ||= self.#{atr}") 
    end 
end 

def set_attr_from_defaults! 
    default_attrs.each do |atr| 
    eval("self.#{atr[0..atr.length-9]} = self.#{atr}") 
    end 
end 

def set_default_attr params 
    params.each { |key, value| self.define_singleton_method(key){value} } 
end 

def default_attrs 
    self.attributes.keys.map{|i| (i+'_default').to_sym} & self.methods 
end 

default_attrs produit une liste des symboles hors des attributs par défaut. set_default_attr définit des méthodes singleton, avec l'utilisation prévue de l'analyse dans un hachage comme attrname_default: 'default_value' ....Le set_attr_from_defaults définira des attributs à partir des valeurs par défaut, lorsqu'un attribut a une paire comme var: var_default. Le numéro 9 est la longueur de _default + 1.