2017-07-23 1 views
0

Mon application crée un objet et lors de la sauvegarde de cet objet sur la base de données, je souhaite créer un objet Quote.new (QuoteRequesthas_oneQuote). Dans une nouvelle instance Quote je veux définir certaines variables d'instance, que diverses méthodes liées à un crawler Watir utilisera, puis le résultat de cette analyse Watir & scrape #save donc persister l'objet Quote à la base de données.Comment dois-je initialiser les variables d'instance sur un objet ApplicationRecord?

Je voudrais définir les variables d'instance Quote dans une méthode initialize dès que l'objet est créé, mais cela crée des problèmes avec le fait qu'il hérite de ce ApplicationRecord il y a un conflit d'initialisation.

Quelle est la manière correcte d'instancier un objet ApplicationRecord avec des variables, sans conflit avec le code d'initialisation de la bibliothèque Rails?

+0

Les deux réponses vraiment utiles, merci les gars. Je voudrais pouvoir accepter deux réponses, mais comme je ne peux pas je me suis senti obligé d'accepter M.Simon Borgs en raison de la longueur de l'explication fournie, bien que Vijay est une réponse parfaitement bonne aussi. Merci encore, Vijay, d'arriver la prochaine fois. – jbk

Répondre

1

Si vous voulez juste définir une variable d'instance sans aucune logique supplémentaire à l'initialisation, alors la réponse de Vijay est bonne. Cependant, vous pouvez avoir plus de flexibilité que d'être limité aux méthodes attr.

Comme avec tout Ruby, vous pouvez toujours profiter de super si vous maintenez la bonne interface à la super méthode. ActiveRecord::Base#initialize ne prend qu'un argument optionnel, un hash d'attributs et de valeurs. Il se rend également à un bloc si un bloc est donné. https://github.com/rails/rails/blob/master/activerecord/lib/active_record/core.rb#L313

Donc, si vous voulez définir une variable d'instance à l'initialisation sans définir également des méthodes d'accès public, vous pouvez:

class Quote < ApplicationRecord 
    def initialize(attributes = nil, &block) 
    @my_var = attributes.delete(:my_var) if attributes 
    super 
    end 
end 

quote = Quote.new(my_var: 'value') 
quote.instance_variable_get(:@my_var) # => "value" 

Vous pouvez également effectuer des opérations plus complexes. Supposons que vous avez User has_many :posts et Post belongs_to :user, User a un :name et Post a un :title.

class User < ApplicationRecord 
    def initialize(attributes = nil, &block) 
    title = attributes.delete(:first_post_title) if attributes 
    super 
    posts.build(title: "#{name}'s first post: #{title}") if title 
    end 
end 

user = User.new(first_post_title: 'Hello, world!', name: 'Matz') 
user.posts.first # => #<Post:xxxxxxxxxxx title: "Matz's first post: Hello, world!"> 

Espérons que montre comment flexible, vous pouvez être même avec ActiveRecord objets, et parfois callbacks sont un peu trop compliqué la mise en œuvre et restrictive en cours d'utilisation lorsque super peut fonctionner très bien et encore mieux quand il est utilisé de façon appropriée.

+0

@ m-simon-borg, j'ai encore du mal à instancier avec un 'attendu 0. Erreur arguments .1', je fais exactement comme vous le suggérez ci-dessus dans la méthode initialize mais avec les variables suivantes; '@username = attributes.delete (: nom d'utilisateur) if attributes' et' @password = attributes.delete (: mot de passe) si attributes' et '@url = attributes.delete (: url) si attributes' puis appel' super' après déclaration de variable. – jbk

+0

@jbk A quoi ressemble votre code? Qu'est-ce que le 'ArgumentError' dit d'autre? 'attendu 0..1, reçu ???' Si votre déclaration de méthode est 'def initialize (attributs = nil, & block)' alors vous devriez appeler 'super' sans argument. Assurez-vous également que votre appel 'Quote.new' utilise la syntaxe de hachage comme vous le feriez autrement. 'Quote.new (nom d'utilisateur:" ... ", mot de passe:" ... ", url:" ... ")'. Si vous voulez le mettre dans un Gist et le lier ici, je serais heureux de regarder. Aussi la réponse de Vijay pourrait être tout ce dont vous avez besoin dans votre cas –

+0

Excusez-moi @ m-simon-borg, une erreur de syntaxe daft à mon extrémité, maintenant cette méthode d'initialisation avec super fonctionne magnifiquement Merci. – jbk

1

Pour les propriétés de Quote vous ne voulez pas enregistrer dans DB, créez attr_reader (ou attr_accessor). Supposons que votre méthode Watir a besoin var1 et var2 objets, votre Quote devrait ressembler à ceci:

class Quote < ApplicationRecord 
    attr_reader :var1, :var2 
end 

Et puis dans after_create de rappel QuoteRequest il vous suffit de passer ces variables comme paire clé-valeur comme toute autre propriété.

obj1 = Var1.new 
obj2 = Var2.new 
quote = Quote.new(var1: obj1, var2: obj2, ....)` 

Maintenant, vous pouvez y accéder en appelant quote.var1 et quote.var2. L'appel save sur quote persisterait toutes les propriétés qui ont des colonnes correspondantes dans db sauf var1, var2.

+0

Dans le contexte ci-dessus, est-ce que 'var1' et 'var2' sur 'Quote.save' seraient conservés dans la base de données si j'avais créé des colonnes pour eux? – jbk

+0

Si vous avez une colonne pour var1 et var2 alors vous n'avez pas besoin d'ajouter cette ligne: 'attr_reader: var1,: var2' –

+0

aha, donc id une colonne est définie sur le schéma du modèle puis les rails ajoute automatiquement' attr_accessor'? – jbk