Sha256: 6f6b520aa5cc3997724dccec95c85b5e956dbf04665b5e42b4f08efbf0a1ff2f

Contents?: true

Size: 1.47 KB

Versions: 1

Compression:

Stored size: 1.47 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, local_variables: true, &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.receiver)

      # copy local_variables
      if local_variables
        bb.local_variables.each {|n| b.local_variable_set(n, bb.local_variable_get(n)) }
      end

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

      # write back local_variables
      if local_variables
        bb.local_variables.each {|n| bb.local_variable_set(n, b.local_variable_get(n)) }
      end

      ret
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
with_refinements-0.4.0 lib/with_refinements.rb