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.
@@ -0,0 +1,30 @@
1
+ require 'manipulation/update/api'
2
+ require 'manipulation/delete/api'
3
+ require 'manipulation/insert/api'
4
+ require 'manipulation/position'
5
+ require 'manipulation/helpers'
6
+
7
+ module RubyCodeAPI
8
+ module Manipulation
9
+ include Insert
10
+ include Update
11
+ include Delete
12
+ include RubyCodeAPI::Misc::Position
13
+ include RubyCodeAPI::Misc::Helper
14
+ end
15
+ end
16
+
17
+ module Ruby
18
+ class Node
19
+ include RubyCodeAPI::Manipulation
20
+ end
21
+ end
22
+
23
+ # Core extensions
24
+
25
+ class String
26
+ # Returns an indented string, all lines of string will be indented with count of chars
27
+ def indent(char, count)
28
+ (char * count) + gsub(/(\n+)/) { $1 + (char * count) }
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module RubyCodeAPI
2
+ module Manipulation
3
+ module Delete
4
+ def delete
5
+ # index = parent.find_index(self)
6
+ parent.get_elements.delete(self)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module RubyCodeAPI
2
+ module Misc
3
+ module Helper
4
+ def find_index(obj)
5
+ get_elements.each_with_index do |elem, i|
6
+ if elem == obj
7
+ return i
8
+ end
9
+ end
10
+ end
11
+
12
+ def elemental?
13
+ respond_to?(:body) || respond_to?(:elements)
14
+ end
15
+
16
+ def get_elements
17
+ case self
18
+ when Ruby::Class
19
+ body.elements
20
+ else
21
+ elements
22
+ end
23
+ end
24
+
25
+ def object
26
+ self.respond_to?(:block) ? self.block : self
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,57 @@
1
+ module RubyCodeAPI
2
+ module Manipulation
3
+ module Insert
4
+ def append_code(code)
5
+ return append_code_simple(code) if !elemental?
6
+ obj = object
7
+ indentation = obj.last_indent
8
+ code = "\n#{code}\n".indent(' ', indentation)
9
+ ruby_code = Ripper::RubyBuilder.build(code)
10
+ inject_code = ruby_code.elements[0]
11
+ obj.get_elements << inject_code
12
+ inject_code
13
+ end
14
+
15
+ def append_code_simple(code)
16
+ indentation = position.col
17
+ code = "\n#{code}\n".indent(' ', indentation)
18
+ ruby_code = Ripper::RubyBuilder.build(code)
19
+ inject_code = ruby_code.elements[0]
20
+ index = parent.find_index(self)
21
+ parent.get_elements.insert(index+1, inject_code)
22
+ inject_code
23
+ end
24
+
25
+ def prepend_code(code)
26
+ obj = object
27
+ indentation = obj.first_indent
28
+ code = "\n#{code}\n".indent(' ', indentation)
29
+ ruby_code = Ripper::RubyBuilder.build(code)
30
+ inject_code = ruby_code.elements[0]
31
+ obj.get_elements.insert(0, inject_code)
32
+ obj
33
+ end
34
+
35
+ def insert_comment(position, text, &block)
36
+ insert(position, "# #{text}", &block)
37
+ end
38
+
39
+ def insert(position, code, &block)
40
+ case position
41
+ when :after
42
+ s = append_code(code)
43
+ when :before
44
+ s = prepend_code(code)
45
+ else
46
+ raise Error, "Invalid position given: #{position}, must be either :before or :after"
47
+ end
48
+
49
+ if block_given?
50
+ block.arity < 1 ? s.instance_eval(&block) : block.call(s)
51
+ else
52
+ s
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,44 @@
1
+ module RubyCodeAPI
2
+ module Misc
3
+ module Position
4
+ protected
5
+
6
+ def last_indent
7
+ case self
8
+ when Ruby::Block, Ruby::Class
9
+ last_position(get_elements)
10
+ else
11
+ puts "unknown: #{obj.class}"
12
+ end
13
+ end
14
+
15
+ def last_position(elements)
16
+ last_element = elements.last
17
+ return position.col
18
+ return position.col if simple_pos?(last_element)
19
+ return last_element.identifier.position.col if elements && elements.size > 0
20
+ inside_indent
21
+ end
22
+
23
+ def simple_pos?(elem)
24
+ [Ruby::Token, Ruby::Variable].include?(elem.class)
25
+ end
26
+
27
+ def first_indent
28
+ case self
29
+ when Ruby::Block, Ruby::Class
30
+ first_position(get_elements)
31
+ else
32
+ puts "unknown: #{obj.class}"
33
+ end
34
+ end
35
+
36
+ def first_position(elements)
37
+ first_element = elements.first
38
+ return position.col if simple_pos?(first_element)
39
+ return first_element.identifier.position.col if elements && elements.size > 0
40
+ inside_indent
41
+ end
42
+ end
43
+ end
44
+ end
@@ -7,7 +7,7 @@ module PositionReplacer
7
7
  def replace_pos_argument(options)
8
8
  case self.arg
9
9
  when Ruby::String
10
- replace_arg_token(options[:replace_arg])
10
+ replace_arg_token(options[:with])
11
11
  end
12
12
  end
13
13
 
@@ -88,24 +88,35 @@ module ValueReplacer
88
88
  end
89
89
  end
90
90
 
91
- module RubyAPI
92
- module Mutator
93
- module Replacer
91
+ module RubyCodeAPI
92
+ module Manipulation
93
+ module Update
94
94
  include PositionReplacer
95
95
  include TokenReplacer
96
96
  include HashReplacer
97
97
  include ValueReplacer
98
+
99
+ # update :select => {...}, :with => {...}
100
+ # update :select => {...}, :with_code => 'code'
101
+ def update(options, &block)
102
+ s = replace_value(options) if options[:value]
103
+ s = replace options[:select].merge(options) if options[:select]
104
+ if block_given?
105
+ block.arity < 1 ? s.instance_eval(&block) : block.call(s)
106
+ else
107
+ s
108
+ end
109
+ end
98
110
 
99
111
  # :arg => 'ripper', :replace_arg => 'rapper'
100
- def replace(options)
101
- if options[:value]
102
- return replace_value(options)
103
- end
104
-
105
- if position_arg?(options[:arg])
106
- return replace_position_arg(options)
107
- end
108
-
112
+ def replace(options)
113
+ return replace_value(options) if options[:value]
114
+ return replace_position_arg(options) if position_arg?(options[:arg])
115
+ return replace_arg(options) if options[:arg]
116
+ raise Error, "Invalid options: #{options}"
117
+ end
118
+
119
+ def replace_arg(options)
109
120
  self.arguments.elements.each_with_index do |elem, i|
110
121
  case elem
111
122
  when Ruby::Arg
@@ -120,7 +131,7 @@ module RubyAPI
120
131
  def replace_argument(options)
121
132
  case self.arg
122
133
  when Ruby::String
123
- replace_arg_token(options[:replace_arg]) if matching_string_arg?(options[:arg])
134
+ replace_arg_token(options[:with]) if matching_string_arg?(options[:arg])
124
135
  end
125
136
  end
126
137
 
data/lib/query/api.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'query/find'
2
+
3
+ module Ruby
4
+ class Node
5
+ include RubyCodeAPI::Query
6
+ end
7
+ end
data/lib/query/find.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'yaml'
2
+
3
+ module RubyCodeAPI
4
+ module Query
5
+ # Use this to generate all methods except for find_block which is a bit special
6
+ NODES = {
7
+ :module => Ruby::Module,
8
+ :class => Ruby::Class,
9
+ :variable => [Ruby::Variable, :token],
10
+ :assignment => [Ruby::Assignment, :left_token],
11
+ :call => Ruby::Call,
12
+ :def => Ruby::Method
13
+ }
14
+
15
+ def find(type, name, options = {}, &block)
16
+ s = send :"find_#{type.to_s}", name, options
17
+ s.extend(options[:extend]) if options[:extend]
18
+ if block_given?
19
+ block.arity < 1 ? s.instance_eval(&block) : block.call(s)
20
+ else
21
+ s
22
+ end
23
+ end
24
+
25
+ def inside(type, name, options = {}, &block)
26
+ raise StandardError, "Must have block argument" if !block_given?
27
+ find(type, name, options = {}, &block)
28
+ end
29
+
30
+ instance_eval do
31
+ NODES.each_pair do |key, value|
32
+ case value
33
+ when Array
34
+ define_method :"find_#{key}" do |name, options|
35
+ find_by(value[1], value[0], name, options)
36
+ end
37
+ else
38
+ define_method :"find_#{key}" do |name, options|
39
+ find_by(:identifier, value, name, options)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def find_by(key, type, name, options)
46
+ options.merge!(key => name)
47
+ get_obj.select(type, options).first
48
+ end
49
+
50
+ def find_block(name, options = {})
51
+ options.merge!(:block => true) if !options.has_key?(:block_params)
52
+ find_by(:identifier, Ruby::Call, name, options)
53
+ end
54
+
55
+ alias_method :find_method, :find_def
56
+
57
+ protected
58
+
59
+ def inside_indent
60
+ if self.class == Ruby::Class
61
+ pos = ldelim.position.col
62
+ return pos
63
+ end
64
+ 2
65
+ end
66
+
67
+ def get_obj(options = {})
68
+ return self.block if self.class == Ruby::Method
69
+ self
70
+ end
71
+
72
+ end
73
+ end
data/lib/rails/api.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rails/gemfile'
2
+
3
+ module RubyCodeAPI
4
+ module Rails
5
+ include Gemfile
6
+ end
7
+ end
8
+
9
+ module Ruby
10
+ class Node
11
+ include RubyCodeAPI::Rails
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- module RubyAPI
1
+ module RubyCodeAPI
2
2
  module Rails
3
3
  module Gemfile
4
4
  def inside_group(name, &block)
@@ -1,4 +1,2 @@
1
1
  require 'ripper2ruby'
2
- require 'traversal/mixin'
3
- require 'traversal/api/traversal'
4
- require 'mutate/api'
2
+ require 'config/api'
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruby_traverser}
8
- s.version = "0.1.1"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kristian Mandrup"]
12
- s.date = %q{2010-04-27}
12
+ s.date = %q{2010-04-28}
13
13
  s.description = %q{traverse a ruby code model and optionally mutate it along the way using a nice rubyish DSL}
14
14
  s.email = %q{kmandrup@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -21,22 +21,30 @@ Gem::Specification.new do |s|
21
21
  "LICENSE",
22
22
  "README.markdown",
23
23
  "Rakefile",
24
+ "TODO.txt",
24
25
  "VERSION",
25
- "lib/mutate/api.rb",
26
- "lib/mutate/replacer.rb",
27
- "lib/rails/api_wrapper.rb",
26
+ "lib/config/api.rb",
27
+ "lib/config/has_ext.rb",
28
+ "lib/config/misc.rb",
29
+ "lib/config/mixin.rb",
30
+ "lib/config/module.rb",
31
+ "lib/config/patch.rb",
32
+ "lib/manipulation/api.rb",
33
+ "lib/manipulation/delete/api.rb",
34
+ "lib/manipulation/helpers.rb",
35
+ "lib/manipulation/insert/api.rb",
36
+ "lib/manipulation/position.rb",
37
+ "lib/manipulation/update/api.rb",
38
+ "lib/query/api.rb",
39
+ "lib/query/find.rb",
40
+ "lib/rails/api.rb",
41
+ "lib/rails/gemfile.rb",
28
42
  "lib/ruby_traverser.rb",
29
- "lib/traversal/api/finders.rb",
30
- "lib/traversal/api/inside.rb",
31
- "lib/traversal/api/traversal.rb",
32
- "lib/traversal/misc.rb",
33
- "lib/traversal/mixin.rb",
34
- "lib/traversal/module.rb",
35
- "lib/traversal/patch.rb",
36
43
  "ruby_traverser.gemspec",
37
44
  "spec/ruby_traverser_spec.rb",
38
45
  "spec/spec.opts",
39
46
  "spec/spec_helper.rb",
47
+ "test/mutate/delete_test.rb",
40
48
  "test/mutate/mutate_class_test.rb",
41
49
  "test/mutate/mutate_test.rb",
42
50
  "test/rails_api/gemfile_api.rb",
@@ -52,6 +60,7 @@ Gem::Specification.new do |s|
52
60
  s.test_files = [
53
61
  "spec/ruby_traverser_spec.rb",
54
62
  "spec/spec_helper.rb",
63
+ "test/mutate/delete_test.rb",
55
64
  "test/mutate/mutate_class_test.rb",
56
65
  "test/mutate/mutate_test.rb",
57
66
  "test/rails_api/gemfile_api.rb",
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'yaml'
3
+
4
+ class TraversalTest < Test::Unit::TestCase
5
+ include TestHelper
6
+
7
+
8
+ define_method :"test find assignment in method definition and replace value of right side" do
9
+ src = %q{
10
+ def hello_world(a)
11
+ my_var = 2
12
+ end
13
+ }
14
+
15
+ code = Ripper::RubyBuilder.build(src)
16
+
17
+ code.inside(:def, 'hello_world', :params => ['a']) do |b|
18
+ # call_node = b.find_call('gem', :args => ['ripper', {:src => 'github'}], :verbose => true)
19
+ ass_node = b.find(:assignment, 'my_var')
20
+ assert_equal Ruby::Assignment, ass_node.class
21
+ ass_node.delete
22
+ puts b.to_ruby
23
+ end
24
+ end
25
+ end