Excellent; your code sample is very clarifying. What you have there is a garden-variety circular dependency, obscured by the peculiarities of Ruby's scope-resolution operator.
When you run the Ruby code require 'foo'
, ruby finds foo.rb
and executes it, and then finds foo/bar.rb
and executes that. So when Ruby encounters your Foo
class and executes include Foo::Bar
, it looks for a constant named Bar
in the class Foo
, because that's what Foo::Bar
denotes. When it fails to find one, it searches other enclosing scopes for constants named Bar
, and eventually finds it at the top level. But that Bar
is a class, and so can't be include
d.
Even if you could persuade require
to run foo/bar.rb
before foo.rb
, it wouldn't help; module Foo::Bar
means "find the constant Foo
, and if it's a class or a module, start defining a module within it called Bar
". Foo
won't have been created yet, so the require will still fail.
Renaming Foo::Bar
to Foo::UserBar
won't help either, since the name clash isn't ultimately at fault.
So what can you do? At a high level, you have to break the cycle somehow. Simplest is to define Foo
in two parts, like so:
# bar.rb
class Bar
A = 4
end
# foo.rb
class Foo
# Stuff that doesn't depend on Foo::Bar goes here.
end
# foo/bar.rb
module Foo::Bar
A = 5
end
class Foo # Yep, we re-open class Foo inside foo/bar.rb
include Bar # Note that you don't need Foo:: as we automatically search Foo first.
end
Bar::A # => 4
Foo::Bar::A # => 5
Hope this helps.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…