Created
February 29, 2012 17:20
-
-
Save ccooke/1942632 to your computer and use it in GitHub Desktop.
A module to allow a class to be split into several root-level subclasses
This file contains hidden or 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
# Define the helper functionality we'll use | |
module MultiplexClass | |
# Set up some variables... | |
def self.extended(base) | |
base.class_exec do | |
# The class we will take subclasses from | |
@_multiplex_subclass_parent = self | |
# A hash of the tagged subclasses createed so far | |
@_multiplex_subclasses = {} | |
# Whether this is the root (real) Class or a subclass | |
@_multiplex_is_a_subclass = false | |
# Should we autovivify a subclass if we get an access before one is created? | |
@_multiplex_autovivify_default_class = true | |
@_multiplex_autovivify_default_name = :default | |
@_multiplex_autovivify_default_options = {} | |
end | |
end | |
# A hook for defining anything we need | |
def multiplex_class_initialize(*args) | |
end | |
# Create a named subclass | |
def create(name,options = {}) | |
parent_class = self | |
subclass = Class.new( parent_class ) do | |
# Set up the variables for this subclass | |
@_multiplex_is_a_subclass = true | |
@_multiplex_subclass_name = name.to_sym | |
@_multiplex_parent_class = parent_class | |
@_multiplex_subclass_parent = parent_class.instance_variable_get :@_multiplex_subclass_parent | |
# A debugging aid: Show the subclass name as part of the class name | |
class << self | |
def inspect | |
"#{@_multiplex_parent_class}[#{@_multiplex_subclass_name.inspect}]" | |
end | |
end | |
# Initialize the subclass | |
multiplex_class_initialize(name, options) | |
end | |
@_multiplex_subclasses[name.to_sym] = subclass | |
end | |
def [](name) | |
@_multiplex_subclasses[name] or raise Exception.new("No such multiplex class subclass: #{name}") | |
end | |
def const_missing(const) | |
# So, we're now in Foo[:something] | |
# and the code just called Foo[:something]::Bar | |
# Create Bar as a subclass of @_multiplex_subclass_parent | |
# Are we accessing from a working create()d subclass? | |
if @_multiplex_is_a_subclass | |
parent_class = self | |
# Make a new 'work' subclass and set it into place. | |
new_class = Class.new( @_multiplex_subclass_parent.const_get( const ) ) do | |
@parent = parent_class | |
end | |
const_set( const, new_class ) | |
else | |
if @_multiplex_subclasses.count == 0 | |
if @_multiplex_autovivify_default_class | |
self.create( | |
@_multiplex_autovivify_default_name, | |
@_multiplex_autovivify_default_options | |
).const_get( const ) | |
else | |
raise Exception.new("No multiplex object defined") | |
end | |
elsif @_multiplex_subclasses.count == 1 | |
@_multiplex_subclasses.values.first.const_get(const) | |
else | |
raise Exception.new("Direct access to classes is ambiguous when more than one server profile has been loaded. Use Foo[:name]::Class") | |
end | |
end | |
end | |
end | |
class Foo | |
# Define the actual work class | |
class API | |
class Bar | |
def self.print_var | |
@parent.options[:var] | |
end | |
end | |
end | |
# Set up the multiplexing | |
extend MultiplexClass | |
@_multiplex_subclass_parent = Foo::API | |
@_multiplex_autovivify_default_options = { | |
:var => 'default_options' | |
} | |
def self.multiplex_class_initialize( name, options ) | |
@options = options | |
end | |
def self.options | |
@options | |
end | |
end | |
Foo::Bar.print_var | |
Foo.create(:test1, :var => 'specified' ) | |
Foo::Bar.print_var | |
Foo[:test1]::Bar.print_var | |
Foo[:default]::Bar.print_var |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment