无论如何要在REPL中重新加载修改后的gem文件

Jik*_*ose 6 ruby gem bundler read-eval-print-loop

在尝试构建Ruby gem(使用Bundler)时,我倾向于使用Bundler提供的REPL来测试代码 - 可通过bundle console.

有没有办法重新加载整个项目?我最终再次加载单个(已更改)文件以测试新的更改.

Ami*_*itA 1

以下 hack 适用于我的一个相对简单的 gem 和 Ruby 2.2.2。我很想看看它是否适合你。它做出以下假设:

  1. 您拥有传统的文件夹结构:一个名为的文件lib/my_gem_name.rb和一个文件夹lib/my_gem_name/,其下面有任何文件/文件夹结构。
  2. 您想要重新加载的所有类都嵌套在您的顶级模块中MyGemName

lib/my_gem_name/如果在MyGemName 命名空间之外的猴子补丁类下的文件之一中,它可能不起作用。

如果您对上述假设感到满意,请将以下代码放入模块定义中lib/my_gem_name.rb并尝试一下:

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
Run Code Online (Sandbox Code Playgroud)

如果您不希望此功能在生产中可用,请考虑在脚本中对其进行猴子修补bin/console,但请确保将该行更改$LOADED_FEATURES.select{|x| x.starts_with?(__FILE__.chomp('.rb'))}为在给定代码新位置的情况下返回相关加载文件列表的内容。

如果您有标准的 gem 结构,这应该可以工作:( $LOADED_FEATURES.select{|x| x.starts_with?(File.expand_path('../../lib/my_gem_name'))}确保将您的猴子修补代码放在IRB.start或 之前Pry.start