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