1
\$\begingroup\$

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.

\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

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
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.