class Proc
Constants
- LBRACE
- TLAMBDA
- TLAMBEG
- TOKEN_PAIRS
Public Class Methods
from_source(prc_src)
click to toggle source
# File lib/core_extensions/proc.rb, line 95 def self.from_source(prc_src) raise ArgumentError unless prc_src.kind_of?(String) prc = eval(prc_src) prc.instance_variable_set(:@source, prc_src) prc end
Public Instance Methods
_actually_starting_a_proc?(tokens, tok)
click to toggle source
# File lib/core_extensions/proc.rb, line 58 def _actually_starting_a_proc?(tokens, tok) return true if tokens.index(tok).eql?(0) look_back = tokens.slice(0..tokens.index(tok)-1) look_back.pop if look_back.last.try(:[], 1).eql? :on_sp if [:on_tlambeg, :on_tlambda].include?(tok[1]) true else ![:on_comma, :on_lparen, :on_label].include?(look_back.last.try(:[], 1)) end end
source()
click to toggle source
Make a best effort to provide the original source for a block based on extracting a string from the file identified in Proc#source_location using Ruby's tokenizer.
This works for first block declared on a line in a source file. If additional blocks are specified inside the first block on the same line as the start of the block, only the outer-most block declaration will be identified as a the block we want.
If you require only the source of blocks-within-other-blocks, start them on a new line as would be best practice for clarity and readability.
# File lib/core_extensions/proc.rb, line 24 def source @source ||= begin file, line_no = source_location raise "no file provided by source_location: #{self}" if file.nil? raise "no line number provided for source_location: #{self}" if line_no.nil? tokens = Ripper.lex File.read(file) tokens_on_line = tokens.select {|pos, lbl, str| pos[0].eql?(line_no) } starting_token = tokens_on_line.detect do |pos, lbl, str| TOKEN_PAIRS.keys.include?([lbl, str]) && _actually_starting_a_proc?(tokens, [pos, lbl, str]) end starting_token_type = [starting_token[1], starting_token[2]] ending_token_type = TOKEN_PAIRS[starting_token_type] source_str = "" remaining_tokens = tokens[tokens.index(starting_token)..-1] nesting = -1 starting_nesting_token_types = if [TLAMBDA, LBRACE].include?(starting_token_type) [TLAMBDA, LBRACE] else [starting_token_type] end while token = remaining_tokens.shift token = [token[1], token[2]] # strip position source_str << token[1] nesting += 1 if starting_nesting_token_types.include? token is_ending_token = token.eql?(ending_token_type) break if is_ending_token && nesting.eql?(0) nesting -= 1 if is_ending_token end source_str end end
source_body()
click to toggle source
Examines the source of a proc to extract the body by removing the outermost block delimiters and any surrounding. whitespace.
Raises exception if the block takes arguments.
# File lib/core_extensions/proc.rb, line 75 def source_body raise "Cannot extract proc body on non-zero arity" unless arity.eql?(0) tokens = Ripper.lex source body_start_idx = 2 if tokens[0][1].eql?(:on_tlambda) body_start_idx = tokens.index(tokens.detect { |t| t[1].eql?(:on_tlambeg) }) + 1 end body_tokens = tokens[body_start_idx..-1] body_tokens.pop # ending token of proc # remove trailing whitespace whitespace = [:on_sp, :on_nl, :on_ignored_nl] body_tokens.pop while whitespace.include?(body_tokens[-1][1]) # remove leading whitespace body_tokens.shift while whitespace.include?(body_tokens[0][1]) # put them back together body_tokens.map {|token| token[2] }.join end