Sha256: 02301213d51c24d24ed1e70cd51c37609c258dc2fcdb5e8901112a0da2a196e9

Contents?: true

Size: 1.21 KB

Versions: 1

Compression:

Stored size: 1.21 KB

Contents

require "with_refinements/version"

module WithRefinements
  class << self
    def clean_binding
      eval('module Class.new::CleanRoom; binding; end')
    end

    def code_from_block(block)
      iseq = RubyVM::InstructionSequence.of(block).to_a
      loc = iseq[4].yield_self {|h| h[:code_range] || h[:code_location] }
      path = iseq[7]
      File.readlines(path)[loc[0]-1..loc[2]-1].tap {|ls|
        if loc[0] == loc[2]
          ls[0] = ls[0][loc[1]...loc[3]]
        else
          ls[0], ls[-1] = ls[0][loc[1]..-1], ls[-1][0..loc[3]]
        end

        # remove -> from -> {}
        if ls[0].start_with?('->')
          ls[0] = ls[0][2..-1]
        end
      }.join
    end
  end

  refine(Object) do
    def with_refinements(*ms, &block)
      # enable refinements
      b = WithRefinements.clean_binding
      b.local_variable_set(:__modules__, ms)
      b.eval('__modules__.each {|m| using m }')

      # setup block eval context
      bb = block.binding
      b.local_variable_set(:__self__, bb.eval('self'))
      bb.local_variables.each {|n| b.local_variable_set(n, bb.local_variable_get(n)) }

      # eval block code
      b.eval("__self__.instance_eval #{WithRefinements.code_from_block(block)}")
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
with_refinements-0.3.0 lib/with_refinements.rb