ruby: how to require correctly (to avoid circular dependencies) ruby: how to require correctly (to avoid circular dependencies) ruby ruby

ruby: how to require correctly (to avoid circular dependencies)


After asking about this on the Ruby mailing list a while back, when I used to have a file in my libraries just for requiring things, I changed to these two rules:

  1. If a file needs code from another in the same library, I use require_relative in the file that needs the code.

  2. If a file needs code from a different library, I use require in the file that needs the code.

As far as I understand it, Ruby requires in the order it is asked to, and so it doesn't matter about circular dependencies.

(Ruby v1.9.2)

In answer to the comment about the example showing circular dependency problems:

actually, the problem with the example isn't that the requires are circular, but that B.calling is called before the requires have completed. If you remove the B.calling from b.rb it works fine. For example, in irb without B.calling in the code file but run afterwards:

$ irb
require '/Volumes/RubyProjects/Test/stackoverflow8057625/b.rb'
=> true
B.calling
doing..
=> nil


A couple of basic things that you hopefully already know:

  1. Ruby is interpreted, not compiled, so you can't execute any code that hasn't been seen by the interpreter.

  2. require just inserts the code from the file into that point of the program, in other words, a require at the top of the program will be interpreted before a require at the bottom.

(Note: Edited to account for require statements behavior)So if you were to do:ruby a.rb this is what the ruby interpreter would see and execute:

#load file b.rb <- from require './b.rb' in 'a.rb' file#load file a.rb <- from require './a.rb' in 'b.rb' file  #this runs because a.rb has not yet been required#second attempt to load b.rb but is ignored <- from require './b.rb' in 'a.rb' file#finish loading the rest of a.rbmodule A  def self.do_something    puts 'doing..'  endend#finish loading the rest of b.rbmodule B  def self.calling    ::A.do_something  endendB.calling#Works because everything is defined 

If instead you ran b first, ruby b.rb, the interpreter would see:

#load file a.rb <- from require './a.rb' in 'b.rb' file#load file b.rb <- from require './b.rb' in 'a.rb' file  #this runs because b.rb has not yet been required#second attempt to load a.rb but is ignored <- from require './a.rb' in 'b.rb' file#finish loading the rest of b.rbmodule B  def self.calling    ::A.do_something  endendB.calling #NameError, ::A.do_something hasn't been defined yet.

Hopefully this explains the good answers the others have given you, and if you think about it, why it's hard to answer your last question about where to put require statements. With Ruby, you're requiring files not modules, so where you put the require in your code, depends on how your files are organized.

If you absolutely need to be able to have modules defined and methods execute in random order, then you could implement something like this to collect calls on modules that don't yet exist, and then call them when they pop into being.

module Delay  @@q = {}    def self.call_mod(*args) #args format is method_name, mod_name, *args    mod_name = args.shift    method_name = args.shift    #remaining args are still in args    mod = Object.const_get(mod_name.to_sym)    mod.send(method_name.to_sym, *args)  end  def self.exec(mod_name, *args)    begin      args.unshift(mod_name)      self.call_mod(*args)    rescue NameError, NoMethodError      @@q[mod_name] ||= []      @@q[mod_name] << args    end  end  def self.included(mod)    #get queued methods    q_list = @@q[mod.name.to_sym]    return unless q_list    #execute delayed methods now that module exists    q_list.each do |args|      self.call_mod(*args)    end  endend 

Be sure to define the Delay module first and then rather than calling B.calling you would use Delay.exec(:B, :calling, any_other_args). So if you have this after the Delay module:

Delay.exec(:B, :calling)   #Module B is not definedmodule B  def self.calling    ::A.do_something  end  include Delay #must be *after* any callable method defsendmodule A  def self.do_something    puts 'doing..'  end  include Delay #must be *after* any callable method defsend

Results in:

#=> doing..

Final step is to break the code up into files. One approach could be to have three files

delay.rb   #holds just Delay modulea.rb       #holds the A module and any calls to other modules b.rb       #holds the B module and any calls to other modules

As long as you make sure require 'delay' is the first line of the module files (a.rb and b.rb) and Delay included at the end of the module, things should work.

Final Note: This implementation only makes sense if you cannot decouple your definition code from the module execution calls.