Skip to content

Instantly share code, notes, and snippets.

@br3nt
Last active August 29, 2015 14:26
Show Gist options
  • Save br3nt/6c54a1ba1b6aa8f4a8c9 to your computer and use it in GitHub Desktop.
Save br3nt/6c54a1ba1b6aa8f4a8c9 to your computer and use it in GitHub Desktop.
Companion Classes
# This is the initial idea to test that it works
module CompanionClasses
extend ActiveSupport::Concern
module ClassMethods
def companion_class_definitions
@companion_class_definitions ||= Hash.new {|h, k| h[k] = {:definition => nil, :class => nil} }
end
def companion(name, &block)
companion_class_definitions[name][:definition] = block
create_companion_class(name, &block)
end
def create_companion_class(name, &block)
# only create the companion class once
companion_class_definitions[name][:class] ||=
# create the companion class as a subclass of the current class but only if self is a class (not a module)
if self.is_a?(Class)
const_set(name, Class.new(self, &block))
end
end
def inherited(sub)
# Don't create companion classes on anonymous classes (stop infinite recursion)
unless sub.name.blank?
inherit_companion_classes(sub)
end
end
def inherit_companion_classes(sub)
companion_class_definitions.each do |name, data|
sub.companion(name, &data[:definition])
end
end
end # ClassMethods
end
#############################################
# Lets check that this works as expected
#############################################
class A
include CompanionClasses
companion 'Enumerator' do
include ::Enumerable
def each(&block); end
end
def test1; 'this should work!'; end
end
A::Enumerator.parent == A # true
A::Enumerator.new.test1 # 'this should work!'
class B < A
def test2; 'this should also work'; end
end
B::Enumerator.parent == B # true
B::Enumerator.new.test1 # 'this should work!'
B::Enumerator.new.test2 # 'this should also work'
class C < B
def test3; 'this should triple work'; end
end
C::Enumerator.parent == C # true
C::Enumerator.new.test1 # 'this should work!'
C::Enumerator.new.test2 # 'this should also work'
C::Enumerator.new.test3 # 'this should triple work'

TODO:

  • Allow existing classes to be companion classes
  • Allow companion classes to be overwritten by subclasses
  • Create module that allows classes to create multiple companion classes:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment