Skip to content

Instantly share code, notes, and snippets.

@dustin
Created July 30, 2008 23:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dustin/3355 to your computer and use it in GitHub Desktop.
Save dustin/3355 to your computer and use it in GitHub Desktop.
Object concatenation class. Combine two objects to get the union of their properties and behaviors.
require 'core_ext'
module Metahackery
# Object concatentation, singleton multiple inheritence, singleton mixin, etc...
# This object allows you to smash two objects together into one object that
# appears to be any of the sub-objects depending on how you look at it.
class Cat
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
def initialize(*objects)
raise "Must specify at least one object." if objects.empty?
@objects = objects
end
def method_missing(name, *args)
ob = @objects.detect{|o| o.respond_to? name}
super if ob.nil?
ob.send name, *args
end
def is_a?(thing)
@objects.any?{|o| o.is_a? thing}
end
def respond_to?(m)
m == :proxied_objects || @objects.any?{|o| o.respond_to? m}
end
def ===(o)
puts "Comparing #{self} === #{o}"
@objects.any?{o| self === o}
end
def proxied_objects
@objects
end
end
end
#!/usr/bin/env ruby
require 'test/unit'
require 'cat'
module CatTestClasses
class A
attr :a
def initialize(a)
@a=a
end
end
class B
attr :b
def initialize(b)
@b=b
end
end
end
class CatTest < Test::Unit::TestCase
include Metahackery
include CatTestClasses
def test_empty_construct
assert_raise(RuntimeError) { Cat.new }
end
def test_one_a
c = Cat.new(A.new('aaay'))
assert A === c
assert c.is_a?(A)
assert c.respond_to?(:a)
assert_equal 'aaay', c.a
assert !(B === c)
assert !c.is_a?(B)
assert !c.respond_to?(:b)
assert_raise(NoMethodError) { c.b }
end
def test_one_b
c = Cat.new(B.new('bee'))
assert B === c
assert c.is_a?(B)
assert c.respond_to?(:b)
assert_equal 'bee', c.b
assert !(A === c)
assert !c.is_a?(A)
assert !c.respond_to?(:a)
assert_raise(NoMethodError) { c.a }
end
def test_one_a_and_b
c = Cat.new(A.new('aaay'), B.new('bee'))
assert A === c
assert c.is_a?(A)
assert c.respond_to?(:a)
assert_equal 'aaay', c.a
assert B === c
assert c.is_a?(B)
assert c.respond_to?(:b)
assert_equal 'bee', c.b
end
end
# Necessary for class to consider a proxy equal to a member.
class Class
alias_method :triple_equal_without_cat, :===
def ===(o)
if o.respond_to? :proxied_objects
o.proxied_objects.any?{|x| self === x}
else
triple_equal_without_cat(o)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment