synvert 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/synvert.rb +1 -0
- data/lib/synvert/node_ext.rb +29 -33
- data/lib/synvert/rewriter.rb +16 -8
- data/lib/synvert/rewriter/action.rb +83 -35
- data/lib/synvert/rewriter/condition.rb +16 -13
- data/lib/synvert/rewriter/instance.rb +60 -22
- data/lib/synvert/rewriter/scope.rb +24 -8
- data/lib/synvert/snippets/factory_girl/syntax_methods.rb +20 -9
- data/lib/synvert/snippets/rails/upgrade_3_2_to_4_0.rb +153 -77
- data/lib/synvert/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/synvert/node_ext_spec.rb +15 -44
- data/spec/synvert/rewriter/action_spec.rb +146 -46
- data/spec/synvert/rewriter/condition_spec.rb +32 -15
- data/spec/synvert/rewriter/instance_spec.rb +74 -35
- data/spec/synvert/rewriter/scope_spec.rb +16 -12
- data/spec/synvert/rewriter_spec.rb +5 -4
- data/spec/synvert/snippets/factory_girl/syntax_methods_spec.rb +154 -0
- data/spec/synvert/snippets/rails/upgrade_3_2_to_4_0_spec.rb +268 -0
- data/synvert.gemspec +2 -0
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a16d51a6f5e6376390a8d50951f1adaca53f6737
|
4
|
+
data.tar.gz: 295744d184ca5bbea1cf1faa0fef5428aeb8a402
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 259cbde5e5e77897abcf7f7c3c0557f9ecfe13ce4ba0638035dbf96147c536465818044cdaa10cc648bd487bf54806aca1c8d1208a358c6a4d530ef8c56892c8
|
7
|
+
data.tar.gz: 52fc74d210b5236e2cccb196d460a485421c99f47d7d825b86d86edb4f6813195cea04b5bd1b5a3ae8ecd2dd6e59fb9250aef407266b12ed0952968d2be8affd
|
data/CHANGELOG.md
CHANGED
data/lib/synvert.rb
CHANGED
data/lib/synvert/node_ext.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Parser::AST::Node
|
2
2
|
def name
|
3
|
-
if :class
|
3
|
+
if [:class, :def].include? self.type
|
4
4
|
self.children[0]
|
5
5
|
else
|
6
6
|
raise NotImplementedError.new "name is not handled for #{self.inspect}"
|
@@ -60,25 +60,9 @@ class Parser::AST::Node
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
64
|
-
self
|
65
|
-
|
66
|
-
|
67
|
-
def to_s
|
68
|
-
case self.type
|
69
|
-
when :const
|
70
|
-
self.children.compact.map(&:to_s).join('::')
|
71
|
-
when :sym
|
72
|
-
':' + self.children[0].to_s
|
73
|
-
when :str
|
74
|
-
"'" + self.children[0].to_s + "'"
|
75
|
-
when :arg, :lvar, :ivar
|
76
|
-
self.children[0].to_s
|
77
|
-
when :self
|
78
|
-
'self'
|
79
|
-
when :send
|
80
|
-
self.children[1].to_s
|
81
|
-
else
|
63
|
+
def source(instance)
|
64
|
+
if self.loc.expression
|
65
|
+
instance.current_source[self.loc.expression.begin_pos...self.loc.expression.end_pos]
|
82
66
|
end
|
83
67
|
end
|
84
68
|
|
@@ -101,21 +85,21 @@ class Parser::AST::Node
|
|
101
85
|
end
|
102
86
|
end
|
103
87
|
|
104
|
-
def match?(options)
|
88
|
+
def match?(instance, options)
|
105
89
|
flat_hash(options).keys.all? do |multi_keys|
|
106
90
|
if multi_keys.last == :any
|
107
|
-
actual_values = actual_value(self, multi_keys[0...-1])
|
91
|
+
actual_values = actual_value(self, instance, multi_keys[0...-1])
|
108
92
|
expected = expected_value(options, multi_keys)
|
109
|
-
actual_values.any? { |actual| match_value?(actual, expected) }
|
93
|
+
actual_values.any? { |actual| match_value?(instance, actual, expected) }
|
110
94
|
else
|
111
|
-
actual = actual_value(self, multi_keys)
|
95
|
+
actual = actual_value(self, instance, multi_keys)
|
112
96
|
expected = expected_value(options, multi_keys)
|
113
|
-
match_value?(actual, expected)
|
97
|
+
match_value?(instance, actual, expected)
|
114
98
|
end
|
115
99
|
end
|
116
100
|
end
|
117
101
|
|
118
|
-
def
|
102
|
+
def rewritten_source(code)
|
119
103
|
code.gsub(/{{(.*?)}}/m) do
|
120
104
|
evaluated = self.instance_eval $1
|
121
105
|
case evaluated
|
@@ -128,23 +112,31 @@ class Parser::AST::Node
|
|
128
112
|
when String
|
129
113
|
evaluated
|
130
114
|
else
|
131
|
-
raise NotImplementedError.new "
|
115
|
+
raise NotImplementedError.new "rewritten_source is not handled for #{evaluated.inspect}"
|
132
116
|
end
|
133
117
|
end
|
134
118
|
end
|
135
119
|
|
136
120
|
private
|
137
121
|
|
138
|
-
def match_value?(actual, expected)
|
122
|
+
def match_value?(instance, actual, expected)
|
139
123
|
case expected
|
140
124
|
when Symbol
|
141
125
|
actual.to_sym == expected
|
142
126
|
when String
|
143
|
-
|
127
|
+
if Parser::AST::Node === actual
|
128
|
+
actual.source(instance) == expected
|
129
|
+
else
|
130
|
+
actual.to_s == expected
|
131
|
+
end
|
144
132
|
when Regexp
|
145
|
-
|
133
|
+
if Parser::AST::Node === actual
|
134
|
+
actual.source(instance) =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
135
|
+
else
|
136
|
+
actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
|
137
|
+
end
|
146
138
|
when Array
|
147
|
-
actual.zip(expected).all? { |a, e| match_value?(a, e) }
|
139
|
+
actual.zip(expected).all? { |a, e| match_value?(instance, a, e) }
|
148
140
|
when NilClass
|
149
141
|
actual.nil?
|
150
142
|
when Parser::AST::Node
|
@@ -166,8 +158,12 @@ private
|
|
166
158
|
new_hash
|
167
159
|
end
|
168
160
|
|
169
|
-
def actual_value(node, multi_keys)
|
170
|
-
multi_keys.inject(node) { |n, key|
|
161
|
+
def actual_value(node, instance, multi_keys)
|
162
|
+
multi_keys.inject(node) { |n, key|
|
163
|
+
if n
|
164
|
+
key == :source ? n.send(key, instance) : n.send(key)
|
165
|
+
end
|
166
|
+
}
|
171
167
|
end
|
172
168
|
|
173
169
|
def expected_value(options, multi_keys)
|
data/lib/synvert/rewriter.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Synvert
|
2
2
|
class Rewriter
|
3
3
|
autoload :Action, 'synvert/rewriter/action'
|
4
|
+
autoload :AppendAction, 'synvert/rewriter/action'
|
4
5
|
autoload :InsertAction, 'synvert/rewriter/action'
|
5
6
|
autoload :InsertAfterAction, 'synvert/rewriter/action'
|
6
7
|
autoload :ReplaceWithAction, 'synvert/rewriter/action'
|
@@ -20,14 +21,21 @@ module Synvert
|
|
20
21
|
|
21
22
|
def initialize(description, &block)
|
22
23
|
@description = description
|
23
|
-
@
|
24
|
-
|
24
|
+
@block = block
|
25
|
+
@assignments = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def set(name, key, value)
|
29
|
+
@assignments[name] ||= {}
|
30
|
+
@assignments[name][key] = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(name, key)
|
34
|
+
@assignments[name] and @assignments[name][key]
|
25
35
|
end
|
26
36
|
|
27
37
|
def process
|
28
|
-
|
29
|
-
@instances.each { |instance| instance.process }
|
30
|
-
end
|
38
|
+
self.instance_eval &@block
|
31
39
|
end
|
32
40
|
|
33
41
|
def gem_spec(name, version)
|
@@ -35,9 +43,9 @@ module Synvert
|
|
35
43
|
end
|
36
44
|
|
37
45
|
def within_file(file_pattern, &block)
|
38
|
-
|
39
|
-
|
40
|
-
|
46
|
+
if @gem_spec.match?
|
47
|
+
Rewriter::Instance.new(self, file_pattern, &block).process
|
48
|
+
end
|
41
49
|
end
|
42
50
|
|
43
51
|
alias within_files within_file
|
@@ -2,42 +2,92 @@
|
|
2
2
|
|
3
3
|
module Synvert
|
4
4
|
class Rewriter::Action
|
5
|
-
def initialize(code)
|
5
|
+
def initialize(instance, code)
|
6
|
+
@instance = instance
|
6
7
|
@code = code
|
8
|
+
@node = @instance.current_node
|
7
9
|
end
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
+
def line
|
12
|
+
@node.loc.expression.line
|
13
|
+
end
|
14
|
+
|
15
|
+
def rewritten_code
|
16
|
+
if rewritten_source.split("\n").length > 1
|
17
|
+
"\n\n" + rewritten_source.split("\n").map { |line|
|
18
|
+
indent(@node) + line
|
19
|
+
}.join("\n")
|
20
|
+
else
|
21
|
+
"\n" + indent(@node) + rewritten_source
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def rewritten_source
|
26
|
+
@rewritten_source ||= @node.rewritten_source(@code)
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(action)
|
30
|
+
self.begin_pos <=> action.begin_pos
|
11
31
|
end
|
12
32
|
end
|
13
33
|
|
14
34
|
class Rewriter::ReplaceWithAction < Rewriter::Action
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
35
|
+
def begin_pos
|
36
|
+
@node.loc.expression.begin_pos
|
37
|
+
end
|
38
|
+
|
39
|
+
def end_pos
|
40
|
+
@node.loc.expression.end_pos
|
41
|
+
end
|
42
|
+
|
43
|
+
def rewritten_code
|
44
|
+
@node.rewritten_source(@code)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Rewriter::AppendAction < Rewriter::Action
|
49
|
+
def begin_pos
|
50
|
+
@node.loc.expression.end_pos - 4
|
51
|
+
end
|
52
|
+
|
53
|
+
def end_pos
|
54
|
+
begin_pos
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def indent(node)
|
60
|
+
if [:block, :class].include? node.type
|
61
|
+
' ' * (node.indent + 2)
|
62
|
+
else
|
63
|
+
' ' * node.indent
|
64
|
+
end
|
20
65
|
end
|
21
66
|
end
|
22
67
|
|
23
68
|
class Rewriter::InsertAction < Rewriter::Action
|
24
|
-
def
|
25
|
-
|
26
|
-
|
69
|
+
def begin_pos
|
70
|
+
insert_position(@node)
|
71
|
+
end
|
72
|
+
|
73
|
+
def end_pos
|
74
|
+
begin_pos
|
27
75
|
end
|
28
76
|
|
77
|
+
private
|
78
|
+
|
29
79
|
def insert_position(node)
|
30
80
|
case node.type
|
31
81
|
when :block
|
32
|
-
node.children[1].loc.expression.end_pos
|
82
|
+
node.children[1].children.empty? ? node.children[0].loc.expression.end_pos + 3 : node.children[1].loc.expression.end_pos
|
33
83
|
when :class
|
34
|
-
node.children[0].loc.expression.end_pos
|
84
|
+
node.children[1] ? node.children[1].loc.expression.end_pos : node.children[0].loc.expression.end_pos
|
35
85
|
else
|
36
86
|
node.children.last.loc.expression.end_pos
|
37
87
|
end
|
38
88
|
end
|
39
89
|
|
40
|
-
def
|
90
|
+
def indent(node)
|
41
91
|
if [:block, :class].include? node.type
|
42
92
|
' ' * (node.indent + 2)
|
43
93
|
else
|
@@ -47,38 +97,36 @@ module Synvert
|
|
47
97
|
end
|
48
98
|
|
49
99
|
class Rewriter::InsertAfterAction < Rewriter::Action
|
50
|
-
def
|
51
|
-
|
52
|
-
source
|
100
|
+
def begin_pos
|
101
|
+
@node.loc.expression.end_pos
|
53
102
|
end
|
54
103
|
|
55
|
-
def
|
104
|
+
def end_pos
|
105
|
+
begin_pos
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def indent(node)
|
56
111
|
' ' * node.indent
|
57
112
|
end
|
58
113
|
end
|
59
114
|
|
60
115
|
class Rewriter::RemoveAction < Rewriter::Action
|
61
|
-
def initialize
|
116
|
+
def initialize(instance, code=nil)
|
117
|
+
super
|
62
118
|
end
|
63
119
|
|
64
|
-
def
|
65
|
-
|
66
|
-
end_pos = node.loc.expression.end_pos
|
67
|
-
line = node.loc.expression.line
|
68
|
-
source[begin_pos...end_pos] = ''
|
69
|
-
remove_code_or_whole_line(source, line)
|
120
|
+
def begin_pos
|
121
|
+
@node.loc.expression.begin_pos
|
70
122
|
end
|
71
123
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
source_arr.join("\n") + (newline_at_end_of_line ? "\n" : '')
|
79
|
-
else
|
80
|
-
source
|
81
|
-
end
|
124
|
+
def end_pos
|
125
|
+
@node.loc.expression.end_pos
|
126
|
+
end
|
127
|
+
|
128
|
+
def rewritten_code
|
129
|
+
''
|
82
130
|
end
|
83
131
|
end
|
84
132
|
end
|
@@ -2,28 +2,31 @@
|
|
2
2
|
|
3
3
|
module Synvert
|
4
4
|
class Rewriter::Condition
|
5
|
-
def initialize(options)
|
5
|
+
def initialize(instance, options, &block)
|
6
|
+
@instance = instance
|
6
7
|
@options = options
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def process
|
12
|
+
@instance.instance_eval &@block if match?
|
7
13
|
end
|
8
14
|
end
|
9
15
|
|
10
16
|
class Rewriter::UnlessExistCondition < Rewriter::Condition
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
!match
|
18
|
-
}
|
17
|
+
def match?
|
18
|
+
match = false
|
19
|
+
@instance.current_node.recursive_children do |child_node|
|
20
|
+
match = match || (child_node && child_node.match?(@instance, @options))
|
21
|
+
end
|
22
|
+
!match
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
class Rewriter::IfOnlyExistCondition < Rewriter::Condition
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
}
|
27
|
+
def match?
|
28
|
+
:begin != @instance.current_node.body.type &&
|
29
|
+
@instance.current_node.body.match?(@instance, @options)
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
@@ -2,64 +2,102 @@
|
|
2
2
|
|
3
3
|
module Synvert
|
4
4
|
class Rewriter::Instance
|
5
|
-
|
5
|
+
attr_accessor :current_node, :current_source, :current_file
|
6
|
+
|
7
|
+
def initialize(rewriter, file_pattern, &block)
|
8
|
+
@rewriter = rewriter
|
9
|
+
@actions = []
|
6
10
|
@file_pattern = file_pattern
|
7
|
-
@
|
11
|
+
@block = block
|
8
12
|
end
|
9
13
|
|
10
14
|
def process
|
11
15
|
parser = Parser::CurrentRuby.new
|
12
16
|
file_pattern = File.join(Configuration.instance.get(:path), @file_pattern)
|
13
|
-
Dir.glob(file_pattern).each do |
|
14
|
-
source = File.read(
|
15
|
-
buffer = Parser::Source::Buffer.new
|
17
|
+
Dir.glob(file_pattern).each do |file_path|
|
18
|
+
source = File.read(file_path)
|
19
|
+
buffer = Parser::Source::Buffer.new file_path
|
16
20
|
buffer.source = source
|
17
21
|
|
18
22
|
parser.reset
|
19
23
|
ast = parser.parse buffer
|
20
24
|
|
21
|
-
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
@current_file = file_path
|
26
|
+
@current_source = source
|
27
|
+
@current_node = ast
|
28
|
+
instance_eval &@block
|
29
|
+
@current_node = ast
|
30
|
+
|
31
|
+
@actions.sort.reverse.each do |action|
|
32
|
+
source[action.begin_pos...action.end_pos] = action.rewritten_code
|
33
|
+
source = remove_code_or_whole_line(source, action.line)
|
27
34
|
end
|
28
|
-
|
35
|
+
@actions = []
|
36
|
+
|
37
|
+
File.write file_path, source
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
41
|
+
def node
|
42
|
+
@current_node
|
43
|
+
end
|
44
|
+
|
32
45
|
def within_node(options, &block)
|
33
|
-
|
34
|
-
instance_eval &block if block_given?
|
46
|
+
Rewriter::Scope.new(self, options, &block).process
|
35
47
|
end
|
36
48
|
|
37
49
|
alias with_node within_node
|
38
50
|
|
39
51
|
def unless_exist_node(options, &block)
|
40
|
-
|
41
|
-
instance_eval &block if block_given?
|
52
|
+
Rewriter::UnlessExistCondition.new(self, options, &block).process
|
42
53
|
end
|
43
54
|
|
44
55
|
def if_only_exist_node(options, &block)
|
45
|
-
|
46
|
-
|
56
|
+
Rewriter::IfOnlyExistCondition.new(self, options, &block).process
|
57
|
+
end
|
58
|
+
|
59
|
+
def append(code)
|
60
|
+
@actions << Rewriter::AppendAction.new(self, code)
|
47
61
|
end
|
48
62
|
|
49
63
|
def insert(code)
|
50
|
-
@
|
64
|
+
@actions << Rewriter::InsertAction.new(self, code)
|
51
65
|
end
|
52
66
|
|
53
67
|
def insert_after(node)
|
54
|
-
@
|
68
|
+
@actions << Rewriter::InsertAfterAction.new(self, node)
|
55
69
|
end
|
56
70
|
|
57
71
|
def replace_with(code)
|
58
|
-
@
|
72
|
+
@actions << Rewriter::ReplaceWithAction.new(self, code)
|
59
73
|
end
|
60
74
|
|
61
75
|
def remove
|
62
|
-
@
|
76
|
+
@actions << Rewriter::RemoveAction.new(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
def assign(name, key, value)
|
80
|
+
@rewriter.set name, key, value
|
81
|
+
end
|
82
|
+
|
83
|
+
def fetch(name, key)
|
84
|
+
@rewriter.get name, key
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def remove_code_or_whole_line(source, line)
|
90
|
+
newline_at_end_of_line = source[-1] == "\n"
|
91
|
+
source_arr = source.split("\n")
|
92
|
+
if source_arr[line - 1] && source_arr[line - 1].strip.empty?
|
93
|
+
source_arr.delete_at(line - 1)
|
94
|
+
if source_arr[line - 2] && source_arr[line - 2].strip.empty? && source_arr[line - 1] && source_arr[line - 1].strip.empty?
|
95
|
+
source_arr.delete_at(line - 1)
|
96
|
+
end
|
97
|
+
source_arr.join("\n") + (newline_at_end_of_line ? "\n" : '')
|
98
|
+
else
|
99
|
+
source
|
100
|
+
end
|
63
101
|
end
|
64
102
|
end
|
65
103
|
end
|