Created
August 2, 2011 15:11
-
-
Save bmc/1120383 to your computer and use it in GitHub Desktop.
Wrapping multiple objects in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Foo | |
attr_reader :a, :b | |
def initialize | |
@a = 'a-Foo' | |
@b = 'b-Foo' | |
end | |
end | |
class Bar | |
attr_reader :a, :c | |
def initialize | |
@a = 'a-Bar' | |
@c = 'a-Bar' | |
end | |
end | |
o = OpenStruct.new(:foo => 'ostruct-Foo') | |
w = WrapMany.new(Foo.new, Bar.new, o) | |
puts w.a | |
puts w.b | |
puts w.c | |
puts w.foo | |
w = WrapMany.new(Foo.new, Bar.new, {"foo" => "hash-Foo"}) | |
puts w.a | |
puts w.b | |
puts w.c | |
puts w.foo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'ostruct' | |
# Wraps multiple objects in a single object. Method calls are resolved by | |
# cycling through all wrapped objects, looking for the appropriate named | |
# method. Useful for temporarily adding methods to an instance, without | |
# monkeypatching the class. The class has special-case logic for OpenStruct | |
# and Hash objects. | |
# | |
# Example 1: Add an "age" value to a User object using an OpenStruct | |
# | |
# require 'ostruct' | |
# require 'wrapmany' | |
# | |
# age_holder = OpenStruct.new(:age => 43) | |
# u = User.find(...) | |
# user_with_age = WrapMany.new(u, age_holder) | |
# | |
# Example 2: Add an "age" value to a User object using a hash | |
# | |
# require 'wrapmany' | |
# | |
# u = User.find(...) | |
# user_with_age = WrapMany.new(u, {:age => 43}) | |
# | |
# Example 3: Add an "age" value to a User object using another class | |
# | |
# require 'wrapmany' | |
# | |
# class AgeHolder | |
# def initialize(age) | |
# @age = age | |
# end | |
# end | |
# | |
# u = User.find(...) | |
# user_with_age = WrapMany.new(u, AgeHolder.new(43)) | |
class WrapMany | |
def initialize(*args) | |
# Map any OpenStruct objects in the arguments to hashes, and add the | |
# current object (in case someone subclasses this class) | |
@objects = args.to_a.map { | |
|a| a.is_a?(OpenStruct) ? a.instance_variable_get("@table") : a | |
} + [self] | |
end | |
def method_missing(meth, *args, &block) | |
method_name = meth.to_s | |
# Loop through all objects, looking for something that satisfies the | |
# method name. | |
@objects.each do |o| | |
# First determine if the method exists as a public instance method. | |
# If so, call it. | |
if o.class.public_instance_methods.include? method_name | |
return o.send(method_name, *args, &block) | |
end | |
# Otherwise, if there are no arguments, then check for fields | |
# and hash keys | |
if args.length == 0 | |
if o.instance_variables.include? ":@#{method_name}" | |
return o.instance_variable_get method_name | |
end | |
# Special case for hash | |
if o.is_a? Hash | |
if o.has_key? meth | |
return o[meth] | |
elsif o.has_key? method_name | |
return o[method_name] | |
end | |
end | |
end | |
end | |
raise NoMethodError.new("Undefined method: '#{method_name}'", meth) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment