2009-05-06 6 views
1

J'utilise l'héritage de table unique (STI) sur l'un de mes modèles pour une application Rails et j'ai des problèmes pour stocker des objets de modèle sur une constante. J'ai isolé le problème dans un exemple de projet et l'ai soumis à GitHub: http://github.com/fcoury/rails-sti-cachingProblème de mise en cache Instances de modèle sur une constante dans Rails

Ce que j'essaie de faire est de charger une instance de modèle (dans ce cas, un modèle de musique, qui hérite du modèle Media via STI) sur un initialiseur (dans Rails répertoire de /config/initializers/) et le maintenir sur une constante:

MUSIC_CACHE = Hash.new 
Music.all.each { |m| MUSIC_CACHE[m.id] = m } 

et j'ai un contrôleur d'échantillon qui effectue les opérations suivantes:

class MusicsController < ApplicationController 
    def index 
    require 'pp' 
    pp MUSIC_CACHE 
    @debug = [] 
    MUSIC_CACHE.each_pair do |k, v| 
     music = Music.find(k) 
     d "Object for Music.find(#{k}) => class: #{music.class} - class obj_id: #{music.class.object_id} - #{music.inspect}" 
     d "Object for MUSIC_CACHE[#{k}] => class: #{v.class} - class obj_id: #{v.class.object_id} - #{v.inspect}" 

     begin 
     d " - Music.is_a?(Media) => #{v.is_a?(Media)}" 
     d " - Try to call name => #{v.name}" 
     rescue 
     d "*** Error raised:\n#{$!}" 
     end 
    end 

    @musics = Music.all 
    end 

    def d(s) 
    puts s 
    @debug << s 
    end 
end 

et en vue d'aller avec elle:

<h1 id="music">Music</h1> 

<ul> 
    <% for m in @musics %> 
    <li><%= m.name %> - <%= m.file %></li> 
    <% end %> 
</ul> 

<pre><%=h @debug.join("\n") %></pre> 

La première fois que ce code fonctionne, la sortie sur l'étiquette <pre> est la suivante:

Object for Music.find(2) => class: Music - class obj_id: 13067420 - #<Music id: 2, name: "5th Symphony", file: "5s.mp3", type: "Music", created_at: "2009-05-06 16:31:41", updated_at: "2009-05-06 16:31:41"> 
    Object for MUSIC_CACHE[2] => class: Music - class obj_id: 13067420 - #<Music id: 2, name: "5th Symphony", file: "5s.mp3", type: "Music", created_at: "2009-05-06 16:31:41", updated_at: "2009-05-06 16:31:41"> 
    - Music.is_a?(Media) => true 
    - Try to call name => 5th Symphony 

Cependant, si je recharge seulement la page, voici ce qui sera émis:

Object for Music.find(2) => class: Music - class obj_id: 18452280 - #<Music id: 2, name: "5th Symphony", file: "5s.mp3", type: "Music", created_at: "2009-05-06 16:31:41", updated_at: "2009-05-06 16:31:41"> 
Object for MUSIC_CACHE[2] => class: Music - class obj_id: 13067420 - #<Music id: 2, name: "5th Symphony", file: "5s.mp3", type: "Music", created_at: "2009-05-06 16:31:41", updated_at: "2009-05-06 16:31:41"> 
    - Music.is_a?(Media) => false 
*** Error raised: 
You have a nil object when you didn't expect it! 
You might have expected an instance of Array. 
The error occurred while evaluating nil.include? 

Est-ce que quelqu'un sait la raison derrière cette erreur?

+0

Pourriez-vous obtenir l'exception Ruby appropriée avec la trace de la pile au lieu de '' Vous avez un objet nul quand vous ne l'attendiez pas! ''? – pts

Répondre

2

Ma première idée serait que Rails libère (invalide) tous les objets du modèle après avoir servi une requête. Donc class: Music - class obj_id: 13067420 devient inutilisable dans la 2ème requête. Je suggère de jeter un coup d'œil au code source d'ActiveRecord et de découvrir qui invalide les objets du modèle.

également ce tutoriel rails de mise en cache de modèle peut être utile: http://railscasts.com/episodes/115-caching-in-rails-2-1

+0

pts, merci pour la réponse. J'utilise Rails.cache maintenant, ce qui est parfait. Je voulais juste éviter les frais généraux du réseau (ce n'est pas un gros problème) et comme cela n'a pas fonctionné, j'ai été curieux. – kolrie

0

Fondamentalement, vous ne pouvez pas à cela.

Dans l'environnement de développement, à chaque requête, vos classes sont rechargées. Cela signifie qu'ils sont complètement détruits et recréés. L'objet de classe original est parti, un nouveau prend sa place.

Si vous conservez un objet entre les requêtes, dans la deuxième requête, l'objet héritera toujours de la classe d'origine, celle qui a été supprimée. La constante qui pointe vers cette classe pointe vers un nouvel objet de classe, qui peut être identique ou non au précédent, selon que vous avez modifié la définition de classe ou les plugins qui l'affectent, mais ce sera toujours un objet de classe différent dans mémoire, et l'ancien objet ne saura pas qu'il doit hériter de ce nouvel objet de classe.

Je suppose que si vous exécutez votre application dans un environnement de production, cela fonctionnera.

Questions connexes