While trying to build a Ruby gem (using Bundler), I tend to test the code using the REPL provided by Bundler - accessible via bundle console
.
Is there any way to reload the entire project in it? I end up loading individual (changed) files again to test the new change.
Install BundlerSelect Tools | Bundler | Install Bundler from the main menu. Press Ctrl twice and execute the gem install bundler command in the invoked popup. Open the RubyMine terminal emulator and execute the gem install bundler command.
Meanwhile, gem pristine --all cleans all installed gems for that Ruby version. If a developer forgets which gems in their project they might have been debugging, the Rubygems gem pristine [GEMNAME] command may be inconvenient. One can avoid waiting for gem pristine --all , and instead run bundle pristine .
A Gemfile describes the gem dependencies required to execute associated Ruby code. Place the Gemfile in the root of the directory containing the associated code. For instance, in a Rails application, place the Gemfile in the same directory as the Rakefile .
The following hack works for a relatively simple gem of mine and Ruby 2.2.2. I'll be curious to see if it works for you. It makes the following assumptions:
lib/my_gem_name.rb
and a folder lib/my_gem_name/
with any files / folder structure underneath.MyGemName
. It will probably not work if in one of the files under lib/my_gem_name/
you monkey-patch classes outside of your MyGemName namespace.
If you're good with the assumptions above, put the following code inside the module definition in lib/my_gem_name.rb
and give it a try:
module MyGemName
def self.reload!
Reloader.new(self).reload
end
class Reloader
def initialize(top)
@top = top
end
def reload
cleanup
load_all
end
private
# @return [Array<String>] array of all files that were loaded to memory
# under the lib/my_gem_name folder.
# This code makes assumption #1 above.
def loaded_files
$LOADED_FEATURES.select{|x| x.starts_with?(__FILE__.chomp('.rb'))}
end
# @return [Array<Module>] Recursively find all modules and classes
# under the MyGemName namespace.
# This code makes assumption number #2 above.
def all_project_objects(current = @top)
return [] unless current.is_a?(Module) and current.to_s.split('::').first == @top.to_s
[current] + current.constants.flat_map{|x| all_project_objects(current.const_get(x))}
end
# @return [Hash] of the format {Module => true} containing all modules
# and classes under the MyGemName namespace
def all_project_objects_lookup
@_all_project_objects_lookup ||= Hash[all_project_objects.map{|x| [x, true]}]
end
# Recursively removes all constant entries of modules and classes under
# the MyGemName namespace
def cleanup(parent = Object, current = @top)
return unless all_project_objects_lookup[current]
current.constants.each {|const| cleanup current, current.const_get(const)}
parent.send(:remove_const, current.to_s.split('::').last.to_sym)
end
# Re-load all files that were previously reloaded
def load_all
loaded_files.each{|x| load x}
true
end
end
end
If you don't want this functionality to be available in production, consider monkey-patching this in the bin/console
script, but make sure to change the line $LOADED_FEATURES.select{|x| x.starts_with?(__FILE__.chomp('.rb'))}
to something that will return a list of relevant loaded files given the new location of the code.
If you have a standard gem structure, this should work:
$LOADED_FEATURES.select{|x| x.starts_with?(File.expand_path('../../lib/my_gem_name'))}
(make sure to put your monkey patching code before the IRB.start
or Pry.start
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With