Ok.. The problems is that the constant are (for some reason) not defined inside the Quz class
, but in the upper scope, the module Baz scope
.
If only we could somehow delegate (current word?) the constants calls in Quz to the upper (Baz) scope- We can:
Using Module#const_missing
I'll show it twice: once for your private case, and once for a more general approach.
1) solve for private case:
module Foo
class Bar
def frob(val)
"frobbed #{val}"
end
end
end
module Baz
include Foo
class Qux
def self.const_missing(c)
#const_get("#{self.to_s.split('::')[-2]}::#{c}")
const_get("Baz::#{c}")
end
def initialize(val)
@val = val
@bar = Bar.new
end
def twiddle
@bar.frob(@val)
end
end
end
puts Baz::Qux.new('My Value').twiddle
So what happens here? almost same thing- only that when the error is received- it's kinda rescued and arriving (or fall back to) the const_missing function
to receive a new value- This apply for both Constants and Classes (apparently they are the same type).
But that mean we have to add the self.const_missing(c)
method to every class inside module Baz- Or we can just iterate every classes in module Baz and add it (There are probably better ways to do it, but it works)
2) A more automated approach:
module Foo
class Bar
def frob(val)
"frobbed #{val}"
end
end
end
module Baz
class Qux
def initialize(val)
@val = val
@bar = Bar.new
end
def twiddle
@bar.frob(@val)
end
end
def self.add_const_missing_to_classes
module_name = self.to_s
#note 1
classes_arr = constants.select{|x| const_get(x).instance_of? Class} #since constants get also constants we only take classes
#iterate classes inside module Baz and for each adding the const_missing method
classes_arr.each do |klass| #for example klass is Qux
const_get(klass).define_singleton_method(:const_missing) do |c|
const_get("#{module_name}::#{c}")
end
end
end
add_const_missing_to_classes
#we moved the include Foo to the end so that (see note1) constants doesn't return Foo's classes as well
include Foo
end
puts Baz::Qux.new('My Value').twiddle
Here at the end of module Baz, after all classes were defined. we iterate them (inside the add_const_missing_to_classes
method). to select them we use the Module#constants method which returns an array of a module constants- meaning both CONSTANTS and CLASSES, so we use select method to only work on classes.
Then we iterate the found classes and add the const_missing
class method to the classes.
Notice we moved the include Foo
method to the end- because we wanted the constants
method not to include constants from module Foo.
Surly there are better ways to do it. But I believe the OP's question:
How can I import Foo constants into Baz such that classes under Baz don't have to qualify them?
Is answered