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 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