overrider 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -25
- data/lib/overrider.rb +110 -40
- data/lib/overrider/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d52cf24ae854860de785dbab4e6d9c2750290482f4691c902304fb9faed1a089
|
4
|
+
data.tar.gz: c1fb2a74aae5d70ac65790546808ae113e82ff3912307007835d8c582fa46108
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 196bb7a40efa56f4a8e0de5490b175e302fdfd39edc6f48d388367580eba66434abc65cc85d8b4a02d93bacfc6b950640e97019bf8a74c6049da2bcdb250150f
|
7
|
+
data.tar.gz: 9b441aa900a934993379f30f21caa95241221e96cdbf0b6bb1fe8b2dc4889db831bed33076ccc7591487fa41ffe1a7e4a86282382b3f34a174f4a425b58d1a6a
|
data/README.md
CHANGED
@@ -6,6 +6,9 @@ This gem adds `override` syntax that is similar to Java's one.
|
|
6
6
|
|
7
7
|
Unless the method has super method, this gem raise `Overrider::NoSuperMethodError`.
|
8
8
|
|
9
|
+
This gem is pseudo static code analyzer by `TracePoint` and `Ripper`.
|
10
|
+
it detect abstract violation when class(module) is defined, not runtime.
|
11
|
+
|
9
12
|
## Installation
|
10
13
|
|
11
14
|
Add this line to your application's Gemfile:
|
@@ -56,31 +59,6 @@ end # => raise
|
|
56
59
|
If you want to disable Overrider, write `Overrider.disable = true` at first line.
|
57
60
|
If Overrider is disabled, TracePoint never runs, and so there is no overhead of VM instruction.
|
58
61
|
|
59
|
-
### Caution
|
60
|
-
|
61
|
-
Must not call `override` outer class definition.
|
62
|
-
|
63
|
-
ex.
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
class A1
|
67
|
-
def foo
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class A2 < A1
|
72
|
-
extend Overrider
|
73
|
-
|
74
|
-
def foo
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
A2.send(:override, :foo)
|
79
|
-
```
|
80
|
-
|
81
|
-
This case leaves enabled TracePoint.
|
82
|
-
It is very high overhead.
|
83
|
-
|
84
62
|
### Examples
|
85
63
|
|
86
64
|
#### include module method after override method
|
data/lib/overrider.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "overrider/version"
|
2
|
-
require
|
2
|
+
require "set"
|
3
|
+
require "ripper"
|
3
4
|
|
4
5
|
module Overrider
|
5
6
|
class NoSuperMethodError < StandardError
|
@@ -12,6 +13,35 @@ module Overrider
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
16
|
+
class SexpTraverser
|
17
|
+
def initialize(sexp)
|
18
|
+
@sexp = sexp
|
19
|
+
end
|
20
|
+
|
21
|
+
def traverse(current_sexp = nil, parent = nil, &block)
|
22
|
+
sexp = current_sexp || @sexp
|
23
|
+
first = sexp[0]
|
24
|
+
if first.is_a?(Symbol) # node
|
25
|
+
yield sexp, parent
|
26
|
+
args = Ripper::PARSER_EVENT_TABLE[first]
|
27
|
+
return if args.nil? || args.zero?
|
28
|
+
|
29
|
+
args.times do |i|
|
30
|
+
param = sexp[i + 1]
|
31
|
+
if param.is_a?(Array)
|
32
|
+
traverse(param, sexp, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
else # array
|
36
|
+
sexp.each do |n|
|
37
|
+
if n.is_a?(Array)
|
38
|
+
traverse(n, sexp, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
15
45
|
@disable = false
|
16
46
|
|
17
47
|
def self.disable=(v)
|
@@ -26,90 +56,130 @@ module Overrider
|
|
26
56
|
!disabled?
|
27
57
|
end
|
28
58
|
|
29
|
-
|
59
|
+
def self.sexps
|
60
|
+
@sexps ||= {}
|
61
|
+
end
|
30
62
|
|
31
|
-
|
32
|
-
refine Module do
|
33
|
-
def detect_event_type
|
34
|
-
caller_info = caller_locations(2, 2)[0]
|
35
|
-
if caller_info.label.match?(/<class/) || caller_info.label.match?(/<module/)
|
36
|
-
[:end, :raise]
|
37
|
-
elsif caller_info.label.match?(/singleton class/)
|
38
|
-
[:end, :raise]
|
39
|
-
else
|
40
|
-
[:b_call, :b_return, :raise]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
}
|
63
|
+
private
|
45
64
|
|
46
65
|
def override(symbol)
|
47
66
|
return if Overrider.disabled?
|
48
67
|
|
49
|
-
|
68
|
+
@ensure_overrides ||= Set.new
|
69
|
+
@ensure_overrides.add(instance_method(symbol))
|
50
70
|
|
51
|
-
|
52
|
-
|
71
|
+
caller_info = caller_locations(1, 1)[0]
|
72
|
+
unless Overrider.sexps[caller_info.absolute_path]
|
73
|
+
Overrider.sexps[caller_info.absolute_path] ||= Ripper.sexp(File.read(caller_info.absolute_path))
|
74
|
+
end
|
75
|
+
|
76
|
+
@__overrider_trace_point ||= TracePoint.trace(:end, :c_return, :return, :return, :raise) do |t|
|
53
77
|
if t.event == :raise
|
54
78
|
@__overrider_trace_point.disable
|
55
79
|
@__overrider_trace_point = nil
|
56
80
|
next
|
57
81
|
end
|
58
82
|
|
59
|
-
block_count += 1 if t.event == :b_call
|
60
|
-
block_count -= 1 if t.event == :b_return
|
61
|
-
|
62
83
|
klass = t.self
|
63
|
-
|
64
|
-
|
65
|
-
|
84
|
+
|
85
|
+
target_outer_override = false
|
86
|
+
if t.event == :return && klass == self && t.method_id == :override
|
87
|
+
c = caller_locations(2, 1)[0]
|
88
|
+
traverser = SexpTraverser.new(Overrider.sexps[c.absolute_path])
|
89
|
+
traverser.traverse do |n, parent|
|
90
|
+
if n[0] == :@ident && n[1] == "override" && n[2][0] == c.lineno
|
91
|
+
if parent[0] == :command || parent[0] == :fcall
|
92
|
+
# override :foo
|
93
|
+
elsif parent[0] == :command_call || parent[0] == :call
|
94
|
+
if parent[1][0] == :var_ref && parent[1][1][0] == :@kw && parent[1][1][1] == "self"
|
95
|
+
# self.override :foo
|
96
|
+
else
|
97
|
+
# unknown case
|
98
|
+
target_outer_override = true
|
99
|
+
end
|
100
|
+
else
|
101
|
+
target_outer_override = true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
target_end_event = klass == self && t.event == :end
|
108
|
+
target_c_return_event = (klass == Class || klass == Module) && t.event == :c_return && t.method_id == :new
|
109
|
+
|
110
|
+
if target_end_event || target_c_return_event || target_outer_override
|
111
|
+
@ensure_overrides.each do |meth|
|
66
112
|
unless meth.super_method
|
67
113
|
@__overrider_trace_point.disable
|
68
114
|
@__overrider_trace_point = nil
|
69
|
-
raise NoSuperMethodError.new(
|
115
|
+
raise NoSuperMethodError.new(self, meth)
|
70
116
|
end
|
71
117
|
end
|
72
118
|
@__overrider_trace_point.disable
|
73
119
|
@__overrider_trace_point = nil
|
74
120
|
end
|
75
121
|
end
|
76
|
-
|
77
|
-
@ensure_overrides.add(symbol)
|
122
|
+
|
78
123
|
symbol
|
79
124
|
end
|
80
125
|
|
81
126
|
def override_singleton_method(symbol)
|
82
127
|
return if Overrider.disabled?
|
83
128
|
|
84
|
-
|
129
|
+
@ensure_overrides ||= Set.new
|
130
|
+
@ensure_overrides.add(singleton_class.instance_method(symbol))
|
85
131
|
|
86
|
-
|
87
|
-
|
132
|
+
caller_info = caller_locations(1, 1)[0]
|
133
|
+
unless Overrider.sexps[caller_info.absolute_path]
|
134
|
+
Overrider.sexps[caller_info.absolute_path] ||= Ripper.sexp(File.read(caller_info.absolute_path))
|
135
|
+
end
|
136
|
+
|
137
|
+
@__overrider_singleton_trace_point ||= TracePoint.trace(:end, :c_return, :return, :raise) do |t|
|
88
138
|
if t.event == :raise
|
89
139
|
@__overrider_singleton_trace_point.disable
|
90
140
|
@__overrider_singleton_trace_point = nil
|
91
141
|
next
|
92
142
|
end
|
93
143
|
|
94
|
-
block_count += 1 if t.event == :b_call
|
95
|
-
block_count -= 1 if t.event == :b_return
|
96
|
-
|
97
144
|
klass = t.self
|
98
|
-
|
99
|
-
|
100
|
-
|
145
|
+
|
146
|
+
target_outer_override_singleton_method = false
|
147
|
+
if t.event == :return && klass == self && t.method_id == :override_singleton_method
|
148
|
+
c = caller_locations(2, 1)[0]
|
149
|
+
traverser = SexpTraverser.new(Overrider.sexps[c.absolute_path])
|
150
|
+
traverser.traverse do |n, parent|
|
151
|
+
if n[0] == :@ident && n[1] == "override_singleton_method" && n[2][0] == c.lineno
|
152
|
+
if parent[0] == :command || parent[0] == :fcall
|
153
|
+
# override_singleton_method :foo
|
154
|
+
elsif parent[0] == :command_call || parent[0] == :call
|
155
|
+
if parent[1][0] == :var_ref && parent[1][1][0] == :@kw && parent[1][1][1] == "self"
|
156
|
+
# self.override_singleton_method :foo
|
157
|
+
else
|
158
|
+
# unknown case
|
159
|
+
target_outer_override_singleton_method = true
|
160
|
+
end
|
161
|
+
else
|
162
|
+
target_outer_override_singleton_method = true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
target_end_event = klass == self && t.event == :end
|
169
|
+
target_c_return_event = (klass == Class || klass == Module) && t.event == :c_return && t.method_id == :new
|
170
|
+
if target_end_event || target_c_return_event || target_outer_override_singleton_method
|
171
|
+
@ensure_overrides.each do |meth|
|
101
172
|
unless meth.super_method
|
102
173
|
@__overrider_singleton_trace_point.disable
|
103
174
|
@__overrider_singleton_trace_point = nil
|
104
|
-
raise NoSuperMethodError.new(
|
175
|
+
raise NoSuperMethodError.new(self, meth)
|
105
176
|
end
|
106
177
|
end
|
107
178
|
@__overrider_singleton_trace_point.disable
|
108
179
|
@__overrider_singleton_trace_point = nil
|
109
180
|
end
|
110
181
|
end
|
111
|
-
|
112
|
-
@ensure_overrides.add(symbol)
|
182
|
+
|
113
183
|
symbol
|
114
184
|
end
|
115
185
|
end
|
data/lib/overrider/version.rb
CHANGED