I have a situation for Ruby, where an object is possibly necessary to be created, but it is not sure. And as the creation of the object might be costly I am not too eager creating it. I think this is a clear case for lazy loading. How can I define an object which is not created only when someone sends a message to it? The object would be created in a block. Is there a way for simple lazy loading/initialisation in Ruby? Are these things supported by some gems, which provide different solutions for various cases of lazy initialisation of objects? Thanks for your suggestions!
3 Answers
There are two ways.
The first is to let the caller handle lazy object creation. This is the simplest solution, and it is a very common pattern in Ruby code.
class ExpensiveObject
def initialize
# Expensive stuff here.
end
end
class Caller
def some_method
my_object.do_something
end
def my_object
# Expensive object is created when my_object is called. Subsequent calls
# will return the same object.
@my_object ||= ExpensiveObject.new
end
end
The second option is to let the object initialise itself lazily. We create a delegate object around our actual object to achieve this. This approach is a little more tricky and not recommended unless you have existing calling code that you can't modify, for example.
class ExpensiveObject # Delegate
class RealExpensiveObject # Actual object
def initialize
# Expensive stuff here.
end
# More methods...
end
def initialize(*args)
@init_args = args
end
def method_missing(method, *args)
# Delegate to expensive object. __object method will create the expensive
# object if necessary.
__object__.send(method, *args)
end
def __object__
@object ||= RealExpensiveObject.new(*@init_args)
end
end
# This will only create the wrapper object (cheap).
obj = ExpensiveObject.new
# Only when the first message is sent will the internal object be initialised.
obj.do_something
You could also use the stdlib delegate
to build this on top of.

- 73,644
- 13
- 135
- 118
-
In the first example I need to keep instance of Caller class. Right? But what is the difference for me - to keep Caller class instance or to keep Expensive class instance? – ceth Mar 21 '10 at 10:35
-
1In the first example, the `Caller` class is just an example of how you would *use* the ExpensiveObject class. The difference: introduce laziness where you *use* the `ExpensiveObject` (simple), or introduce laziness in the `ExpensiveObject` *itself* (slightly more complicated). – molf Mar 25 '10 at 17:46
-
1@molf: Whenever you override `method_missing` you *must* also override `respond_to?` (or preferably `respond_to_missing?` in 1.9.2). See http://blog.marc-andre.ca/2010/11/methodmissing-politely.html – Nemo157 Aug 31 '11 at 07:44
If you want to lazily evaluate pieces of code, use a proxy:
class LazyProxy
# blank slate... (use BasicObject in Ruby 1.9)
instance_methods.each do |method|
undef_method(method) unless method =~ /^__/
end
def initialize(&lazy_proxy_block)
@lazy_proxy_block = lazy_proxy_block
end
def method_missing(method, *args, &block)
@lazy_proxy_obj ||= @lazy_proxy_block.call # evaluate the real receiver
@lazy_proxy_obj.send(method, *args, &block) # delegate unknown methods to the real receiver
end
end
You then use it like this:
expensive_object = LazyProxy.new { ExpensiveObject.new }
expensive_object.do_something
You can use this code to do arbitrarily complex initialization of expensive stuff:
expensive_object = LazyProxy.new do
expensive_helper = ExpensiveHelper.new
do_really_expensive_stuff_with(expensive_helper)
ExpensiveObject.new(:using => expensive_helper)
end
expensive_object.do_something
How does it work? You instantiate a LazyProxy object that holds instructions on how to build some expensive object in a Proc. If you then call some method on the proxy object, it first instantiates the expensive object and then delegates the method call to it.

- 10,148
- 1
- 39
- 40
With Ruby 3.x, I use the gem concurrent-ruby
.
A possible lazy initialization use case looks as following:
require 'concurrent'
# put expensive code inside a "future":
very_lazy = Concurrent::Promises.future { some_expensive_code_block }
# the "future" starts performing work in background
# use
puts very_lazy.value # blocks, until the "future" is ready
puts very_lazy.value # repeated calls just re-use existing value
If I'm wrong, someone please correct me.

- 10,748
- 12
- 69
- 103