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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f5f6427ceb87b0ec35bc2cd671ee20adec487cec6d5b360194ef010700fe8b7
4
- data.tar.gz: f060e48860d1e11560fefb8e7008b16d99e7c44d11389802b6004243aacd399b
3
+ metadata.gz: d52cf24ae854860de785dbab4e6d9c2750290482f4691c902304fb9faed1a089
4
+ data.tar.gz: c1fb2a74aae5d70ac65790546808ae113e82ff3912307007835d8c582fa46108
5
5
  SHA512:
6
- metadata.gz: f8ed5bf14feef878b73f1be4153badae266baed307def1c95440092dc2120df56e3bbc45dd4de47fa3bb805effb67aa7998b554f5c5cd5a02318b7611079e5f7
7
- data.tar.gz: 05a8c4c1dc1c2b7e91ba36a3b0f134a879dec3d688940fae35aea12e5f3e7119ff1fedfd47a60335a56a8e76da87f4b9fe55fa336fdc7669ef78c6e924725992
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 'set'
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
- private
59
+ def self.sexps
60
+ @sexps ||= {}
61
+ end
30
62
 
31
- using Module.new {
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
- event_type = detect_event_type
68
+ @ensure_overrides ||= Set.new
69
+ @ensure_overrides.add(instance_method(symbol))
50
70
 
51
- block_count = 1
52
- @__overrider_trace_point ||= TracePoint.trace(*event_type) do |t|
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
- if klass == self && (t.event == :end || t.event == :b_return && block_count.zero?)
64
- @ensure_overrides.each do |n|
65
- meth = klass.instance_method(n)
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(klass, meth)
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
- @ensure_overrides ||= Set.new
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
- event_type = detect_event_type
129
+ @ensure_overrides ||= Set.new
130
+ @ensure_overrides.add(singleton_class.instance_method(symbol))
85
131
 
86
- block_count = 1
87
- @__overrider_singleton_trace_point ||= TracePoint.trace(*event_type) do |t|
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
- if klass == self && (t.event == :end || t.event == :b_return && block_count.zero?)
99
- @ensure_overrides.each do |n|
100
- meth = klass.singleton_class.instance_method(n)
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(klass, meth)
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
- @ensure_overrides ||= Set.new
112
- @ensure_overrides.add(symbol)
182
+
113
183
  symbol
114
184
  end
115
185
  end
@@ -1,3 +1,3 @@
1
1
  module Overrider
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: overrider
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - joker1007