Mocking out a class object

Okay, I'm trying to mock up a class object. It needs to do two
things:

1. Respond to #new with a object *I* provide, rather than a new
   instance of the class; and
2. Contain a constant, such that code containing 'MyMockClass::FOO'
   will work.

I can think of a naive implementation:

    class MyMockClass
        FOO="some value"
        def self.set_instance(inst)
            @instance=inst
        end
        def self.new(*)
            return @instance
        end
    end

I'm wondering, though, is there a way to do this inline with
metaprogramming, and avoid defining the set_instance method? I've
tried something like this:

    my_instance=...

    Class.new :MyMockClass {
        const_set :FOO, "some value"
        define_method(:new) do |*|
            return my_instance
        end
    }

But that doesn't work, because the define_method is creating an
instance method, not a class method. I've also tried to use a regular
old object, instead of a class object, but then the constant (FOO)
doesn't work. Any ideas?

···

--
ABG

Hi --

Okay, I'm trying to mock up a class object. It needs to do two
things:

1. Respond to #new with a object *I* provide, rather than a new
  instance of the class; and
2. Contain a constant, such that code containing 'MyMockClass::FOO'
  will work.

[...]

I'm wondering, though, is there a way to do this inline with
metaprogramming, and avoid defining the set_instance method? I've
tried something like this:

   my_instance=...

   Class.new :MyMockClass {
       const_set :FOO, "some value"
       define_method(:new) do |*|
           return my_instance
       end
   }

But that doesn't work, because the define_method is creating an
instance method, not a class method. I've also tried to use a regular
old object, instead of a class object, but then the constant (FOO)
doesn't work. Any ideas?

You're actually dealing with two classes: MyMockClass, and
MyMockClass's singleton class. So you would probably want to do
something like this:

     # This is to make it look nicer (as per the
     # pending RCR :slight_smile:
     module Kernel
       def singleton_class
         class << self; self; end
       end
     end

     my_instance = ""

     MyMockClass = Class.new {
       self::FOO = "some value"
       singleton_class.class_eval {
         define_method(:new) {|*| my_instance}
       }
     }

     p MyMockClass::FOO # "some value"
     p MyMockClass.new # ""

David

···

On Thu, 14 Apr 2005, Avdi Grimm wrote:

--
David A. Black
dblack@wobblini.net

"Avdi Grimm" <Avdi_B_Grimm@raytheon.com> schrieb im Newsbeitrag
news:ur7hep6w5.fsf@raytheon.com...

Okay, I'm trying to mock up a class object. It needs to do two
things:

1. Respond to #new with a object *I* provide, rather than a new
   instance of the class; and
2. Contain a constant, such that code containing 'MyMockClass::FOO'
   will work.

I can think of a naive implementation:

    class MyMockClass
        FOO="some value"
        def self.set_instance(inst)
            @instance=inst
        end
        def self.new(*)
            return @instance
        end
    end

I'm wondering, though, is there a way to do this inline with
metaprogramming, and avoid defining the set_instance method? I've
tried something like this:

    my_instance=...

    Class.new :MyMockClass {
        const_set :FOO, "some value"
        define_method(:new) do |*|
            return my_instance
        end
    }

But that doesn't work, because the define_method is creating an
instance method, not a class method. I've also tried to use a regular
old object, instead of a class object, but then the constant (FOO)
doesn't work. Any ideas?

You don't really need to make it so complicated IMHO:

class MockClass
  def initialize(obj) @obj = obj end
  def new() @obj end
end

MockClass::Foo = MockClass.new "foo"

=> #<MockClass:0x10189e38 @obj="foo">

MockClass::Foo.new

=> "foo"

MockClass::Foo.new.object_id

=> 135024424

MockClass::Foo.new.object_id

=> 135024424

MockClass::Foo.new.object_id

=> 135024424

MockClass::Foo.new.object_id

=> 135024424

Alternative

class MockClass
  def initialize(sym, obj)
    @obj = obj
    self.class.const_set sym, self
  end
  def new() @obj end
end

MockClass.new :Foo, "foo"

=> #<MockClass:0x101863e8 @obj="foo">

MockClass::Foo.new

=> "foo"

MockClass::Foo.new.object_id

=> 135016960

MockClass::Foo.new.object_id

=> 135016960

MockClass::Foo.new.object_id

=> 135016960

MockClass::Foo.new.object_id

=> 135016960

Kind regards

    robert

"David A. Black" <dblack@wobblini.net> writes:

You're actually dealing with two classes: MyMockClass, and
MyMockClass's singleton class. So you would probably want to do
something like this:

     # This is to make it look nicer (as per the
     # pending RCR :slight_smile:
     module Kernel
       def singleton_class
         class << self; self; end
       end
     end

     my_instance = ""

     MyMockClass = Class.new {
       self::FOO = "some value"
       singleton_class.class_eval {
         define_method(:new) {|*| my_instance}
       }
     }

     p MyMockClass::FOO # "some value"
     p MyMockClass.new # ""

Thanks, that looks exactly like what I was looking for. It reveals
that I don't understand Ruby metaprogramming quite as well as I
thought, though. Would you mind explaining what singleton_instance is
doing, and how it differs from just using 'self' in the class
definition?

Thanks,

Avdi