I have a module (in file dialect.rb) defined as such:
require 'dialect/generators/elements'
module Dialect
def self.included(caller)
caller.extend Dialect::Generator::Element
end
def self.version
"Dialect v#{Dialect::VERSION}"
end
end
Then I have the file dialect/generators/elements.rb, which looks like this:
module Dialect
module Generator
module Element
puts Dialect.version
end
end
end
If I run my app, I get:
/lib/dialect/generators/elements.rb:7:in `<module:Element>':
undefined method `version' for Dialect:Module (NoMethodError)
My question/problem is: I didn't understand why the Element module could not find the version method here.
How I call Dialect is like this:
require 'dialect'
class PageTest
include Dialect
end
So you can see Dialect is mixed-in to an existing class. It's when this class is instantiated that I get the error above.
When I try a simple IRB session doing what appears to be this same logic, this all seems to work:
irb(main):001:0> module Dialect
irb(main):002:1> def self.version
irb(main):003:2> puts "Version number"
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> module Dialect
irb(main):007:1> module Generator
irb(main):008:2> module Element
irb(main):009:3> puts Dialect.version
irb(main):010:3> end
irb(main):011:2> end
irb(main):012:1> end
Version number
Here I get the "Version number" text back, which tells me (I think?) that Dialect::Generator::Element can call the method version on Dialect.
The issue ended up being corrected by simply moving my require statement to the end, like this:
module Dialect
def self.included(caller)
caller.extend Dialect::Generator::Element
end
def self.version
"Dialect v#{Dialect::VERSION}"
end
end
require 'dialect/generators/elements'
Having the require statement at the end solves the problem I was having.
The question then becomes: is this a good way to do this? I feel like making my logic depend on where the require statement goes seems like a bad idea.
1 Answer 1
The require
line in the first file means that ruby runs the second file before the first file, which is before the version
method is declared...
Doing it the other way around will work, since only when included
is called is the module Element
is required.
dialect/dialect.rb:
module Dialect
def self.included(caller)
caller.extend Dialect::Generator::Element
end
def self.version
"Dialect v#{Dialect::VERSION}"
end
end
dialect/generators/elements.rb:
require 'dialect/dialect'
module Dialect
module Generator
module Element
puts Dialect.version
end
end
end
Alternatively, you could also write a file declaring only the version:
dialect/version.rb:
module Dialect
def self.version
"Dialect v#{Dialect::VERSION}"
end
end
Edit
To answer your question - this is not a good way to do this. The problem you encountered with the require
hints to circular dependency - Dialect
calls Element
, which uses Dialect
on its creation.
The correct resolution is to break this dependency by using my version.rb
suggestion above, so the code will look like this:
dialect/version.rb:
module Dialect
def self.version
"Dialect v#{Dialect::VERSION}"
end
end
dialect/generators/elements.rb:
require 'dialect/version'
module Dialect
module Generator
module Element
puts Dialect.version
end
end
end
dialect/dialect.rb:
require 'dialect/version'
require 'dialect/generators/elements'
module Dialect
def self.included(caller)
caller.extend Dialect::Generator::Element
end
end