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 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