method_introspection 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a281ed170dd1e993975cc0cdd4ea889caa72a286
4
+ data.tar.gz: e3916ce0e0c21b1f2ef4fe2006e5bf5f5676b41c
5
+ SHA512:
6
+ metadata.gz: a38dca730b231aa479f30b2ffb65ed0ef62236cef7786ed95d5f0f616da9360a765f86509cd2a59e952d5b11387baf037059805a90584a653909b348767241d6
7
+ data.tar.gz: 13857fafe0c960a482abb0612406fb0782df8572a9c3c7f34c9a1fc665e77a7e95b841f3f7f8d8214c6714997815d94f9156255a14ee04b522c17b04ec5f8abc
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ method_introspection
2
+ ====================
3
+
4
+ This is a simplified variant of showing comments and source code.
5
+
6
+ It is based on John Mair's (banisterfiend) gem called _method_source_.
7
+
8
+ The gem is fine - however had I found that I only required MRI support
9
+ for my own gems since that is what I am using.
10
+
11
+ Thus, a lot of the code was not useful for me since it would be based
12
+ on jruby or rubinius or also ruby 1.8. Hence why I stripped down and
13
+ put it into a separate gem.
14
+
15
+ Credit where credit is due and in this case it was banisterfiend's
16
+ initial work.
17
+
18
+ The license is thus the MIT license. Please refer to his gem called
19
+ method_source for a description of that gem.
20
+
21
+ _Introspect ruby methods at runtime_
22
+
23
+ Method comments can be obtained via the `comment` method.
24
+
25
+ This library is written in pure Ruby.
26
+
27
+ Examples: display method source
28
+ ------------------------------
29
+
30
+ Set.instance_method(:merge).source.display
31
+ # =>
32
+ def merge(enum)
33
+ if enum.instance_of?(self.class)
34
+ @hash.update(enum.instance_variable_get(:@hash))
35
+ else
36
+ do_with_enum(enum) { |o| add(o) }
37
+ end
38
+
39
+ self
40
+ end
41
+
42
+ Example: display method comments
43
+ --------------------------------
44
+
45
+ Set.instance_method(:merge).comment.display
46
+ # =>
47
+ # Merges the elements of the given enumerable object to the set and
48
+ # returns self.
@@ -0,0 +1,177 @@
1
+ module MethodIntrospection
2
+
3
+ module CodeHelpers
4
+
5
+ # ========================================================================= #
6
+ # === expression_at
7
+ #
8
+ # Retrieve the first expression starting on the given line of the
9
+ # given file.
10
+ #
11
+ # This is useful to get module or method source code.
12
+ #
13
+ # @param [Array<String>, File, String] file The file to parse, either as a File or as
14
+ # @param [Fixnum] line_number The line number at which to look.
15
+ # NOTE: The first line in a file is
16
+ # line 1!
17
+ #
18
+ # @param [Hash] options The optional configuration parameters.
19
+ # @option options [Boolean] :strict If set to true, then only completely
20
+ # valid expressions are returned. Otherwise heuristics are used to extract
21
+ # expressions that may have been valid inside an eval.
22
+ # @option options [Fixnum] :consume A number of lines to automatically
23
+ # consume (add to the expression buffer) without checking for validity.
24
+ # @return [String] The first complete expression
25
+ # @raise [SyntaxError] If the first complete expression can't be identified
26
+ # ========================================================================= #
27
+ def expression_at(
28
+ file,
29
+ line_number,
30
+ options = {}
31
+ )
32
+ options = {
33
+ :strict => false,
34
+ :consume => 0
35
+ }.merge!(options)
36
+
37
+ if file.is_a? Array
38
+ lines = file
39
+ else
40
+ lines = file.each_line.to_a
41
+ end
42
+
43
+ relevant_lines = lines[(line_number - 1)..-1] || []
44
+
45
+ extract_first_expression(relevant_lines, options[:consume])
46
+ rescue SyntaxError => e
47
+ raise if options[:strict]
48
+
49
+ begin
50
+ extract_first_expression(relevant_lines) { |code|
51
+ code.gsub(/\#\{.*?\}/, "temp")
52
+ }
53
+ rescue SyntaxError
54
+ raise e
55
+ end
56
+ end
57
+
58
+ # ========================================================================= #
59
+ # === comment_describing
60
+ #
61
+ # Retrieve the comment describing the expression on the given line of
62
+ # the given file.
63
+ #
64
+ # This is useful to get module or method documentation.
65
+ #
66
+ # @param [Array<String>, File, String] file The file to parse, either as a File or as
67
+ # a String or an Array of lines.
68
+ # @param [Fixnum] line_number The line number at which to look.
69
+ # NOTE: The first line in a file is line 1!
70
+ # @return [String] The comment
71
+ # ========================================================================= #
72
+ def comment_describing(file, line_number)
73
+ lines = file.is_a?(Array) ? file : file.each_line.to_a
74
+ extract_last_comment(lines[0..(line_number - 2)])
75
+ end
76
+
77
+ # ========================================================================= #
78
+ # === complete_expression?
79
+ #
80
+ # Determine if a string of code is a complete Ruby expression.
81
+ #
82
+ # @param [String] code The code to validate.
83
+ # @return [Boolean] Whether or not the code is a complete Ruby expression.
84
+ # @raise [SyntaxError] Any SyntaxError that does not represent incompleteness.
85
+ # @example
86
+ # complete_expression?("class Hello") #=> false
87
+ # complete_expression?("class Hello; end") #=> true
88
+ # complete_expression?("class 123") #=> SyntaxError: unexpected tINTEGER
89
+ # ========================================================================= #
90
+ def complete_expression?(i)
91
+ old_verbose = $VERBOSE
92
+ $VERBOSE = nil
93
+ catch(:valid) {
94
+ eval("BEGIN{throw :valid}\n#{i}")
95
+ }
96
+ # Assert that a line which ends with a , or \ is incomplete.
97
+ i !~ /[,\\]\s*\z/
98
+ rescue IncompleteExpression
99
+ false
100
+ ensure
101
+ $VERBOSE = old_verbose
102
+ end
103
+
104
+ private
105
+
106
+ # ========================================================================= #
107
+ # === extract_first_expression
108
+ #
109
+ # Get the first expression from the input.
110
+ #
111
+ # @param [Array<String>] lines
112
+ # @param [Fixnum] consume A number of lines to automatically
113
+ # consume (add to the expression buffer) without checking for validity.
114
+ # @yield a clean-up function to run before checking for complete_expression
115
+ # @return [String] a valid ruby expression
116
+ # @raise [SyntaxError]
117
+ # ========================================================================= #
118
+ def extract_first_expression(lines, consume = 0, &block)
119
+ code = consume.zero? ? '' : lines.slice!(0..(consume - 1)).join
120
+ lines.each { |entry|
121
+ code << entry
122
+ return code if complete_expression?(block ? block.call(code) : code)
123
+ }
124
+ raise SyntaxError, "unexpected $end"
125
+ end
126
+
127
+ # ========================================================================= #
128
+ # === extract_last_comment
129
+ #
130
+ # Get the last comment from the input.
131
+ #
132
+ # @param [Array<String>] lines
133
+ # @return [String]
134
+ # ========================================================================= #
135
+ def extract_last_comment(lines)
136
+ result = ''
137
+ lines.each { |line|
138
+ # ===================================================================== #
139
+ # Add any line that is a valid ruby comment but stop the moment
140
+ # we hit a non-comment line.
141
+ # ===================================================================== #
142
+ if (line =~ /^\s*#/) || (line =~ /^\s*$/)
143
+ result << line.lstrip
144
+ else
145
+ result.replace("")
146
+ end
147
+ }
148
+ return result
149
+ end
150
+
151
+ # ========================================================================= #
152
+ # === IncompleteExpression
153
+ #
154
+ # An exception matcher that matches only subsets of SyntaxErrors that can be
155
+ # fixed by adding more input to the buffer.
156
+ # ========================================================================= #
157
+ module IncompleteExpression
158
+ GENERIC_REGEXPS = [
159
+ /unexpected (\$end|end-of-file|end-of-input|END_OF_FILE)/, # mri, jruby, ruby-2.0, ironruby
160
+ /embedded document meets end of file/, # =begin
161
+ /unterminated (quoted string|string|regexp) meets end of file/, # "quoted string" is ironruby
162
+ /can not find the string ".*" anywhere before EOF/, # rbx and jruby
163
+ /missing 'end' for/, /expecting kWHEN/ # rbx
164
+ ]
165
+
166
+ def self.===(ex)
167
+ return false unless SyntaxError === ex
168
+ case ex.message
169
+ when *GENERIC_REGEXPS
170
+ true
171
+ else
172
+ false
173
+ end
174
+ end
175
+ end
176
+
177
+ end; end
@@ -0,0 +1,23 @@
1
+ module MethodIntrospection # require 'method_source/project_base_directory.rb'
2
+
3
+ # ========================================================================= #
4
+ # === PROJECT_BASE_DIRECTORY
5
+ # ========================================================================= #
6
+ PROJECT_BASE_DIRECTORY = RbConfig::CONFIG['sitelibdir']+'/method_introspection/'
7
+
8
+ # ========================================================================= #
9
+ # === MethodIntrospection.project_base_dir?
10
+ # ========================================================================= #
11
+ def self.project_base_dir?
12
+ PROJECT_BASE_DIRECTORY
13
+ end
14
+
15
+ # ========================================================================= #
16
+ # Next we load up the various .rb files of that project.
17
+ # ========================================================================= #
18
+ require MethodIntrospection.project_base_dir?+'version/version.rb'
19
+ require MethodIntrospection.project_base_dir?+'source_location'
20
+ require MethodIntrospection.project_base_dir?+'code_helpers'
21
+ require MethodIntrospection.project_base_dir?+'module_methods'
22
+
23
+ end
@@ -0,0 +1,78 @@
1
+ module MethodIntrospection
2
+
3
+ # ========================================================================= #
4
+ # === SourceNotFoundError
5
+ #
6
+ # An Exception to mark errors that were raised trying to find the
7
+ # source from a given source_location.
8
+ # ========================================================================= #
9
+ class SourceNotFoundError < StandardError; end
10
+
11
+ # ========================================================================= #
12
+ # === MethodIntrospection.raise_this
13
+ #
14
+ # Second argument is optional, and allows for a longer description.
15
+ # ========================================================================= #
16
+ def self.raise_this(name, optional_extra = nil)
17
+ result = "We could not locate the source for #{name}"
18
+ if optional_extra
19
+ result << ": #{optional_extra}"
20
+ else
21
+ result << '.'
22
+ end
23
+ raise SourceNotFoundError, result
24
+ end
25
+
26
+ # ========================================================================= #
27
+ # === MethodIntrospection.source_helper
28
+ #
29
+ # Helper method responsible for extracting the body of a method.
30
+ #
31
+ # Defined here to avoid polluting `Method` class.
32
+ # @param [Array] source_location The array returned by Method#source_location
33
+ # @param [String] method_name
34
+ # @return [String] The method body
35
+ # ========================================================================= #
36
+ def self.source_helper(source_location, name = nil)
37
+ raise_this(name) unless source_location
38
+ file, line = *source_location
39
+ expression_at(lines_for(file), line)
40
+ rescue SyntaxError => error
41
+ raise SourceNotFoundError,
42
+ "Could not parse source for #{name}: #{error.message}"
43
+ end
44
+
45
+ # ========================================================================= #
46
+ # === MethodIntrospection.comment_helper
47
+ #
48
+ # Helper method responsible for opening source file and buffering up
49
+ # the comments for a specified method. Defined here to avoid polluting
50
+ # `Method` class.
51
+ # @param [Array] source_location The array returned by Method#source_location
52
+ # @param [String] method_name
53
+ # @return [String] The comments up to the point of the method.
54
+ # ========================================================================= #
55
+ def self.comment_helper(source_location, name = nil)
56
+ raise_this(name) unless source_location
57
+ file, line = *source_location
58
+ comment_describing(lines_for(file), line)
59
+ end
60
+
61
+ # ========================================================================= #
62
+ # === MethodIntrospection.lines_for
63
+ #
64
+ # Load a memoized copy of the lines in a file.
65
+ #
66
+ # @param [String] file_name
67
+ # @param [String] method_name
68
+ # @return [Array<String>] the contents of the file
69
+ # @raise [SourceNotFoundError]
70
+ # ========================================================================= #
71
+ def self.lines_for(file_name, name = nil)
72
+ @lines_for_file ||= {}
73
+ @lines_for_file[file_name] ||= File.readlines(file_name)
74
+ rescue Errno::ENOENT => e
75
+ raise_this(name, e.message)
76
+ end
77
+
78
+ end
@@ -0,0 +1,68 @@
1
+ module MethodIntrospection
2
+ module SourceLocation
3
+ module MethodExtensions
4
+ # ===================================================================== #
5
+ # === trace_func
6
+ # ===================================================================== #
7
+ def trace_func(event, file, line, id, binding, classname)
8
+ return unless event == 'call'
9
+ set_trace_func(nil)
10
+ @file, @line = file, line
11
+ raise :found
12
+ end; private :trace_func
13
+ end
14
+
15
+ module ProcExtensions
16
+ # Return the source location for a Proc (in implementations
17
+ # without Proc#source_location)
18
+ #
19
+ # @return [Array] A two element array. First element is the
20
+ # file, second element is the line in the file where the
21
+ # proc definition is found.
22
+ def source_location
23
+ self.to_s =~ /@(.*):(\d+)/
24
+ [$1, $2.to_i]
25
+ end
26
+ end
27
+
28
+ module UnboundMethodExtensions
29
+ # Return the source location of an instance method for Ruby 1.8.
30
+ #
31
+ # @return [Array] A two element array. First element is the
32
+ # file, second element is the line in the file where the
33
+ # method definition is found.
34
+ def source_location
35
+ klass = case owner
36
+ when Class
37
+ owner
38
+ when Module
39
+ method_owner = owner
40
+ Class.new { include(method_owner) }
41
+ end
42
+
43
+ # deal with immediate values
44
+ case
45
+ when klass == Symbol
46
+ return :a.method(name).source_location
47
+ when klass == Fixnum
48
+ return 0.method(name).source_location
49
+ when klass == TrueClass
50
+ return true.method(name).source_location
51
+ when klass == FalseClass
52
+ return false.method(name).source_location
53
+ when klass == NilClass
54
+ return nil.method(name).source_location
55
+ end
56
+ begin
57
+ Object.instance_method(:method).bind(klass.allocate).call(name).source_location
58
+ rescue TypeError
59
+ # Assume we are dealing with a Singleton Class:
60
+ # 1. Get the instance object
61
+ # 2. Forward the source_location lookup to the instance
62
+ instance ||= ObjectSpace.each_object(owner).first
63
+ Object.instance_method(:method).bind(instance).call(name).source_location
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ module MethodIntrospection
2
+
3
+ VERSION = '0.0.1'
4
+
5
+ end
@@ -0,0 +1,58 @@
1
+ require 'method_introspection/load_project.rb'
2
+
3
+ module MethodIntrospection
4
+
5
+ extend MethodIntrospection::CodeHelpers
6
+
7
+ # This module is to be included by `Method` and `UnboundMethod` and
8
+ # provides the `#source` functionality
9
+ module MethodExtensions
10
+ # === source
11
+ #
12
+ # The method source() will return the sourcecode for the method as
13
+ # a String.
14
+ #
15
+ # @return [String] The method sourcecode as a string
16
+ # @raise SourceNotFoundException
17
+ #
18
+ # @example
19
+ # Set.instance_method(:clear).source.display
20
+ # =>
21
+ # def clear
22
+ # @hash.clear
23
+ # self
24
+ # end
25
+ def source
26
+ MethodIntrospection.source_helper(source_location, defined?(name) ? name : inspect)
27
+ end
28
+
29
+ # === comment
30
+ #
31
+ # Return the comments associated with the method as a string.
32
+ # @return [String] The method's comments as a string
33
+ # @raise SourceNotFoundException
34
+ #
35
+ # @example
36
+ # Set.instance_method(:clear).comment.display
37
+ # =>
38
+ # # Removes all elements and returns self.
39
+ def comment
40
+ MethodIntrospection.comment_helper(source_location, defined?(name) ? name : inspect)
41
+ end
42
+ end
43
+ end
44
+
45
+ class Method
46
+ include MethodIntrospection::SourceLocation::MethodExtensions
47
+ include MethodIntrospection::MethodExtensions
48
+ end
49
+
50
+ class UnboundMethod
51
+ include MethodIntrospection::SourceLocation::UnboundMethodExtensions
52
+ include MethodIntrospection::MethodExtensions
53
+ end
54
+
55
+ class Proc
56
+ include MethodIntrospection::SourceLocation::ProcExtensions
57
+ include MethodIntrospection::MethodExtensions
58
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'method_introspection/version/version.rb'
3
+
4
+ DESCRIPTION = 'Allows us to inspect the source code for a method and the documentation.'
5
+
6
+ Gem::Specification.new { |s|
7
+ s.name = 'method_introspection'
8
+ s.version = MethodIntrospection::VERSION
9
+
10
+ s.authors = ['Robert A. Heiler']
11
+ s.date = Time.now.strftime('%Y-%m-%d')
12
+ s.email = 'shevegen@gmail.com'
13
+ s.files = %w(
14
+ README.md
15
+ lib/method_introspection.rb
16
+ lib/method_introspection/code_helpers.rb
17
+ lib/method_introspection/source_location.rb
18
+ lib/method_introspection/load_project.rb
19
+ lib/method_introspection/module_methods.rb
20
+ lib/method_introspection/version/version.rb
21
+ test/test.rb
22
+ test/test_code_helpers.rb
23
+ test/test_helper.rb
24
+ method_introspection.gemspec
25
+ )
26
+ s.homepage = 'http://shevegen.square7.ch/'
27
+ s.require_paths = ['lib']
28
+ s.summary = DESCRIPTION
29
+ s.description = DESCRIPTION
30
+ s.license = 'MIT'
31
+ s.test_files = [
32
+ 'test/test.rb',
33
+ 'test/test_code_helpers.rb',
34
+ 'test/test_helper.rb'
35
+ ]
36
+
37
+ s.required_ruby_version = '>= 2.2.2'
38
+ s.rubygems_version = '2.4.8'
39
+
40
+ }
data/test/test.rb ADDED
@@ -0,0 +1,138 @@
1
+ direc = File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'rubygems'
4
+ require 'bacon'
5
+ require 'method_introspection/method_source.rb'
6
+ require "#{direc}/test_helper"
7
+
8
+ describe MethodIntrospection do
9
+
10
+ describe "source_location (testing 1.8 implementation)" do
11
+ it 'should return correct source_location for a method' do
12
+ method(:hello).source_location.first.should =~ /test_helper/
13
+ end
14
+
15
+ it 'should not raise for immediate instance methods' do
16
+ [Symbol, Fixnum, TrueClass, FalseClass, NilClass].each do |immediate_class|
17
+ lambda { immediate_class.instance_method(:to_s).source_location }.should.not.raise
18
+ end
19
+ end
20
+
21
+ it 'should not raise for immediate methods' do
22
+ [:a, 1, true, false, nil].each do |immediate|
23
+ lambda { immediate.method(:to_s).source_location }.should.not.raise
24
+ end
25
+ end
26
+ end
27
+
28
+ before do
29
+ @hello_module_source = " def hello; :hello_module; end\n"
30
+ @hello_singleton_source = "def $o.hello; :hello_singleton; end\n"
31
+ @hello_source = "def hello; :hello; end\n"
32
+ @hello_comment = "# A comment for hello\n# It spans two lines and is indented by 2 spaces\n"
33
+ @lambda_comment = "# This is a comment for MyLambda\n"
34
+ @lambda_source = "MyLambda = lambda { :lambda }\n"
35
+ @proc_source = "MyProc = Proc.new { :proc }\n"
36
+ @hello_instance_evaled_source = " def hello_\#{name}(*args)\n send_mesg(:\#{name}, *args)\n end\n"
37
+ @hello_instance_evaled_source_2 = " def \#{name}_two()\n if 44\n 45\n end\n end\n"
38
+ @hello_class_evaled_source = " def hello_\#{name}(*args)\n send_mesg(:\#{name}, *args)\n end\n"
39
+ @hi_module_evaled_source = " def hi_\#{name}\n @var = \#{name}\n end\n"
40
+ end
41
+
42
+ it 'should define methods on Method and UnboundMethod and Proc' do
43
+ Method.method_defined?(:source).should == true
44
+ UnboundMethod.method_defined?(:source).should == true
45
+ Proc.method_defined?(:source).should == true
46
+ end
47
+
48
+ describe "Methods" do
49
+ it 'should return source for method' do
50
+ method(:hello).source.should == @hello_source
51
+ end
52
+
53
+ it 'should return source for a method defined in a module' do
54
+ M.instance_method(:hello).source.should == @hello_module_source
55
+ end
56
+
57
+ it 'should return source for a singleton method as an instance method' do
58
+ class << $o; self; end.instance_method(:hello).source.should == @hello_singleton_source
59
+ end
60
+
61
+ it 'should return source for a singleton method' do
62
+ $o.method(:hello).source.should == @hello_singleton_source
63
+ end
64
+
65
+ it 'should return a comment for method' do
66
+ method(:hello).comment.should == @hello_comment
67
+ end
68
+
69
+ # These tests fail because of http://jira.codehaus.org/browse/JRUBY-4576
70
+ unless defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
71
+ it 'should return source for an *_evaled method' do
72
+ M.method(:hello_name).source.should == @hello_instance_evaled_source
73
+ M.method(:name_two).source.should == @hello_instance_evaled_source_2
74
+ M.instance_method(:hello_name).source.should == @hello_class_evaled_source
75
+ M.instance_method(:hi_name).source.should == @hi_module_evaled_source
76
+ end
77
+ end
78
+
79
+ it "should raise error for evaled methods that do not pass __FILE__ and __LINE__ + 1 as its arguments" do
80
+ lambda { M.instance_method(:name_three).source }.should.raise MethodSource::SourceNotFoundError
81
+ end
82
+
83
+ if !is_rbx?
84
+ it 'should raise for C methods' do
85
+ lambda { method(:puts).source }.should.raise MethodSource::SourceNotFoundError
86
+ end
87
+ end
88
+ end
89
+
90
+ # if RUBY_VERSION =~ /1.9/ || is_rbx?
91
+ describe "Lambdas and Procs" do
92
+ it 'should return source for proc' do
93
+ MyProc.source.should == @proc_source
94
+ end
95
+
96
+ it 'should return an empty string if there is no comment' do
97
+ MyProc.comment.should == ''
98
+ end
99
+
100
+ it 'should return source for lambda' do
101
+ MyLambda.source.should == @lambda_source
102
+ end
103
+
104
+ it 'should return comment for lambda' do
105
+ MyLambda.comment.should == @lambda_comment
106
+ end
107
+ end
108
+ # end
109
+ describe "Comment tests" do
110
+ before do
111
+ @comment1 = "# a\n# b\n"
112
+ @comment2 = "# a\n# b\n"
113
+ @comment3 = "# a\n#\n# b\n"
114
+ @comment4 = "# a\n# b\n"
115
+ @comment5 = "# a\n# b\n# c\n# d\n"
116
+ end
117
+
118
+ it "should correctly extract multi-line comments" do
119
+ method(:comment_test1).comment.should == @comment1
120
+ end
121
+
122
+ it "should correctly strip leading whitespace before comments" do
123
+ method(:comment_test2).comment.should == @comment2
124
+ end
125
+
126
+ it "should keep empty comment lines" do
127
+ method(:comment_test3).comment.should == @comment3
128
+ end
129
+
130
+ it "should ignore blank lines between comments" do
131
+ method(:comment_test4).comment.should == @comment4
132
+ end
133
+
134
+ it "should align all comments to same indent level" do
135
+ method(:comment_test5).comment.should == @comment5
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,42 @@
1
+ require 'method_introspection'
2
+
3
+ describe MethodSource::CodeHelpers do
4
+ before {
5
+ @tester = Object.new.extend(MethodSource::CodeHelpers)
6
+ }
7
+ [
8
+ ["p = '", "'"],
9
+ ["def", 'a', "(); end"],
10
+ ["p = <<FOO", "lots", "and", "lots of", "foo", "FOO"],
11
+ ["[", ":lets,", "'list',", "[/nested/", "], things ]"],
12
+ ["abc =~ /hello", "/"],
13
+ ["issue = %W/", "343/"],
14
+ ["pouts(<<HI, 'foo", "bar", "HI", "baz')"],
15
+ ["=begin", "no-one uses this syntax anymore...", "=end"],
16
+ ["puts 1, 2,", "3"],
17
+ ["puts 'hello'\\", "'world'"]
18
+ ].each do |lines|
19
+ it "should not raise an error on broken lines: #{lines.join("\\n")}" do
20
+ 1.upto(lines.size - 1) do |i|
21
+ @tester.complete_expression?(lines[0...i].join("\n") + "\n").should == false
22
+ end
23
+ @tester.complete_expression?(lines.join("\n")).should == true
24
+ end
25
+ end
26
+
27
+ [
28
+ ["end"],
29
+ ["puts )("],
30
+ ["1 1"],
31
+ ["puts :"]
32
+ ] + (RbConfig::CONFIG['ruby_install_name'] == 'rbx' ? [] : [
33
+ ["def", "method(1"], # in this case the syntax error is "expecting ')'".
34
+ ["o = Object.new.tap{ def o.render;","'MEH'", "}"] # in this case the syntax error is "expecting keyword_end".
35
+ ]).compact.each do |foo|
36
+ it "should raise an error on invalid syntax like #{foo.inspect}" do
37
+ lambda{
38
+ @tester.complete_expression?(foo.join("\n"))
39
+ }.should.raise(SyntaxError)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,98 @@
1
+ def is_rbx?
2
+ defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/
3
+ end
4
+
5
+ def jruby?
6
+ defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
7
+ end
8
+
9
+
10
+ module M
11
+ def hello; :hello_module; end
12
+ end
13
+
14
+ $o = Object.new
15
+ def $o.hello; :hello_singleton; end
16
+
17
+ # A comment for hello
18
+
19
+ # It spans two lines and is indented by 2 spaces
20
+ def hello; :hello; end
21
+
22
+ # a
23
+ # b
24
+ def comment_test1; end
25
+
26
+ # a
27
+ # b
28
+ def comment_test2; end
29
+
30
+ # a
31
+ #
32
+ # b
33
+ def comment_test3; end
34
+
35
+ # a
36
+
37
+ # b
38
+ def comment_test4; end
39
+
40
+
41
+ # a
42
+ # b
43
+ # c
44
+ # d
45
+ def comment_test5; end
46
+
47
+ # This is a comment for MyLambda
48
+ MyLambda = lambda { :lambda }
49
+ MyProc = Proc.new { :proc }
50
+
51
+
52
+ name = "name"
53
+
54
+ M.instance_eval <<-METHOD, __FILE__, __LINE__ + 1
55
+ def hello_#{name}(*args)
56
+ send_mesg(:#{name}, *args)
57
+ end
58
+ METHOD
59
+
60
+ M.class_eval <<-METHOD, __FILE__, __LINE__ + 1
61
+ def hello_#{name}(*args)
62
+ send_mesg(:#{name}, *args)
63
+ end
64
+ METHOD
65
+
66
+ # module_eval to DRY code up
67
+ #
68
+ M.module_eval <<-METHOD, __FILE__, __LINE__ + 1
69
+
70
+ # module_eval is used here
71
+ #
72
+ def hi_#{name}
73
+ @var = #{name}
74
+ end
75
+ METHOD
76
+
77
+ # case where 2 methods are defined inside an _eval block
78
+ #
79
+ M.instance_eval <<EOF, __FILE__, __LINE__ + 1
80
+
81
+ def #{name}_one()
82
+ if 43
83
+ 44
84
+ end
85
+ end
86
+
87
+
88
+ def #{name}_two()
89
+ if 44
90
+ 45
91
+ end
92
+ end
93
+ EOF
94
+
95
+ # class_eval without filename and lineno + 1 parameter
96
+
97
+ M.class_eval "def #{name}_three; @tempfile.#{name}; end"
98
+
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: method_introspection
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Robert A. Heiler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Allows us to inspect the source code for a method and the documentation.
14
+ email: shevegen@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - README.md
20
+ - lib/method_introspection.rb
21
+ - lib/method_introspection/code_helpers.rb
22
+ - lib/method_introspection/load_project.rb
23
+ - lib/method_introspection/module_methods.rb
24
+ - lib/method_introspection/source_location.rb
25
+ - lib/method_introspection/version/version.rb
26
+ - method_introspection.gemspec
27
+ - test/test.rb
28
+ - test/test_code_helpers.rb
29
+ - test/test_helper.rb
30
+ homepage: http://shevegen.square7.ch/
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.2.2
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 2.4.5.1
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Allows us to inspect the source code for a method and the documentation.
54
+ test_files:
55
+ - test/test.rb
56
+ - test/test_code_helpers.rb
57
+ - test/test_helper.rb