From: jeffz_2002@... Date: 2007-01-03T08:50:06+09:00 Subject: Managing "requires" in projects with many subdirectories I'd like to know how people people managing their file dependencies with projects with several directories. Described below is the problem, and a demo of a possible rudimentary solution for which I'd like feedback/discussion. (I've searched and can't find any notes for handling requires elegantly. It could be that I'm searching in the wrong place ... please point me in the right direction if yes) I've been working on a small project with several directories. Using the $:.unshift File.join( ( File.expand_path( File.dirname( __FILE__ ) ) ), '..', '..', 'blah' ) trick gets pretty boring to type, doesn't do much for the code readability, and doesn't help when files get moved around. Side note: As a relic of my C++ interest, I also like my requires to be self-contained so my files are independent (e.g., if A uses B and C, I'll require B and C in A, even if B requires C ... that way, if the require 'C' is removed from B, A will still work). Here's the quick demo of one possible solution for handling requires for a project with multiple subdirectories: in root: -------- ** File add_dirs_to_search_path.rb: require 'find' Find.find( File.expand_path( File.dirname( __FILE__ ) ) ) do |path| if FileTest.directory?(path) $:.unshift path end end in root/A: ---------- ** File a_thing.rb: module A class AThing; def go() "A go!"; end; end end in root/B: ---------- ** File a_client.rb: require 'a_thing' module B class BThing; def go() "B go: #{A::AThing.new.go}"; end; end end Back in root: ------------- ** File main.rb: $:.unshift File.expand_path( File.dirname( __FILE__ )) require 'add_dirs_to_search_path' require 'a_client' b = B::BThing.new puts b.go Result when called from root: $ ruby main.rb B go: A go! Notes, advantages, drawbacks: - Module names mimic the directory structure (per the code conventions at RWiki) - B only has to use "require 'a_thing', since a_thing's directory was pushed into the include search path - Any code that uses B must now have specified the search path to a_thing.rb somehow, either using the -I command-line directive, an implicit require (requiring some code that requires A), or by requiring add_dirs_to_search_path.rb - main has to have "$:.unshift File.expand_path( File.dirname( __FILE__ )); require 'add_dirs_to_search_path'". So would any test suite, etc. - Directory changes are easily handled. You can move A::AThing to a different directory (note that this change should probably be accompanied by a change in module name, per RWiki code conventions) - To disambiguate the desired file, include the directory. For example, if root/C also contains a file a_thing.rb, and you wanted to use *that* file in main, you'd change the require in main to "require File.join( 'C', 'a_thing.rb' )". - The require gets mixed up if different folders with the same name contain files with the same name. For example, if there's another file A/C/a_thing.rb, and the module definition matches the directory structure (module A; module C; class AThing; ...), the require *can* bomb since the wrong file might be included. - in add_dirs_to_search_path.rb, I'm dynamically finding the files, but you could also hardcode them: dirs = [ 'a', 'a|suba', 'b', 'b|subb', 'c' ] root = File.expand_path( File.dirname( __FILE__ ) ) dirs.each do |d| $:.unshift "#{root}|#{dirs}".gsub( '|', File::SEPARATOR ) end This whole idea might be kind of silly ... it might be better to be explicit when including files (like you have to do in Java ... "import com.x.my.utilities.rock.Thingy"), and to always fully specify modules whenever you're referring to a class. Sorry for the long post - but I'd like to hear any good strategies for managing requires. Thanks, Jeff