module RequireAll

Constants

LoadError

Public Instance Methods

autoload_all(*paths) click to toggle source

Performs Kernel#autoload on all of the files rather than requiring immediately.

Note that all Ruby files inside of the specified directories should have same module name as the directory itself and file names should reflect the class/module names. For example if there is a my_file.rb in directories dir1/dir2/ then there should be a declaration like this in my_file.rb:

 module Dir1
   module Dir2
     class MyFile
       ...
     end
   end
end

If the filename and namespaces won’t match then my_file.rb will be loaded into wrong module! Better to fix these files.

Set $DEBUG=true to see how files will be autoloaded if experiencing any problems.

If trying to perform autoload on some individual file or some inner module, then you’d have to always specify :base_dir option to specify where top-level namespace resides. Otherwise it’s impossible to know the namespace of the loaded files.

For example loading only my_file.rb from dir1/dir2 with autoload_all:

autoload_all File.dirname(__FILE__) + '/dir1/dir2/my_file',
             base_dir: File.dirname(__FILE__) + '/dir1'

WARNING: All modules will be created even if files themselves aren’t loaded yet, meaning that all the code which depends of the modules being loaded or not will not work, like usages of define? and it’s friends.

Also, normal caveats of using Kernel#autoload apply - you have to remember that before applying any monkey-patches to code using autoload, you’ll have to reference the full constant to load the code before applying your patch!

    # File lib/require_all.rb
175 def autoload_all(*paths)
176   paths.flatten!
177   return false if paths.empty?
178   require "pathname"
179 
180   options = {method: :autoload}
181   options.merge!(paths.pop) if paths.last.is_a?(Hash)
182 
183   paths.each do |path|
184     require_all path, {base_dir: path}.merge(options)
185   end
186 end
autoload_rel(*paths) click to toggle source

Performs autoloading relatively from the caller instead of using current working directory

    # File lib/require_all.rb
189 def autoload_rel(*paths)
190   paths.flatten!
191   return false if paths.empty?
192   require "pathname"
193 
194   options = {method: :autoload}
195   options.merge!(paths.pop) if paths.last.is_a?(Hash)
196 
197   source_directory = File.dirname caller.first.sub(/:\d+$/, '')
198   paths.each do |path|
199     file_path = Pathname.new(source_directory).join(path).to_s
200     require_all file_path, {method: :autoload,
201                             base_dir: source_directory}.merge(options)
202   end
203 end
load_all(*paths) click to toggle source

Loads all files like require_all instead of requiring

    # File lib/require_all.rb
123 def load_all(*paths)
124   require_all paths, method: :load
125 end
load_rel(*paths) click to toggle source

Loads all files by using relative paths of the caller rather than the current working directory

    # File lib/require_all.rb
129 def load_rel(*paths)
130   paths.flatten!
131   return false if paths.empty?
132 
133   source_directory = File.dirname caller.first.sub(/:\d+$/, '')
134   paths.each do |path|
135     require_all File.join(source_directory, path), method: :load
136   end
137 end
require_all(*args) click to toggle source

A wonderfully simple way to load your code.

The easiest way to use require_all is to just point it at a directory containing a bunch of .rb files. These files can be nested under subdirectories as well:

require_all 'lib'

This will find all the .rb files under the lib directory and load them.

If a file required by require_all references a constant that is not yet loaded, a RequireAll::LoadError will be thrown.

You can also give it a glob, which will enumerate all the matching files:

require_all 'lib/**/*.rb'

It will also accept an array of files:

require_all Dir.glob("blah/**/*.rb").reject { |f| stupid_file(f) }

Or if you want, just list the files directly as arguments:

require_all 'lib/a.rb', 'lib/b.rb', 'lib/c.rb', 'lib/d.rb'
    # File lib/require_all.rb
 35 def require_all(*args)
 36   # Handle passing an array as an argument
 37   args.flatten!
 38 
 39   options = {method: :require}
 40   options.merge!(args.pop) if args.last.is_a?(Hash)
 41 
 42   if args.empty?
 43     puts "no files were loaded due to an empty Array" if $DEBUG
 44     return false
 45   end
 46 
 47   if args.size > 1
 48     # Expand files below directories
 49     files = args.map do |path|
 50       if File.directory? path
 51         Dir[File.join(path, '**', '*.rb')]
 52       else
 53         path
 54       end
 55     end.flatten
 56   else
 57     arg = args.first
 58     begin
 59       # Try assuming we're doing plain ol' require compat
 60       stat = File.stat(arg)
 61 
 62       if stat.file?
 63         files = [arg]
 64       elsif stat.directory?
 65         files = Dir.glob File.join(arg, '**', '*.rb')
 66       else
 67         raise ArgumentError, "#{arg} isn't a file or directory"
 68       end
 69     rescue SystemCallError
 70       # If the stat failed, maybe we have a glob!
 71       files = Dir.glob arg
 72 
 73       # Maybe it's an .rb file and the .rb was omitted
 74       if File.file?(arg + '.rb')
 75         file = arg + '.rb'
 76         options[:method] != :autoload ? __require(options[:method], file) : __autoload(file, file, options)
 77         return true
 78       end
 79 
 80       # If we ain't got no files, the glob failed
 81       raise LoadError, "no such file to load -- #{arg}" if files.empty?
 82     end
 83   end
 84 
 85   return if files.empty?
 86 
 87   if options[:method] == :autoload
 88     files.map! { |file_| [file_, File.expand_path(file_)] }
 89     files.each do |file_, full_path|
 90       __autoload(file_, full_path, options)
 91     end
 92 
 93     return true
 94   end
 95 
 96   files.map { |file_| File.expand_path file_ }.sort.each do |file_|
 97     begin
 98       __require(options[:method], file_)
 99     rescue NameError => e
100       # Only wrap NameError exceptions for uninitialized constants
101       raise e unless e.instance_of?(NameError) && e.message.include?('uninitialized constant')
102       raise LoadError, "Could not require #{file_} (#{e}). Please require the necessary files"
103     end
104   end
105 
106   true
107 end
require_rel(*paths) click to toggle source

Works like require_all, but paths are relative to the caller rather than the current working directory

    # File lib/require_all.rb
111 def require_rel(*paths)
112   # Handle passing an array as an argument
113   paths.flatten!
114   return false if paths.empty?
115 
116   source_directory = File.dirname caller.first.sub(/:\d+$/, '')
117   paths.each do |path|
118     require_all File.join(source_directory, path)
119   end
120 end

Private Instance Methods

__autoload(file, full_path, options) click to toggle source
    # File lib/require_all.rb
211 def __autoload(file, full_path, options)
212   last_module = "Object" # default constant where namespaces are created into
213   begin
214     base_dir = Pathname.new(options[:base_dir]).realpath
215   rescue Errno::ENOENT
216     raise LoadError, ":base_dir doesn't exist at #{options[:base_dir]}"
217   end
218   Pathname.new(file).realpath.descend do |entry|
219     # skip until *entry* is same as desired directory
220     # or anything inside of it avoiding to create modules
221     # from the top-level directories
222     next if (entry <=> base_dir) < 0
223 
224     # get the module into which a new module is created or
225     # autoload performed
226     mod = Object.class_eval(last_module)
227 
228     without_ext = entry.basename(entry.extname).to_s
229 
230     const =
231       if defined? ActiveSupport::Inflector
232         ActiveSupport::Inflector.camelize(without_ext)
233       else
234         without_ext.split("_").map {|word| word.capitalize}.join
235       end
236 
237     if entry.file? || (entry.directory? && entry.sub_ext('.rb').file?)
238       mod.class_eval do
239         puts "autoloading #{mod}::#{const} from #{full_path}" if $DEBUG
240         autoload const, full_path
241       end
242     else
243       mod.class_eval "module #{const} end" if entry.directory?
244     end
245 
246     last_module += "::#{const}" if entry.directory?
247   end
248 end
__require(method, file) click to toggle source
    # File lib/require_all.rb
207 def __require(method, file)
208   Kernel.send(method, file)
209 end