astrolabe 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 46c2d6149556152034a1102c6a17a722068b7d05
4
+ data.tar.gz: 89ff885e03757862cd9d5345bfae9127cd5665c0
5
+ SHA512:
6
+ metadata.gz: b76199cb0b7abcab33a57187876733b87eac1e88f0c1db2f96954b99c13b2f707481bfdb71778eac70c02e180bc65a35508b11dceadc46c9bc41aa3be28a7839
7
+ data.tar.gz: 3cee02d4725f5ed4a98a3b7ba49818855480a2cdcbd241855dabeb37dea5fe6aaa697544ff8da485f91e5acd9433d24d41a57f435ef9ad9db73b32740a1bdbe0
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,29 @@
1
+
2
+ LineLength:
3
+ Max: 100
4
+
5
+ AlignHash:
6
+ # Alignment of entries using hash rocket as separator. Valid values are:
7
+ #
8
+ # key - left alignment of keys
9
+ # 'a' => 2
10
+ # 'bb' => 3
11
+ # separator - alignment of hash rockets, keys are right aligned
12
+ # 'a' => 2
13
+ # 'bb' => 3
14
+ # table - left alignment of keys, hash rockets, and values
15
+ # 'a' => 2
16
+ # 'bb' => 3
17
+ EnforcedHashRocketStyle: table
18
+ # Alignment of entries using colon as separator. Valid values are:
19
+ #
20
+ # key - left alignment of keys
21
+ # a: 0
22
+ # bb: 1
23
+ # separator - alignment of colons, keys are right aligned
24
+ # a: 0
25
+ # bb: 1
26
+ # table - left alignment of keys and values
27
+ # a: 0
28
+ # bb: 1
29
+ EnforcedColonStyle: separator
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1
6
+ - ruby-head
7
+ - jruby
8
+ - rbx-2
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: ruby-head
12
+ before_install: gem update --remote bundler
13
+ script: bundle exec rake ci
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --markup markdown
2
+ --hide-api private
3
+ -
4
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # coding: utf-8
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :test do
8
+ gem 'coveralls', '~> 0.7'
9
+ end
data/Guardfile ADDED
@@ -0,0 +1,16 @@
1
+ # coding: utf-8
2
+
3
+ # This group allows Guard to skip running RuboCop if RSpec failed.
4
+ group :red_green_refactor, halt_on_fail: true do
5
+ guard :rspec, all_on_start: true, cmd: 'bundle exec rspec --format Fuubar' do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
8
+ watch('spec/spec_helper.rb') { 'spec' }
9
+ watch(%r{^spec/support/.+\.rb$}) { 'spec' }
10
+ end
11
+
12
+ guard :rubocop, cli: '--format fuubar' do
13
+ watch(%r{.+\.rb$})
14
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
15
+ end
16
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Yuji Nakayama
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ [![Gem Version](http://img.shields.io/gem/v/astrolabe.svg)](http://badge.fury.io/rb/astrolabe)
2
+ [![Dependency Status](http://img.shields.io/gemnasium/yujinakayama/astrolabe.svg)](https://gemnasium.com/yujinakayama/astrolabe)
3
+ [![Build Status](https://travis-ci.org/yujinakayama/astrolabe.svg?branch=master)](https://travis-ci.org/yujinakayama/astrolabe)
4
+ [![Coverage Status](http://img.shields.io/coveralls/yujinakayama/astrolabe/master.svg)](https://coveralls.io/r/yujinakayama/astrolabe)
5
+ [![Code Climate](http://img.shields.io/codeclimate/github/yujinakayama/astrolabe.svg)](https://codeclimate.com/github/yujinakayama/astrolabe)
6
+
7
+ # Astrolabe
8
+
9
+ **Astrolabe** is an AST node library that provides an object-oriented way to handle AST by extending [Parser](https://github.com/whitequark/parser)'s node class.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your `Gemfile`:
14
+
15
+ ```ruby
16
+ gem 'astrolabe'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ ```bash
22
+ $ bundle install
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ You can generate an AST that consists of `Astrolabe::Node` by using `Astrolabe::Builder` along with `Parser`:
28
+
29
+ ```ruby
30
+ require 'astrolabe/builder'
31
+ require 'parser/current'
32
+
33
+ buffer = Parser::Source::Buffer.new('(string)')
34
+ buffer.source = 'puts :foo'
35
+
36
+ builder = Astrolabe::Builder.new
37
+ parser = Parser::CurrentRuby.new(builder)
38
+
39
+ root_node = parser.parse(buffer)
40
+ root_node.class # => Astrolabe::Node
41
+ ```
42
+
43
+ `Astrolabe::Node` is a subclass of [`Parser::AST::Node`](http://rubydoc.info/gems/parser/Parser/AST/Node).
44
+
45
+ ## APIs
46
+
47
+ See these references for all the public APIs:
48
+
49
+ * [`Astrolabe::Node`](http://rubydoc.info/gems/astrolabe/Astrolabe/Node)
50
+ * [`Astrolabe::Builder`](http://rubydoc.info/gems/astrolabe/Astrolabe/Builder)
51
+
52
+ ### Node Type Predicate Methods
53
+
54
+ These would be useful especially when combined with `Enumerable` methods (described below).
55
+
56
+ ```ruby
57
+ node.send_type? # Equivalent to: `node.type == :send`
58
+ node.op_asgn_type? # Equivalent to: `node.type == :op_asgn`
59
+
60
+ # Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
61
+ node.defined_type? # Equivalent to: `node.type == :defined?`
62
+ ```
63
+
64
+ ### Access to Parent Node
65
+
66
+ ```ruby
67
+ def method_taking_block?(node)
68
+ return unless node.parent.block_type?
69
+ node.parent.children.first.equal?(node)
70
+ end
71
+
72
+ block_node = parser.parse(buffer)
73
+ # (block
74
+ # (send
75
+ # (int 3) :times)
76
+ # (args
77
+ # (arg :i))
78
+ # (send nil :do_something))
79
+
80
+ send_node, args_node, body_node = *block_node
81
+ method_taking_block?(send_node) # => true
82
+ ```
83
+
84
+ ### AST Traversal
85
+
86
+ These methods bring the power of `Enumerable` to AST.
87
+
88
+ Note that you may want to use [`Parser::AST::Processor`](http://rubydoc.info/gems/parser/Parser/AST/Processor)
89
+ if you don't need to track context of AST.
90
+
91
+ ```ruby
92
+ # Iterate ancestor nodes in the order from parent to root.
93
+ node.each_ancestor do |ancestor_node|
94
+ p ancestor_node
95
+ end
96
+
97
+ # This is different from `node.children.each { |child| ... }`
98
+ # 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?)
105
+ ```
106
+
107
+ ## Compatibility
108
+
109
+ Tested on MRI 1.9, 2.0, 2.1, JRuby in 1.9 mode and Rubinius 2.0+.
110
+
111
+ ## License
112
+
113
+ Copyright (c) 2014 Yuji Nakayama
114
+
115
+ See the [LICENSE.txt](LICENSE.txt) for details.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new(:style)
9
+
10
+ task default: %w(spec style)
11
+ task ci: %w(spec style)
data/astrolabe.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'astrolabe/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'astrolabe'
9
+ spec.version = Astrolabe::Version.to_s
10
+ spec.authors = ['Yuji Nakayama']
11
+ spec.email = ['nkymyj@gmail.com']
12
+ spec.summary = 'Object-oriented AST library for Parser'
13
+ spec.description = spec.summary
14
+ spec.homepage = 'https://github.com/yujinakayama/astrolabe'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(/^spec\//)
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_runtime_dependency 'parser', '~> 2.1'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake', '~> 10.3'
26
+ spec.add_development_dependency 'yard', '~> 0.8'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'fuubar', '~> 2.0.0.rc1'
29
+ spec.add_development_dependency 'simplecov', '~> 0.7'
30
+ spec.add_development_dependency 'rubocop', '~> 0.24'
31
+ spec.add_development_dependency 'guard-rspec', '>= 4.2.3', '< 5.0'
32
+ spec.add_development_dependency 'guard-rubocop', '~> 1.0'
33
+ spec.add_development_dependency 'ruby_gntp', '~> 0.3'
34
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+
3
+ require 'astrolabe/node'
4
+ require 'parser'
5
+
6
+ module Astrolabe
7
+ # `Astrolabe::Builder` is an AST builder that is utilized to let `Parser` generate AST with
8
+ # {Astrolabe::Node}.
9
+ #
10
+ # @example
11
+ # require 'astrolabe/builder'
12
+ # require 'parser/current'
13
+ #
14
+ # buffer = Parser::Source::Buffer.new('(string)')
15
+ # buffer.source = 'puts :foo'
16
+ #
17
+ # builder = Astrolabe::Builder.new
18
+ # parser = Parser::CurrentRuby.new(builder)
19
+ # root_node = parser.parse(buffer)
20
+ class Builder < Parser::Builders::Default
21
+ # Generates {Node} from the given information.
22
+ #
23
+ # @return [Node] the generated node
24
+ def n(type, children, source_map)
25
+ Node.new(type, children, location: source_map)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,133 @@
1
+ # coding: utf-8
2
+
3
+ require 'parser'
4
+
5
+ module Astrolabe
6
+ # `Astrolabe::Node` is a subclass of `Parser::AST::Node`. It provides an access to parent node and
7
+ # an object-oriented way to handle AST with the power of `Enumerable`.
8
+ #
9
+ # Though not described in the auto-generated API documentation, it has predicate methods for every
10
+ # node type. These methods would be useful especially when combined with `Enumerable` methods.
11
+ #
12
+ # @example
13
+ # node.send_type? # Equivalent to: `node.type == :send`
14
+ # node.op_asgn_type? # Equivalent to: `node.type == :op_asgn`
15
+ #
16
+ # # Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
17
+ # node.defined_type? # Equivalent to: `node.type == :defined?`
18
+ #
19
+ # # Collect all lvar nodes under the receiver node.
20
+ # lvar_nodes = node.each_descendant.select(&:lvar_type?)
21
+ class Node < Parser::AST::Node
22
+ # @see http://rubydoc.info/gems/ast/AST/Node:initialize
23
+ def initialize(type, children = [], properties = {})
24
+ @mutable_attributes = {}
25
+
26
+ # ::AST::Node#initialize freezes itself.
27
+ super
28
+
29
+ # #parent= would be invoked multiple times for a node because there are pending nodes while
30
+ # constructing AST and they are replaced later.
31
+ # For example, `lvar` and `send` type nodes are initially created as an `ident` type node and
32
+ # fixed to each type later.
33
+ # So, the #parent attribute needs to be mutable.
34
+ each_child_node do |child_node|
35
+ child_node.parent = self
36
+ end
37
+ end
38
+
39
+ Parser::Meta::NODE_TYPES.each do |node_type|
40
+ method_name = "#{node_type.to_s.gsub(/\W/, '')}_type?"
41
+ define_method(method_name) do
42
+ type == node_type
43
+ end
44
+ end
45
+
46
+ # Returns the parent node, or `nil` if the receiver is a root node.
47
+ #
48
+ # @return [Node, nil] the parent node or `nil`
49
+ def parent
50
+ @mutable_attributes[:parent]
51
+ end
52
+
53
+ def parent=(node)
54
+ @mutable_attributes[:parent] = node
55
+ end
56
+
57
+ protected :parent=
58
+
59
+ # Returns whether the receiver is a root node or not.
60
+ #
61
+ # @return [Boolean] whether the receiver is a root node or not
62
+ def root?
63
+ parent.nil?
64
+ end
65
+
66
+ # Calls the given block for each ancestor node in the order from parent to root.
67
+ # If no block is given, an `Enumerator` is returned.
68
+ #
69
+ # @yieldparam [Node] node each ancestor node
70
+ # @return [self] if a block is given
71
+ # @return [Enumerator] if no block is given
72
+ def each_ancestor(&block)
73
+ return to_enum(__method__) unless block_given?
74
+
75
+ if parent
76
+ yield parent
77
+ parent.each_ancestor(&block)
78
+ end
79
+
80
+ self
81
+ end
82
+
83
+ # Calls the given block for each child node.
84
+ # If no block is given, an `Enumerator` is returned.
85
+ #
86
+ # Note that this is different from `node.children.each { |child| ... }` which yields all
87
+ # children including non-node element.
88
+ #
89
+ # @yieldparam [Node] node each child node
90
+ # @return [self] if a block is given
91
+ # @return [Enumerator] if no block is given
92
+ def each_child_node
93
+ return to_enum(__method__) unless block_given?
94
+
95
+ children.each do |child|
96
+ next unless child.is_a?(Node)
97
+ yield child
98
+ end
99
+
100
+ self
101
+ end
102
+
103
+ # Calls the given block for each descendant node with depth first order.
104
+ # If no block is given, an `Enumerator` is returned.
105
+ #
106
+ # @yieldparam [Node] node each descendant node
107
+ # @return [self] if a block is given
108
+ # @return [Enumerator] if no block is given
109
+ def each_descendant(&block)
110
+ return to_enum(__method__) unless block_given?
111
+
112
+ each_child_node do |child_node|
113
+ yield child_node
114
+ child_node.each_descendant(&block)
115
+ end
116
+ end
117
+
118
+ # Calls the given block for the receiver and each descendant node with depth first order.
119
+ # If no block is given, an `Enumerator` is returned.
120
+ #
121
+ # This method would be useful when you treat the receiver node as a root of tree and want to
122
+ # iterate all nodes in the tree.
123
+ #
124
+ # @yieldparam [Node] node each node
125
+ # @return [self] if a block is given
126
+ # @return [Enumerator] if no block is given
127
+ def each_node(&block)
128
+ return to_enum(__method__) unless block_given?
129
+ yield self
130
+ each_descendant(&block)
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,15 @@
1
+ # coding: utf-8
2
+
3
+ module Astrolabe
4
+ # @api private
5
+ # http://semver.org/
6
+ module Version
7
+ MAJOR = 0
8
+ MINOR = 3
9
+ PATCH = 0
10
+
11
+ def self.to_s
12
+ [MAJOR, MINOR, PATCH].join('.')
13
+ end
14
+ end
15
+ end
data/lib/astrolabe.rb ADDED
@@ -0,0 +1,3 @@
1
+ # coding: utf-8
2
+
3
+ require 'astrolabe/builder'
data/spec/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+
2
+ inherit_from: ../.rubocop.yml
3
+
4
+ Documentation:
5
+ Enabled: false
@@ -0,0 +1,318 @@
1
+ # coding: utf-8
2
+
3
+ require 'astrolabe/node'
4
+
5
+ module Astrolabe
6
+ describe Node, :ast do
7
+ describe '#parent' do
8
+ let(:source) { <<-END }
9
+ def some_method(arg_a, arg_b)
10
+ do_something
11
+ end
12
+ END
13
+
14
+ # (def :some_method
15
+ # (args
16
+ # (arg :arg_a)
17
+ # (arg :arg_b))
18
+ # (send nil :do_something))
19
+
20
+ context 'when the node has parent' do
21
+ let(:target_node) { root_node.each_node.find(&:args_type?) }
22
+
23
+ it 'returns the parent node' do
24
+ expect(target_node.parent).to be_def_type
25
+ end
26
+ end
27
+
28
+ context 'when the node does not have parent' do
29
+ it 'returns nil' do
30
+ expect(root_node.parent).to be_nil
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#root?' do
36
+ let(:source) { <<-END }
37
+ def some_method
38
+ do_something
39
+ end
40
+ END
41
+
42
+ subject { target_node.root? }
43
+
44
+ context 'with root node' do
45
+ let(:target_node) { root_node }
46
+ it { is_expected.to be true }
47
+ end
48
+
49
+ context 'with non-root node' do
50
+ let(:target_node) { root_node.each_child_node.to_a.first }
51
+ it { is_expected.to be false }
52
+ end
53
+ end
54
+
55
+ describe '#each_ancestor' do
56
+ let(:source) { <<-END }
57
+ class SomeClass
58
+ attr_reader :some_attr
59
+
60
+ def some_method(arg_a, arg_b)
61
+ do_something
62
+ end
63
+ end
64
+ END
65
+
66
+ # (class
67
+ # (const nil :SomeClass) nil
68
+ # (begin
69
+ # (send nil :attr_reader
70
+ # (sym :some_attr))
71
+ # (def :some_method
72
+ # (args
73
+ # (arg :arg_a)
74
+ # (arg :arg_b))
75
+ # (send nil :do_something))))
76
+
77
+ let(:target_node) { root_node.each_node.find(&:args_type?) }
78
+ let(:expected_types) { [:def, :begin, :class] }
79
+
80
+ context 'when a block is given' do
81
+ it 'yields each ancestor node in order from parent to root' do
82
+ yielded_types = []
83
+
84
+ target_node.each_ancestor do |node|
85
+ yielded_types << node.type
86
+ end
87
+
88
+ expect(yielded_types).to eq(expected_types)
89
+ end
90
+
91
+ it 'returns itself' do
92
+ returned_value = target_node.each_ancestor {}
93
+ expect(returned_value).to equal(target_node)
94
+ end
95
+ end
96
+
97
+ context 'when no block is given' do
98
+ it 'returns an enumerator' do
99
+ expect(target_node.each_ancestor).to be_a Enumerator
100
+ end
101
+
102
+ describe 'the returned enumerator' do
103
+ subject(:enumerator) { target_node.each_ancestor }
104
+
105
+ it 'enumerates ancestor nodes' do
106
+ expected_types.each do |expected_type|
107
+ expect(enumerator.next.type).to eq(expected_type)
108
+ end
109
+
110
+ expect { enumerator.next }.to raise_error(StopIteration)
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#each_child_node' do
117
+ let(:source) { <<-END }
118
+ def some_method(arg_a, arg_b)
119
+ do_something
120
+ end
121
+ END
122
+
123
+ # (def :some_method
124
+ # (args
125
+ # (arg :arg_a)
126
+ # (arg :arg_b))
127
+ # (send nil :do_something))
128
+
129
+ let(:target_node) { root_node.each_node.find(&:def_type?) }
130
+ let(:expected_types) { [:args, :send] }
131
+
132
+ context 'when a block is given' do
133
+ it 'yields each child node' do
134
+ yielded_types = []
135
+
136
+ target_node.each_child_node do |node|
137
+ yielded_types << node.type
138
+ end
139
+
140
+ expect(yielded_types).to eq(expected_types)
141
+ end
142
+
143
+ it 'returns itself' do
144
+ returned_value = target_node.each_child_node {}
145
+ expect(returned_value).to equal(target_node)
146
+ end
147
+ end
148
+
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)
152
+ end
153
+
154
+ describe 'the returned enumerator' do
155
+ subject(:enumerator) { target_node.each_child_node }
156
+
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
161
+
162
+ expect { enumerator.next }.to raise_error(StopIteration)
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ describe '#each_descendant' do
169
+ let(:source) { <<-END }
170
+ class SomeClass
171
+ attr_reader :some_attr
172
+
173
+ def some_method(arg_a, arg_b)
174
+ do_something
175
+ end
176
+ end
177
+ END
178
+
179
+ # (class
180
+ # (const nil :SomeClass) nil
181
+ # (begin
182
+ # (send nil :attr_reader
183
+ # (sym :some_attr))
184
+ # (def :some_method
185
+ # (args
186
+ # (arg :arg_a)
187
+ # (arg :arg_b))
188
+ # (send nil :do_something))))
189
+
190
+ let(:target_node) { root_node }
191
+ let(:expected_types) { [:const, :begin, :send, :sym, :def, :args, :arg, :arg, :send] }
192
+
193
+ context 'when a block is given' do
194
+ it 'yields each descendant node with depth first order' do
195
+ yielded_types = []
196
+
197
+ target_node.each_descendant do |node|
198
+ yielded_types << node.type
199
+ end
200
+
201
+ expect(yielded_types).to eq(expected_types)
202
+ end
203
+
204
+ it 'returns itself' do
205
+ returned_value = target_node.each_descendant {}
206
+ expect(returned_value).to equal(target_node)
207
+ end
208
+ end
209
+
210
+ context 'when no block is given' do
211
+ it 'returns an enumerator' do
212
+ expect(target_node.each_descendant).to be_a(Enumerator)
213
+ end
214
+
215
+ describe 'the returned enumerator' do
216
+ subject(:enumerator) { target_node.each_descendant }
217
+
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
222
+
223
+ expect { enumerator.next }.to raise_error(StopIteration)
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ describe '#each_node' do
230
+ let(:source) { <<-END }
231
+ class SomeClass
232
+ attr_reader :some_attr
233
+
234
+ def some_method(arg_a, arg_b)
235
+ do_something
236
+ end
237
+ end
238
+ END
239
+
240
+ # (class
241
+ # (const nil :SomeClass) nil
242
+ # (begin
243
+ # (send nil :attr_reader
244
+ # (sym :some_attr))
245
+ # (def :some_method
246
+ # (args
247
+ # (arg :arg_a)
248
+ # (arg :arg_b))
249
+ # (send nil :do_something))))
250
+
251
+ let(:target_node) { root_node }
252
+ let(:expected_types) { [:class, :const, :begin, :send, :sym, :def, :args, :arg, :arg, :send] }
253
+
254
+ context 'when a block is given' do
255
+ it 'yields itself and each descendant node with depth first order' do
256
+ yielded_types = []
257
+
258
+ target_node.each_node do |node|
259
+ yielded_types << node.type
260
+ end
261
+
262
+ expect(yielded_types).to eq(expected_types)
263
+ end
264
+
265
+ it 'returns itself' do
266
+ returned_value = target_node.each_node {}
267
+ expect(returned_value).to equal(target_node)
268
+ end
269
+ end
270
+
271
+ context 'when no block is given' do
272
+ it 'returns an enumerator' do
273
+ expect(target_node.each_node).to be_a(Enumerator)
274
+ end
275
+
276
+ describe 'the returned enumerator' do
277
+ subject(:enumerator) { target_node.each_node }
278
+
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
283
+
284
+ expect { enumerator.next }.to raise_error(StopIteration)
285
+ end
286
+ end
287
+ end
288
+ end
289
+
290
+ describe '#send_type?' do
291
+ subject { root_node.send_type? }
292
+
293
+ context 'with send type node' do
294
+ let(:source) { 'do_something' }
295
+ it { is_expected.to be true }
296
+ end
297
+
298
+ context 'with non-send type node' do
299
+ let(:source) { 'foo = 1' }
300
+ it { is_expected.to be false }
301
+ end
302
+ end
303
+
304
+ describe '#defined_type?' do
305
+ subject { root_node.defined_type? }
306
+
307
+ context 'with defined? type node' do
308
+ let(:source) { 'defined?(Foo)' }
309
+ it { is_expected.to be true }
310
+ end
311
+
312
+ context 'non-defined? type node' do
313
+ let(:source) { 'foo = 1' }
314
+ it { is_expected.to be false }
315
+ end
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,95 @@
1
+ # coding: utf-8
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
6
+ # file to always be loaded, without a need to explicitly require it in any files.
7
+ #
8
+ # Given that it is always loaded, you are encouraged to keep this file as
9
+ # light-weight as possible. Requiring heavyweight dependencies from this file
10
+ # will add to the boot time of your test suite on EVERY test run, even for an
11
+ # individual file that may not need all of that loaded. Instead, make a
12
+ # separate helper file that requires this one and then use it only in the specs
13
+ # that actually need it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+ RSpec.configure do |config|
20
+ # The settings below are suggested to provide a good initial experience
21
+ # with RSpec, but feel free to customize to your heart's content.
22
+
23
+ # These two settings work together to allow you to limit a spec run
24
+ # to individual examples or groups you care about by tagging them with
25
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
26
+ # get run.
27
+ config.filter_run :focus
28
+ config.run_all_when_everything_filtered = true
29
+
30
+ # Many RSpec users commonly either run the entire suite or an individual
31
+ # file, and it's useful to allow more verbose output when running an
32
+ # individual spec file.
33
+ if config.files_to_run.one?
34
+ # Use the documentation formatter for detailed output,
35
+ # unless a formatter has already been configured
36
+ # (e.g. via a command-line flag).
37
+ config.default_formatter = 'doc'
38
+ end
39
+
40
+ # Print the 10 slowest examples and example groups at the
41
+ # end of the spec run, to help surface which specs are running
42
+ # particularly slow.
43
+ # config.profile_examples = 10
44
+
45
+ # Run specs in random order to surface order dependencies. If you find an
46
+ # order dependency and want to debug it, you can fix the order by providing
47
+ # the seed, which is printed after each run.
48
+ # --seed 1234
49
+ config.order = :random
50
+
51
+ # Seed global randomization in this process using the `--seed` CLI option.
52
+ # Setting this allows you to use `--seed` to deterministically reproduce
53
+ # test failures related to randomization by passing the same `--seed` value
54
+ # as the one that triggered the failure.
55
+ Kernel.srand config.seed
56
+
57
+ # rspec-expectations config goes here. You can use an alternate
58
+ # assertion/expectation library such as wrong or the stdlib/minitest
59
+ # assertions if you prefer.
60
+ config.expect_with :rspec do |expectations|
61
+ # Enable only the newer, non-monkey-patching expect syntax.
62
+ # For more details, see:
63
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
64
+ expectations.syntax = :expect
65
+ end
66
+
67
+ # rspec-mocks config goes here. You can use an alternate test double
68
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
69
+ config.mock_with :rspec do |mocks|
70
+ # Enable only the newer, non-monkey-patching expect syntax.
71
+ # For more details, see:
72
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
73
+ mocks.syntax = :expect
74
+
75
+ # Prevents you from mocking or stubbing a method that does not exist on
76
+ # a real object. This is generally recommended.
77
+ mocks.verify_partial_doubles = true
78
+ end
79
+ end
80
+
81
+ Dir[File.join(File.dirname(__FILE__), 'support', '*')].each do |path|
82
+ require path
83
+ end
84
+
85
+ if ENV['TRAVIS']
86
+ require 'simplecov'
87
+ require 'coveralls'
88
+
89
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
90
+
91
+ SimpleCov.start do
92
+ add_filter '/spec/'
93
+ add_filter '/vendor/'
94
+ end
95
+ end
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+
3
+ shared_context 'AST', :ast do
4
+ let(:root_node) do
5
+ fail '#source must be defined with #let' unless respond_to?(:source)
6
+
7
+ require 'astrolabe/builder'
8
+ require 'parser/current'
9
+
10
+ buffer = Parser::Source::Buffer.new('(string)')
11
+ buffer.source = source
12
+
13
+ builder = Astrolabe::Builder.new
14
+ parser = Parser::CurrentRuby.new(builder)
15
+ parser.parse(buffer)
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,228 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: astrolabe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Yuji Nakayama
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fuubar
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.0.0.rc1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.0.0.rc1
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.24'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.24'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 4.2.3
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '5.0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 4.2.3
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '5.0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: guard-rubocop
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '1.0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: ruby_gntp
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.3'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '0.3'
173
+ description: Object-oriented AST library for Parser
174
+ email:
175
+ - nkymyj@gmail.com
176
+ executables: []
177
+ extensions: []
178
+ extra_rdoc_files: []
179
+ files:
180
+ - ".gitignore"
181
+ - ".rspec"
182
+ - ".rubocop.yml"
183
+ - ".travis.yml"
184
+ - ".yardopts"
185
+ - Gemfile
186
+ - Guardfile
187
+ - LICENSE.txt
188
+ - README.md
189
+ - Rakefile
190
+ - astrolabe.gemspec
191
+ - lib/astrolabe.rb
192
+ - lib/astrolabe/builder.rb
193
+ - lib/astrolabe/node.rb
194
+ - lib/astrolabe/version.rb
195
+ - spec/.rubocop.yml
196
+ - spec/astrolabe/node_spec.rb
197
+ - spec/spec_helper.rb
198
+ - spec/support/shared_contexts.rb
199
+ homepage: https://github.com/yujinakayama/astrolabe
200
+ licenses:
201
+ - MIT
202
+ metadata: {}
203
+ post_install_message:
204
+ rdoc_options: []
205
+ require_paths:
206
+ - lib
207
+ required_ruby_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ required_rubygems_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ requirements: []
218
+ rubyforge_project:
219
+ rubygems_version: 2.3.0
220
+ signing_key:
221
+ specification_version: 4
222
+ summary: Object-oriented AST library for Parser
223
+ test_files:
224
+ - spec/.rubocop.yml
225
+ - spec/astrolabe/node_spec.rb
226
+ - spec/spec_helper.rb
227
+ - spec/support/shared_contexts.rb
228
+ has_rdoc: