today i was facing a strange problem: got a 'missing method' error on a module, but the method was there and the file where the module was defined was required. After some searching i found a circular dependency, where 2 files required each other, and now i assume ruby silently aborts circular requires.
Edit Begin: Example
File 'a.rb':
require './b.rb'
module A
def self.do_something
puts 'doing..'
end
end
File 'b.rb':
require './a.rb'
module B
def self.calling
::A.do_something
end
end
B.calling
Executing b.rb gives b.rb:5:in 'calling': uninitialized constant A (NameError)
. The requires have to be there for both files as they are intended to be run on their own from command line (i ommitted that code to keep it short).
So the B.calling has to be there. One possible solution is to wrap the requires in if __FILE__ == $0
, but that does not seem the right way to go.
Edit End
to avoid these hard-to-find errors (wouldn't it be nicer if the require threw an exception, by the way?), are there some guidelines/rules on how to structure a project and where to require what? For example, if i have
module MainModule
module SubModule
module SubSubModule
end
end
end
where should i require the submodules? all in the main, or only the sub in the main and the subsub in the sub?
any help would be very nice.
An explanation why this happens is discussed in forforfs answer and comments.
So far best practice (as pointed out or hinted to by lain) seems to be the following (please correct me if i'm wrong):
MainModule
would require the SubModule
, and the Submodule
would require the SubSubModule
)thanks to everyone who answered/commented, it helped me a lot!
Avoiding circular dependencies by refactoring The NestJS documentation advises that circular dependencies be avoided where possible. Circular dependencies create tight couplings between the classes or modules involved, which means both classes or modules have to be recompiled every time either of them is changed.
A cyclic dependency is an indication of a design or modeling problem in your software. Although you can construct your object graph by using property injection, you will ignore the root cause and add another problem: property injection causes Temporal Coupling. Instead, the solution is to look at the design closely.
There are a couple of options to get rid of circular dependencies. For a longer chain, A -> B -> C -> D -> A , if one of the references is removed (for instance, the D -> A reference), the cyclic reference pattern is broken, as well. For simpler patterns, such as A -> B -> A , refactoring may be necessary.
A cyclic dependency exists when a dependency of a service directly or indirectly depends on the service itself. For example, if UserService depends on EmployeeService , which also depends on UserService . Angular will have to instantiate EmployeeService to create UserService , which depends on UserService , itself.
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, 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.rb
module A
def self.do_something
puts 'doing..'
end
end
#finish loading the rest of b.rb
module B
def self.calling
::A.do_something
end
end
B.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.rb
module B
def self.calling
::A.do_something
end
end
B.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
end
end
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 defined
module B
def self.calling
::A.do_something
end
include Delay #must be *after* any callable method defs
end
module A
def self.do_something
puts 'doing..'
end
include Delay #must be *after* any callable method defs
end
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 module
a.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With