synvert 0.0.13 → 0.0.14
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/.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
|