Skip to content

Instantly share code, notes, and snippets.

@rob-murray
Last active November 13, 2016 10:03
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 rob-murray/7f1f037ebf755e4b5f98fb2c70902a5d to your computer and use it in GitHub Desktop.
Save rob-murray/7f1f037ebf755e4b5f98fb2c70902a5d to your computer and use it in GitHub Desktop.
Dynamically instantiate classes by method call, recursive until method call is a class to work with Modules
# Dynamically instantiate classes by method call, recursive until method call is a class to work with Modules
# Given classes Foo and Bar::Foo exist
#
# app.foo => Foo.new
# app.bar.foo => Bar::Foo.new
#
# Note: requires classes to be pre-required/loaded - ie will not work with Rails
# lazy class loading
#
class App
def initialize(base_mod = Kernel)
@base_mod = base_mod
end
def method_missing(method_name, *args, &block)
if valid_method_name?(method_name)
create_klass_or_new(method_name)
else
super
end
end
def respond_to_missing?(method_name, *)
valid_method_name?(method_name) || super
end
private
def valid_method_name?(method_name)
@base_mod.const_defined? method_name_to_class_or_module(method_name)
end
def create_klass_or_new(method_name)
klass_const = @base_mod.const_get method_name_to_class_or_module(method_name)
if klass_const.instance_of? Module
self.class.new(klass_const)
else
klass_const.new
end
end
def method_name_to_class_or_module(method_name)
method_name.to_s.camelize
end
end
# Dynamically instantiate classes by method call, recursive until method call is a class to work with Modules.
# Memoize the method call result.
# Given classes Foo and Bar::Foo exist
# app.foo => Foo.new
# app.bar.foo => Bar::Foo.new
#
# Note: requires classes to be pre-required/loaded - ie will not work with Rails
# lazy class loading
#
class App
def initialize(base_mod = Kernel)
@base_mod = base_mod
end
def method_missing(method_name, *args, &block)
if valid_method_name?(method_name)
define_accessor_for_class_or_module(method_name)
public_send(method_name)
else
super
end
end
def respond_to_missing?(method_name, *)
valid_method_name?(method_name) || super
end
private
def valid_method_name?(method_name)
@base_mod.const_defined? method_name_to_class_or_module(method_name)
end
def define_accessor_for_class_or_module(method_name)
klass_const = @base_mod.const_get method_name_to_class_or_module(method_name)
if klass_const.instance_of? Module
define_memoized_getter(method_name, self.class.new(klass_const))
else
define_memoized_getter(method_name, klass_const.new)
end
end
def define_memoized_getter(method_name, value_to_assign)
self.class.send(:define_method, method_name) do
unless instance_variable_defined?("@_#{method_name}")
instance_variable_set("@_#{method_name}", value_to_assign)
end
instance_variable_get("@_#{method_name}")
end
end
def method_name_to_class_or_module(method_name)
method_name.to_s.camelize
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment