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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa1fe37f86bbf786414a8e6f5190599bf5ee1e54
4
- data.tar.gz: 818ff996234d1fb5400afdd9740c777ee7a3a691
3
+ metadata.gz: 53fe528849d65b7f8eb9b66f09d247396cfd4e2a
4
+ data.tar.gz: e7df07ddf60b80af2e487dfeb31caccba9b8d993
5
5
  SHA512:
6
- metadata.gz: 4f12b89003300b961506a631fadb4981877dd49e012866ca742dc5af0f86a71aa2db00c2835939e21da96c95af55b4f71d2dcda55eb5ebb5b038cb32c158bfdd
7
- data.tar.gz: 4efb84e75b9fb0c313596004b757d78cf40356c8aaae3b8a1d470eabd7f9ef4c92f9e856352ec7e1535db3fad8f0322958a245c9580506a710e129bea5a0eaed
6
+ metadata.gz: 062e20ab65705cb6a0eff85b6c574ae7e50b6add1e5093dbe96423fec012ae87e99217893193f421655d49de323fef7f09d061a8858c40e86acf1933e41aa90f
7
+ data.tar.gz: 3621636399af83a884659e1ffdf2063b3e4770897b74762095d1929fbca132a82f1a73d16d29d815c060ff49765c20085dc349d3a1bba023e54f06dd00773093
data/CHANGELOG.md CHANGED
@@ -1,15 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.5.3
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
- # Parser::AST::Node monkey patch.
2
- class Parser::AST::Node
3
- # Get name node of :class, :module, :def and :defs node.
4
- #
5
- # @return [Parser::AST::Node] name node.
6
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
7
- def name
8
- case self.type
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
- # Get parent_class node of :class node.
19
- #
20
- # @return [Parser::AST::Node] parent_class node.
21
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
22
- def parent_class
23
- if :class == self.type
24
- self.children[1]
25
- else
26
- raise Synvert::Core::MethodNotSupported.new "parent_class is not handled for #{self.inspect}"
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
- # Get receiver node of :send node.
31
- #
32
- # @return [Parser::AST::Node] receiver node.
33
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
34
- def receiver
35
- if :send == self.type
36
- self.children[0]
37
- else
38
- raise Synvert::Core::MethodNotSupported.new "receiver is not handled for #{self.inspect}"
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
- # Get message node of :send node.
43
- #
44
- # @return [Parser::AST::Node] mesage node.
45
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
46
- def message
47
- if :send == self.type
48
- self.children[1]
49
- else
50
- raise Synvert::Core::MethodNotSupported.new "message is not handled for #{self.inspect}"
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
- # Get arguments node of :send, :block or :defined? node.
55
- #
56
- # @return [Array<Parser::AST::Node>] arguments node.
57
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
58
- def arguments
59
- case self.type
60
- when :send
61
- self.children[2..-1]
62
- when :block
63
- self.children[1].children
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
- # Get caller node of :block node.
72
- #
73
- # @return [Parser::AST::Node] caller node.
74
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
75
- def caller
76
- if :block == self.type
77
- self.children[0]
78
- else
79
- raise Synvert::Core::MethodNotSupported.new "caller is not handled for #{self.inspect}"
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
- # Get body node of :begin or :block node.
84
- #
85
- # @return [Array<Parser::AST::Node>] body node.
86
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
87
- def body
88
- case self.type
89
- when :begin
90
- self.children
91
- when :def, :block
92
- return [] if self.children[2].nil?
93
- :begin == self.children[2].type ? self.children[2].body : self.children[2..-1]
94
- when :defs
95
- return [] if self.children[3].nil?
96
- :begin == self.children[3].type ? self.children[3].body : self.children[3..-1]
97
- else
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
- # Get condition node of :if node.
103
- #
104
- # @return [Parser::AST::Node] condition node.
105
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
106
- def condition
107
- if :if == self.type
108
- self.children[0]
109
- else
110
- raise Synvert::Core::MethodNotSupported.new "condition is not handled for #{self.inspect}"
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
- # Get keys node of :hash node.
115
- #
116
- # @return [Array<Parser::AST::Node>] keys node.
117
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
118
- def keys
119
- if :hash == self.type
120
- self.children.map { |child| child.children[0] }
121
- else
122
- raise Synvert::Core::MethodNotSupported.new "keys is not handled for #{self.inspect}"
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
- # Get values node of :hash node.
127
- #
128
- # @return [Array<Parser::AST::Node>] values node.
129
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
130
- def values
131
- if :hash == self.type
132
- self.children.map { |child| child.children[1] }
133
- else
134
- raise Synvert::Core::MethodNotSupported.new "keys is not handled for #{self.inspect}"
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
- # Test if hash node contains specified key.
139
- #
140
- # @param [Object] key value.
141
- # @return [Boolean] true if specified key exists.
142
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
143
- def has_key?(key)
144
- if :hash == self.type
145
- self.children.any? { |pair_node| pair_node.key.to_value == key }
146
- else
147
- raise Synvert::Core::MethodNotSupported.new "has_key? is not handled for #{self.inspect}"
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
- # Get hash value node according to specified key.
152
- #
153
- # @param [Object] key value.
154
- # @return [Parser::AST::Node] value node.
155
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
156
- def hash_value(key)
157
- if :hash == self.type
158
- value_node = self.children.find { |pair_node| pair_node.key.to_value == key }
159
- value_node ? value_node.value : nil
160
- else
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
- # Get key node of hash :pair node.
166
- #
167
- # @return [Parser::AST::Node] key node.
168
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
169
- def key
170
- if :pair == self.type
171
- self.children.first
172
- else
173
- raise Synvert::Core::MethodNotSupported.new "key is not handled for #{self.inspect}"
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
- # Get value node of hash :pair node.
178
- #
179
- # @return [Parser::AST::Node] value node.
180
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
181
- def value
182
- if :pair == self.type
183
- self.children.last
184
- else
185
- raise Synvert::Core::MethodNotSupported.new "value is not handled for #{self.inspect}"
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
- # Return the exact value.
190
- #
191
- # @return [Object] exact value.
192
- # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
193
- def to_value
194
- case self.type
195
- when :int, :str, :sym
196
- self.children.last
197
- when :true
198
- true
199
- when :false
200
- false
201
- when :array
202
- self.children.map(&:to_value)
203
- when :irange
204
- (self.children.first.to_value..self.children.last.to_value)
205
- when :begin
206
- self.children.first.to_value
207
- else
208
- raise Synvert::Core::MethodNotSupported.new "to_value is not handled for #{self.inspect}"
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
- # Get the source code of current node.
213
- #
214
- # @return [String] source code.
215
- def to_source
216
- if self.loc.expression
217
- self.loc.expression.source
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
- # Get the indent of current node.
222
- #
223
- # @return [Integer] indent.
224
- def indent
225
- self.loc.expression.column
226
- end
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
- # Recursively iterate all child nodes of current node.
229
- #
230
- # @yield [child] Gives a child node.
231
- # @yieldparam child [Parser::AST::Node] child node
232
- def recursive_children
233
- self.children.each do |child|
234
- if Parser::AST::Node === child
235
- yield child
236
- child.recursive_children { |c| yield c }
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
- # Match current node with rules.
242
- #
243
- # @param rules [Hash] rules to match.
244
- # @return true if matches.
245
- def match?(rules)
246
- flat_hash(rules).keys.all? do |multi_keys|
247
- if multi_keys.last == :any
248
- actual_values = actual_value(self, multi_keys[0...-1])
249
- expected = expected_value(rules, multi_keys)
250
- actual_values.any? { |actual| match_value?(actual, expected) }
251
- elsif multi_keys.last == :not
252
- actual = actual_value(self, multi_keys[0...-1])
253
- expected = expected_value(rules, multi_keys)
254
- !match_value?(actual, expected)
255
- else
256
- actual = actual_value(self, multi_keys)
257
- expected = expected_value(rules, multi_keys)
258
- match_value?(actual, expected)
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
- # Get rewritten source code.
264
- # @example
265
- # node.rewritten_source("create({{arguments}})") #=> "create(:post)"
266
- #
267
- # @param code [String] raw code.
268
- # @return [String] rewritten code, replace string in block {{ }} in raw code.
269
- # @raise [Synvert::Core::MethodNotSupported] if string in block {{ }} does not support.
270
- def rewritten_source(code)
271
- code.gsub(/{{(.*?)}}/m) do
272
- old_code = $1
273
- if self.respond_to? old_code.split(/\.|\[/).first
274
- evaluated = self.instance_eval old_code
275
- case evaluated
276
- when Parser::AST::Node
277
- evaluated.loc.expression.source
278
- when Array
279
- if evaluated.size > 0
280
- source = evaluated.first.loc.expression.source_buffer.source
281
- source[evaluated.first.loc.expression.begin_pos...evaluated.last.loc.expression.end_pos]
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
- raise Synvert::Core::MethodNotSupported.new "rewritten_source is not handled for #{evaluated.inspect}"
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
- # Compare actual value with expected value.
299
- #
300
- # @param actual [Object] actual value.
301
- # @param expected [Object] expected value.
302
- # @return [Integer] -1, 0 or 1.
303
- # @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
304
- def match_value?(actual, expected)
305
- case expected
306
- when Symbol
307
- if Parser::AST::Node === actual
308
- actual.to_source == ":#{expected}"
309
- else
310
- actual.to_sym == expected
311
- end
312
- when String
313
- if Parser::AST::Node === actual
314
- actual.to_source == expected || actual.to_source[1...-1] == expected
315
- else
316
- actual.to_s == expected
317
- end
318
- when Regexp
319
- if Parser::AST::Node === actual
320
- actual.to_source =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
321
- else
322
- actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
323
- end
324
- when Array
325
- return false unless expected.length == actual.length
326
- actual.zip(expected).all? { |a, e| match_value?(a, e) }
327
- when NilClass
328
- actual.nil?
329
- when Numeric
330
- if Parser::AST::Node === actual
331
- actual.children[0] == expected
332
- else
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
- # Convert a hash to flat one.
347
- #
348
- # @example
349
- # flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
350
- # #=> {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
351
- # @param h [Hash] original hash.
352
- # @return flatten hash.
353
- def flat_hash(h, k = [])
354
- new_hash = {}
355
- h.each_pair do |key, val|
356
- if val.is_a?(Hash)
357
- new_hash.merge!(flat_hash(val, k + [key]))
358
- else
359
- new_hash[k + [key]] = val
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
- # Get actual value from the node.
366
- #
367
- # @param node [Parser::AST::Node]
368
- # @param multi_keys [Array<Symbol>]
369
- # @return [Object] actual value.
370
- def actual_value(node, multi_keys)
371
- multi_keys.inject(node) { |n, key|
372
- if n
373
- key == :source ? n.send(key) : n.send(key)
374
- end
375
- }
376
- end
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
- # Get expected value from rules.
379
- #
380
- # @param rules [Hash]
381
- # @param multi_keys [Array<Symbol>]
382
- # @return [Object] expected value.
383
- def expected_value(rules, multi_keys)
384
- multi_keys.inject(rules) { |o, key| o[key] }
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::Scope] to find matching ast nodes,
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::Scope.new(self, rules, &block).process
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 the child nodes which match rules.
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 for each matching node.
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = "0.5.3"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
@@ -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::Scope).to receive(:new).with(instance, type: 'send', message: 'create', &block).and_return(scope)
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::Scope).to receive(:new).with(instance, type: 'send', message: 'create', &block).and_return(scope)
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::Scope do
5
- let(:source) {
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
- before :each do
9
- @user = FactoryGirl.create :user
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
- let(:instance) { double(:current_node => node, :current_node= => node) }
21
- before { allow(instance).to receive(:process_with_node).and_yield }
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::Scope.new instance, type: 'send', message: 'missing' do
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
- scope = Rewriter::Scope.new instance, type: 'send', receiver: 'FactoryGirl', message: 'create', arguments: [':post'] do
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.5.3
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: