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