Last active
November 13, 2016 10:03
-
-
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
This file contains 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
# 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 |
This file contains 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
# 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