require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "ObjectSpace.each_object" do it "calls the block once for each living, non-immediate object in the Ruby process" do klass = Class.new new_obj = klass.new yields = 0 count = ObjectSpace.each_object(klass) do |obj| obj.should == new_obj yields += 1 end count.should == 1 yields.should == 1 # this is needed to prevent the new_obj from being GC'd too early new_obj.should_not == nil end it "calls the block once for each class, module in the Ruby process" do klass = Class.new mod = Module.new [klass, mod].each do |k| yields = 0 got_it = false count = ObjectSpace.each_object(k.class) do |obj| got_it = true if obj == k yields += 1 end got_it.should == true count.should == yields end end it "returns an enumerator if not given a block" do klass = Class.new new_obj = klass.new counter = ObjectSpace.each_object(klass) counter.should be_an_instance_of(Enumerator) counter.each{}.should == 1 # this is needed to prevent the new_obj from being GC'd too early new_obj.should_not == nil end it "finds an object stored in a global variable" do $object_space_global_variable = ObjectSpaceFixtures::ObjectToBeFound.new(:global) ObjectSpaceFixtures.to_be_found_symbols.should include(:global) end it "finds an object stored in a top-level constant" do ObjectSpaceFixtures.to_be_found_symbols.should include(:top_level_constant) end it "finds an object stored in a second-level constant" do ObjectSpaceFixtures.to_be_found_symbols.should include(:second_level_constant) end it "finds an object stored in a local variable" do local = ObjectSpaceFixtures::ObjectToBeFound.new(:local) ObjectSpaceFixtures.to_be_found_symbols.should include(:local) end it "finds an object stored in a local variable captured in a block explicitly" do proc = Proc.new { local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_explicit) Proc.new { local_in_block } }.call ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_explicit) end it "finds an object stored in a local variable captured in a block implicitly" do proc = Proc.new { local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_implicit) Proc.new { } }.call ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_implicit) end it "finds an object stored in a local variable captured in by a method defined with a block" do ObjectSpaceFixtures.to_be_found_symbols.should include(:captured_by_define_method) end it "finds an object stored in a local variable captured in a Proc#binding" do binding = Proc.new { local_in_proc_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_proc_binding) Proc.new { }.binding }.call ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_proc_binding) end it "finds an object stored in a local variable captured in a Kernel#binding" do b = Proc.new { local_in_kernel_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_kernel_binding) binding }.call ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_kernel_binding) end it "finds an object stored in a local variable set in a binding manually" do b = binding b.eval("local = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_manual_binding)") ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_manual_binding) end it "finds an object stored in an array" do array = [ObjectSpaceFixtures::ObjectToBeFound.new(:array)] ObjectSpaceFixtures.to_be_found_symbols.should include(:array) end it "finds an object stored in a hash key" do hash = {ObjectSpaceFixtures::ObjectToBeFound.new(:hash_key) => :value} ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_key) end it "finds an object stored in a hash value" do hash = {a: ObjectSpaceFixtures::ObjectToBeFound.new(:hash_value)} ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_value) end it "finds an object stored in an instance variable" do local = ObjectSpaceFixtures::ObjectWithInstanceVariable.new ObjectSpaceFixtures.to_be_found_symbols.should include(:instance_variable) end it "finds an object stored in a thread local" do thread = Thread.new {} thread.thread_variable_set(:object_space_thread_local, ObjectSpaceFixtures::ObjectToBeFound.new(:thread_local)) ObjectSpaceFixtures.to_be_found_symbols.should include(:thread_local) thread.join end it "finds an object stored in a fiber local" do Thread.current[:object_space_fiber_local] = ObjectSpaceFixtures::ObjectToBeFound.new(:fiber_local) ObjectSpaceFixtures.to_be_found_symbols.should include(:fiber_local) end it "finds an object captured in an at_exit handler" do Proc.new { local = ObjectSpaceFixtures::ObjectToBeFound.new(:at_exit) at_exit do local end }.call ObjectSpaceFixtures.to_be_found_symbols.should include(:at_exit) end it "finds an object captured in finalizer" do alive = Object.new Proc.new { local = ObjectSpaceFixtures::ObjectToBeFound.new(:finalizer) ObjectSpace.define_finalizer(alive, Proc.new { local }) }.call ObjectSpaceFixtures.to_be_found_symbols.should include(:finalizer) alive.should_not be_nil end describe "on singleton classes" do before :each do @klass = Class.new instance = @klass.new @sclass = instance.singleton_class @meta = @klass.singleton_class end it "does not walk hidden metaclasses" do klass = Class.new.singleton_class ancestors = ObjectSpace.each_object(Class).select { |c| klass.is_a? c } hidden = ancestors.find { |h| h.inspect.include? klass.inspect } hidden.should == nil end it "walks singleton classes" do @sclass.should be_kind_of(@meta) ObjectSpace.each_object(@meta).to_a.should include(@sclass) end end it "walks a class and its normal descendants when passed the class's singleton class" do a = Class.new b = Class.new(a) c = Class.new(a) d = Class.new(b) c_instance = c.new c_sclass = c_instance.singleton_class expected = [ a, b, c, d ] expected << c_sclass c_sclass.should be_kind_of(a.singleton_class) b.extend Enumerable # included modules should not be walked classes = ObjectSpace.each_object(a.singleton_class).to_a classes.sort_by(&:object_id).should == expected.sort_by(&:object_id) end end