Skip to content

Instantly share code, notes, and snippets.

@chanakasan
Last active November 13, 2015 13:20
Show Gist options
  • Save chanakasan/c2c6a491218a0dbf96be to your computer and use it in GitHub Desktop.
Save chanakasan/c2c6a491218a0dbf96be to your computer and use it in GitHub Desktop.
Ruby Virtual Proxy Pattern
class VirtualProxy < BasicObject
def initialize(&loader)
@loader = loader
@object = nil
end
def method_missing(name, *args, &block)
__load__
@object.public_send(name, *args, &block)
end
def inspect
"VirtualProxy(#{@object ? @object.inspect : ''})"
end
def __object__
@object
end
def __load__
@object ||= @loader.call
end
end
require 'spec_helper'
require_relative '../../lib/virtual_proxy'
RSpec.describe VirtualProxy, type: :unit do
before :context do
Movie = Struct.new(:title, :year, :director)
Director = Struct.new(:name)
end
let(:movie1) { Movie.new('This Is Spinal Tap', 1984) }
it "is a virtual proxy class" do
vp = VirtualProxy.new { movie1 }
expect(vp.inspect).to eq("VirtualProxy()")
expect(vp.__object__).to eq(nil)
vp.__load__
expect(vp.inspect).to eq("VirtualProxy(#{movie1.inspect})")
expect(vp.__object__).to eq(movie1)
end
it "calls the loading block only the first time" do
vp = VirtualProxy.new {
puts 'Loading!'
movie1
}
expect{ vp.title }.to output("Loading!\n").to_stdout
expect{ vp.title }.to_not output("Loading!\n").to_stdout
end
it "forwards the method calls to the target object" do
vp = VirtualProxy.new { movie1 }
expect(vp.title).to eq('This Is Spinal Tap')
expect(vp.year).to eq(1984)
end
context "When used in place of an association" do
it "keeps forwarding messages and stays as a proxy" do
movie_1 = create_movie_example_1
expect(movie_1.director.class).to eq(Director)
expect(Director === movie_1.director).to be false
end
it "forwards the first message and replaces itself with the real object" do
movie_2 = create_movie_example_2
expect(movie_2.director.class).to eq(Director)
expect(Director === movie_2.director).to be true
end
end
private
def create_movie_example_1
vp = VirtualProxy.new { Director.new('Rob Reiner') }
Movie.new('This Is Spinal Tap', 1984, vp)
end
def create_movie_example_2
movie = Movie.new('This Is Spinal Tap', 1984)
movie.director = VirtualProxy.new {
movie.director = Director.new('Rob Reiner')
}
movie
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment