ruby18_source_location 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ *.so
3
+ *.o
4
+ .rvmrc
5
+ ext/Makefile
@@ -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.
@@ -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.
@@ -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
- typedef struct RNode {
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
- source_pair(const char *filename, int lineno)
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
- return array;
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
- return source_pair(filename, lineno);
65
+ if (filename && lineno) {
66
+ VALUE str = rb_str_new2(filename);
95
67
 
96
- return Qnil;
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
- if (node = data->body) {
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
- * meth.source_location => [String, Fixnum]
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
- if ((node = data->frame.node) || (node = data->body)) {
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
- void
142
- Init_ruby18_source_location()
143
- {
144
- rb_define_method(rb_cUnboundMethod, "source_location", method_source_location, 0);
145
- rb_define_method(rb_cMethod, "source_location", method_source_location, 0);
146
- rb_define_method(rb_cProc, "source_location", proc_source_location, 0);
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
@@ -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
- hash: 9
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
- files:
29
- - ext/ruby18_source_location.c
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
- hash: 3
45
- segments:
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
- hash: 3
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
-