Skip to content

Instantly share code, notes, and snippets.

@ccooke
Created February 29, 2012 17:20
Show Gist options
  • Save ccooke/1942632 to your computer and use it in GitHub Desktop.
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
# 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