synvert-core 0.5.3 → 0.6.0
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/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:
|