overrider 0.1.3 → 0.1.4
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 +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