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:
If a file needs code from another in the same library, I use
require_relative
in the file that needs the code.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:
Ruby is interpreted, not compiled, so you can't execute any code that hasn't been seen by the interpreter.
require
just inserts the code from the file into that point of the program, in other words, arequire
at the top of the program will be interpreted before arequire
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.