1

So, I'm kind of wanting to do something similar to rspec / mocha's mock, but only for two objects, and not all of them. This is what I have so far:

def mock(obj, method_to_mock, value)
    obj.class << obj do
        define_method(method_to_mock) do
            return value
        end
    end
end

I got the idea to write it like that from this post: https://stackoverflow.com/a/185969/356849

So then I can do things like:

mock(self.instantiated, :sections, sections)

and it would override the object I have stored in self.instantiated's sections with my array of Section objects, sections.

The reason why I'm doing this, is because I'm storing a serialized and encrypted version of of an object, and I want to be able to unencrypt and unserialize the object, and then restore all the relationships such that I can view that object in my views, as if it were being read from the database. but that's not important, and most of it's done.

So, I want to be able to do this:

mock(<Instance of object>, :<method of object that is going to be overridden, to avoid db access>, <the stuff to return when the overridden method is invoked)

CUrrently, I'm getting an error on the obj.class << obj do line with this:

NoMethodError: undefined method `obj' for #<MyObject::Encrypted:0x7f190eebcd18>

ideas?


UPDATE

changed the second line to class << obj which now infinite loops.

from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_pool.rb:351:in `retrieve_connection_pool'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_pool.rb:351:in `retrieve_connection_pool'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_pool.rb:325:in `retrieve_connection'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_specification.rb:123:in `retrieve_connection'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_specification.rb:115:in `connection'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/base.rb:1305:in `columns'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/base.rb:1318:in `column_names'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/searchlogic-2.4.28/lib/searchlogic/named_scopes/ordering.rb:35:in `ordering_condition_details'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/searchlogic-2.4.28/lib/searchlogic/named_scopes/ordering.rb:26:in `method_missing'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/searchlogic-2.4.28/lib/searchlogic/named_scopes/or_conditions.rb:28:in `method_missing'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/base.rb:2002:in `method_missing_without_paginate'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/will_paginate-2.3.16/lib/will_paginate/finder.rb:170:in `method_missing_without_attr_encrypted'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/bundler/gems/attr_encrypted-a4b25f01d137/lib/attr_encrypted/adapters/active_record.rb:50:in `method_missing'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:119:in `mock'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:79:in `instantiate'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:58:in `each'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:58:in `instantiate'
Community
  • 1
  • 1
NullVoxPopuli
  • 61,906
  • 73
  • 206
  • 352
  • 1
    `class << self` is a special expression to enter the singleton class of self. Try `class << obj`. `class`is a keyword, not a method. – BernardK Jan 16 '13 at 19:07
  • now that I've done that, it infinite loops. o.o My guess is that now it defined the new method for every instance of `obj` rather than just the one. :-\ – NullVoxPopuli Jan 16 '13 at 19:11

2 Answers2

2
def mock(obj, method_to_mock, value=nil)
  obj.define_singleton_method(method_to_mock) do value end
end  
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
1

obj.class << obj do makes no sense.

What you probably wanted to say is

def mock(obj, method_to_mock, value)
  (class << obj; self; end).class_eval do
    define_method(method_to_mock) do
      return value
    end
  end
end

The (class << obj; self; end).class_eval syntax is opening the singleton class of obj returning that singleton class, then invoking class_eval on that singleton class passing the block.

In your syntax, obj.class sends the :class message to obj as a receiver which returns a reference to obj's class (not its singleton class), on which you then try to invoke the << method passing the result of evaluating obj do...end as an arg. Since obj is not a method of self (MyObject::Encrypted:0x7f190eebcd1) you get the NoMethodError.

In modern ruby, instead of saying the relatively arcane, (class << obj; self; end) to get the singleton class, you can use the singleton_class method like so: obj.singleton_class.class_eval do ... end

dbenhur
  • 20,008
  • 4
  • 48
  • 45
  • thanks for the explanation! Does every object have a singleton class? I remember singletons from my java days just being a class that can only be instantiated once. – NullVoxPopuli Jan 16 '13 at 20:06
  • 1
    In ruby, every object has a singleton class, sometimes also known as metaclass or eigenclass. A ruby singleton class is essentially an anonymous class inserted in the ancestor chain between the object and its base class. The things we call "class methods" are actually singleton methods of a class (remember classes are just objects in ruby). [Fuller explanation here](http://www.devalot.com/articles/2008/09/ruby-singleton) This is not the same thing as a [Singleton](http://en.wikipedia.org/wiki/Singleton_pattern) which is an object which can have only one instance. – dbenhur Jan 16 '13 at 21:21