2

我想要类似以下的东西,但希望它可用于不同的类。

如何重构此代码,以便以最小的努力将其包含在一个类中,并且该类将在调用 new 时自动收集实例?

我已经尝试了各种各样的事情,比如覆盖新的或初始化的,但就是无法让魔法发生。

   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

2 回答 2

4

更新的答案

如果您只想在请求期间保持它可用,那么以前的答案都无法做到仅在请求-响应周期内保持其可用的解决方案是使用在控制器方法中分配的线程本地,例如:

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 框架或服务器结构可能会尝试重用线程,唯一真正的解决方案是在请求完成后清理它们以避免内存泄漏。


旧答案

不确定您要做什么,但是您可以使用以下命令轻松选择类的每个实例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 ,每当缓存未命中时,将调用给定的块并创建您的对象。

您可以在此处阅读有关 rails 缓存的更多信息,您可以在此处查看所有支持的ActiveSupport::Cache::Store方法。

另一种不使用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
于 2012-06-18T03:02:07.760 回答
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

人.rb

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

然后这个工作:

Person.new
Person.new
Person.instances
于 2012-06-18T02:59:01.453 回答