2009-05-06 11 views
1

Ich verwende Single-Table Inheritance (STI) auf einem meiner Modelle für eine Rails App und ich habe Probleme beim Speichern von Modellobjekten auf einer Konstante. Ich habe das Problem in ein Beispielprojekt isoliert und es GitHub: http://github.com/fcoury/rails-sti-cachingProblem Caching Model Instanzen auf einer Konstante in Rails

Was ich versuche, ist das Laden einer Modellinstanz (in diesem Fall ein Musikmodell, das vom Medienmodell über STI erbt) auf einem initializer (in Rails' /config/initializers/ Verzeichnis) und halten sie sie auf einem konstanten:

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

und ich habe eine Probe-Controller, die die folgenden:

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 

und Blick mit ihm zu gehen:

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

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

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

Das erste Mal, dieser Code ausgeführt wird, wird der Ausgang auf dem <pre>-Tag, ist dies:

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 

Allerdings, wenn ich die Seite nur neu zu laden, hier ist das, was ausgegeben wird:

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? 

Kennt jemand die Gründe für diesen Fehler?

+0

Könnten Sie irgendwie die richtige Ruby-Ausnahme mit dem Stack-Trace anstelle von '' Sie haben ein Null-Objekt, wenn Sie es nicht erwartet haben! ''? – pts

Antwort

2

Meine erste Idee wäre, dass Rails alle Modellobjekte freigibt (invalidiert), nachdem eine Anforderung gesendet wurde. So wird class: Music - class obj_id: 13067420 in der 2. Anfrage unbrauchbar. Ich würde vorschlagen, den ActiveRecord-Quellcode anzusehen und herauszufinden, wer Modellobjekte ungültig macht.

Auch Schienen dieses Modell Caching Tutorial nützlich sein kann: http://railscasts.com/episodes/115-caching-in-rails-2-1

+0

pts, danke für die Antwort. Ich verwende jetzt Rails.cache, was perfekt ist. Ich wollte nur den Netzwerk-Overhead vermeiden (es ist nicht so ein großer Deal) und da es nicht funktioniert habe, bin ich neugierig geworden. – kolrie

0

Grundsätzlich kann man nicht darauf.

In der Entwicklungsumgebung werden Ihre Klassen bei jeder Anforderung neu geladen. Das heißt, sie sind komplett zerstört und neu erstellt. Das ursprüngliche Klassenobjekt ist weg, ein neues tritt an seine Stelle.

Wenn Sie ein Objekt über Anforderungen hinweg verwalten, übernimmt das Objekt in der zweiten Anforderung immer noch die ursprüngliche Klasse, die entfernt wurde. Die Konstante, die auf diese Klasse zeigt, zeigt nun auf ein neues Klassenobjekt, das mit dem vorherigen identisch sein kann oder nicht, je nachdem, ob Sie die Klassendefinition oder Plugins, die es betreffen, geändert haben, aber es ist immer noch ein anderes Klassenobjekt Speicher, und das alte Objekt wird nicht wissen, dass es von diesem neuen Klassenobjekt erben soll.

Ich nehme an, wenn Sie Ihre Anwendung in der Produktionsumgebung ausführen, wird es funktionieren.