Pry reload!

One thing I always liked about RoR development is being able to reload model code without needing to restart rails c by way of using the reload! method from within rails console. I was wondering if there was any way to port this method over to pry and gem development.

Setup

The setup is rather simple and can be applied to IRB as well as pry. This blog post will focus on pry however.

Within your gem directory, edit your Rakefile to include the following code:

require "bundler/gem_tasks"

task :console do
  require 'pry'
  require 'gem_name'

  def reload!
    # Change 'gem_name' here too:
    files = $LOADED_FEATURES.select { |feat| feat =~ /\/gem_name\// }
    files.each { |file| load file }
  end

  ARGV.clear
  Pry.start
end

Explanation

The above code snippet adds a rake task called console. This adds a rake task that will open a pry session with your gem already loaded.

zachallett@ ~/code/test_gem
 λ rake console
[1] pry(main)> TestGem
=> TestGem

Once you have edited your gem code, you can simply call reload! within your pry session to reload the gem code and check that the new method is there.

zachallett@ ~/code/test_gem
 λ rake console
[1] pry(main)> TestGem.hello
NoMethodError: undefined method `hello' for TestGem:Module
from (pry):1:in `__pry__'
[2] pry(main)> reload!
/Users/zachallett/code/test_gem/lib/test_gem/version.rb:2: warning: already     initialized constant TestGem::VERSION
/Users/zachallett/code/test_gem/lib/test_gem/version.rb:2: warning: previous    definition of VERSION was here
=> ["/Users/zachallett/code/test_gem/lib/test_gem/version.rb", "/Users/ zachallett/code/test_gem/lib/test_gem.rb"]
[3] pry(main)> TestGem.hello
Hello
=> nil

To see how this works, let's dive into how this is accomplished. At the core of it, the implementation of reload! is relying on just one global variable, $LOADED_FEATURES.

This global variable is an array that is added to every time an object is required within the pry, or irb session. Since the gem is automatically required when the pry session starts, it is included with $LOADED_FEATURES when the initial session starts. This is accomplished by the following annotated code:

task :console do
  require 'pry' # loads pry when the rake task is called
  require 'gem_name' # loads your gem within pry after it starts

  ...
end

The reload! method is equally as simple:

  def reload!
    # Change 'gem_name' here too:

    # iterate over the $LOADED_FEATURES array and select only those
    # who are within the path of your gem

    files = $LOADED_FEATURES.select { |feat| feat =~ /\/gem_name\// }

    # after it builds the correct array, it will load the file once more into
    # the current irb / pry session
    files.each { |file| load file }
  end

Sources

You can view the example implementation code at Test Gem on GitHub