astrolabe 0.4.0 → 0.5.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: 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