astrolabe 0.4.0 → 0.5.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: 9b3016609691bcb111bfe66a66ff77da7e533aab
4
- data.tar.gz: f3314ca723a831d03df024b7e5be1539a5483f76
3
+ metadata.gz: c17d30b9bee8cee926de9b4b2527950eb5c50763
4
+ data.tar.gz: 218e6135e0a8019b136651f4b92c0ca8648326a2
5
5
  SHA512:
6
- metadata.gz: 59af596675bb9815ee963ab92275f636034031c2ec1e245163af0a77b3602a82cdf01072191e958d1faa746d1248a7737fe27907eae741225858d172eaf6c854
7
- data.tar.gz: c200880804038aa30c87fe8d64f0c50d51823ef5c7c878d7bb3ad6d319238557dd47c2e8585e988efc0a18c8ba3f7a62b451739919ad5a95da1125bb0a4dc3d9
6
+ metadata.gz: 0e705c920bc838e45e1e0abc555e21582647c37f857e43ad36bea9057db9e9e50431c0e6eb6a9a57a7f271d8f0ec66bf45735907e1a03c159ec7016ddaf9d72b
7
+ data.tar.gz: 255f1615ab127be042ead82d9c363c06667c1e50caf9983920330313e2c9ac95fdcf09ff3b255449469a8b557e616fce7de0f02a6858b31463110ac7f4a0ceda
data/README.md CHANGED
@@ -90,18 +90,33 @@ if you don't need to track context of AST.
90
90
 
91
91
  ```ruby
92
92
  # Iterate ancestor nodes in the order from parent to root.
93
- node.each_ancestor do |ancestor_node|
94
- p ancestor_node
95
- end
93
+ node.each_ancestor { |ancestor_node| ... }
96
94
 
97
95
  # This is different from `node.children.each { |child| ... }`
98
96
  # which yields all children including non-node element.
99
- node.each_child_node do |child_node|
100
- p child_node
101
- end
102
-
103
- # Collect all lvar nodes under the receiver node.
104
- lvar_nodes = node.each_descendant.select(&:lvar_type?)
97
+ node.each_child_node { |child_node| ... }
98
+
99
+ # These iteration methods can be chained by Enumerable methods.
100
+ # Find the first lvar node under the receiver node.
101
+ lvar_node = node.each_descendant.find(&:lvar_type?)
102
+
103
+ # Iterate the receiver node itself and the descendant nodes.
104
+ # This would be useful when you treat the receiver node as a root of tree
105
+ # and want to iterate all nodes in the tree.
106
+ ast.each_node { |node| ... }
107
+
108
+ # Yield only specific type nodes.
109
+ ast.each_node(:send) { |send_node| ... }
110
+ # This is equivalent to:
111
+ ast.each_node.select(&:send_type?).each { |send_node| ... }
112
+
113
+ # Yield only nodes matching any of the types.
114
+ ast.each_node(:send, :block) { |send_or_block_node| ... }
115
+ ast.each_node([:send, :block]) { |send_or_block_node| ... }
116
+ # These are equivalent to:
117
+ ast.each_node
118
+ .select { |node| [:send, :block].include?(node.type) }
119
+ .each { |send_or_block_node| ... }
105
120
  ```
106
121
 
107
122
  ## Compatibility
@@ -67,9 +67,10 @@ describe 'performance' do
67
67
  def each_descendant
68
68
  return to_enum(__method__) unless block_given?
69
69
 
70
- each_child_node do |child_node|
71
- yield child_node
72
- child_node.each_descendant { |node| yield node }
70
+ children.each do |child|
71
+ next unless child.is_a?(self.class)
72
+ yield child
73
+ child.each_descendant { |node| yield node }
73
74
  end
74
75
  end
75
76
  end
@@ -82,9 +83,10 @@ describe 'performance' do
82
83
  def each_descendant(&block)
83
84
  return to_enum(__method__) unless block_given?
84
85
 
85
- each_child_node do |child_node|
86
- yield child_node
87
- child_node.each_descendant(&block)
86
+ children.each do |child|
87
+ next unless child.is_a?(self.class)
88
+ yield child
89
+ child.each_descendant(&block)
88
90
  end
89
91
  end
90
92
  end
@@ -100,9 +102,10 @@ describe 'performance' do
100
102
 
101
103
  proc_object ||= block
102
104
 
103
- each_child_node do |child_node|
104
- proc_object.call(child_node)
105
- child_node.each_descendant(proc_object)
105
+ children.each do |child|
106
+ next unless child.is_a?(self.class)
107
+ proc_object.call(child)
108
+ child.each_descendant(proc_object)
106
109
  end
107
110
  end
108
111
  end
@@ -16,8 +16,8 @@ module Astrolabe
16
16
  # # Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
17
17
  # node.defined_type? # Equivalent to: `node.type == :defined?`
18
18
  #
19
- # # Collect all lvar nodes under the receiver node.
20
- # lvar_nodes = node.each_descendant.select(&:lvar_type?)
19
+ # # Find the first lvar node under the receiver node.
20
+ # lvar_node = node.each_descendant.find(&:lvar_type?)
21
21
  class Node < Parser::AST::Node
22
22
  # @see http://rubydoc.info/gems/ast/AST/Node:initialize
23
23
  def initialize(type, children = [], properties = {})
@@ -66,16 +66,29 @@ module Astrolabe
66
66
  # Calls the given block for each ancestor node in the order from parent to root.
67
67
  # If no block is given, an `Enumerator` is returned.
68
68
  #
69
+ # @overload each_ancestor
70
+ # Yield all nodes.
71
+ # @overload each_ancestor(type)
72
+ # Yield only nodes matching the type.
73
+ # @param [Symbol] type a node type
74
+ # @overload each_ancestor(type_a, type_b, ...)
75
+ # Yield only nodes matching any of the types.
76
+ # @param [Symbol] type_a a node type
77
+ # @param [Symbol] type_b a node type
78
+ # @overload each_ancestor(types)
79
+ # Yield only nodes matching any of types in the array.
80
+ # @param [Array<Symbol>] types an array containing node types
69
81
  # @yieldparam [Node] node each ancestor node
70
82
  # @return [self] if a block is given
71
83
  # @return [Enumerator] if no block is given
72
- def each_ancestor
84
+ def each_ancestor(*types)
73
85
  return to_enum(__method__) unless block_given?
74
86
 
87
+ types.flatten!
75
88
  last_node = self
76
89
 
77
90
  while (current_node = last_node.parent)
78
- yield current_node
91
+ yield current_node if types.empty? || types.include?(current_node.type)
79
92
  last_node = current_node
80
93
  end
81
94
 
@@ -88,15 +101,29 @@ module Astrolabe
88
101
  # Note that this is different from `node.children.each { |child| ... }` which yields all
89
102
  # children including non-node element.
90
103
  #
104
+ # @overload each_child_node
105
+ # Yield all nodes.
106
+ # @overload each_child_node(type)
107
+ # Yield only nodes matching the type.
108
+ # @param [Symbol] type a node type
109
+ # @overload each_child_node(type_a, type_b, ...)
110
+ # Yield only nodes matching any of the types.
111
+ # @param [Symbol] type_a a node type
112
+ # @param [Symbol] type_b a node type
113
+ # @overload each_child_node(types)
114
+ # Yield only nodes matching any of types in the array.
115
+ # @param [Array<Symbol>] types an array containing node types
91
116
  # @yieldparam [Node] node each child node
92
117
  # @return [self] if a block is given
93
118
  # @return [Enumerator] if no block is given
94
- def each_child_node
119
+ def each_child_node(*types)
95
120
  return to_enum(__method__) unless block_given?
96
121
 
122
+ types.flatten!
123
+
97
124
  children.each do |child|
98
125
  next unless child.is_a?(Node)
99
- yield child
126
+ yield child if types.empty? || types.include?(child.type)
100
127
  end
101
128
 
102
129
  self
@@ -105,12 +132,25 @@ module Astrolabe
105
132
  # Calls the given block for each descendant node with depth first order.
106
133
  # If no block is given, an `Enumerator` is returned.
107
134
  #
135
+ # @overload each_descendant
136
+ # Yield all nodes.
137
+ # @overload each_descendant(type)
138
+ # Yield only nodes matching the type.
139
+ # @param [Symbol] type a node type
140
+ # @overload each_descendant(type_a, type_b, ...)
141
+ # Yield only nodes matching any of the types.
142
+ # @param [Symbol] type_a a node type
143
+ # @param [Symbol] type_b a node type
144
+ # @overload each_descendant(types)
145
+ # Yield only nodes matching any of types in the array.
146
+ # @param [Array<Symbol>] types an array containing node types
108
147
  # @yieldparam [Node] node each descendant node
109
148
  # @return [self] if a block is given
110
149
  # @return [Enumerator] if no block is given
111
- def each_descendant(&block)
150
+ def each_descendant(*types, &block)
112
151
  return to_enum(__method__) unless block_given?
113
- visit_descendants(&block)
152
+ types.flatten!
153
+ visit_descendants(types, &block)
114
154
  self
115
155
  end
116
156
 
@@ -120,22 +160,36 @@ module Astrolabe
120
160
  # This method would be useful when you treat the receiver node as a root of tree and want to
121
161
  # iterate all nodes in the tree.
122
162
  #
163
+ # @overload each_node
164
+ # Yield all nodes.
165
+ # @overload each_node(type)
166
+ # Yield only nodes matching the type.
167
+ # @param [Symbol] type a node type
168
+ # @overload each_node(type_a, type_b, ...)
169
+ # Yield only nodes matching any of the types.
170
+ # @param [Symbol] type_a a node type
171
+ # @param [Symbol] type_b a node type
172
+ # @overload each_node(types)
173
+ # Yield only nodes matching any of types in the array.
174
+ # @param [Array<Symbol>] types an array containing node types
123
175
  # @yieldparam [Node] node each node
124
176
  # @return [self] if a block is given
125
177
  # @return [Enumerator] if no block is given
126
- def each_node(&block)
178
+ def each_node(*types, &block)
127
179
  return to_enum(__method__) unless block_given?
128
- yield self
129
- each_descendant(&block)
180
+ types.flatten!
181
+ yield self if types.empty? || types.include?(type)
182
+ visit_descendants(types, &block)
183
+ self
130
184
  end
131
185
 
132
186
  protected
133
187
 
134
- def visit_descendants(&block)
188
+ def visit_descendants(types, &block)
135
189
  children.each do |child|
136
190
  next unless child.is_a?(Node)
137
- yield child
138
- child.visit_descendants(&block)
191
+ yield child if types.empty? || types.include?(child.type)
192
+ child.visit_descendants(types, &block)
139
193
  end
140
194
  end
141
195
  end
@@ -5,7 +5,7 @@ module Astrolabe
5
5
  # http://semver.org/
6
6
  module Version
7
7
  MAJOR = 0
8
- MINOR = 4
8
+ MINOR = 5
9
9
  PATCH = 0
10
10
 
11
11
  def self.to_s
@@ -52,6 +52,26 @@ module Astrolabe
52
52
  end
53
53
  end
54
54
 
55
+ shared_examples 'node enumerator' do |method_name|
56
+ context 'when no block is given' do
57
+ it 'returns an enumerator' do
58
+ expect(target_node.send(method_name)).to be_a Enumerator
59
+ end
60
+
61
+ describe 'the returned enumerator' do
62
+ subject(:enumerator) { target_node.send(method_name) }
63
+
64
+ it 'enumerates ancestor nodes' do
65
+ expected_types.each do |expected_type|
66
+ expect(enumerator.next.type).to eq(expected_type)
67
+ end
68
+
69
+ expect { enumerator.next }.to raise_error(StopIteration)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
55
75
  describe '#each_ancestor' do
56
76
  let(:source) { <<-END }
57
77
  class SomeClass
@@ -94,21 +114,41 @@ module Astrolabe
94
114
  end
95
115
  end
96
116
 
97
- context 'when no block is given' do
98
- it 'returns an enumerator' do
99
- expect(target_node.each_ancestor).to be_a Enumerator
117
+ include_examples 'node enumerator', :each_ancestor
118
+
119
+ context 'when a node type symbol is passed' do
120
+ it 'scans all the ancestor nodes but yields only nodes matching the type' do
121
+ yielded_types = []
122
+
123
+ target_node.each_ancestor(:begin) do |node|
124
+ yielded_types << node.type
125
+ end
126
+
127
+ expect(yielded_types).to eq([:begin])
100
128
  end
129
+ end
101
130
 
102
- describe 'the returned enumerator' do
103
- subject(:enumerator) { target_node.each_ancestor }
131
+ context 'when multiple node type symbols are passed' do
132
+ it 'scans all the ancestor nodes but yields only nodes matching any of the types' do
133
+ yielded_types = []
104
134
 
105
- it 'enumerates ancestor nodes' do
106
- expected_types.each do |expected_type|
107
- expect(enumerator.next.type).to eq(expected_type)
108
- end
135
+ target_node.each_ancestor(:begin, :def) do |node|
136
+ yielded_types << node.type
137
+ end
109
138
 
110
- expect { enumerator.next }.to raise_error(StopIteration)
139
+ expect(yielded_types).to eq([:def, :begin])
140
+ end
141
+ end
142
+
143
+ context 'when an array including type symbols are passed' do
144
+ it 'scans all the ancestor nodes but yields only nodes matching any of the types' do
145
+ yielded_types = []
146
+
147
+ target_node.each_ancestor([:begin, :def]) do |node|
148
+ yielded_types << node.type
111
149
  end
150
+
151
+ expect(yielded_types).to eq([:def, :begin])
112
152
  end
113
153
  end
114
154
  end
@@ -146,21 +186,41 @@ module Astrolabe
146
186
  end
147
187
  end
148
188
 
149
- context 'when no block is given' do
150
- it 'returns an enumerator' do
151
- expect(target_node.each_child_node).to be_a(Enumerator)
189
+ include_examples 'node enumerator', :each_child_node
190
+
191
+ context 'when a node type symbol is passed' do
192
+ it 'scans all the child nodes but yields only nodes matching the type' do
193
+ yielded_types = []
194
+
195
+ target_node.each_child_node(:send) do |node|
196
+ yielded_types << node.type
197
+ end
198
+
199
+ expect(yielded_types).to eq([:send])
152
200
  end
201
+ end
153
202
 
154
- describe 'the returned enumerator' do
155
- subject(:enumerator) { target_node.each_child_node }
203
+ context 'when multiple node type symbols are passed' do
204
+ it 'scans all the child nodes but yields only nodes matching any of the types' do
205
+ yielded_types = []
156
206
 
157
- it 'enumerates the child nodes' do
158
- expected_types.each do |expected_type|
159
- expect(enumerator.next.type).to eq(expected_type)
160
- end
207
+ target_node.each_child_node(:send, :args) do |node|
208
+ yielded_types << node.type
209
+ end
161
210
 
162
- expect { enumerator.next }.to raise_error(StopIteration)
211
+ expect(yielded_types).to eq([:args, :send])
212
+ end
213
+ end
214
+
215
+ context 'when an array including type symbols are passed' do
216
+ it 'scans all the child nodes but yields only nodes matching any of the types' do
217
+ yielded_types = []
218
+
219
+ target_node.each_child_node([:send, :args]) do |node|
220
+ yielded_types << node.type
163
221
  end
222
+
223
+ expect(yielded_types).to eq([:args, :send])
164
224
  end
165
225
  end
166
226
  end
@@ -207,21 +267,41 @@ module Astrolabe
207
267
  end
208
268
  end
209
269
 
210
- context 'when no block is given' do
211
- it 'returns an enumerator' do
212
- expect(target_node.each_descendant).to be_a(Enumerator)
270
+ include_examples 'node enumerator', :each_descendant
271
+
272
+ context 'when a node type symbol is passed' do
273
+ it 'scans all the descendant nodes but yields only nodes matching the type' do
274
+ yielded_types = []
275
+
276
+ target_node.each_descendant(:send) do |node|
277
+ yielded_types << node.type
278
+ end
279
+
280
+ expect(yielded_types).to eq([:send, :send])
213
281
  end
282
+ end
214
283
 
215
- describe 'the returned enumerator' do
216
- subject(:enumerator) { target_node.each_descendant }
284
+ context 'when multiple node type symbols are passed' do
285
+ it 'scans all the descendant nodes but yields only nodes matching any of the types' do
286
+ yielded_types = []
217
287
 
218
- it 'enumerates the descendant nodes' do
219
- expected_types.each do |expected_type|
220
- expect(enumerator.next.type).to eq(expected_type)
221
- end
288
+ target_node.each_descendant(:send, :def) do |node|
289
+ yielded_types << node.type
290
+ end
222
291
 
223
- expect { enumerator.next }.to raise_error(StopIteration)
292
+ expect(yielded_types).to eq([:send, :def, :send])
293
+ end
294
+ end
295
+
296
+ context 'when an array including type symbols are passed' do
297
+ it 'scans all the descendant nodes but yields only nodes matching any of the types' do
298
+ yielded_types = []
299
+
300
+ target_node.each_descendant([:send, :def]) do |node|
301
+ yielded_types << node.type
224
302
  end
303
+
304
+ expect(yielded_types).to eq([:send, :def, :send])
225
305
  end
226
306
  end
227
307
  end
@@ -252,7 +332,7 @@ module Astrolabe
252
332
  let(:expected_types) { [:class, :const, :begin, :send, :sym, :def, :args, :arg, :arg, :send] }
253
333
 
254
334
  context 'when a block is given' do
255
- it 'yields itself and each descendant node with depth first order' do
335
+ it 'yields the node itself and each descendant node with depth first order' do
256
336
  yielded_types = []
257
337
 
258
338
  target_node.each_node do |node|
@@ -268,21 +348,41 @@ module Astrolabe
268
348
  end
269
349
  end
270
350
 
271
- context 'when no block is given' do
272
- it 'returns an enumerator' do
273
- expect(target_node.each_node).to be_a(Enumerator)
351
+ include_examples 'node enumerator', :each_node
352
+
353
+ context 'when a node type symbol is passed' do
354
+ it 'scans all the nodes but yields only nodes matching the type' do
355
+ yielded_types = []
356
+
357
+ target_node.each_node(:send) do |node|
358
+ yielded_types << node.type
359
+ end
360
+
361
+ expect(yielded_types).to eq([:send, :send])
274
362
  end
363
+ end
275
364
 
276
- describe 'the returned enumerator' do
277
- subject(:enumerator) { target_node.each_node }
365
+ context 'when multiple node type symbols are passed' do
366
+ it 'scans all the nodes but yields only nodes matching any of the types' do
367
+ yielded_types = []
278
368
 
279
- it 'enumerates the origin and the descendant nodes' do
280
- expected_types.each do |expected_type|
281
- expect(enumerator.next.type).to eq(expected_type)
282
- end
369
+ target_node.each_node(:send, :def) do |node|
370
+ yielded_types << node.type
371
+ end
283
372
 
284
- expect { enumerator.next }.to raise_error(StopIteration)
373
+ expect(yielded_types).to eq([:send, :def, :send])
374
+ end
375
+ end
376
+
377
+ context 'when an array including type symbols are passed' do
378
+ it 'scans all the nodes but yields only nodes matching any of the types' do
379
+ yielded_types = []
380
+
381
+ target_node.each_node([:send, :def]) do |node|
382
+ yielded_types << node.type
285
383
  end
384
+
385
+ expect(yielded_types).to eq([:send, :def, :send])
286
386
  end
287
387
  end
288
388
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: astrolabe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuji Nakayama