ruby18_source_location 0.1 → 0.2
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.
- data/.gitignore +5 -0
- data/LICENSE.MIT +19 -0
- data/README.md +38 -0
- data/Rakefile +32 -0
- data/ext/ruby18_source_location.c +66 -55
- data/ruby18_source_location.gemspec +14 -0
- data/test/examples.rb +64 -0
- data/test/test_ruby18_source_location.rb +106 -0
- metadata +26 -37
data/LICENSE.MIT
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Conrad Irwin <conrad.irwin@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
The `ruby18_source_location` gem backports the `source_location` instance method of `Method`, `UnboundMethod` and
|
2
|
+
`Proc` from Ruby 1.9.
|
3
|
+
|
4
|
+
This allows you to use `Method#source_location` on ruby 1.8.7, and also all the other gems that depend on it.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
gem install ruby18_source_location
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'rubygems'
|
13
|
+
require 'ruby18_source_location'
|
14
|
+
|
15
|
+
def example
|
16
|
+
2
|
17
|
+
end
|
18
|
+
|
19
|
+
puts method(:example).source_location.inspect
|
20
|
+
# => ["/tmp/example.rb", 4]
|
21
|
+
|
22
|
+
puts Proc.new{}.source_location.inspect
|
23
|
+
# => ["/tmp/example.rb", 11]
|
24
|
+
|
25
|
+
puts method(:puts).source_location.inspect
|
26
|
+
# => nil
|
27
|
+
```
|
28
|
+
|
29
|
+
Known issues
|
30
|
+
------------
|
31
|
+
|
32
|
+
Under Ruby-1.8.7, it's not currently possible to get the `source_location` of methods defined by `attr_reader`,
|
33
|
+
`attr_writer` or `attr_accessor`, unless you are running Ruby Enterprise Edition.
|
34
|
+
|
35
|
+
Meta-foo
|
36
|
+
--------
|
37
|
+
|
38
|
+
This is made available under the MIT license (see LICENSE.MIT), contributions and bug reports are welcome.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
def dir(*args); Dir.chdir(File.join(File.dirname(__FILE__), *args)); end
|
3
|
+
desc "Compile local copy"
|
4
|
+
task :compile do
|
5
|
+
dir('ext')
|
6
|
+
system "ruby extconf.rb && make"
|
7
|
+
end
|
8
|
+
|
9
|
+
task :clean do
|
10
|
+
dir('ext')
|
11
|
+
system "make clean"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Package the gem"
|
15
|
+
task :gem do
|
16
|
+
dir('.')
|
17
|
+
system "gem build ruby18_source_location.gemspec"
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Run the tests"
|
21
|
+
task :test do
|
22
|
+
dir("test")
|
23
|
+
system "ruby test_ruby18_source_location.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Run an interactive shell"
|
27
|
+
task :pry do
|
28
|
+
dir('.')
|
29
|
+
system "pry -f -r ext/ruby18_source_location -r test/examples"
|
30
|
+
end
|
31
|
+
|
32
|
+
task :default => [:clean, :compile, :test]
|
@@ -1,32 +1,7 @@
|
|
1
|
-
// Include the Ruby headers and goodies
|
2
1
|
#include "ruby.h"
|
2
|
+
#include "node.h"
|
3
3
|
|
4
|
-
|
5
|
-
unsigned long flags;
|
6
|
-
char *nd_file;
|
7
|
-
union {
|
8
|
-
struct RNode *node;
|
9
|
-
ID id;
|
10
|
-
VALUE value;
|
11
|
-
VALUE (*cfunc)(ANYARGS);
|
12
|
-
ID *tbl;
|
13
|
-
} u1;
|
14
|
-
union {
|
15
|
-
struct RNode *node;
|
16
|
-
ID id;
|
17
|
-
long argc;
|
18
|
-
VALUE value;
|
19
|
-
} u2;
|
20
|
-
union {
|
21
|
-
struct RNode *node;
|
22
|
-
ID id;
|
23
|
-
long state;
|
24
|
-
struct global_entry *entry;
|
25
|
-
long cnt;
|
26
|
-
VALUE value;
|
27
|
-
} u3;
|
28
|
-
} NODE;
|
29
|
-
|
4
|
+
// Cargo-culted from ruby internal headers.
|
30
5
|
struct FRAME {
|
31
6
|
VALUE self;
|
32
7
|
int argc;
|
@@ -68,20 +43,17 @@ struct BLOCK {
|
|
68
43
|
struct BLOCK *outer;
|
69
44
|
struct BLOCK *prev;
|
70
45
|
};
|
71
|
-
#define RNODE(obj) (R_CAST(RNode)(obj))
|
72
|
-
#define NODE_LSHIFT (FL_USHIFT+8)
|
73
|
-
#define NODE_LMASK (((long)1<<(sizeof(NODE*)*CHAR_BIT-NODE_LSHIFT))-1)
|
74
|
-
#define nd_line(n) ((unsigned int)(((RNODE(n))->flags>>NODE_LSHIFT)&NODE_LMASK))
|
75
46
|
|
76
47
|
|
77
|
-
static VALUE
|
78
|
-
|
79
|
-
{
|
80
|
-
VALUE array = rb_ary_new();
|
81
|
-
rb_ary_push(array, rb_str_new2(filename));
|
82
|
-
rb_ary_push(array, INT2FIX(lineno));
|
48
|
+
static VALUE method_source_location(VALUE);
|
49
|
+
static VALUE proc_source_location(VALUE);
|
83
50
|
|
84
|
-
|
51
|
+
void
|
52
|
+
Init_ruby18_source_location()
|
53
|
+
{
|
54
|
+
rb_define_method(rb_cUnboundMethod, "source_location", method_source_location, 0);
|
55
|
+
rb_define_method(rb_cMethod, "source_location", method_source_location, 0);
|
56
|
+
rb_define_method(rb_cProc, "source_location", proc_source_location, 0);
|
85
57
|
}
|
86
58
|
|
87
59
|
static VALUE
|
@@ -90,10 +62,17 @@ node_source_location(NODE *node)
|
|
90
62
|
const char *filename = node->nd_file;
|
91
63
|
int lineno = nd_line(node);
|
92
64
|
|
93
|
-
if (filename && lineno)
|
94
|
-
|
65
|
+
if (filename && lineno) {
|
66
|
+
VALUE str = rb_str_new2(filename);
|
95
67
|
|
96
|
-
|
68
|
+
// TODO: is there a better way of telling whether the file should be absolute
|
69
|
+
if (Qtrue == rb_funcall(rb_cFile, rb_intern("exist?"), 1, str))
|
70
|
+
str = rb_file_expand_path(str, Qnil);
|
71
|
+
|
72
|
+
return rb_assoc_new(str, INT2FIX(lineno));
|
73
|
+
}
|
74
|
+
|
75
|
+
rb_raise(rb_eRuntimeError, "source_location: no file for node");
|
97
76
|
}
|
98
77
|
|
99
78
|
/*
|
@@ -108,18 +87,52 @@ static VALUE
|
|
108
87
|
method_source_location(VALUE method)
|
109
88
|
{
|
110
89
|
struct METHOD *data;
|
90
|
+
struct BLOCK *block;
|
111
91
|
NODE *node;
|
112
92
|
|
113
93
|
Data_Get_Struct(method, struct METHOD, data);
|
114
|
-
|
94
|
+
|
95
|
+
if (!(node = data->body))
|
96
|
+
rb_raise(rb_eRuntimeError, "source_location: no body for method");
|
97
|
+
|
98
|
+
switch (nd_type(node)) {
|
99
|
+
// methods defined by C extensions
|
100
|
+
case NODE_CFUNC:
|
101
|
+
return Qnil;
|
102
|
+
|
103
|
+
// attr_accessor :foo
|
104
|
+
case NODE_IVAR: // method(:foo)
|
105
|
+
case NODE_ATTRSET: // method(:foo=)
|
106
|
+
|
107
|
+
// ruby enterpise edition fixes nd_line on these nodes
|
108
|
+
#ifdef MBARI_API
|
115
109
|
return node_source_location(node);
|
110
|
+
#else
|
111
|
+
// FIXME, Ruby-1.8.7 returns a bad nd_line on these.
|
112
|
+
return Qnil;
|
113
|
+
#endif
|
114
|
+
|
115
|
+
// def foo; end
|
116
|
+
case NODE_SCOPE:
|
117
|
+
return node_source_location((NODE *)node->u3.value);
|
118
|
+
|
119
|
+
// define_method(:foo, method(:bar))
|
120
|
+
case NODE_DMETHOD:
|
121
|
+
return method_source_location(node->u3.value);
|
122
|
+
|
123
|
+
// define_method(:foo) { }, define_method(:foo, &bar)
|
124
|
+
case NODE_BMETHOD:
|
125
|
+
return proc_source_location(node->u3.value);
|
126
|
+
|
127
|
+
default:
|
128
|
+
rb_raise(rb_eRuntimeError, "source_location: unhandled method type %d", nd_type(node));
|
129
|
+
|
116
130
|
}
|
117
|
-
return Qnil;
|
118
131
|
}
|
119
132
|
|
120
133
|
/*
|
121
134
|
* call-seq:
|
122
|
-
*
|
135
|
+
* proc.source_location => [String, Fixnum]
|
123
136
|
*
|
124
137
|
* returns a pair of the Filename and line number on which the method is defined
|
125
138
|
*
|
@@ -130,18 +143,16 @@ proc_source_location(VALUE block)
|
|
130
143
|
{
|
131
144
|
struct BLOCK *data;
|
132
145
|
NODE *node;
|
133
|
-
|
134
146
|
Data_Get_Struct(block, struct BLOCK, data);
|
135
|
-
|
147
|
+
|
148
|
+
// Proc.new {}, or Proc.new &proc
|
149
|
+
if ((node = data->frame.node) && nd_type(node) == NODE_ITER)
|
136
150
|
return node_source_location(node);
|
137
|
-
}
|
138
|
-
return Qnil;
|
139
|
-
}
|
140
151
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
152
|
+
// Proc.new &method(:foo)
|
153
|
+
if ((node = data->body) && nd_type(node) == NODE_IFUNC)
|
154
|
+
return method_source_location(node->u2.value);
|
155
|
+
|
156
|
+
rb_raise(rb_eRuntimeError, "source_location: unhandled proc type");
|
157
|
+
|
147
158
|
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "ruby18_source_location"
|
4
|
+
s.version = "0.2"
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.author = "Conrad Irwin"
|
7
|
+
s.email = "conrad.irwin@gmail.com"
|
8
|
+
s.homepage = "http://github.com/ConradIrwin/ruby18_source_location"
|
9
|
+
s.summary = "Add .source_location to methods in Ruby 1.8.7"
|
10
|
+
s.description = "Allows you to make use of lots of ruby 1.9.2 specific goodness"
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.extensions = "ext/extconf.rb"
|
13
|
+
s.require_path = "ext"
|
14
|
+
end
|
data/test/examples.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
class Example
|
2
|
+
|
3
|
+
def example1
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
define_method(:example2) {
|
8
|
+
|
9
|
+
}
|
10
|
+
|
11
|
+
Example3 = Proc.new do
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
Example4 = lambda {
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
define_method(:example5, Example3)
|
20
|
+
|
21
|
+
define_method(:example6, &Example4)
|
22
|
+
|
23
|
+
define_method(:example7, instance_method(:example1))
|
24
|
+
|
25
|
+
define_method(:example8, &Example.new.method(:example2))
|
26
|
+
|
27
|
+
Example9 = Proc.new &Example3
|
28
|
+
|
29
|
+
Example10 = Proc.new &Example.new.method(:example1)
|
30
|
+
|
31
|
+
eval <<-EVAL
|
32
|
+
def example11
|
33
|
+
|
34
|
+
end
|
35
|
+
EVAL
|
36
|
+
|
37
|
+
eval <<-EVAL, binding, __FILE__, __LINE__+1
|
38
|
+
def example12
|
39
|
+
|
40
|
+
end
|
41
|
+
EVAL
|
42
|
+
|
43
|
+
def self.example13
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
alias example14 example1
|
48
|
+
|
49
|
+
alias_method :example15, :example2
|
50
|
+
|
51
|
+
attr_reader :example16
|
52
|
+
|
53
|
+
attr_accessor :example17
|
54
|
+
|
55
|
+
def example18(&block)
|
56
|
+
block
|
57
|
+
end
|
58
|
+
|
59
|
+
Example19 = lambda &method(:puts)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
# vim: number
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
puts "Testing with #{RUBY_VERSION}"
|
4
|
+
if Method.method_defined?(:source_location)
|
5
|
+
warn "using BUILTING source_location"
|
6
|
+
else
|
7
|
+
require "#{File.dirname(__FILE__)}/../ext/ruby18_source_location"
|
8
|
+
end
|
9
|
+
require "#{File.dirname(__FILE__)}/examples"
|
10
|
+
|
11
|
+
class TestRuby18SourceLocation < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def assert_on_line(line, method)
|
14
|
+
assert_equal [File.expand_path('./examples.rb'), line], method.source_location
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert_same_source(m1, m2)
|
18
|
+
assert_equal m1.source_location, m2.source_location
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_normal_method
|
22
|
+
assert_on_line 3, Example.instance_method(:example1)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_define_method_with_block
|
26
|
+
assert_on_line 7, Example.instance_method(:example2)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_proc_new
|
30
|
+
assert_on_line 11, Example::Example3
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_lambda
|
34
|
+
assert_on_line 15, Example::Example4
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_define_method_with_proc_arg
|
38
|
+
assert_same_source Example::Example3, Example.new.method(:example5)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_define_method_with_amp_proc
|
42
|
+
assert_same_source Example::Example4, Example.new.method(:example6)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_define_method_with_method_arg
|
46
|
+
assert_same_source Example.new.method(:example1), Example.new.method(:example7)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_define_method_with_amp_method
|
50
|
+
assert_same_source Example.new.method(:example2), Example.new.method(:example8)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_proc_with_amp_proc
|
54
|
+
assert_same_source Example::Example3, Example::Example9
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_proc_with_amp_method
|
58
|
+
assert_same_source Example.instance_method(:example1), Example::Example10
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_eval
|
62
|
+
assert_equal ['(eval)', 1], Example.instance_method(:example11).source_location
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_eval_with_overridden_line
|
66
|
+
assert_on_line 38, Example.new.method(:example12)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_class_method
|
70
|
+
assert_on_line 43, Example.method(:example13)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_alias
|
74
|
+
assert_same_source Example.new.method(:example1), Example.new.method(:example14)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_alias_method
|
78
|
+
assert_same_source Example.new.method(:example2), Example.new.method(:example15)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_c_method
|
82
|
+
assert_equal nil, method(:puts).source_location
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_attr_reader
|
86
|
+
assert_on_line 51, Example.instance_method(:example16)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_attr_accessor
|
90
|
+
assert_on_line 53, Example.instance_method(:example17)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_attr_assign
|
94
|
+
assert_on_line 53, Example.instance_method(:example17=)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_blocks
|
98
|
+
assert_equal [File.expand_path(__FILE__), __LINE__], Example.new.example18{
|
99
|
+
|
100
|
+
}.source_location
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_lambdas_with_c_methods
|
104
|
+
assert_equal nil, Example::Example19.source_location
|
105
|
+
end
|
106
|
+
end
|
metadata
CHANGED
@@ -1,65 +1,54 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby18_source_location
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
version: "0.1"
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Conrad Irwin
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
date: 2011-10-22 00:00:00 Z
|
12
|
+
date: 2011-10-23 00:00:00.000000000Z
|
18
13
|
dependencies: []
|
19
|
-
|
20
14
|
description: Allows you to make use of lots of ruby 1.9.2 specific goodness
|
21
15
|
email: conrad.irwin@gmail.com
|
22
16
|
executables: []
|
23
|
-
|
24
|
-
extensions:
|
17
|
+
extensions:
|
25
18
|
- ext/extconf.rb
|
26
19
|
extra_rdoc_files: []
|
27
|
-
|
28
|
-
|
29
|
-
-
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- LICENSE.MIT
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
30
25
|
- ext/extconf.rb
|
26
|
+
- ext/ruby18_source_location.c
|
27
|
+
- ruby18_source_location.gemspec
|
28
|
+
- test/examples.rb
|
29
|
+
- test/test_ruby18_source_location.rb
|
31
30
|
homepage: http://github.com/ConradIrwin/ruby18_source_location
|
32
31
|
licenses: []
|
33
|
-
|
34
32
|
post_install_message:
|
35
33
|
rdoc_options: []
|
36
|
-
|
37
|
-
require_paths:
|
34
|
+
require_paths:
|
38
35
|
- ext
|
39
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
37
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
45
|
-
|
46
|
-
- 0
|
47
|
-
version: "0"
|
48
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
43
|
none: false
|
50
|
-
requirements:
|
51
|
-
- -
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
|
54
|
-
segments:
|
55
|
-
- 0
|
56
|
-
version: "0"
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
57
48
|
requirements: []
|
58
|
-
|
59
49
|
rubyforge_project:
|
60
50
|
rubygems_version: 1.8.6
|
61
51
|
signing_key:
|
62
52
|
specification_version: 3
|
63
53
|
summary: Add .source_location to methods in Ruby 1.8.7
|
64
54
|
test_files: []
|
65
|
-
|