synvert 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +4 -2
- data/lib/synvert/cli.rb +87 -18
- data/lib/synvert/configuration.rb +9 -0
- data/lib/synvert/exceptions.rb +13 -0
- data/lib/synvert/node_ext.rb +106 -13
- data/lib/synvert/rewriter/action.rb +76 -0
- data/lib/synvert/rewriter/condition.rb +14 -0
- data/lib/synvert/rewriter/gem_spec.rb +12 -1
- data/lib/synvert/rewriter/instance.rb +69 -0
- data/lib/synvert/rewriter/scope.rb +10 -0
- data/lib/synvert/rewriter.rb +120 -11
- data/lib/synvert/snippets/factory_girl/syntax_methods.rb +51 -1
- data/lib/synvert/snippets/rails/convert_dynamic_finders.rb +12 -1
- data/lib/synvert/snippets/rails/strong_parameters.rb +22 -1
- data/lib/synvert/snippets/rails/upgrade_3_0_to_3_1.rb +48 -7
- data/lib/synvert/snippets/rails/upgrade_3_1_to_3_2.rb +14 -1
- data/lib/synvert/snippets/rails/upgrade_3_2_to_4_0.rb +90 -19
- data/lib/synvert/snippets/rspec/be_close_to_be_within.rb +7 -1
- data/lib/synvert/snippets/rspec/block_to_expect.rb +9 -1
- data/lib/synvert/snippets/rspec/boolean_matcher.rb +8 -1
- data/lib/synvert/snippets/rspec/collection_matcher.rb +12 -1
- data/lib/synvert/snippets/rspec/its_to_it.rb +34 -1
- data/lib/synvert/snippets/rspec/message_expectation.rb +14 -1
- data/lib/synvert/snippets/rspec/method_stub.rb +22 -1
- data/lib/synvert/snippets/rspec/negative_error_expectation.rb +8 -1
- data/lib/synvert/snippets/rspec/new_syntax.rb +5 -1
- data/lib/synvert/snippets/rspec/one_liner_expectation.rb +20 -1
- data/lib/synvert/snippets/rspec/should_to_expect.rb +15 -1
- data/lib/synvert/snippets/rspec/stub_and_mock_to_double.rb +8 -1
- data/lib/synvert/snippets/ruby/new_hash_syntax.rb +7 -1
- data/lib/synvert/snippets/ruby/new_lambda_syntax.rb +7 -1
- data/lib/synvert/version.rb +1 -1
- data/lib/synvert.rb +2 -1
- data/spec/synvert/rewriter/gem_spec_spec.rb +2 -2
- data/spec/synvert/rewriter_spec.rb +71 -34
- data/spec/synvert/snippets/rails/upgrade_3_0_to_3_1_spec.rb +2 -2
- metadata +4 -4
- data/lib/synvert/rewriter_not_found.rb +0 -4
@@ -1,17 +1,28 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Synvert
|
4
|
+
# Action defines rewriter action, add, replace or remove code.
|
4
5
|
class Rewriter::Action
|
6
|
+
# Initialize an action.
|
7
|
+
#
|
8
|
+
# @param instance [Synvert::Rewriter::Instance]
|
9
|
+
# @param code {String] new code to add, replace or remove.
|
5
10
|
def initialize(instance, code)
|
6
11
|
@instance = instance
|
7
12
|
@code = code
|
8
13
|
@node = @instance.current_node
|
9
14
|
end
|
10
15
|
|
16
|
+
# Line number of the node.
|
17
|
+
#
|
18
|
+
# @return [Integer] line number.
|
11
19
|
def line
|
12
20
|
@node.loc.expression.line
|
13
21
|
end
|
14
22
|
|
23
|
+
# The rewritten source code with proper indent.
|
24
|
+
#
|
25
|
+
# @return [String] rewritten code.
|
15
26
|
def rewritten_code
|
16
27
|
if rewritten_source.split("\n").length > 1
|
17
28
|
"\n\n" + rewritten_source.split("\n").map { |line|
|
@@ -22,24 +33,41 @@ module Synvert
|
|
22
33
|
end
|
23
34
|
end
|
24
35
|
|
36
|
+
# The rewritten source code.
|
37
|
+
#
|
38
|
+
# @return [String] rewritten source code.
|
25
39
|
def rewritten_source
|
26
40
|
@rewritten_source ||= @node.rewritten_source(@code)
|
27
41
|
end
|
28
42
|
|
43
|
+
# Compare actions by begin position.
|
44
|
+
#
|
45
|
+
# @param action [Synvert::Rewriter::Action]
|
46
|
+
# @return [Integer] -1, 0 or 1
|
29
47
|
def <=>(action)
|
30
48
|
self.begin_pos <=> action.begin_pos
|
31
49
|
end
|
32
50
|
end
|
33
51
|
|
52
|
+
# ReplaceWithAction to replace code.
|
34
53
|
class Rewriter::ReplaceWithAction < Rewriter::Action
|
54
|
+
# Begin position of code to replace.
|
55
|
+
#
|
56
|
+
# @return [Integer] begin position.
|
35
57
|
def begin_pos
|
36
58
|
@node.loc.expression.begin_pos
|
37
59
|
end
|
38
60
|
|
61
|
+
# End position of code to replace.
|
62
|
+
#
|
63
|
+
# @return [Integer] end position.
|
39
64
|
def end_pos
|
40
65
|
@node.loc.expression.end_pos
|
41
66
|
end
|
42
67
|
|
68
|
+
# The rewritten source code with proper indent.
|
69
|
+
#
|
70
|
+
# @return [String] rewritten code.
|
43
71
|
def rewritten_code
|
44
72
|
if rewritten_source.split("\n").length > 1
|
45
73
|
"\n\n" + rewritten_source.split("\n").map { |line|
|
@@ -52,12 +80,20 @@ module Synvert
|
|
52
80
|
|
53
81
|
private
|
54
82
|
|
83
|
+
# Indent of the node
|
84
|
+
#
|
85
|
+
# @param node [Parser::AST::Node]
|
86
|
+
# @return [String] n times whitesphace
|
55
87
|
def indent(node)
|
56
88
|
' ' * node.indent
|
57
89
|
end
|
58
90
|
end
|
59
91
|
|
92
|
+
# AppendWithAction to append code to the bottom of node body.
|
60
93
|
class Rewriter::AppendAction < Rewriter::Action
|
94
|
+
# Begin position to append code.
|
95
|
+
#
|
96
|
+
# @return [Integer] begin position.
|
61
97
|
def begin_pos
|
62
98
|
if :begin == @node.type
|
63
99
|
@node.loc.expression.end_pos
|
@@ -66,12 +102,19 @@ module Synvert
|
|
66
102
|
end
|
67
103
|
end
|
68
104
|
|
105
|
+
# End position, always same to begin position.
|
106
|
+
#
|
107
|
+
# @return [Integer] end position.
|
69
108
|
def end_pos
|
70
109
|
begin_pos
|
71
110
|
end
|
72
111
|
|
73
112
|
private
|
74
113
|
|
114
|
+
# Indent of the node.
|
115
|
+
#
|
116
|
+
# @param node [Parser::AST::Node]
|
117
|
+
# @return [String] n times whitesphace
|
75
118
|
def indent(node)
|
76
119
|
if [:block, :class].include? node.type
|
77
120
|
' ' * (node.indent + 2)
|
@@ -81,17 +124,27 @@ module Synvert
|
|
81
124
|
end
|
82
125
|
end
|
83
126
|
|
127
|
+
# InsertAction to insert code to the top of node body.
|
84
128
|
class Rewriter::InsertAction < Rewriter::Action
|
129
|
+
# Begin position to insert code.
|
130
|
+
#
|
131
|
+
# @return [Integer] begin position.
|
85
132
|
def begin_pos
|
86
133
|
insert_position(@node)
|
87
134
|
end
|
88
135
|
|
136
|
+
# End position, always same to begin position.
|
137
|
+
#
|
138
|
+
# @return [Integer] end position.
|
89
139
|
def end_pos
|
90
140
|
begin_pos
|
91
141
|
end
|
92
142
|
|
93
143
|
private
|
94
144
|
|
145
|
+
# Insert position.
|
146
|
+
#
|
147
|
+
# @return [Integer] insert position.
|
95
148
|
def insert_position(node)
|
96
149
|
case node.type
|
97
150
|
when :block
|
@@ -103,6 +156,10 @@ module Synvert
|
|
103
156
|
end
|
104
157
|
end
|
105
158
|
|
159
|
+
# Indent of the node.
|
160
|
+
#
|
161
|
+
# @param node [Parser::AST::Node]
|
162
|
+
# @return [String] n times whitesphace
|
106
163
|
def indent(node)
|
107
164
|
if [:block, :class].include? node.type
|
108
165
|
' ' * (node.indent + 2)
|
@@ -112,35 +169,54 @@ module Synvert
|
|
112
169
|
end
|
113
170
|
end
|
114
171
|
|
172
|
+
# InsertAfterAction to insert code next to the node.
|
115
173
|
class Rewriter::InsertAfterAction < Rewriter::Action
|
174
|
+
# Begin position to insert code.
|
175
|
+
#
|
176
|
+
# @return [Integer] begin position.
|
116
177
|
def begin_pos
|
117
178
|
@node.loc.expression.end_pos
|
118
179
|
end
|
119
180
|
|
181
|
+
# End position, always same to begin position.
|
182
|
+
#
|
183
|
+
# @return [Integer] end position.
|
120
184
|
def end_pos
|
121
185
|
begin_pos
|
122
186
|
end
|
123
187
|
|
124
188
|
private
|
125
189
|
|
190
|
+
# Indent of the node.
|
191
|
+
#
|
192
|
+
# @param node [Parser::AST::Node]
|
193
|
+
# @return [String] n times whitesphace
|
126
194
|
def indent(node)
|
127
195
|
' ' * node.indent
|
128
196
|
end
|
129
197
|
end
|
130
198
|
|
199
|
+
# RemoveAction to remove code.
|
131
200
|
class Rewriter::RemoveAction < Rewriter::Action
|
132
201
|
def initialize(instance, code=nil)
|
133
202
|
super
|
134
203
|
end
|
135
204
|
|
205
|
+
# Begin position of code to replace.
|
206
|
+
#
|
207
|
+
# @return [Integer] begin position.
|
136
208
|
def begin_pos
|
137
209
|
@node.loc.expression.begin_pos
|
138
210
|
end
|
139
211
|
|
212
|
+
# End position of code to replace.
|
213
|
+
#
|
214
|
+
# @return [Integer] end position.
|
140
215
|
def end_pos
|
141
216
|
@node.loc.expression.end_pos
|
142
217
|
end
|
143
218
|
|
219
|
+
# The rewritten code, always empty string.
|
144
220
|
def rewritten_code
|
145
221
|
''
|
146
222
|
end
|
@@ -1,19 +1,29 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Synvert
|
4
|
+
# Condition checks if rules matches.
|
4
5
|
class Rewriter::Condition
|
6
|
+
# Initialize a condition.
|
7
|
+
#
|
8
|
+
# @param instance [Synvert::Rewriter::Instance]
|
9
|
+
# @param rules [Hash]
|
10
|
+
# @param block [Block]
|
11
|
+
# @return [Synvert::Rewriter::Condition]
|
5
12
|
def initialize(instance, rules, &block)
|
6
13
|
@instance = instance
|
7
14
|
@rules = rules
|
8
15
|
@block = block
|
9
16
|
end
|
10
17
|
|
18
|
+
# If condition matches, run the block code.
|
11
19
|
def process
|
12
20
|
@instance.instance_eval &@block if match?
|
13
21
|
end
|
14
22
|
end
|
15
23
|
|
24
|
+
# IfExistCondition checks if matching node exists in the node children.
|
16
25
|
class Rewriter::IfExistCondition < Rewriter::Condition
|
26
|
+
# check if any child node matches the rules.
|
17
27
|
def match?
|
18
28
|
match = false
|
19
29
|
@instance.current_node.recursive_children do |child_node|
|
@@ -23,7 +33,9 @@ module Synvert
|
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
36
|
+
# UnlessExistCondition checks if matching node doesn't exist in the node children.
|
26
37
|
class Rewriter::UnlessExistCondition < Rewriter::Condition
|
38
|
+
# check if none of child node matches the rules.
|
27
39
|
def match?
|
28
40
|
match = false
|
29
41
|
@instance.current_node.recursive_children do |child_node|
|
@@ -33,7 +45,9 @@ module Synvert
|
|
33
45
|
end
|
34
46
|
end
|
35
47
|
|
48
|
+
# IfExistCondition checks if node has only one child node and the child node matches rules.
|
36
49
|
class Rewriter::IfOnlyExistCondition < Rewriter::Condition
|
50
|
+
# check if only have one child node and the child node matches rules.
|
37
51
|
def match?
|
38
52
|
@instance.current_node.body.size == 1 &&
|
39
53
|
@instance.current_node.body.first.match?(@instance, @rules)
|
@@ -1,8 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Synvert
|
4
|
+
# GemSpec checks and compares gem version.
|
4
5
|
class Rewriter::GemSpec
|
5
6
|
OPERATORS = {eq: '==', lt: '<', gt: '>', lte: '<=', gte: '>=', ne: '!='}
|
7
|
+
|
8
|
+
# Initialize a gem_spec.
|
9
|
+
#
|
10
|
+
# @param name [String] gem name
|
11
|
+
# @param comparator [Hash] comparator to gem version, e.g. {eg: '2.0.0'},
|
12
|
+
# comparator key can be eq, lt, gt, lte, gte or ne.
|
6
13
|
def initialize(name, comparator)
|
7
14
|
@name = name
|
8
15
|
if Hash === comparator
|
@@ -14,6 +21,10 @@ module Synvert
|
|
14
21
|
end
|
15
22
|
end
|
16
23
|
|
24
|
+
# Check if the specified gem version in Gemfile.lock matches gem_spec comparator.
|
25
|
+
#
|
26
|
+
# @return [Boolean] true if matches, otherwise false.
|
27
|
+
# @raise [Synvert::GemfileLockNotFound] raise if Gemfile.lock does not exist.
|
17
28
|
def match?
|
18
29
|
gemfile_lock_path = File.join(Configuration.instance.get(:path), 'Gemfile.lock')
|
19
30
|
if File.exists? gemfile_lock_path
|
@@ -24,7 +35,7 @@ module Synvert
|
|
24
35
|
false
|
25
36
|
end
|
26
37
|
else
|
27
|
-
raise
|
38
|
+
raise GemfileLockNotFound.new 'Gemfile.lock does not exist'
|
28
39
|
end
|
29
40
|
end
|
30
41
|
end
|
@@ -1,15 +1,33 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Synvert
|
4
|
+
# Instance is an execution unit, it finds specified ast nodes,
|
5
|
+
# checks if the nodes match some conditions, then add, replace or remove code.
|
6
|
+
#
|
7
|
+
# One instance can contains one or many [Synvert::Rewriter::Scope] and [Synvert::Rewriter::Condition].
|
4
8
|
class Rewriter::Instance
|
9
|
+
# @!attribute [rw] current_node
|
10
|
+
# @return current parsing node
|
11
|
+
# @!attribute [rw] current_source
|
12
|
+
# @return current source code of file
|
13
|
+
# @!attribute [rw] current_file
|
14
|
+
# @return current filename
|
5
15
|
attr_accessor :current_node, :current_source, :current_file
|
6
16
|
|
17
|
+
# Initialize an instance.
|
18
|
+
#
|
19
|
+
# @param file_pattern [String] pattern to find files, e.g. spec/**/*_spec.rb
|
20
|
+
# @param block [Block] block code to find nodes, match conditions and rewrite code.
|
21
|
+
# @return [Synvert::Rewriter::Instance]
|
7
22
|
def initialize(file_pattern, &block)
|
8
23
|
@actions = []
|
9
24
|
@file_pattern = file_pattern
|
10
25
|
@block = block
|
11
26
|
end
|
12
27
|
|
28
|
+
# Process the instance.
|
29
|
+
# It finds all files, for each file, it executes the block code, gets all rewrite actions,
|
30
|
+
# and rewrite source code back to original file.
|
13
31
|
def process
|
14
32
|
parser = Parser::CurrentRuby.new
|
15
33
|
file_pattern = File.join(Configuration.instance.get(:path), @file_pattern)
|
@@ -41,50 +59,97 @@ module Synvert
|
|
41
59
|
end
|
42
60
|
end
|
43
61
|
|
62
|
+
# Gets current node, it allows to get current node in block code.
|
63
|
+
#
|
64
|
+
# @return [Parser::AST::Node]
|
44
65
|
def node
|
45
66
|
@current_node
|
46
67
|
end
|
47
68
|
|
69
|
+
#######
|
70
|
+
# DSL #
|
71
|
+
#######
|
72
|
+
|
73
|
+
# Parse within_node dsl, it creates a [Synvert::Rewriter::Scope] to find matching ast nodes,
|
74
|
+
# then continue operating on each matching ast node.
|
75
|
+
#
|
76
|
+
# @param rules [Hash] rules to find mathing ast nodes.
|
77
|
+
# @param block [Block] block code to continue operating on the matching nodes.
|
48
78
|
def within_node(rules, &block)
|
49
79
|
Rewriter::Scope.new(self, rules, &block).process
|
50
80
|
end
|
51
81
|
|
52
82
|
alias with_node within_node
|
53
83
|
|
84
|
+
# Parse if_exist_node dsl, it creates a [Synvert::Rewriter::IfExistCondition] to check
|
85
|
+
# if matching nodes exist in the child nodes, if so, then continue operating on each matching ast node.
|
86
|
+
#
|
87
|
+
# @param rules [Hash] rules to check mathing ast nodes.
|
88
|
+
# @param block [Block] block code to continue operating on the matching nodes.
|
54
89
|
def if_exist_node(rules, &block)
|
55
90
|
Rewriter::IfExistCondition.new(self, rules, &block).process
|
56
91
|
end
|
57
92
|
|
93
|
+
# Parse unless_exist_node dsl, it creates a [Synvert::Rewriter::UnlessExistCondition] to check
|
94
|
+
# if matching nodes doesn't exist in the child nodes, if so, then continue operating on each matching ast node.
|
95
|
+
#
|
96
|
+
# @param rules [Hash] rules to check mathing ast nodes.
|
97
|
+
# @param block [Block] block code to continue operating on the matching nodes.
|
58
98
|
def unless_exist_node(rules, &block)
|
59
99
|
Rewriter::UnlessExistCondition.new(self, rules, &block).process
|
60
100
|
end
|
61
101
|
|
102
|
+
# Parse if_only_exist_node dsl, it creates a [Synvert::Rewriter::IfOnlyExistCondition] to check
|
103
|
+
# if current node has only one child node and the child node matches rules,
|
104
|
+
# if so, then continue operating on each matching ast node.
|
105
|
+
#
|
106
|
+
# @param rules [Hash] rules to check mathing ast nodes.
|
107
|
+
# @param block [Block] block code to continue operating on the matching nodes.
|
62
108
|
def if_only_exist_node(rules, &block)
|
63
109
|
Rewriter::IfOnlyExistCondition.new(self, rules, &block).process
|
64
110
|
end
|
65
111
|
|
112
|
+
# Parse append dsl, it creates a [Synvert::Rewriter::AppendAction] to
|
113
|
+
# append the code to the bottom of current node body.
|
114
|
+
#
|
115
|
+
# @param code [String] code need to be appended.
|
66
116
|
def append(code)
|
67
117
|
@actions << Rewriter::AppendAction.new(self, code)
|
68
118
|
end
|
69
119
|
|
120
|
+
# Parse insert dsl, it creates a [Synvert::Rewriter::InsertAction] to
|
121
|
+
# insert the code to the top of current node body.
|
122
|
+
#
|
123
|
+
# @param code [String] code need to be inserted.
|
70
124
|
def insert(code)
|
71
125
|
@actions << Rewriter::InsertAction.new(self, code)
|
72
126
|
end
|
73
127
|
|
128
|
+
# Parse insert_after dsl, it creates a [Synvert::Rewriter::InsertAfterAction] to
|
129
|
+
# insert the code next to the current node.
|
130
|
+
#
|
131
|
+
# @param code [String] code need to be inserted.
|
74
132
|
def insert_after(node)
|
75
133
|
@actions << Rewriter::InsertAfterAction.new(self, node)
|
76
134
|
end
|
77
135
|
|
136
|
+
# Parse replace_with dsl, it creates a [Synvert::Rewriter::ReplaceWithAction] to
|
137
|
+
# replace current node with code.
|
138
|
+
#
|
139
|
+
# @param code [String] code need to be replaced with.
|
78
140
|
def replace_with(code)
|
79
141
|
@actions << Rewriter::ReplaceWithAction.new(self, code)
|
80
142
|
end
|
81
143
|
|
144
|
+
# Parse remove dsl, it creates a [Synvert::Rewriter::RemoveAction] to current node.
|
82
145
|
def remove
|
83
146
|
@actions << Rewriter::RemoveAction.new(self)
|
84
147
|
end
|
85
148
|
|
86
149
|
private
|
87
150
|
|
151
|
+
# It changes source code from bottom to top, and it can change source code twice at the same time.
|
152
|
+
# So if there is an overlap between two actions, it removes the conflict actions and operate them in the next loop.
|
88
153
|
def check_conflict_actions
|
89
154
|
i = @actions.length - 1
|
90
155
|
@conflict_actions = []
|
@@ -97,6 +162,10 @@ module Synvert
|
|
97
162
|
@conflict_actions
|
98
163
|
end
|
99
164
|
|
165
|
+
# It checks if code is removed and that line is empty.
|
166
|
+
#
|
167
|
+
# @param source [String] source code of file
|
168
|
+
# @param line [String] the line number
|
100
169
|
def remove_code_or_whole_line(source, line)
|
101
170
|
newline_at_end_of_line = source[-1] == "\n"
|
102
171
|
source_arr = source.split("\n")
|
@@ -1,13 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Synvert
|
4
|
+
# Scope finds the child nodes which match rules.
|
4
5
|
class Rewriter::Scope
|
6
|
+
# Initialize a scope
|
7
|
+
#
|
8
|
+
# @param instance [Synvert::Rewriter::Instance]
|
9
|
+
# @param rules [Hash]
|
10
|
+
# @param block [Block]
|
5
11
|
def initialize(instance, rules, &block)
|
6
12
|
@instance = instance
|
7
13
|
@rules = rules
|
8
14
|
@block = block
|
9
15
|
end
|
10
16
|
|
17
|
+
# Find the matching nodes. It checks the current node and iterates all child nodes,
|
18
|
+
# then run the block code for each matching node.
|
11
19
|
def process
|
12
20
|
current_node = @instance.current_node
|
13
21
|
return unless current_node
|
@@ -27,6 +35,8 @@ module Synvert
|
|
27
35
|
|
28
36
|
private
|
29
37
|
|
38
|
+
# Set instance current node properly and process.
|
39
|
+
# @param node [Parser::AST::Node]
|
30
40
|
def process_with_node(node)
|
31
41
|
@instance.current_node = node
|
32
42
|
yield
|
data/lib/synvert/rewriter.rb
CHANGED
@@ -1,4 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Synvert
|
4
|
+
# Rewriter is the top level namespace in a snippet.
|
5
|
+
#
|
6
|
+
# One Rewriter can contain one or many [Synvert::Rewriter::Instance],
|
7
|
+
# which define the behavior what files and what codes to detect and rewrite to what code.
|
8
|
+
#
|
9
|
+
# Synvert::Rewriter.new 'factory_girl_short_syntax', 'use FactoryGirl short syntax' do
|
10
|
+
# if_gem 'factory_girl', {gte: '2.0.0'}
|
11
|
+
#
|
12
|
+
# within_files 'spec/**/*.rb' do
|
13
|
+
# with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
|
14
|
+
# replace_with "create({{arguments}})"
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
2
18
|
class Rewriter
|
3
19
|
autoload :Action, 'synvert/rewriter/action'
|
4
20
|
autoload :AppendAction, 'synvert/rewriter/action'
|
@@ -19,11 +35,28 @@ module Synvert
|
|
19
35
|
autoload :GemSpec, 'synvert/rewriter/gem_spec'
|
20
36
|
|
21
37
|
class <<self
|
38
|
+
# Register a rewriter with its name.
|
39
|
+
#
|
40
|
+
# @param name [String] the unique rewriter name.
|
41
|
+
# @param rewriter [Synvert::Rewriter] the rewriter to register.
|
22
42
|
def register(name, rewriter)
|
23
43
|
@rewriters ||= {}
|
24
44
|
@rewriters[name.to_s] = rewriter
|
25
45
|
end
|
26
46
|
|
47
|
+
# Fetch a rewriter by name.
|
48
|
+
#
|
49
|
+
# @param name [String] rewrtier name.
|
50
|
+
# @return [Synvert::Rewriter] the matching rewriter.
|
51
|
+
def fetch(name)
|
52
|
+
@rewriters[name.to_s]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get a registered rewriter by name and process that rewriter.
|
56
|
+
#
|
57
|
+
# @param name [String] the rewriter name.
|
58
|
+
# @return [Synvert::Rewriter] the registered rewriter.
|
59
|
+
# @raise [Synvert::RewriterNotFound] if the registered rewriter is not found.
|
27
60
|
def call(name)
|
28
61
|
if (rewriter = @rewriters[name.to_s])
|
29
62
|
rewriter.process
|
@@ -33,34 +66,87 @@ module Synvert
|
|
33
66
|
end
|
34
67
|
end
|
35
68
|
|
69
|
+
# Get all available rewriters
|
70
|
+
#
|
71
|
+
# @return [Array<Synvert::Rewriter>]
|
36
72
|
def availables
|
37
73
|
@rewriters.values
|
38
74
|
end
|
39
75
|
|
76
|
+
# Clear all registered rewriters.
|
40
77
|
def clear
|
41
78
|
@rewriters.clear
|
42
79
|
end
|
43
80
|
end
|
44
81
|
|
45
|
-
|
46
|
-
|
47
|
-
|
82
|
+
# @!attribute [r] name
|
83
|
+
# @return [String] the unique name of rewriter
|
84
|
+
# @!attribute [r] sub_snippets
|
85
|
+
# @return [Array<Synvert::Rewriter>] all rewriters this rewiter calls.
|
86
|
+
attr_reader :name, :sub_snippets
|
87
|
+
|
88
|
+
# Initialize a rewriter.
|
89
|
+
# When a rewriter is initialized, it is also registered.
|
90
|
+
#
|
91
|
+
# @param name [String] name of the rewriter.
|
92
|
+
# @param block [Block] a block defines the behaviors of the rewriter, block code won't be called when initialization.
|
93
|
+
# @return [Synvert::Rewriter]
|
94
|
+
def initialize(name, &block)
|
48
95
|
@name = name
|
49
|
-
@description = description
|
50
96
|
@block = block
|
51
97
|
@helpers = []
|
98
|
+
@sub_snippets = []
|
52
99
|
self.class.register(name, self)
|
53
100
|
end
|
54
101
|
|
102
|
+
# Process the rewriter.
|
103
|
+
# It will call the block.
|
55
104
|
def process
|
56
105
|
self.instance_eval &@block
|
57
106
|
end
|
58
107
|
|
108
|
+
# Process rewriter with sandbox mode.
|
109
|
+
# It will call the block but doesn't change any file.
|
110
|
+
def process_with_sandbox
|
111
|
+
@sandbox = true
|
112
|
+
self.process
|
113
|
+
@sandbox = false
|
114
|
+
end
|
115
|
+
|
116
|
+
#######
|
117
|
+
# DSL #
|
118
|
+
#######
|
119
|
+
|
120
|
+
# Parse description dsl, it sets description of the rewrite.
|
121
|
+
# Or get description.
|
122
|
+
#
|
123
|
+
# @param description [String] rewriter description.
|
124
|
+
# @return rewriter description.
|
125
|
+
def description(description=nil)
|
126
|
+
if description
|
127
|
+
@description = description
|
128
|
+
else
|
129
|
+
@description
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Parse if_gem dsl, it compares version of the specified gem.
|
134
|
+
#
|
135
|
+
# @param name [String] gem name.
|
136
|
+
# @param comparator [Hash] equal, less than or greater than specified version, e.g. {gte: '2.0.0'},
|
137
|
+
# key can be eq, lt, gt, lte, gte or ne.
|
59
138
|
def if_gem(name, comparator)
|
60
139
|
@gem_spec = Rewriter::GemSpec.new(name, comparator)
|
61
140
|
end
|
62
141
|
|
63
|
-
|
142
|
+
# Parse within_files dsl, it finds specified files.
|
143
|
+
# It creates a [Synvert::Rewriter::Instance] to rewrite code.
|
144
|
+
#
|
145
|
+
# @param file_pattern [String] pattern to find files, e.g. spec/**/*_spec.rb
|
146
|
+
# @param block [Block] the block to rewrite code in the matching files.
|
147
|
+
def within_files(file_pattern, &block)
|
148
|
+
return if @sandbox
|
149
|
+
|
64
150
|
if !@gem_spec || @gem_spec.match?
|
65
151
|
instance = Rewriter::Instance.new(file_pattern, &block)
|
66
152
|
@helpers.each { |helper| instance.singleton_class.send(:define_method, helper[:name], &helper[:block]) }
|
@@ -68,24 +154,47 @@ module Synvert
|
|
68
154
|
end
|
69
155
|
end
|
70
156
|
|
71
|
-
|
157
|
+
# Parse within_file dsl, it finds a specifiled file.
|
158
|
+
alias within_file within_files
|
159
|
+
|
160
|
+
# Parses add_file dsl, it adds a new file.
|
161
|
+
#
|
162
|
+
# @param filename [String] file name of newly created file.
|
163
|
+
# @param content [String] file body of newly created file.
|
164
|
+
def add_file(filename, content)
|
165
|
+
return if @sandbox
|
72
166
|
|
73
|
-
|
74
|
-
File.open file, 'w' do |file|
|
167
|
+
File.open filename, 'w' do |file|
|
75
168
|
file.write content
|
76
169
|
end
|
77
170
|
end
|
78
171
|
|
172
|
+
# Parse add_snippet dsl, it calls anther rewriter.
|
173
|
+
#
|
174
|
+
# @param name [String] name of another rewriter.
|
79
175
|
def add_snippet(name)
|
80
|
-
self.class.call(name)
|
176
|
+
@sub_snippets << self.class.call(name)
|
81
177
|
end
|
82
178
|
|
179
|
+
# Parse helper_method dsl, it defines helper method for [Synvert::Rewriter::Instance].
|
180
|
+
#
|
181
|
+
# @param name [String] helper method name.
|
182
|
+
# @param block [Block] helper method block.
|
83
183
|
def helper_method(name, &block)
|
84
184
|
@helpers << {name: name, block: block}
|
85
185
|
end
|
86
186
|
|
87
|
-
|
88
|
-
|
187
|
+
# Parse todo dsl, it sets todo of the rewriter.
|
188
|
+
# Or get todo.
|
189
|
+
#
|
190
|
+
# @param todo_list [String] rewriter todo.
|
191
|
+
# @return [String] rewriter todo.
|
192
|
+
def todo(todo=nil)
|
193
|
+
if todo
|
194
|
+
@todo = todo
|
195
|
+
else
|
196
|
+
@todo
|
197
|
+
end
|
89
198
|
end
|
90
199
|
end
|
91
200
|
end
|