Reputation: 1975
I have a class Ryte::Theme. If I call included_modules on the class I get back (abbreviated):
[Ryte::Bundleable::Core, Ryte::Bundleable::Validators, Ryte::Bundleable::Builder, ActiveModel::Validations::HelperMethods, ActiveModel::Validations, ActiveSupport::Callbacks, Ryte::Bundleable]
Ryte::Theme is pulling in nested modules through a single module Ryte::Bundleable. Here are the relevant class and module definitions:
class Ryte::Theme
include Ryte::Bundleable
end
module Ryte::Bundleable
extend ActiveSupport::Concern
included do
include ActiveModel::Validations
include Ryte::Bundleable::Builder
include Ryte::Bundleable::Validators
include Ryte::Bundleable::Core
end
end
Given this, why is it I receive the following response:
Ryte::Theme.include?(Ryte::Theme::Validators)
=> true
I have not included this additional module (yet).. This is evident in the included_modules response. Is this related to ActiveSupport Concern? I want to be able to include Ryte::Theme::Validators and have it mixin as well but since it thinks it is already included it does not include it 'again' (as it shouldn't if that were true). As such it gets left behind when I add the include to the class definition, like so:
class Ryte::Theme
include Ryte::Bundleable
include Ryte::Theme::Validators # <- Does not load
end
Why is this additional module Ryte::Theme::Validators not mixing in as well?
Ok just realized:
1.9.3p194 :005 > Ryte::Bundleable::Validators == Ryte::Theme::Validators
=> true
Odd.. why is this?
Upvotes: 4
Views: 140
Reputation: 27374
UPDATE: This is related to ActiveSupport::Concern (see below).
Try the following:
# initialize constants
class A; end # class Ryte
module A::B; end # module Ryte::Bundleable
module A::B::C; end # module Ryte::Bundleable::Validators
module A::B # module Ryte::Bundleable
def self.include(base)
base.class_eval do
include A::B::C # include Ryte::Bundleable::Validators
end
end
end
class D # class Ryte::Theme
include A::B # include Ryte::Bundleable
end
A::B::C == D::C #=> true
This happens because of the way the modules and classes are namespaced: when you include A::B::C
from inside of A::B
, the module name is referenced relative to the module itself, which becomes just C
(in your case, just Validators
). Thus when you include A::B
in some other class D
, rather than including a module named A::B::C
(i.e. Ryte::Bundleable::Validators
), ruby includes a module named just C
(i.e. Validators
). This is why Ryte::Bundleable::Validators == Ryte::Theme::Validators
evaluates to true.
However, with the example above:
D.include?(A::B::C) #=> false
So this is where ActiveSupport::Concern
kicks in. Redefine the module A::B
above as follows:
module A::B # module Ryte::Bundleable
extend ActiveSupport::Concern
included do
include A::B::C # include Ryte::Bundleable::Validators
end
end
And you will find that D.include?(A::B::C)
now evaluates to true. I'm honestly not sure why this happens, but it must have something to do with the namespacing above.
Upvotes: 6