2016-04-11 20 views
1

我試圖在Rails加載時創建一個類的多個實例,並保持這些實例可用。 (我從YAML文件加載數據,但是我已經抽象出這個問題的詳細信息。)我有一個非ActiveRecord模型,其中有一個instantiate類加載數據的方法。當我從config.after_initialize和/或從自定義初始值設定項中調用instantiate時,將創建實例,但到rails console完成加載時,它們消失。我可以在哪裏實例化數據,以便它可以在rails控制檯(和服務器)中使用?在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方法創建的實例,可以是這樣的:

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 

如果你想Test類記中實例化的所有對象。您可以更改這些行:

... 
    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方法添加記憶。特別是,我發現在rails控制檯中運行reload!時,Criteria類的內容將被刪除。我的解決方案是在all中添加一個測試,並且實例化memoized實例變量是否爲空或零。這也意味着初始化器不再是必需的,因爲Class現在在初次使用時實例化。請注意,each調用all,並且通過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!'刪除所有自動加載的類。對於這個問題,另一個解決方案是Hm,如果不需要這個類的重載功能,就把它添加到'config/application.rb'中的'config.autoload_once_paths'中。這樣它就不能重新加載。在這裏看到更多:http://guides.rubyonrails.org/configuring.html –

相關問題