2016-04-11 10 views
1

Railsがロードされている間に複数のインスタンスを作成しようとしています。 (私はYAMLファイルからデータをロードしていますが、この質問の詳細を抽象化しています)。データをロードするinstantiateクラスメソッドを持つ非ActiveRecordモデルがあります。私がinstantiateconfig.after_initializeから、そして/またはカスタムイニシャライザから呼び出すと、インスタンスが作成されますが、rails consoleのロードが完了するまでには、それらはなくなります。データをインスタンス化して、レールコンソール(およびサーバー)内で使用できるようにするにはどうすればよいですか?Railsの起動時にクラスのインスタンスを作成する

# app/models/test.rb 
class Test 
    include ActiveModel::Model 
    attr_accessor :name 

    class << self 
    include Enumerable 

    def each 
     ObjectSpace.each_object(self).each do |object| 
     yield object 
     end 
     self 
    end 

    def find_by_name(input) 
     find { |object| object.name.to_s == input.to_s } 
    end 

    def instantiate 
     new(name: 'Alice') 
     new(name: 'Bob') 
    end 
    end 

    def initialize(*parameters) 
    super(*parameters) 
    freeze 
    end 

    delegate :to_s, to: :name 
end 


# config/application.rb 
module MyApp 
    class Application < Rails::Application 
    config.after_initialize do 
     p "Test instances before after_initialize: #{Test.count}" 
     Test.instantiate 
     p "Test instances after after_initialize: #{Test.count}" 
    end 
    end 
end 


# config/initializer/test_initializer.rb 
p "Test instances before test_initializer: #{Test.count}" 
Test.instantiate 
p "Test instances after test_initializer: #{Test.count}" 


$ rails console 
"Test instances before test_initializer: 2" 
"Test instances after test_initializer: 4" 
"Test instances before after_initialize: 0" 
"Test instances after after_initialize: 2" 
Loading development environment (Rails 4.2.6) 
irb(main):001:0> Test.count 
=> 0 
irb(main):002:0> Test.instantiate 
=> #<Test:0x007fad2204d8b0 @name="Bob"> 
irb(main):003:0> Test.count 
=> 2 

答えて

1

投稿した解決策を参照してください。インスタンシエーションプロセスを制御しているので、ObjectSpaceをまったく使用する必要はありません。あなたがしたい場合Testクラスは、#instantiate方法で作成しただけで2つのインスタンスを覚えて、あなたは可能性がこのような何か:

あなたは Testクラスはインスタンス化されたすべてのオブジェクトを覚えておきたい場合は
class Test 
    @objects = [] 

    include ActiveModel::Model 
    attr_accessor :name 

    class << self 
    include Enumerable 

    def each 
     if block_given? 
     objects.each { |o| yield o } 
     else 
     objects.to_enum 
     end 
    end 

    def find_by_name(input) 
     find { |object| object.name.to_s == input.to_s } 
    end 

    def instantiate 
     objects << new(name: 'alice') 
     objects << new(name: 'Bob') 
    end 

    private 

    attr_accessor :objects 
    end 

    def initialize(*parameters) 
    super(*parameters) 
    freeze 
    end 

    delegate :to_s, to: :name 
end 

。あなたは行を変更することができます:

... 
    def instantiate 
     new(name: 'alice') 
     new(name: 'Bob') 
    end 
    end 

    def initialize(*parameters) 
    super(*parameters) 
    freeze 
    self.class.objects << self 
    end 
... 
+0

ありがとう。あなたの提案をどのように反復したかについては、私の改訂版の回答を参照してください。 – dankohn

3

Rubyガベージコレクタはインスタンスをメモリから削除します。新しく作成されたインスタンスを参照する直接リンクは存在しないので、Rubyはそれらを不要とみなします。

あなたは追加することによって、これを試すことができます。

module MyApp 
    class Application < Rails::Application 
    config.after_initialize do 
     GC.disable # Disable garbage collector 
     p "Test instances before after_initialize: #{Test.count}" 
     Test.instantiate 
     p "Test instances after after_initialize: #{Test.count}" 
    end 
    end 
end 

しかし、ガベージコレクタを無効にするには良いアイデアではありません。インスタンスを何らかの方法で参照すると、それらは掃除されず、起動後に参照によって利用可能になります。

0

[書き換え/更新されたコード]あなたの答えのために非常に多くの

@Laura Paakkinenのおかげ。根本的な問題は、参照されていないインスタンスのガベージコレクションだということは間違いありません。また、クラスがインスタンス化を行っているので、クラスのすべてのメンバーを追跡するためにインスタンス変数を保持することもできます。

以下の方法とは異なり、私はallメソッドでメモを追加することにしました。特に、レールコンソールにreload!を実行すると、Criteriaクラスの内容が消去されることがわかりました。私の解決策は、allにテストを追加し、memoizedインスタンス変数が空であるかnilであるかをインスタンス化することです。これは、クラスが最初の使用時にインスタンス化するように、イニシャライザが不要になったことを意味します。 eachallを呼び出し、Enumerable mixinを介して他のすべてのクラスメソッドはeachを呼び出します。

あなたの思慮深い返信をお待ちしております。

class Test 
    include ActiveModel::Model 
    attr_accessor :name, :value 

    class << self 
    include Enumerable 

    def [](key) 
     instantiate if @test.blank? 
     @test[key.to_sym] 
    end 

    def all 
     instantiate if @test.blank? 
     @test.values 
    end 

    def each 
     all.each do |object| 
     yield object 
     end 
     self 
    end 

    def keys 
     map(&:name) 
    end 

    def instantiate 
     @test = {} 
     @test[:Alice] = new(name: 'Alice', value: 1) 
     @test[:Bob] = new(name: 'Bob', value: 2) 
    end 
    end 

    # Instance Methods 

    def initialize(*parameters) 
    super(*parameters) 
    freeze 
    end 

    delegate :to_s, to: :name 
end 
+0

ええ、Railsは、開発中に 'reload! 'ですべての自動ロードされたクラスを削除します。この問題のもう一つの解決策は、このクラスのリロード機能を必要としない場合は、 'config/application.rb'の' config.autoload_once_paths'にそれを追加することです。そうすれば、リロードから除外されます。もっと見る:http://guides.rubyonrails.org/configuring.html –

関連する問題