2012-06-18 31 views
2

我想要类似下面的东西,但希望它可以重用于不同的类。如何创建一个存储实例的类?

我该如何重构这段代码,只需要很少的努力就可以将它包含在一个类中,并且每当有新的被调用时,这个类将自动收集实例?

我已经尝试了各种各样的重写新的或初始化的东西,但不能得到发生的魔法。

class Person  
     @@people_instances = [] 

     def initialize 
     @@people_instances << self 
     end 

     def self.instances 
     @@people_instances 
     end 

    end 


People.new 
People.new 
Poople.instances 

=> [#<Person:0x000001071a7e28>, #<Person:0x000001071a3828>] 

经过一些反馈后,我不认为答案是把实例放在一个类变量中,因为它将永远留在内存中。 Rails缓存也不太适合,因为我不需要实例持久化。

以下代码使用类实例变量而不是类变量。

http://www.dzone.com/snippets/class-variables-vs-class

class Employee 
    class << self; attr_accessor :instances; end 
    def store 
    self.class.instances ||= [] 
    self.class.instances << self 
    end 
    def initialize name 
    @name = name 
    end 
end 
class Overhead < Employee; end 
class Programmer < Employee; end 
Overhead.new('Martin').store 
Overhead.new('Roy').store 
Programmer.new('Erik').store 
puts Overhead.instances.size # => 2 
puts Programmer.instances.size # => 1 

将这些实例变量是唯一每个rails请求,否则将继续存在?

回答

4

修订ANSWER

我如果您希望在请求期间保持它可用,没有以前的答案可以做到这一点。只有在请求响应周期保持它提供的解决方案是使用一个线程局部是在控制器的方法分配,例如:

class YourController < ApplicationController 

    around_filter :cache_objects 

    protected 

    def cache_objects 
    Thread.current[:my_objects] = ['my-object', 'my-other-object'] 
    yield 
    ensure 
     Thread.current[:my_objects] 
    end 

end 

然后,在需要它的代码,你只是做Thread.current[:my_objects]并做任何你想做的事情。您需要使用around_filter,因为您的Web框架或服务器结构可能会尝试重用线程,唯一真正的解决方案是在请求完成后清除它们以避免内存泄漏。


OLD ANSWER

不知道你想要做什么,但你可以很容易地使用ObjectSpace挑一类的每一个实例:

ObjectSpace.each_object(String) { |s| puts(s) } 

如果你需要什么是作为数据库缓存只使用Rails缓存,一次加载这些对象,然后将它们保存在缓存中。当使用Rails的缓存中的所有你需要做的就是把你的对象缓存:

Rails.cache.write("my_cached_objects", [ 'first-object', 'second-object' ]) 

,然后让他们在其他地方:

Rails.cache.fetch("my_cached_objects") do 
    # generate your objects here if there was a cache miss 
    [ 'first-object', 'second-object' ] 
end 

正如你所看到的,你甚至不用要调用cache.write,您只需使用fetch,并且每当有缓存未命中时,将调用给定的块并创建对象。

您可以阅读更多关于轨道缓存here的信息,您可以看到ActiveSupport::Cache::Store here的所有支持方法。

另一种方法,而无需使用ObjectSpace但仍然有一个丑陋的解决方案,现在用alias_method

module Counter 

    def self.included(base) 
    base.extend(ClassMethods) 
    base.class_eval do 
     alias_method :initialize_without_counter, :initialize 
     alias_method :initialize, :initialize_with_counter 
    end 
    end 

    def count_class_variable_name 
    :"@@#{self.class.name.downcase}_instances" 
    end 

    def initialize_with_counter(*args) 
    unless self.class.class_variable_defined?(count_class_variable_name) 
     self.class.class_variable_set(count_class_variable_name, []) 
    end 

    self.class.class_variable_get(count_class_variable_name) << self 
    initialize_without_counter(*args) 
    end 

    module ClassMethods 

    def all_instances 
     class_variable_get(:"@@#{name.downcase}_instances") 
    end 

    end 

end 


class Person 

    def initialize 
    puts 'new person' 
    end 

    include Counter 

end 

p1 = Person.new 
p2 = Person.new 
p3 = Person.new 

puts Person.all_instances.size 
+0

我从那开始,但我读到,你可能会得到尚未GC'd的旧对象。任何人都可以确认吗? – Tim

+0

是的,那是可能的。你想做什么?你为什么想要访问这些实例?如果你解释**你想要做什么,然后可能会有更好的**如何**,那就简单多了。 –

+0

这是否意味着如果我使用带有Passenger的Rails,新连接将共享实例或者是等同于启动另一个进程的新连接? – Tim

2

的lib/keeper.rb

def initialize 
    instance_eval "@@#{self.class.to_s.downcase}_instances ||= []" 
    instance_eval "@@#{self.class.to_s.downcase}_instances << self" 
end 

def self.instances 
    return class_eval "@@#{self.to_s.downcase}_instances" 
end 

person.rb

class Person 
    eval File.open('./lib/keeper.rb','rb').read 
end 

然后这个工程:

Person.new 
Person.new 
Person.instances 
+0

看起来你失踪的代码。 – Tim

+0

@Tim对不起,在进入响应时被拉开了。 – Anil

+0

@DaveNewton答案现在就可以开始检查了!我相信你可以帮助改进它。谢谢。 – Anil

相关问题