0

Background - in a Rails app, I'm seeing a bug in production where a file cannot find a class from the standard library; but in development, everything works fine. Presumably, some gem that I have in the development group but not the production group is requiring the necessary library, so the symbol is define for development but not for production.

Is there any way I can get Ruby to tell me where require was called for a given file?

(I'm deliberately not naming the offending library, because I don't want suggestions as to what might be requiring it; I want to know how I can find out myself)

Chowlett
  • 45,935
  • 20
  • 116
  • 150
  • How about just running `gem dependency`? See https://stackoverflow.com/questions/19050654/how-to-see-the-dependency-tree-just-from-gemfile – lacostenycoder Sep 25 '17 at 09:56
  • 1
    How does the bug look like? Doesn't it contain a stack trace pointing to the `require`? – Stefan Sep 25 '17 at 09:58
  • Honestly, I'd just look for clues based on the undefined class/method name and the context of the code. (Neither of which are in your post.) You could probably even just google the error and see what pops up.... – Tom Lord Sep 25 '17 at 10:01
  • @lacostenycoder assuming I understand that correctly, it won't give what I want (the missing require is not a gem, but part of the standard library) – Chowlett Sep 25 '17 at 11:00
  • @Stefan No - the bug is a `NameError` - `Uninitialized constant`, due to referencing something defined in the library. – Chowlett Sep 25 '17 at 11:01
  • @TomLord Indeed, I did that and eventually found the gems responsible by grepping their github repos. But there were 5 of them, so getting a list programmatically would definitely have helped! – Chowlett Sep 25 '17 at 11:04
  • What would also help is organising the `Gemfile` into groups of development/test gems, and writing comments to make it clear what each library is for. I don't normally advocate writing comments in code, but `Gemfile`s are certainly an exception to the rule IMO. – Tom Lord Sep 25 '17 at 11:15

1 Answers1

3

To answer your direct question: patch/override the require method and get access to all the info you need.

app/config/environment.rb

# set up intrumentation
module RequireSpy
  def require(*args)
    puts "requiring #{args.join(', ')} from #{caller.first}"
    super
  end
end
Object.include(RequireSpy)

# init your rails app as usual
require File.expand_path('../application', __FILE__)
Rails.application.initialize!

Upon booting the rails app, it'll print a LOT of output:

requiring tilt from /Users/sergio/.gem/ruby/2.4.2/gems/sinatra-1.4.8/lib/sinatra/base.rb:3:in `<top (required)>'
requiring rack/protection from /Users/sergio/.gem/ruby/2.4.2/gems/sinatra-1.4.8/lib/sinatra/base.rb:4:in `<top (required)>'
requiring thread from /Users/sergio/.gem/ruby/2.4.2/gems/sinatra-1.4.8/lib/sinatra/base.rb:7:in `<top (required)>'
requiring time from /Users/sergio/.gem/ruby/2.4.2/gems/sinatra-1.4.8/lib/sinatra/base.rb:8:in `<top (required)>'
requiring uri from /Users/sergio/.gem/ruby/2.4.2/gems/sinatra-1.4.8/lib/sinatra/base.rb:9:in `<top (required)>'

You probably want to filter that.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367