method_introspection 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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