Module: MethodSource::CodeHelpers

Included in:
MethodSource
Defined in:
lib/method_source/code_helpers.rb

Defined Under Namespace

Modules: IncompleteExpression

Constant Summary collapse

JRUBY_9200 =

Returns:

  • (Boolean)
(JRUBY_VERSION) || false) && JRUBY_VERSION == '9.2.0.0'

Instance Method Summary collapse

Instance Method Details

#comment_describing(file, line_number) ⇒ String

Retrieve the comment describing the expression on the given line of the given file.

This is useful to get module or method documentation.

Parameters:

  • file (Array<String>, File, String)

    The file to parse, either as a File or as a String or an Array of lines.

  • line_number (Integer)

    The line number at which to look. NOTE: The first line in a file is line 1!

Returns:

  • (String)

    The comment



75
76
77
78
79
# File 'lib/method_source/code_helpers.rb', line 75

def comment_describing(file, line_number)
  lines = file.is_a?(Array) ? file : file.each_line.to_a

  extract_last_comment(lines[0..(line_number - 2)])
end

#complete_expression?(str) ⇒ Boolean

Determine if a string of code is a complete Ruby expression.

Examples:

complete_expression?("class Hello") #=> false
complete_expression?("class Hello; end") #=> true
complete_expression?("class 123") #=> SyntaxError: unexpected tINTEGER

Parameters:

  • code (String)

    The code to validate.

Returns:

  • (Boolean)

    Whether or not the code is a complete Ruby expression.

Raises:

  • (SyntaxError)

    Any SyntaxError that does not represent incompleteness.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/method_source/code_helpers.rb', line 89

def complete_expression?(str)
  old_verbose = $VERBOSE
  $VERBOSE = nil

  catch(:valid) do
    eval("BEGIN{throw :valid}\n#{str}")
  end

  # Assert that a line which ends with a , or \ is incomplete.
  str !~ /[,\\]\s*\z/
rescue IncompleteExpression
  false
ensure
  $VERBOSE = old_verbose
end

#expression_at(file, line_number, options = {}) ⇒ String

Retrieve the first expression starting on the given line of the given file.

This is useful to get module or method source code.

                      line 1!

Parameters:

  • file (Array<String>, File, String)

    The file to parse, either as a File or as

  • line_number (Integer)

    The line number at which to look. NOTE: The first line in a file is

  • options (Hash) (defaults to: {})

    The optional configuration parameters.

Options Hash (options):

  • :strict (Boolean)

    If set to true, then only completely valid expressions are returned. Otherwise heuristics are used to extract expressions that may have been valid inside an eval.

  • :consume (Integer)

    A number of lines to automatically consume (add to the expression buffer) without checking for validity.

Returns:

  • (String)

    The first complete expression

Raises:

  • (SyntaxError)

    If the first complete expression can't be identified



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/method_source/code_helpers.rb', line 23

def expression_at(file, line_number, options={})
  options = {
    :strict  => false,
    :consume => 0
  }.merge!(options)

  lines = file.is_a?(Array) ? file : file.each_line.to_a

  relevant_lines = lines[(line_number - 1)..-1] || []

  extract_first_expression(relevant_lines, options[:consume])
rescue SyntaxError => e
  # JRuby 9.2.0.0 breaks #source_location for Procs (it reports line number
  # as the last line of the Proc). This raises SyntaxError.
  # See https://github.com/pry/pry/issues/1804 for details.
  #
  # To fix this, this hack rewinds source location one step at a time and
  # tries to see if the new location is a complete expression.
  #
  # TODO: delete this once latest JRuby version is bumped.
  # See https://github.com/banister/method_source/issues/52
  if JRUBY_9200 && line_number > 0
    loop do
      line_number -= 1

      # Skip empty lines since they are not real expressions.
      break unless lines[line_number - 1] == "\n"
    end

    retry
  end

  raise if options[:strict]

  begin
    extract_first_expression(relevant_lines) do |code|
      code.gsub(/\#\{.*?\}/, "temp")
    end
  rescue SyntaxError
    raise e
  end
end