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:
# 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: