Donato Donato - 11 months ago 39
Ruby Question

requiring a file within a method

I was looking at the contents of the Ransack ruby gem. Basically, it calls a method called

. And that method itself requires a file:

# ransack.rb
require 'ransack/adapters'

# adapters.rb
module Ransack
module Adapters

def self.object_mapper
@object_mapper ||= instantiate_object_mapper

def self.instantiate_object_mapper
if defined?(::ActiveRecord::Base)
elsif defined?(::Mongoid)

class ActiveRecordAdapter
def require_constants
require 'ransack/adapters/active_record/ransack/constants'

# constants.rb
module Ransack
module Constants

The first
copies the content of adapters.rb in ransack.rb, I believe. Hence, we can then reference
without an undefined error.

However, when we call
, it appears to copy the contents of
into the method definition of

I find that kind of confusing. We are copying a module inside of a method. What benefit do we get of copying a module inside of a method, rather than just doing it like the other
? Second, I know the module is not a local variable, but I couldn't even define a module in the console when I tried it:

class A
def a
module B end
SyntaxError: (irb):14: module definition in method body

So what is
doing that does not cause the syntax error?

Answer Source

"Copy" is the wrong word here. require does not copy anything. It reads the source code in the given file and executes that code in Ruby's global ("main") context (unless the file has already been required; then it does nothing and returns false). To quote the docs (emphasis mine):

Any constants or globals within the loaded source file will be available in the calling program’s global namespace.

require does not behave differently inside a method call, or inside a module, or anywhere else, than it does at the top of a file.

When you see require inside a method call the reason is usually that the module is only needed in a particular scenario, and loading it before that scenario occurs would be wasteful (because, for example, the module takes up a lot of memory or takes a long time to load). It does not mean that the module is being loaded "into" the method or the surrounding code, because that's not what require does.

To demonstrate, suppose we have a module like this:

# baby_module.rb
module BabyModule
  NAME = "Baby"

And suppose we run the following program:

module TheCorner
  def self.load_baby_module
    require File.expand_path("baby_module", __dir__)


if defined?(TheCorner::BabyModule)
  puts "#{TheCorner::BabyModule::NAME} is in TheCorner"
elsif defined?(BabyModule)
  puts "Nobody puts #{BabyModule::NAME} in TheCorner"

As you have perhaps already guessed, this program's output will be:

Nobody puts Baby in TheCorner