synvert-core 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -8
- data/lib/synvert/core/node_ext.rb +354 -329
- data/lib/synvert/core/rewriter.rb +2 -0
- data/lib/synvert/core/rewriter/instance.rb +11 -2
- data/lib/synvert/core/rewriter/scope.rb +31 -3
- data/lib/synvert/core/version.rb +1 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +10 -2
- data/spec/synvert/core/rewriter/scope_spec.rb +50 -16
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53fe528849d65b7f8eb9b66f09d247396cfd4e2a
|
4
|
+
data.tar.gz: e7df07ddf60b80af2e487dfeb31caccba9b8d993
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 062e20ab65705cb6a0eff85b6c574ae7e50b6add1e5093dbe96423fec012ae87e99217893193f421655d49de323fef7f09d061a8858c40e86acf1933e41aa90f
|
7
|
+
data.tar.gz: 3621636399af83a884659e1ffdf2063b3e4770897b74762095d1929fbca132a82f1a73d16d29d815c060ff49765c20085dc349d3a1bba023e54f06dd00773093
|
data/CHANGELOG.md
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
## 0.
|
4
|
-
|
5
|
-
* Raise RewriterNotFound if rewriter not found
|
6
|
-
|
7
|
-
## 0.5.2
|
3
|
+
## 0.6.0 (2014-09-01)
|
8
4
|
|
5
|
+
* Add goto_node dsl
|
6
|
+
* Add ArgumentsNode to handle args both as a node and as an array
|
9
7
|
* Add body for :defs node
|
10
|
-
|
11
|
-
## 0.5.1
|
12
|
-
|
8
|
+
* Raise RewriterNotFound if rewriter not found
|
13
9
|
* Remove Rewriter::Instance class methods current and current_source
|
14
10
|
|
15
11
|
## 0.5.0 (2014-08-21)
|
@@ -1,386 +1,411 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
when :class, :module, :def
|
10
|
-
self.children[0]
|
11
|
-
when :defs
|
12
|
-
self.children[1]
|
13
|
-
else
|
14
|
-
raise Synvert::Core::MethodNotSupported.new "name is not handled for #{self.inspect}"
|
1
|
+
module Parser::AST
|
2
|
+
# ArgumentsNode allows to handle all args as one node or handle all args as an array.
|
3
|
+
class ArgumentsNode
|
4
|
+
# Initialize
|
5
|
+
#
|
6
|
+
# @param node [Parser::AST::Node] args node.
|
7
|
+
def initialize(node)
|
8
|
+
@node = node
|
15
9
|
end
|
16
|
-
end
|
17
10
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
11
|
+
# If args node responds method itself, call method on args node.
|
12
|
+
# If args children (array) responds method, call method on args children.
|
13
|
+
# Otherwise raise method missing error.
|
14
|
+
def method_missing(meth, *args, &block)
|
15
|
+
if @node.respond_to?(meth)
|
16
|
+
@node.send meth, *args, &block
|
17
|
+
elsif @node.children.respond_to?(meth)
|
18
|
+
@node.children.send meth, *args, &block
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
27
22
|
end
|
28
23
|
end
|
29
24
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
if
|
36
|
-
|
37
|
-
|
38
|
-
|
25
|
+
# Parser::AST::Node monkey patch.
|
26
|
+
class Node
|
27
|
+
# Get name node of :class, :module, :def and :defs node.
|
28
|
+
#
|
29
|
+
# @return [Parser::AST::Node] name node.
|
30
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
31
|
+
def name
|
32
|
+
case self.type
|
33
|
+
when :class, :module, :def
|
34
|
+
self.children[0]
|
35
|
+
when :defs
|
36
|
+
self.children[1]
|
37
|
+
else
|
38
|
+
raise Synvert::Core::MethodNotSupported.new "name is not handled for #{self.inspect}"
|
39
|
+
end
|
39
40
|
end
|
40
|
-
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
# Get parent_class node of :class node.
|
43
|
+
#
|
44
|
+
# @return [Parser::AST::Node] parent_class node.
|
45
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
46
|
+
def parent_class
|
47
|
+
if :class == self.type
|
48
|
+
self.children[1]
|
49
|
+
else
|
50
|
+
raise Synvert::Core::MethodNotSupported.new "parent_class is not handled for #{self.inspect}"
|
51
|
+
end
|
51
52
|
end
|
52
|
-
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
when :defined?
|
65
|
-
self.children
|
66
|
-
else
|
67
|
-
raise Synvert::Core::MethodNotSupported.new "arguments is not handled for #{self.inspect}"
|
54
|
+
# Get receiver node of :send node.
|
55
|
+
#
|
56
|
+
# @return [Parser::AST::Node] receiver node.
|
57
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
58
|
+
def receiver
|
59
|
+
if :send == self.type
|
60
|
+
self.children[0]
|
61
|
+
else
|
62
|
+
raise Synvert::Core::MethodNotSupported.new "receiver is not handled for #{self.inspect}"
|
63
|
+
end
|
68
64
|
end
|
69
|
-
end
|
70
65
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
66
|
+
# Get message node of :send node.
|
67
|
+
#
|
68
|
+
# @return [Parser::AST::Node] mesage node.
|
69
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
70
|
+
def message
|
71
|
+
if :send == self.type
|
72
|
+
self.children[1]
|
73
|
+
else
|
74
|
+
raise Synvert::Core::MethodNotSupported.new "message is not handled for #{self.inspect}"
|
75
|
+
end
|
80
76
|
end
|
81
|
-
end
|
82
77
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
:
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
raise Synvert::Core::MethodNotSupported.new "body is not handled for #{self.inspect}"
|
78
|
+
# Get arguments node of :send, :block or :defined? node.
|
79
|
+
#
|
80
|
+
# @return [Array<Parser::AST::Node>] arguments node.
|
81
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
82
|
+
def arguments
|
83
|
+
case self.type
|
84
|
+
when :send
|
85
|
+
self.children[2..-1]
|
86
|
+
when :block
|
87
|
+
ArgumentsNode.new self.children[1]
|
88
|
+
when :defined?
|
89
|
+
self.children
|
90
|
+
else
|
91
|
+
raise Synvert::Core::MethodNotSupported.new "arguments is not handled for #{self.inspect}"
|
92
|
+
end
|
99
93
|
end
|
100
|
-
end
|
101
94
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
95
|
+
# Get caller node of :block node.
|
96
|
+
#
|
97
|
+
# @return [Parser::AST::Node] caller node.
|
98
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
99
|
+
def caller
|
100
|
+
if :block == self.type
|
101
|
+
self.children[0]
|
102
|
+
else
|
103
|
+
raise Synvert::Core::MethodNotSupported.new "caller is not handled for #{self.inspect}"
|
104
|
+
end
|
111
105
|
end
|
112
|
-
end
|
113
106
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
107
|
+
# Get body node of :begin or :block node.
|
108
|
+
#
|
109
|
+
# @return [Array<Parser::AST::Node>] body node.
|
110
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
111
|
+
def body
|
112
|
+
case self.type
|
113
|
+
when :begin
|
114
|
+
self.children
|
115
|
+
when :def, :block
|
116
|
+
return [] if self.children[2].nil?
|
117
|
+
:begin == self.children[2].type ? self.children[2].body : self.children[2..-1]
|
118
|
+
when :defs
|
119
|
+
return [] if self.children[3].nil?
|
120
|
+
:begin == self.children[3].type ? self.children[3].body : self.children[3..-1]
|
121
|
+
else
|
122
|
+
raise Synvert::Core::MethodNotSupported.new "body is not handled for #{self.inspect}"
|
123
|
+
end
|
123
124
|
end
|
124
|
-
end
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
126
|
+
# Get condition node of :if node.
|
127
|
+
#
|
128
|
+
# @return [Parser::AST::Node] condition node.
|
129
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
130
|
+
def condition
|
131
|
+
if :if == self.type
|
132
|
+
self.children[0]
|
133
|
+
else
|
134
|
+
raise Synvert::Core::MethodNotSupported.new "condition is not handled for #{self.inspect}"
|
135
|
+
end
|
135
136
|
end
|
136
|
-
end
|
137
137
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
138
|
+
# Get keys node of :hash node.
|
139
|
+
#
|
140
|
+
# @return [Array<Parser::AST::Node>] keys node.
|
141
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
142
|
+
def keys
|
143
|
+
if :hash == self.type
|
144
|
+
self.children.map { |child| child.children[0] }
|
145
|
+
else
|
146
|
+
raise Synvert::Core::MethodNotSupported.new "keys is not handled for #{self.inspect}"
|
147
|
+
end
|
148
148
|
end
|
149
|
-
end
|
150
149
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
raise Synvert::Core::MethodNotSupported.new "has_key? is not handled for #{self.inspect}"
|
150
|
+
# Get values node of :hash node.
|
151
|
+
#
|
152
|
+
# @return [Array<Parser::AST::Node>] values node.
|
153
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
154
|
+
def values
|
155
|
+
if :hash == self.type
|
156
|
+
self.children.map { |child| child.children[1] }
|
157
|
+
else
|
158
|
+
raise Synvert::Core::MethodNotSupported.new "keys is not handled for #{self.inspect}"
|
159
|
+
end
|
162
160
|
end
|
163
|
-
end
|
164
161
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
self.
|
172
|
-
|
173
|
-
|
162
|
+
# Test if hash node contains specified key.
|
163
|
+
#
|
164
|
+
# @param [Object] key value.
|
165
|
+
# @return [Boolean] true if specified key exists.
|
166
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
167
|
+
def has_key?(key)
|
168
|
+
if :hash == self.type
|
169
|
+
self.children.any? { |pair_node| pair_node.key.to_value == key }
|
170
|
+
else
|
171
|
+
raise Synvert::Core::MethodNotSupported.new "has_key? is not handled for #{self.inspect}"
|
172
|
+
end
|
174
173
|
end
|
175
|
-
end
|
176
174
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
self.
|
184
|
-
|
185
|
-
|
175
|
+
# Get hash value node according to specified key.
|
176
|
+
#
|
177
|
+
# @param [Object] key value.
|
178
|
+
# @return [Parser::AST::Node] value node.
|
179
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
180
|
+
def hash_value(key)
|
181
|
+
if :hash == self.type
|
182
|
+
value_node = self.children.find { |pair_node| pair_node.key.to_value == key }
|
183
|
+
value_node ? value_node.value : nil
|
184
|
+
else
|
185
|
+
raise Synvert::Core::MethodNotSupported.new "has_key? is not handled for #{self.inspect}"
|
186
|
+
end
|
186
187
|
end
|
187
|
-
end
|
188
188
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
self.
|
207
|
-
|
208
|
-
|
189
|
+
# Get key node of hash :pair node.
|
190
|
+
#
|
191
|
+
# @return [Parser::AST::Node] key node.
|
192
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
193
|
+
def key
|
194
|
+
if :pair == self.type
|
195
|
+
self.children.first
|
196
|
+
else
|
197
|
+
raise Synvert::Core::MethodNotSupported.new "key is not handled for #{self.inspect}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Get value node of hash :pair node.
|
202
|
+
#
|
203
|
+
# @return [Parser::AST::Node] value node.
|
204
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
205
|
+
def value
|
206
|
+
if :pair == self.type
|
207
|
+
self.children.last
|
208
|
+
else
|
209
|
+
raise Synvert::Core::MethodNotSupported.new "value is not handled for #{self.inspect}"
|
210
|
+
end
|
209
211
|
end
|
210
|
-
end
|
211
212
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
self.
|
213
|
+
# Return the exact value.
|
214
|
+
#
|
215
|
+
# @return [Object] exact value.
|
216
|
+
# @raise [Synvert::Core::MethodNotSupported] if calls on other node.
|
217
|
+
def to_value
|
218
|
+
case self.type
|
219
|
+
when :int, :str, :sym
|
220
|
+
self.children.last
|
221
|
+
when :true
|
222
|
+
true
|
223
|
+
when :false
|
224
|
+
false
|
225
|
+
when :array
|
226
|
+
self.children.map(&:to_value)
|
227
|
+
when :irange
|
228
|
+
(self.children.first.to_value..self.children.last.to_value)
|
229
|
+
when :begin
|
230
|
+
self.children.first.to_value
|
231
|
+
else
|
232
|
+
raise Synvert::Core::MethodNotSupported.new "to_value is not handled for #{self.inspect}"
|
233
|
+
end
|
218
234
|
end
|
219
|
-
end
|
220
235
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
236
|
+
# Get the source code of current node.
|
237
|
+
#
|
238
|
+
# @return [String] source code.
|
239
|
+
def to_source
|
240
|
+
if self.loc.expression
|
241
|
+
self.loc.expression.source
|
242
|
+
end
|
243
|
+
end
|
227
244
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
245
|
+
# Get the indent of current node.
|
246
|
+
#
|
247
|
+
# @return [Integer] indent.
|
248
|
+
def indent
|
249
|
+
self.loc.expression.column
|
250
|
+
end
|
251
|
+
|
252
|
+
# Recursively iterate all child nodes of current node.
|
253
|
+
#
|
254
|
+
# @yield [child] Gives a child node.
|
255
|
+
# @yieldparam child [Parser::AST::Node] child node
|
256
|
+
def recursive_children
|
257
|
+
self.children.each do |child|
|
258
|
+
if Parser::AST::Node === child
|
259
|
+
yield child
|
260
|
+
child.recursive_children { |c| yield c }
|
261
|
+
end
|
237
262
|
end
|
238
263
|
end
|
239
|
-
end
|
240
264
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
265
|
+
# Match current node with rules.
|
266
|
+
#
|
267
|
+
# @param rules [Hash] rules to match.
|
268
|
+
# @return true if matches.
|
269
|
+
def match?(rules)
|
270
|
+
flat_hash(rules).keys.all? do |multi_keys|
|
271
|
+
if multi_keys.last == :any
|
272
|
+
actual_values = actual_value(self, multi_keys[0...-1])
|
273
|
+
expected = expected_value(rules, multi_keys)
|
274
|
+
actual_values.any? { |actual| match_value?(actual, expected) }
|
275
|
+
elsif multi_keys.last == :not
|
276
|
+
actual = actual_value(self, multi_keys[0...-1])
|
277
|
+
expected = expected_value(rules, multi_keys)
|
278
|
+
!match_value?(actual, expected)
|
279
|
+
else
|
280
|
+
actual = actual_value(self, multi_keys)
|
281
|
+
expected = expected_value(rules, multi_keys)
|
282
|
+
match_value?(actual, expected)
|
283
|
+
end
|
259
284
|
end
|
260
285
|
end
|
261
|
-
end
|
262
286
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
287
|
+
# Get rewritten source code.
|
288
|
+
# @example
|
289
|
+
# node.rewritten_source("create({{arguments}})") #=> "create(:post)"
|
290
|
+
#
|
291
|
+
# @param code [String] raw code.
|
292
|
+
# @return [String] rewritten code, replace string in block {{ }} in raw code.
|
293
|
+
# @raise [Synvert::Core::MethodNotSupported] if string in block {{ }} does not support.
|
294
|
+
def rewritten_source(code)
|
295
|
+
code.gsub(/{{(.*?)}}/m) do
|
296
|
+
old_code = $1
|
297
|
+
if self.respond_to? old_code.split(/\.|\[/).first
|
298
|
+
evaluated = self.instance_eval old_code
|
299
|
+
case evaluated
|
300
|
+
when Parser::AST::Node
|
301
|
+
evaluated.loc.expression.source
|
302
|
+
when Array
|
303
|
+
if evaluated.size > 0
|
304
|
+
source = evaluated.first.loc.expression.source_buffer.source
|
305
|
+
source[evaluated.first.loc.expression.begin_pos...evaluated.last.loc.expression.end_pos]
|
306
|
+
end
|
307
|
+
when String, Symbol
|
308
|
+
evaluated
|
309
|
+
when NilClass
|
310
|
+
'nil'
|
311
|
+
else
|
312
|
+
raise Synvert::Core::MethodNotSupported.new "rewritten_source is not handled for #{evaluated.inspect}"
|
282
313
|
end
|
283
|
-
when String, Symbol
|
284
|
-
evaluated
|
285
|
-
when NilClass
|
286
|
-
'nil'
|
287
314
|
else
|
288
|
-
|
315
|
+
"{{#{old_code}}}"
|
289
316
|
end
|
290
|
-
else
|
291
|
-
"{{#{old_code}}}"
|
292
317
|
end
|
293
318
|
end
|
294
|
-
end
|
295
319
|
|
296
|
-
private
|
320
|
+
private
|
297
321
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
322
|
+
# Compare actual value with expected value.
|
323
|
+
#
|
324
|
+
# @param actual [Object] actual value.
|
325
|
+
# @param expected [Object] expected value.
|
326
|
+
# @return [Integer] -1, 0 or 1.
|
327
|
+
# @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
|
328
|
+
def match_value?(actual, expected)
|
329
|
+
case expected
|
330
|
+
when Symbol
|
331
|
+
if Parser::AST::Node === actual
|
332
|
+
actual.to_source == ":#{expected}"
|
333
|
+
else
|
334
|
+
actual.to_sym == expected
|
335
|
+
end
|
336
|
+
when String
|
337
|
+
if Parser::AST::Node === actual
|
338
|
+
actual.to_source == expected || actual.to_source[1...-1] == expected
|
339
|
+
else
|
340
|
+
actual.to_s == expected
|
341
|
+
end
|
342
|
+
when Regexp
|
343
|
+
if Parser::AST::Node === actual
|
344
|
+
actual.to_source =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
345
|
+
else
|
346
|
+
actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
347
|
+
end
|
348
|
+
when Array
|
349
|
+
return false unless expected.length == actual.length
|
350
|
+
actual.zip(expected).all? { |a, e| match_value?(a, e) }
|
351
|
+
when NilClass
|
352
|
+
actual.nil?
|
353
|
+
when Numeric
|
354
|
+
if Parser::AST::Node === actual
|
355
|
+
actual.children[0] == expected
|
356
|
+
else
|
357
|
+
actual == expected
|
358
|
+
end
|
359
|
+
when TrueClass
|
360
|
+
:true == actual.type
|
361
|
+
when FalseClass
|
362
|
+
:false == actual.type
|
363
|
+
when Parser::AST::Node
|
333
364
|
actual == expected
|
365
|
+
else
|
366
|
+
raise Synvert::Core::MethodNotSupported.new "#{expected.class} is not handled for match_value?"
|
334
367
|
end
|
335
|
-
when TrueClass
|
336
|
-
:true == actual.type
|
337
|
-
when FalseClass
|
338
|
-
:false == actual.type
|
339
|
-
when Parser::AST::Node
|
340
|
-
actual == expected
|
341
|
-
else
|
342
|
-
raise Synvert::Core::MethodNotSupported.new "#{expected.class} is not handled for match_value?"
|
343
368
|
end
|
344
|
-
end
|
345
369
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
370
|
+
# Convert a hash to flat one.
|
371
|
+
#
|
372
|
+
# @example
|
373
|
+
# flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
|
374
|
+
# #=> {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
|
375
|
+
# @param h [Hash] original hash.
|
376
|
+
# @return flatten hash.
|
377
|
+
def flat_hash(h, k = [])
|
378
|
+
new_hash = {}
|
379
|
+
h.each_pair do |key, val|
|
380
|
+
if val.is_a?(Hash)
|
381
|
+
new_hash.merge!(flat_hash(val, k + [key]))
|
382
|
+
else
|
383
|
+
new_hash[k + [key]] = val
|
384
|
+
end
|
360
385
|
end
|
386
|
+
new_hash
|
361
387
|
end
|
362
|
-
new_hash
|
363
|
-
end
|
364
388
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
389
|
+
# Get actual value from the node.
|
390
|
+
#
|
391
|
+
# @param node [Parser::AST::Node]
|
392
|
+
# @param multi_keys [Array<Symbol>]
|
393
|
+
# @return [Object] actual value.
|
394
|
+
def actual_value(node, multi_keys)
|
395
|
+
multi_keys.inject(node) { |n, key|
|
396
|
+
if n
|
397
|
+
key == :source ? n.send(key) : n.send(key)
|
398
|
+
end
|
399
|
+
}
|
400
|
+
end
|
377
401
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
402
|
+
# Get expected value from rules.
|
403
|
+
#
|
404
|
+
# @param rules [Hash]
|
405
|
+
# @param multi_keys [Array<Symbol>]
|
406
|
+
# @return [Object] expected value.
|
407
|
+
def expected_value(rules, multi_keys)
|
408
|
+
multi_keys.inject(rules) { |o, key| o[key] }
|
409
|
+
end
|
385
410
|
end
|
386
411
|
end
|
@@ -29,6 +29,8 @@ module Synvert::Core
|
|
29
29
|
autoload :Instance, 'synvert/core/rewriter/instance'
|
30
30
|
|
31
31
|
autoload :Scope, 'synvert/core/rewriter/scope'
|
32
|
+
autoload :WithinScope, 'synvert/core/rewriter/scope'
|
33
|
+
autoload :GotoScope, 'synvert/core/rewriter/scope'
|
32
34
|
|
33
35
|
autoload :Condition, 'synvert/core/rewriter/condition'
|
34
36
|
autoload :IfExistCondition, 'synvert/core/rewriter/condition'
|
@@ -123,13 +123,22 @@ module Synvert::Core
|
|
123
123
|
# DSL #
|
124
124
|
#######
|
125
125
|
|
126
|
-
# Parse within_node dsl, it creates a [Synvert::Core::Rewriter::
|
126
|
+
# Parse within_node dsl, it creates a [Synvert::Core::Rewriter::WithinScope] to find matching ast nodes,
|
127
127
|
# then continue operating on each matching ast node.
|
128
128
|
#
|
129
129
|
# @param rules [Hash] rules to find mathing ast nodes.
|
130
130
|
# @param block [Block] block code to continue operating on the matching nodes.
|
131
131
|
def within_node(rules, &block)
|
132
|
-
Rewriter::
|
132
|
+
Rewriter::WithinScope.new(self, rules, &block).process
|
133
|
+
end
|
134
|
+
|
135
|
+
# Parse goto_node dsl, it creates a [Synvert::Core::Rewriter::GotoScope] to go to a child node,
|
136
|
+
# then continue operating on the child node.
|
137
|
+
#
|
138
|
+
# @param child_node_name [String] the name of the child node.
|
139
|
+
# @param block [Block] block code to continue operating on the matching nodes.
|
140
|
+
def goto_node(child_node_name, &block)
|
141
|
+
Rewriter::GotoScope.new(self, child_node_name, &block).process
|
133
142
|
end
|
134
143
|
|
135
144
|
alias with_node within_node
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
|
-
# Scope finds
|
4
|
+
# Scope finds out nodes which match rules.
|
5
5
|
class Rewriter::Scope
|
6
|
+
end
|
7
|
+
|
8
|
+
# WithinScope finds out nodes which match rules, then change its scope to matching node.
|
9
|
+
class Rewriter::WithinScope < Rewriter::Scope
|
6
10
|
# Initialize a scope
|
7
11
|
#
|
8
12
|
# @param instance [Synvert::Core::Rewriter::Instance]
|
@@ -14,8 +18,8 @@ module Synvert::Core
|
|
14
18
|
@block = block
|
15
19
|
end
|
16
20
|
|
17
|
-
# Find the matching nodes. It checks the current node and iterates all child nodes,
|
18
|
-
# then run the block code
|
21
|
+
# Find out the matching nodes. It checks the current node and iterates all child nodes,
|
22
|
+
# then run the block code with each matching node.
|
19
23
|
def process
|
20
24
|
current_node = @instance.current_node
|
21
25
|
return unless current_node
|
@@ -33,4 +37,28 @@ module Synvert::Core
|
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
40
|
+
|
41
|
+
# Go to and change its scope to a child node.
|
42
|
+
class Rewriter::GotoScope < Rewriter::Scope
|
43
|
+
# Initialize a scope
|
44
|
+
#
|
45
|
+
# @param instance [Synvert::Core::Rewriter::Instance]
|
46
|
+
# @param child_node_name [String]
|
47
|
+
# @param block [Block]
|
48
|
+
def initialize(instance, child_node_name, &block)
|
49
|
+
@instance = instance
|
50
|
+
@child_node_name = child_node_name
|
51
|
+
@block = block
|
52
|
+
end
|
53
|
+
|
54
|
+
# Go to a child now, then run the block code with the the child node.
|
55
|
+
def process
|
56
|
+
current_node = @instance.current_node
|
57
|
+
return unless current_node
|
58
|
+
child_node = current_node.send @child_node_name
|
59
|
+
@instance.process_with_other_node child_node do
|
60
|
+
@instance.instance_eval &@block
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
36
64
|
end
|
data/lib/synvert/core/version.rb
CHANGED
@@ -12,7 +12,7 @@ module Synvert::Core
|
|
12
12
|
it 'parses within_node' do
|
13
13
|
scope = double()
|
14
14
|
block = Proc.new {}
|
15
|
-
expect(Rewriter::
|
15
|
+
expect(Rewriter::WithinScope).to receive(:new).with(instance, type: 'send', message: 'create', &block).and_return(scope)
|
16
16
|
expect(scope).to receive(:process)
|
17
17
|
instance.within_node(type: 'send', message: 'create', &block)
|
18
18
|
end
|
@@ -20,11 +20,19 @@ module Synvert::Core
|
|
20
20
|
it 'parses with_node' do
|
21
21
|
scope = double()
|
22
22
|
block = Proc.new {}
|
23
|
-
expect(Rewriter::
|
23
|
+
expect(Rewriter::WithinScope).to receive(:new).with(instance, type: 'send', message: 'create', &block).and_return(scope)
|
24
24
|
expect(scope).to receive(:process)
|
25
25
|
instance.with_node(type: 'send', message: 'create', &block)
|
26
26
|
end
|
27
27
|
|
28
|
+
it 'parses goto_node' do
|
29
|
+
scope = double()
|
30
|
+
block = Proc.new {}
|
31
|
+
expect(Rewriter::GotoScope).to receive(:new).with(instance, :caller, &block).and_return(scope)
|
32
|
+
expect(scope).to receive(:process)
|
33
|
+
instance.goto_node(:caller, &block)
|
34
|
+
end
|
35
|
+
|
28
36
|
it 'parses if_exist_node' do
|
29
37
|
condition = double()
|
30
38
|
block = Proc.new {}
|
@@ -1,29 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
|
-
describe Rewriter::
|
5
|
-
let(:
|
6
|
-
|
4
|
+
describe Rewriter::WithinScope do
|
5
|
+
let(:instance) {
|
6
|
+
rewriter = Rewriter.new('foo', 'bar')
|
7
|
+
Rewriter::Instance.new(rewriter, 'file pattern')
|
8
|
+
}
|
9
|
+
let(:source) {"""
|
7
10
|
describe Post do
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'gets posts' do
|
13
|
-
post1 = FactoryGirl.create :post
|
14
|
-
post2 = FactoryGirl.create :post
|
11
|
+
it 'gets post' do
|
12
|
+
FactoryGirl.create :post
|
15
13
|
end
|
16
14
|
end
|
17
|
-
"""
|
18
|
-
}
|
15
|
+
"""}
|
19
16
|
let(:node) { Parser::CurrentRuby.parse(source) }
|
20
|
-
|
21
|
-
|
17
|
+
before do
|
18
|
+
Rewriter::Instance.reset
|
19
|
+
instance.current_node = node
|
20
|
+
end
|
22
21
|
|
23
22
|
describe '#process' do
|
24
23
|
it 'not call block if no matching node' do
|
25
24
|
run = false
|
26
|
-
scope = Rewriter::
|
25
|
+
scope = Rewriter::WithinScope.new instance, type: 'send', message: 'missing' do
|
27
26
|
run = true
|
28
27
|
end
|
29
28
|
scope.process
|
@@ -32,11 +31,46 @@ end
|
|
32
31
|
|
33
32
|
it 'call block if there is matching node' do
|
34
33
|
run = false
|
35
|
-
|
34
|
+
type_in_scope = nil
|
35
|
+
scope = Rewriter::WithinScope.new instance, type: 'send', receiver: 'FactoryGirl', message: 'create', arguments: [':post'] do
|
36
|
+
run = true
|
37
|
+
type_in_scope = node.type
|
38
|
+
end
|
39
|
+
scope.process
|
40
|
+
expect(run).to be_truthy
|
41
|
+
expect(type_in_scope).to eq :send
|
42
|
+
expect(instance.current_node.type).to eq :block
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe Rewriter::GotoScope do
|
48
|
+
let(:instance) {
|
49
|
+
rewriter = Rewriter.new('foo', 'bar')
|
50
|
+
Rewriter::Instance.new(rewriter, 'file pattern')
|
51
|
+
}
|
52
|
+
let(:source) {'''
|
53
|
+
Factory.define :user do |user|
|
54
|
+
end
|
55
|
+
'''}
|
56
|
+
let(:node) { Parser::CurrentRuby.parse(source) }
|
57
|
+
before do
|
58
|
+
Rewriter::Instance.reset
|
59
|
+
instance.current_node = node
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#process' do
|
63
|
+
it 'call block with child node' do
|
64
|
+
run = false
|
65
|
+
type_in_scope = nil
|
66
|
+
scope = Rewriter::GotoScope.new instance, :caller do
|
36
67
|
run = true
|
68
|
+
type_in_scope = node.type
|
37
69
|
end
|
38
70
|
scope.process
|
39
71
|
expect(run).to be_truthy
|
72
|
+
expect(type_in_scope).to eq :send
|
73
|
+
expect(instance.current_node.type).to eq :block
|
40
74
|
end
|
41
75
|
end
|
42
76
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synvert-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
@@ -176,3 +176,4 @@ test_files:
|
|
176
176
|
- spec/synvert/core/rewriter/scope_spec.rb
|
177
177
|
- spec/synvert/core/rewriter/warning_spec.rb
|
178
178
|
- spec/synvert/core/rewriter_spec.rb
|
179
|
+
has_rdoc:
|