Question

How can you display the hierarchy of 'require's that take place in a Ruby app?

Some files require files which require additional files.

However, by running an application in debug mode you only trigger a subset of required files - only the ones that are used by whatever subset of functionality your application is using at any given point in time.

How could you display a comprehensive hierarchy of all of the requires in an application as a tree?

Was it helpful?

Solution

The issue is that in development mode, all files are loaded with load rather than require so that they can be reloaded on each request. In production they are loaded only once. With the exception of some of the framework classes, most files are still only loaded when they are first used. This happens because ActiveSupport overrides const_missing to automatically attempt to load unknown constants from files with the appropriate naming scheme (ConstantName.to_s.underscore would give require 'constant_name'). This of course really muddles up the 'require' hierarchy.

For a trivial case, you can modify the following to meet some of your needs (also check out dependencies in active_support)

  $require_level = []
  alias :orig_require :require
  def require(file)
    puts "#{$require_level.join}#{file}"
    $require_level << "-"
    r = orig_require(file)
    $require_level.pop
    r
  end

  require 'foo'
  require 'baz'


 ben@legba-2:~ $ ruby check_requires.rb 
 foo
 -bar
 baz

Good luck

EDIT: Explanation

What that does is create a global array to store the nesting level of requires. The first puts outputs the required file. Then a dash is added to the nesting level. The file is then actually required. If the loaded file calls require, then this whole process starts again, except that nesting level is 1 deep so "-#{file}" is puts-ed. This process repeats except as the nesting level grows, so do the dashes. After a file and all of its dependencies are loaded, require takes off the dash that it added so that nesting level is in the same state it was when the require started. This keeps the tree structure accurate.

const_missing is similar to method_missing. Basically, just like when you call AnObject.some_unknown_method ruby will call AnObject.method_missing(:some_unknown_method) before raising a NoMethodError, using SomeUnknownConstant triggers a const_missing(:SomeUnknownConstant) before raising a NameError. Rails defines const_missing such that it will search certain specified paths for files that might define the missing constant. It uses a naming convention to facilitate this, e.g. SomeUnknownConstant is expected to be in some_unknown_constant.rb

There is a method to much of this rails madness.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top