2009-08-21 8 views
1

En essayant d'être DRY, j'essaye d'assigner à la variable d'instance d'un modèle après l'initialisation de l'objet.after_initialize provoque un débordement de pile

class WorkNote < ActiveRecord::Base 

    def after_initialize 
    self[:clockin]= WorkNote.last_clockout 
    end 

    def self.last_clockout 
    WorkNote.find(:first, :order => "clockout DESC").clockout 
    end 
end 

Cependant, l'appel de méthode en after_initialize provoque une SystemStackError:

ActiveRecord::StatementInvalid: SystemStackError: stack level too deep: SELECT * FROM "work_notes" ORDER BY clockout DESC LIMIT 1 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/abstract_adapter.rb:212:in `log' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/sqlite_adapter.rb:157:in `execute' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/sqlite_adapter.rb:402:in `catch_schema_changes' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/sqlite_adapter.rb:157:in `execute' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/sqlite_adapter.rb:305:in `select' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all_without_query_cache' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:62:in `select_all' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:661:in `find_by_sql' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1553:in `find_every' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1510:in `find_initial' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:613:in `find' 
    from /Users/noob/jobs/app/models/work_note.rb:10:in `last_clockout' 
    from /Users/noob/jobs/app/models/work_note.rb:6:in `after_initialize' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/callbacks.rb:347:in `send' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/callbacks.rb:347:in `callback' 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1662:in `send' 
... 5116 levels... 

Si je commente sur after_initialize, la méthode last_clockout n'a pas de problème. Cela ne se produit pas non plus lorsque j'utilise un rappel comme before_save au lieu de after_initialize. Pourquoi after_initialize provoque-t-il cela?

Merci!

Répondre

5

Après l'initialisation est appelée chaque fois qu'un objet est instancié (nouveau). Votre appel de recherche dans self.last_clockout crée un objet, puis appelle récursivement l'after_initialize. D'où la récurrence infinie et le débordement de pile.

Before_save ou after_create sont plus appropriés.

Voir http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

1

J'ai trouvé default_value_for est une très bonne façon de le faire.

+0

Merci pour les réponses utiles! Je comprends ce qu'est le problème. Est-ce que 'default_value_for' n'aurait pas le même problème, cependant? On dirait que tant que la valeur initiale de l'attribut nécessite un find(), un nouvel objet WorkNote devra être instancié et la boucle continuera à se produire. D'autres façons que je peux penser à faire cela: a. affecter la valeur initiale dans le contrôleur, bien que ce ne soit pas là où il appartient b. stocker la valeur par défaut dans un autre modèle, comme Utilisateurs D'autres suggestions? Merci! – rahum

+0

default_value_for fonctionne en remplaçant le getter et en fournissant une valeur par défaut seulement s'il n'en a pas déjà. Il ne modifie pas réellement la valeur dans la base de données ou l'objet sous-jacent. Puisque default_value_for ne s'exécute pas automatiquement lors de la création de l'objet (uniquement lors de l'accès à la colonne), il est impossible de réutiliser une utilisation similaire à la question d'origine. – Luke

0

Une autre approche serait de vérifier si l'objet est initialisé nouveau ou non, par exemple .:

def after_initialize 
    return unless self.new_record? 
    self.clockin = WorkNote.last_clockout 
end 
Questions connexes