ruby_traverser 0.1.1 → 0.2.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.
- data/README.markdown +27 -253
- data/TODO.txt +12 -0
- data/VERSION +1 -1
- data/lib/config/api.rb +11 -0
- data/lib/config/has_ext.rb +44 -0
- data/lib/{traversal → config}/misc.rb +4 -4
- data/lib/config/mixin.rb +15 -0
- data/lib/{traversal → config}/module.rb +1 -1
- data/lib/{traversal → config}/patch.rb +4 -4
- data/lib/manipulation/api.rb +30 -0
- data/lib/manipulation/delete/api.rb +10 -0
- data/lib/manipulation/helpers.rb +30 -0
- data/lib/manipulation/insert/api.rb +57 -0
- data/lib/manipulation/position.rb +44 -0
- data/lib/{mutate/replacer.rb → manipulation/update/api.rb} +25 -14
- data/lib/query/api.rb +7 -0
- data/lib/query/find.rb +73 -0
- data/lib/rails/api.rb +13 -0
- data/lib/rails/{api_wrapper.rb → gemfile.rb} +1 -1
- data/lib/ruby_traverser.rb +1 -3
- data/ruby_traverser.gemspec +21 -12
- data/test/mutate/delete_test.rb +25 -0
- data/test/mutate/mutate_class_test.rb +7 -7
- data/test/mutate/mutate_test.rb +18 -17
- data/test/traversal/gemfile_test.rb +6 -6
- metadata +23 -14
- data/lib/mutate/api.rb +0 -124
- data/lib/traversal/api/finders.rb +0 -41
- data/lib/traversal/api/inside.rb +0 -36
- data/lib/traversal/api/traversal.rb +0 -20
- data/lib/traversal/mixin.rb +0 -50
data/README.markdown
CHANGED
@@ -1,273 +1,47 @@
|
|
1
|
-
# Ruby
|
1
|
+
# Ruby code Query and Manipulation language ##
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
This Ruby DSL enables querying, traversing and manipulating Ruby code using a nice Rubish DSL.
|
4
|
+
|
5
|
+
1. First parsing the ruby code into an object graph using `ripper2ruby`
|
6
|
+
2. Query the graph to find the code constructs of interest using the Query API
|
7
|
+
3. Manipulate the code constructs using the Manipulation API
|
8
|
+
|
9
|
+
This project is a DSL layer on top of `ripper2ruby` which again is on top of `ripper` which comes with ruby 1.9.
|
10
|
+
Ruby 1.9 is therefore a requirement!
|
5
11
|
|
6
12
|
See the unit tests in the test directory for examples of use.
|
7
13
|
|
14
|
+
See the [Wiki](http://wiki.github.com/kristianmandrup/ruby_traverser_dsl "Wiki") for more details.
|
15
|
+
|
8
16
|
## Requirements ##
|
9
17
|
* Ruby 1.9
|
10
18
|
* ripper2ruby >= 0.0.2
|
11
19
|
|
12
|
-
##
|
13
|
-
|
14
|
-
* find_module(name)
|
15
|
-
* find_class(name, options = {})
|
16
|
-
* find_call(name, options = {})
|
17
|
-
* find_block(name, options = {})
|
18
|
-
* find_def(name, options = {})
|
19
|
-
* find_variable(name, options = {})
|
20
|
-
* find_assignment(name, options = {})
|
21
|
-
|
22
|
-
## Find module ##
|
23
|
-
|
24
|
-
<pre>
|
25
|
-
src = %q{
|
26
|
-
module Xyz::Xxx::Blip
|
27
|
-
2
|
28
|
-
end
|
29
|
-
}
|
30
|
-
|
31
|
-
code = Ripper::RubyBuilder.build(src)
|
32
|
-
module_node = code.find_module('Xyz::Xxx::Blip')
|
33
|
-
assert_equal Ruby::Module, module_node.class
|
34
|
-
</pre>
|
35
|
-
|
36
|
-
## Find class ##
|
37
|
-
|
38
|
-
<pre>
|
39
|
-
# class Monty::Python ... end
|
40
|
-
clazz_node = code.find_class('Monty::Python')
|
41
|
-
</pre>
|
42
|
-
|
43
|
-
Find class inheriting from a certain superclass
|
44
|
-
<pre>
|
45
|
-
# class Monty < Abc::Blip ... end
|
46
|
-
clazz_node = code.find_class('Monty', :superclass => 'Abc::Blip')
|
47
|
-
</pre>
|
48
|
-
|
49
|
-
## Find call ##
|
50
|
-
|
51
|
-
<pre>
|
52
|
-
# gem 'ripper', :src => 'github'
|
53
|
-
gem_call = code.find_call('gem', :args => ['ripper', {:src => 'github'}])
|
54
|
-
</pre>
|
55
|
-
|
56
|
-
|
57
|
-
## Find block ##
|
58
|
-
|
59
|
-
<pre>
|
60
|
-
# my_block do ... end
|
61
|
-
block_node = code.find_block('my_block')
|
62
|
-
</pre>
|
63
|
-
|
64
|
-
<pre>
|
65
|
-
# my_block do |v| ... end
|
66
|
-
block_node = code.find_block('my_block', :block_params => ['v'])
|
67
|
-
</pre>
|
68
|
-
|
69
|
-
<pre>
|
70
|
-
# my_block 7, 'a' do ... end
|
71
|
-
block_node = code.find_block('my_block', :args => [7, 'a'])
|
72
|
-
</pre>
|
73
|
-
|
74
|
-
<pre>
|
75
|
-
# my_block 7, 'a', :k => 32 do |v| ... end
|
76
|
-
block_node = code.find_block('my_block', :args => [7, 'a', {:k => 32}], :block_params => ['v'])
|
77
|
-
</pre>
|
78
|
-
|
79
|
-
<pre>
|
80
|
-
# my_block :a => 7, b => 3 do |v| ... end
|
81
|
-
block_node = code.find_block('my_block', :args => [{:a => 7, 'b' => 3}])
|
82
|
-
</pre>
|
83
|
-
|
84
|
-
<pre>
|
85
|
-
# my_block ['a', 'b'] do |v| ... end
|
86
|
-
block_node = code.find_block('my_block', :args => [{:array =>['a', 'b']}])
|
87
|
-
</pre>
|
88
|
-
|
89
|
-
|
90
|
-
## Find variable ##
|
91
|
-
|
92
|
-
Source code:
|
93
|
-
<pre>
|
94
|
-
def hello_world(a)
|
95
|
-
my_var
|
96
|
-
end
|
97
|
-
</pre>
|
98
|
-
|
99
|
-
Ruby code find DSL:
|
100
|
-
<pre>
|
101
|
-
code = Ripper::RubyBuilder.build(src)
|
102
|
-
code.inside_def('hello_world', :params => ['a']) do |b|
|
103
|
-
call_node = b.find_variable('my_var')
|
104
|
-
assert_equal Ruby::Variable, call_node.class
|
105
|
-
puts call_node.to_ruby
|
106
|
-
end
|
107
|
-
</pre>
|
108
|
-
|
109
|
-
|
110
|
-
## Find assignment ##
|
111
|
-
|
112
|
-
Source code:
|
113
|
-
<pre>
|
114
|
-
def hello_world(a)
|
115
|
-
my_var = 2
|
116
|
-
end
|
117
|
-
</pre>
|
118
|
-
|
119
|
-
Ruby code find DSL:
|
120
|
-
<pre>
|
121
|
-
code = Ripper::RubyBuilder.build(src)
|
122
|
-
code.inside_def('hello_world', :params => ['a']) do |b|
|
123
|
-
call_node = b.find_assignment('my_var')
|
124
|
-
end
|
125
|
-
</pre>
|
126
|
-
|
127
|
-
## Inside ##
|
128
|
-
|
129
|
-
The following finder methods have corresponding `inside_` functions, which support block DSL constructs as shown below.
|
130
|
-
|
131
|
-
* inside_module
|
132
|
-
* inside_class
|
133
|
-
* inside_def
|
134
|
-
* inside_block
|
135
|
-
|
136
|
-
<pre>
|
137
|
-
# source code
|
138
|
-
src = %q{
|
139
|
-
gem 'ripper', :src => 'github', :blip => 'blap'
|
140
|
-
group :test do
|
141
|
-
gem 'ripper', :src => 'github'
|
142
|
-
end
|
143
|
-
}
|
144
|
-
</pre>
|
145
|
-
|
146
|
-
<pre>
|
147
|
-
code = Ripper::RubyBuilder.build(src)
|
148
|
-
# chaining finders using 'inside__' DSL block constructs
|
149
|
-
code.inside_block('group', :args => [:test]) do |b|
|
150
|
-
call_node = b.find_call('gem', :args => ['ripper', {:src => 'github'}])
|
151
|
-
assert_equal Ruby::Call, call_node.class
|
152
|
-
puts call_node.to_ruby # output ruby code as string for found node
|
153
|
-
end
|
154
|
-
</pre>
|
155
|
-
|
156
|
-
<pre>
|
157
|
-
src = %q{
|
158
|
-
def hello_world(b)
|
159
|
-
3
|
160
|
-
end
|
161
|
-
|
162
|
-
def hello_world(a)
|
163
|
-
gem 'ripper', :src => 'github'
|
164
|
-
end
|
165
|
-
|
166
|
-
}
|
167
|
-
</pre>
|
168
|
-
|
169
|
-
<pre>
|
170
|
-
# chaining finders using 'inside__' DSL block constructs
|
171
|
-
code = Ripper::RubyBuilder.build(src)
|
172
|
-
code.inside_def('hello_world', :params => ['a']) do |b|
|
173
|
-
call_node = b.find_call('gem', :args => ['ripper', {:src => 'github'}])
|
174
|
-
assert_equal Ruby::Call, call_node.class
|
175
|
-
puts call_node.to_ruby # output ruby code as string for found node
|
176
|
-
end
|
177
|
-
</pre>
|
178
|
-
|
179
|
-
## Code Mutation API ##
|
180
|
-
|
181
|
-
The API now also supports a wide variety of code mutations using a DSL.
|
182
|
-
More information will soon be available here or on the github wiki.
|
183
|
-
Check the test/mutate folder for test demonstrating what is currently possible.
|
184
|
-
|
185
|
-
## Example use of Mutation API ##
|
186
|
-
|
187
|
-
Source BEFORE mutations:
|
188
|
-
<pre>
|
189
|
-
class Monty < Abc::Blip
|
190
|
-
end
|
191
|
-
</pre>
|
192
|
-
|
193
|
-
<pre>
|
194
|
-
src = %q{ class Monty < Abc::Blip
|
195
|
-
end}
|
196
|
-
|
197
|
-
def_src = %q{
|
198
|
-
def my_fun
|
199
|
-
end}
|
200
|
-
|
201
|
-
def_code = Ripper::RubyBuilder.build(def_src)
|
202
|
-
code = Ripper::RubyBuilder.build(src)
|
203
|
-
|
204
|
-
# append code examples
|
205
|
-
code.inside_class('Monty', :superclass => 'Abc::Blip') do |b|
|
206
|
-
assert_equal Ruby::Class, b.class
|
207
|
-
gem_abc = b.append_code("gem 'abc'")
|
208
|
-
blip = b.append_code("blip")
|
209
|
-
gem_123 = gem_abc.append_code("gem '123'")
|
210
|
-
gem_123.append_comment("hello")
|
211
|
-
my_def = b.append_code(def_src)
|
212
|
-
|
213
|
-
b.prepend_code("gem '789'")
|
214
|
-
puts b.to_ruby
|
215
|
-
end
|
216
|
-
</pre>
|
20
|
+
## Ruby code Query API ##
|
217
21
|
|
218
|
-
|
219
|
-
<pre>
|
220
|
-
class Monty < Abc::Blip
|
221
|
-
gem '789'
|
222
|
-
gem 'abc'
|
223
|
-
gem '123'
|
224
|
-
blip
|
22
|
+
See [Query API](http://wiki.github.com/kristianmandrup/ruby_traverser_dsl/finder-api "Query API")
|
225
23
|
|
226
|
-
|
227
|
-
|
228
|
-
end
|
229
|
-
</pre>
|
24
|
+
* find(type, name, options, &block)
|
25
|
+
* inside (alias for find which requires a block argument)
|
230
26
|
|
27
|
+
The following symbols are supported `[:module, :class, :variable, :assignment, :call, :block, :def]`
|
231
28
|
|
232
|
-
##
|
29
|
+
## Manipulation API ##
|
233
30
|
|
234
|
-
|
235
|
-
<pre>
|
236
|
-
group :test do
|
237
|
-
gem 'ripper', :src
|
238
|
-
my_var = 2
|
239
|
-
end
|
240
|
-
</pre>
|
31
|
+
See [Manipulation API](http://wiki.github.com/kristianmandrup/ruby_traverser_dsl/manipulation-api "Manipulation API")
|
241
32
|
|
242
|
-
|
243
|
-
src = %q{
|
244
|
-
group :test do
|
245
|
-
gem 'ripper', :src
|
246
|
-
my_var = 2
|
247
|
-
end
|
248
|
-
}
|
33
|
+
The insert and update API support blocks and always returns the updated or inserted node
|
249
34
|
|
250
|
-
|
251
|
-
|
252
|
-
code.inside_block('group', :args => [:test]) do |b|
|
253
|
-
call_node = b.find_call('gem', :args => ['ripper'])
|
254
|
-
assert_equal Ruby::Call, call_node.class
|
255
|
-
call_node.replace(:arg => :src , :replace_code => "{:src => 'unknown'}")
|
256
|
-
call_node.replace(:value => "3")
|
257
|
-
puts b.to_ruby
|
258
|
-
end
|
259
|
-
end
|
260
|
-
</pre>
|
35
|
+
### Insert ##
|
36
|
+
* insert(position, code, &block)
|
261
37
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
my_var = 3
|
267
|
-
end
|
268
|
-
</pre>
|
38
|
+
### Update ##
|
39
|
+
* update(:select, :with, &block)
|
40
|
+
* update(:select, :with_code, &block)
|
41
|
+
* update(:value, &block)
|
269
42
|
|
270
|
-
|
43
|
+
### Delete ##
|
44
|
+
* delete
|
271
45
|
|
272
46
|
## Note on Patches/Pull Requests ##
|
273
47
|
|
data/TODO.txt
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/config/api.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Ruby
|
2
|
+
class Node
|
3
|
+
module Extension
|
4
|
+
module Has
|
5
|
+
def has_const?(value)
|
6
|
+
if respond_to?(:const)
|
7
|
+
if namespace?(value)
|
8
|
+
name = value.split('::').last
|
9
|
+
return self.const.identifier.token == name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def has_block?
|
16
|
+
respond_to? :block
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_namespace?(value)
|
20
|
+
if respond_to?(:namespace)
|
21
|
+
return self.namespace.identifier.token == value
|
22
|
+
end
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_identifier?(value)
|
27
|
+
if respond_to?(:identifier)
|
28
|
+
id = self.identifier
|
29
|
+
|
30
|
+
if namespace?(value)
|
31
|
+
return id.token.to_s == value.to_s if id.respond_to?(:token)
|
32
|
+
if id.respond_to?(:identifier)
|
33
|
+
name = value.split('::').last
|
34
|
+
return id.identifier.token == name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
else
|
38
|
+
has_const?(value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ruby
|
2
2
|
class Node
|
3
|
-
module
|
3
|
+
module Extension
|
4
4
|
module Misc
|
5
5
|
def args?(value, with_block = nil)
|
6
6
|
found = 0
|
@@ -21,11 +21,11 @@ module Ruby
|
|
21
21
|
def argument_count(args)
|
22
22
|
args.reject{|arg| !valid_arg?(arg)}.size
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def valid_arg?(arg)
|
26
26
|
arg && arg != {}
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def has_a_block?(with_block)
|
30
30
|
with_block && self.respond_to?(:block)
|
31
31
|
end
|
@@ -148,5 +148,5 @@ module Ruby
|
|
148
148
|
end
|
149
149
|
end
|
150
150
|
end
|
151
|
-
end
|
151
|
+
end
|
152
152
|
end
|
data/lib/config/mixin.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'config/module'
|
2
|
+
require 'config/misc'
|
3
|
+
require 'config/has_ext'
|
4
|
+
require 'config/patch'
|
5
|
+
|
6
|
+
module Ruby
|
7
|
+
class Node
|
8
|
+
include Extension::Patch
|
9
|
+
include Extension::Module
|
10
|
+
include Extension::Misc
|
11
|
+
include Extension::Has
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Ruby
|
2
2
|
class Node
|
3
|
-
module
|
3
|
+
module Extension
|
4
4
|
module Patch
|
5
5
|
def select(*args, &block)
|
6
6
|
result = []
|
@@ -35,7 +35,7 @@ module Ruby
|
|
35
35
|
end
|
36
36
|
res
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def check_pair(type, value)
|
40
40
|
res = case type
|
41
41
|
when :is_a
|
@@ -76,8 +76,8 @@ module Ruby
|
|
76
76
|
# puts "check pair type:#{type}, value:#{value} => #{res}, self: #{self.inspect}" if self.class == Ruby::Variable
|
77
77
|
res
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
end # Patch
|
81
81
|
end
|
82
82
|
end
|
83
|
-
end
|
83
|
+
end
|