How do I get a list of files that have been `required` in Ruby?
Asked Answered
N

3

33

This is purely an experiment, but I'm wondering if it's possible to get a list of the require'd gems at runtime via some kind of metaprogramming. For example, say I have:

require 'rubygems'
require 'sinatra'
require 'nokogiri'

# don't know what to do here

How can I print out the following at runtime?

this app needs rubygems, sinatra, nokogiri
Newspaperman answered 25/8, 2011 at 12:4 Comment(2)
If $LOADED_FEATURES didn't exist, you could monkeypatch require to do what you wanted.People
This question has neat answers using Gem: #2748490Msg
C
42

You can't do this exactly, because requiring one file may require others, and Ruby can't tell the difference between the file that you required and the file that someone else required.

You can check out $LOADED_FEATURES for a list of every single thing that's been required. But you should use Bundler if you want to specify dependencies explicitly.

Here's a thoroughly imperfect way to guess at the gem names and enumerate everything:

ruby-1.9.2-p180 :001 > $LOADED_FEATURES.
   select { |feature| feature.include? 'gems' }.
   map { |feature| File.dirname(feature) }.
   map { |feature| feature.split('/').last }.
   uniq.sort
 => ["1.9.1", "action_dispatch", "action_pack", "action_view", "actions", "active_model", "active_record", "active_support", "addressable", "agent", "array", "aws", "builder", "bundler", "cache_stores", "cancan", "cdn", "class", "client", "common", "compute", "connection", "control", "controllers", "core", "core_ext", "core_extensions", "css", "data_mapper", "decorators", "dependencies", "dependency_detection", "deprecation", "devise", "digest", "dns", "encodings", "encryptor", "engine", "errors", "excon", "ext", "failure", "faraday", "fields", "fog", "formatador", "geographer", "haml", "hash", "helpers", "heroku_san", "hmac", "hooks", "hoptoad_notifier", "html", "http", "i18n", "idna", "importers", "inflector", "initializers", "instrumentation", "integrations", "interpolate", "interval_skip_list", "jquery-rails", "json", "kaminari", "kernel", "lib", "mail", "metric_parser", "mime", "mixins", "model_adapters", "models", "module", "mongo_mapper", "mongoid", "multibyte", "new_relic", "node", "nokogiri", "numeric", "oauth", "object", "omniauth", "orm_adapter", "package", "parser", "parsers", "plugin", "pp", "providers", "queued", "rack", "rails", "railtie", "redis", "request", "request_proxy", "resp    ruby-1.9.2-p180 :008 >onse", "resque", "retriever_methods", "routing", "ruby_extensions", "ruby_flipper", "rubygems", "runtime", "samplers", "sass", "sax", "script", "scss", "selector", "sequel", "ses", "shell", "signature", "simple_geo", "state_machine", "stats_engine", "storage", "strategies", "string", "tar_reader", "template", "terremark", "thor", "tokens", "tree", "treetop", "twitter", "us", "util", "vendor", "version_specific", "visitors", "warden", "xml", "xml_mini", "xpath", "xslt"] 
Coattail answered 25/8, 2011 at 12:16 Comment(1)
$" is alias for $LOADED_FEATURESQuennie
C
27

Here's a way to get all the calls to require. Create this file: show_requires.rb

alias :orig_require :require
def require s
  print "Requires #{s}\n" if orig_require(s)
end

Then start your app with

ruby -r show_requires.rb myapp.rb

This produces something like:

C:\code\test>ruby -r show_requires.rb test.rb
Requires stringio
Requires yaml/error
Requires syck
Requires yaml/ypath
Requires yaml/basenode
Requires yaml/syck
Requires yaml/tag
Requires yaml/stream
Requires yaml/constants
Requires date/format
Requires date
Requires yaml/rubytypes
Requires yaml/types
Requires yaml
Requires etc
Requires dl
Requires rbreadline
Requires readline

If you want only the top-level requires, add a global to track the nesting level:

$_rq_lvl = 0
alias :orig_require :require
def require s
  $_rq_lvl+=1
  print "Requires #{s}\n" if orig_require(s) and $_rq_lvl == 1
  $_rq_lvl -=1
end

Then you get:

C:\code\test>ruby -r require_test.rb test.rb
Requires yaml
Requires readline
Copilot answered 26/8, 2011 at 17:57 Comment(2)
Thanks for this post. This got me closer to what I was looking for than the accepted answer. However I only got the results I needed by monkey patching this into KernelFragonard
It fails with some /lib/bundler.rb:85:in ui': uninitialized constant #<Class:Bundler>::UI (NameError), probably because bundler uses autoload.Entertainment
S
2

Just a slight touch to add to the previous -- consider that in order to precisely replace the behaviour of #require then you must also return a boolean value, so this is a more faithful override:

module Kernel
  alias :orig_require :require
  def require(name)
    print "Requiring #{name}"
    is_okay = orig_require(name)
    puts " - #{is_okay ? 'Yup!' : 'Nope :('}"
    is_okay
  end
end

Interestingly with some testing I was doing -- tracking down a chain of stuff blowing up when requiring a module -- then this became necessary!

Stogy answered 30/11, 2020 at 15:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.