ruby_traverser 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,273 +1,47 @@
1
- # Ruby traverser ##
1
+ # Ruby code Query and Manipulation language ##
2
2
 
3
- A DSL for traversing ruby code as an object model (graph), which lets you find parts of the ruby code of interest.
4
- The traverser leverages `ripper2ruby`, a library for generating a model from ruby source code. `ripper2ruby` leverages `ripper` which comes with ruby 1.9.
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
- ## Finders ##
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
- Source AFTER mutations:
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
- def my_fun
227
- end
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
- ## Replace example ##
29
+ ## Manipulation API ##
233
30
 
234
- Source BEFORE mutations:
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
- <pre>
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
- code = Ripper::RubyBuilder.build(src)
251
- # replace examples
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
- Source AFTER mutations:
263
- <pre>
264
- group :test do
265
- gem 'ripper', {:src => 'unknown'}
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
- Note: The mutation API code was developed quickly in a test-driven fashion, but is in need of a major refactoring overhaul sometime soon...
43
+ ### Delete ##
44
+ * delete
271
45
 
272
46
  ## Note on Patches/Pull Requests ##
273
47
 
data/TODO.txt ADDED
@@ -0,0 +1,12 @@
1
+ find
2
+ find :after
3
+ find :before
4
+
5
+ insert :after, ...
6
+ insert :before, ...
7
+
8
+ update :select => {...}, :with => {...}
9
+ update :select => {...}, :with_code => 'code'
10
+
11
+ delete
12
+ delete :select => {...}
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
data/lib/config/api.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'query/api'
2
+ require 'manipulation/api'
3
+ require 'config/mixin'
4
+
5
+ module Ruby
6
+ class Node
7
+ include RubyCodeAPI::Query
8
+ include RubyCodeAPI::Manipulation
9
+ include RubyCodeAPI::Misc
10
+ end
11
+ end
@@ -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 Traversal
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
@@ -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 Traversal
3
+ module Extension
4
4
  module Module
5
5
  def namespace?(full_name)
6
6
  if full_name.split('::').size > 1
@@ -1,6 +1,6 @@
1
1
  module Ruby
2
2
  class Node
3
- module Traversal
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