Loading Code in Rails ActiveSupport Gem

Loading code means reading the file containing code into memory and executing it. You can open the activesupport gem in your text editor if it is part of Rails project (because it needs Gemfile to resolve which version to use). You must have set the $EDITOR variable to your favorite text editor. In my case, it is Textmate.

echo $EDITOR
/usr/local/bin/mate

If you are not working on a Rails project or have not setup Gemfile for the bundler, you will get the error:

bundle open activesupport
Could not locate Gemfile or .bundle/ directory

In such cases, you can use gemedit gem. Install it.

gem install gemedit

And open the gem using the edit as the argument to the gem command.

gem edit activesupport
Opening the activesupport gem, version 5.0.1, with mate from /Users/bparanj/.rvm/gems/ruby-2.2.3/gems/activesupport-5.0.1

Browsing the source code, I noticed there is no gemspec in it. The files directive in gemspec specifies the files to be included in the gem, like this:

s.files        = Dir["CHANGELOG.md", "MIT-LICENSE", "README.rdoc", "lib/**/*"]

This is from the activesupport gemspec file. Ideally, we should be able to do `require 'activesupport':

$ irb
> require 'activesupport'
LoadError: cannot load such file -- activesupport

This throws an error. If you use underscore to separate the words:

$ irb
> require 'active_support'
 => true

It works. Why the gem name is not used in the require? Why do you need the underscore? If you read the rubygems guide, you can see the recommended convention:

Twitter Bootstrap 4

Go to activesupport github repository, look at the gemspec, name within the block shows that it is called activesupport. So:

Gem name is : activesupport
Main module is : ActiveSupport in active_support.rb
The require uses : active_support.

Unfortunately, the activesupport gem does not follow the ruby gems convention.

How to Name Gems

by Eric Hodel

Reproduced here, because his original blog post fell out of the Internet ;-)

Use underscores for multiple words.

Use underscore to separate multiple words. This matches the file the user will require makes it easier to start using the gem. Install by gem install my_gem. Load it by require 'my_gem'.

Use dashes for extensions.

If you're adding functionality to another gem use a dash. The dash is different-enough from an underscore to be noticeable. If you tilt the dash a bit it becomes a slash as well, making it easier for the user to know what to require. gem install net-http-persistent becomes require 'net/http/persistent'

Mix underscores and dashes appropriately

Read name your gem for more details.

What to Require ?

How do people answering questions on forums figure out what to require ? How do we load the code we need to use the methods from the library?

require 'active_support'
 => true
 ''.blank?
NoMethodError: undefined method `blank?' for "":String

Simply requiring the entire gem does not do anything. This is by design, to reduce the memory footprint. We know blank? is a method on the String object. Searching for def blank? using Textmate in activesupport gives 4 different files and 10 locations where it is defined. Reading the source code, the active_support/core_ext/object/blank.rb seems to be relevant to our task at hand. We can include this file in the IRB and use the blank? method.

$ irb
require 'active_support/core_ext/object/blank'
 => true
> ''.blank?
 => true

What happens if we load all the core extensions of active_support?

$ irb
 require 'active_support/core_ext'
NameError: uninitialized constant ActiveSupport::Autoload

We need to require the active_support first that does the initialization of autoload. So:

require 'active_support'
 => true
> require 'active_support/core_ext' 
/Users/bparanj/.rvm/gems/ruby-2.2.3/gems/activesupport-5.0.1/lib/active_support/core_ext.rb:1: warning: already initialized constant DEPRECATED_FILES
/Users/bparanj/.rvm/gems/ruby-2.2.3/gems/activesupport-5.0.1/lib/active_support/core_ext.rb:1: warning: previous definition of DEPRECATED_FILES was here
 => true
> ''.blank?
 => true

We are now able to use the blank? method. This is different from including just the blank.rb because, we can use any core extension method of activesupport if we require 'active_support/core_ext'.

Loading Grouped Core Extensions

We can load all the extensions defined for the Ruby Object by doing this:

require 'active_support'
require 'active_support/core_ext/object'

The object.rb in active_support/core_ext folder is like this:

require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/deep_dup'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/inclusion'

require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'

require 'active_support/core_ext/object/json'
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/object/with_options'

You can see that it loads all the files inside the active_support/core_ext/object folder. You can verify it by browsing the source code for activesupport.

Loading All Core Extensions

You can:

require 'active_support/all'

to load everything in activesupport gem. The all.rb looks like this:

require 'active_support'
require 'active_support/time'
require 'active_support/core_ext'

For the third require, the core_ext.rb in active_support/core_ext.rb looks like this:

DEPRECATED_FILES = ["#{File.dirname(__FILE__)}/core_ext/struct.rb"]
(Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"] - DEPRECATED_FILES).each do |path|
  require path
end

For the second require, the time.rb in active_support/time.rb looks like this:

module ActiveSupport
  autoload :Duration, 'active_support/duration'
  autoload :TimeWithZone, 'active_support/time_with_zone'
  autoload :TimeZone, 'active_support/values/time_zone'
end

require 'date'
require 'time'

require 'active_support/core_ext/time'
require 'active_support/core_ext/date'
require 'active_support/core_ext/date_time'

require 'active_support/core_ext/integer/time'
require 'active_support/core_ext/numeric/time'

require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/string/zones'

How to make sense of the Implementation of core_ext.rb? Let's deconstruct the code in core_ext.rb by designing small experiments to figure out how this code works. I am reducing the unknown to just one concept in each experiment. I am in my home directory.

pwd
/Users/bparanj

Let's see what __FILE__ means.

cat test.rb
puts __FILE__

Running this program prints the name of the ruby file containing the code.

$ ruby test.rb
test.rb

The __FILE__ is the current file name. Let's pass the __FILE to the dirname method.

$ cat test.rb
puts File.dirname(__FILE__)

Running this program prints a dot, meaning the current directory.

ruby test.rb
.

According to the Ruby docs, the dirname(file_name):

Returns all components of the filename given in file_name except the last one. The filename can be formed using both File::SEPARATOR and File::ALT_SEPARATOR as the separator when File::ALT_SEPARATOR is not nil.

Let's now add the Dir method to our test.rb.

cat test.rb
puts Dir["#{File.dirname(__FILE__)}/tok/con/app/models/*.rb"]

This prints the directory location going from current directory (my home directory) all the files including the file name.

ruby test.rb
./tok/con/app/models/application_record.rb
./tok/con/app/models/article.rb

So, this:

(Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"]).each do |path|
  require path
end

expands to this:

require './core_ext/array'
require './core_ext/benchmark'
...
... # more files not shown here
...
require './core_ext/uri'

to include all the .rb files in the core_ext directory. That's how we are able to use any of the methods in the core extension of the activesupport library. In this article, you learned how Rails loads code in activesupport gem. Understanding this concept will help you figure out what to require when you want to use activesupport gem outside of Rails. The rails console automatically does require so you don't need to know any of these concepts.

References


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.