synvert-core 0.15.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -2
- data/Gemfile +2 -0
- data/Guardfile +2 -0
- data/Rakefile +2 -0
- data/lib/synvert/core.rb +2 -3
- data/lib/synvert/core/configuration.rb +2 -1
- data/lib/synvert/core/engine.rb +1 -1
- data/lib/synvert/core/engine/erb.rb +31 -23
- data/lib/synvert/core/exceptions.rb +5 -3
- data/lib/synvert/core/node_ext.rb +107 -101
- data/lib/synvert/core/rewriter.rb +20 -14
- data/lib/synvert/core/rewriter/action.rb +5 -7
- data/lib/synvert/core/rewriter/action/append_action.rb +8 -6
- data/lib/synvert/core/rewriter/action/insert_action.rb +18 -19
- data/lib/synvert/core/rewriter/action/insert_after_action.rb +2 -2
- data/lib/synvert/core/rewriter/action/remove_action.rb +2 -2
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +5 -4
- data/lib/synvert/core/rewriter/action/replace_with_action.rb +7 -5
- data/lib/synvert/core/rewriter/condition.rb +1 -1
- data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +2 -2
- data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +2 -3
- data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +2 -2
- data/lib/synvert/core/rewriter/gem_spec.rb +10 -10
- data/lib/synvert/core/rewriter/helper.rb +4 -6
- data/lib/synvert/core/rewriter/instance.rb +36 -22
- data/lib/synvert/core/rewriter/ruby_version.rb +1 -1
- data/lib/synvert/core/rewriter/scope.rb +1 -1
- data/lib/synvert/core/rewriter/scope/goto_scope.rb +2 -1
- data/lib/synvert/core/rewriter/scope/within_scope.rb +23 -7
- data/lib/synvert/core/rewriter/warning.rb +1 -1
- data/lib/synvert/core/version.rb +2 -2
- data/spec/spec_helper.rb +3 -1
- data/spec/support/parser_helper.rb +2 -0
- data/spec/synvert/core/configuration_spec.rb +3 -1
- data/spec/synvert/core/engine/erb_spec.rb +32 -30
- data/spec/synvert/core/node_ext_spec.rb +57 -54
- data/spec/synvert/core/rewriter/action/append_action_spec.rb +2 -0
- data/spec/synvert/core/rewriter/action/insert_action_spec.rb +10 -8
- data/spec/synvert/core/rewriter/action/insert_after_action_spec.rb +5 -3
- data/spec/synvert/core/rewriter/action/remove_action_spec.rb +3 -1
- data/spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb +2 -0
- data/spec/synvert/core/rewriter/action/replace_with_action_spec.rb +17 -11
- data/spec/synvert/core/rewriter/action_spec.rb +2 -0
- data/spec/synvert/core/rewriter/condition/if_exist_condition_spec.rb +19 -9
- data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +23 -12
- data/spec/synvert/core/rewriter/condition/unless_exist_condition_spec.rb +19 -9
- data/spec/synvert/core/rewriter/condition_spec.rb +2 -0
- data/spec/synvert/core/rewriter/gem_spec_spec.rb +13 -10
- data/spec/synvert/core/rewriter/helper_spec.rb +36 -31
- data/spec/synvert/core/rewriter/instance_spec.rb +118 -66
- data/spec/synvert/core/rewriter/scope/goto_scope_spec.rb +10 -6
- data/spec/synvert/core/rewriter/scope/within_scope.rb +18 -9
- data/spec/synvert/core/rewriter/scope_spec.rb +2 -0
- data/spec/synvert/core/rewriter/warning_spec.rb +2 -0
- data/spec/synvert/core/rewriter_spec.rb +106 -73
- data/synvert-core.gemspec +2 -2
- metadata +12 -12
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# Rewriter is the top level namespace in a snippet.
|
@@ -42,7 +42,14 @@ module Synvert::Core
|
|
42
42
|
autoload :RubyVersion, 'synvert/core/rewriter/ruby_version'
|
43
43
|
autoload :GemSpec, 'synvert/core/rewriter/gem_spec'
|
44
44
|
|
45
|
-
class <<self
|
45
|
+
class << self
|
46
|
+
# Execute the temporary rewriter without group and name.
|
47
|
+
#
|
48
|
+
# @param block [Block] a block defines the behaviors of the rewriter.
|
49
|
+
def execute(&block)
|
50
|
+
Rewriter.new('', '', &block).process
|
51
|
+
end
|
52
|
+
|
46
53
|
# Register a rewriter with its group and name.
|
47
54
|
#
|
48
55
|
# @param group [String] the rewriter group.
|
@@ -65,7 +72,7 @@ module Synvert::Core
|
|
65
72
|
if exist? group, name
|
66
73
|
rewriters[group][name]
|
67
74
|
else
|
68
|
-
raise RewriterNotFound
|
75
|
+
raise RewriterNotFound, "Rewriter #{group} #{name} not found"
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
@@ -82,7 +89,7 @@ module Synvert::Core
|
|
82
89
|
rewriter.process
|
83
90
|
rewriter
|
84
91
|
else
|
85
|
-
raise RewriterNotFound
|
92
|
+
raise RewriterNotFound, "Rewriter #{group}/#{name} not found"
|
86
93
|
end
|
87
94
|
end
|
88
95
|
|
@@ -112,7 +119,7 @@ module Synvert::Core
|
|
112
119
|
rewriters.clear
|
113
120
|
end
|
114
121
|
|
115
|
-
|
122
|
+
private
|
116
123
|
|
117
124
|
def rewriters
|
118
125
|
@rewriters ||= {}
|
@@ -151,14 +158,14 @@ module Synvert::Core
|
|
151
158
|
# Process the rewriter.
|
152
159
|
# It will call the block.
|
153
160
|
def process
|
154
|
-
|
161
|
+
instance_eval &@block
|
155
162
|
end
|
156
163
|
|
157
164
|
# Process rewriter with sandbox mode.
|
158
165
|
# It will call the block but doesn't change any file.
|
159
166
|
def process_with_sandbox
|
160
167
|
@sandbox = true
|
161
|
-
|
168
|
+
process
|
162
169
|
@sandbox = false
|
163
170
|
end
|
164
171
|
|
@@ -178,7 +185,7 @@ module Synvert::Core
|
|
178
185
|
#
|
179
186
|
# @param description [String] rewriter description.
|
180
187
|
# @return rewriter description.
|
181
|
-
def description(description=nil)
|
188
|
+
def description(description = nil)
|
182
189
|
if description
|
183
190
|
@description = description
|
184
191
|
else
|
@@ -208,17 +215,16 @@ module Synvert::Core
|
|
208
215
|
# @param file_pattern [String] pattern to find files, e.g. spec/**/*_spec.rb
|
209
216
|
# @param options [Hash] instance options.
|
210
217
|
# @param block [Block] the block to rewrite code in the matching files.
|
211
|
-
def within_files(file_pattern, options={}, &block)
|
218
|
+
def within_files(file_pattern, options = {}, &block)
|
212
219
|
return if @sandbox
|
213
220
|
|
214
|
-
if (!@ruby_version || @ruby_version.match?) &&
|
215
|
-
(!@gem_spec || @gem_spec.match?)
|
221
|
+
if (!@ruby_version || @ruby_version.match?) && (!@gem_spec || @gem_spec.match?)
|
216
222
|
Rewriter::Instance.new(self, file_pattern, options, &block).process
|
217
223
|
end
|
218
224
|
end
|
219
225
|
|
220
226
|
# Parse within_file dsl, it finds a specifiled file.
|
221
|
-
|
227
|
+
alias within_file within_files
|
222
228
|
|
223
229
|
# Parses add_file dsl, it adds a new file.
|
224
230
|
#
|
@@ -262,7 +268,7 @@ module Synvert::Core
|
|
262
268
|
# @param name [String] helper method name.
|
263
269
|
# @param block [Block] helper method block.
|
264
270
|
def helper_method(name, &block)
|
265
|
-
@helpers << {name: name, block: block}
|
271
|
+
@helpers << { name: name, block: block }
|
266
272
|
end
|
267
273
|
|
268
274
|
# Parse todo dsl, it sets todo of the rewriter.
|
@@ -270,7 +276,7 @@ module Synvert::Core
|
|
270
276
|
#
|
271
277
|
# @param todo_list [String] rewriter todo.
|
272
278
|
# @return [String] rewriter todo.
|
273
|
-
def todo(todo=nil)
|
279
|
+
def todo(todo = nil)
|
274
280
|
if todo
|
275
281
|
@todo = todo
|
276
282
|
else
|
@@ -1,16 +1,17 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# Action defines rewriter action, add, replace or remove code.
|
5
5
|
class Rewriter::Action
|
6
|
-
DEFAULT_OPTIONS = { autoindent: true }
|
6
|
+
DEFAULT_OPTIONS = { autoindent: true }.freeze
|
7
|
+
DEFAULT_INDENT = 2
|
7
8
|
|
8
9
|
# Initialize an action.
|
9
10
|
#
|
10
11
|
# @param instance [Synvert::Core::Rewriter::Instance]
|
11
12
|
# @param code [String] new code to add, replace or remove.
|
12
13
|
# @param options [Hash] action options, it includes :autoindent.
|
13
|
-
def initialize(instance, code, options={})
|
14
|
+
def initialize(instance, code, options = {})
|
14
15
|
@instance = instance
|
15
16
|
@code = code
|
16
17
|
@options = DEFAULT_OPTIONS.merge(options)
|
@@ -29,9 +30,7 @@ module Synvert::Core
|
|
29
30
|
# @return [String] rewritten code.
|
30
31
|
def rewritten_code
|
31
32
|
if rewritten_source.split("\n").length > 1
|
32
|
-
"\n\n" + rewritten_source.split("\n").map { |line|
|
33
|
-
indent(@node) + line
|
34
|
-
}.join("\n")
|
33
|
+
"\n\n" + rewritten_source.split("\n").map { |line| indent(@node) + line }.join("\n")
|
35
34
|
else
|
36
35
|
"\n" + indent(@node) + rewritten_source
|
37
36
|
end
|
@@ -45,4 +44,3 @@ module Synvert::Core
|
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
48
|
-
|
@@ -1,8 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
|
-
#
|
4
|
+
# AppendAction to append code to the bottom of node body.
|
5
5
|
class Rewriter::AppendAction < Rewriter::Action
|
6
|
+
END_LENGTH = "\nend".length
|
7
|
+
|
6
8
|
# Begin position to append code.
|
7
9
|
#
|
8
10
|
# @return [Integer] begin position.
|
@@ -10,7 +12,7 @@ module Synvert::Core
|
|
10
12
|
if :begin == @node.type
|
11
13
|
@node.loc.expression.end_pos
|
12
14
|
else
|
13
|
-
@node.loc.expression.end_pos - @node.indent -
|
15
|
+
@node.loc.expression.end_pos - @node.indent - END_LENGTH
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
@@ -21,15 +23,15 @@ module Synvert::Core
|
|
21
23
|
begin_pos
|
22
24
|
end
|
23
25
|
|
24
|
-
|
26
|
+
private
|
25
27
|
|
26
28
|
# Indent of the node.
|
27
29
|
#
|
28
30
|
# @param node [Parser::AST::Node]
|
29
31
|
# @return [String] n times whitesphace
|
30
32
|
def indent(node)
|
31
|
-
if [
|
32
|
-
' ' * (node.indent +
|
33
|
+
if %i[block class].include? node.type
|
34
|
+
' ' * (node.indent + DEFAULT_INDENT)
|
33
35
|
else
|
34
36
|
' ' * node.indent
|
35
37
|
end
|
@@ -1,13 +1,26 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# InsertAction to insert code to the top of node body.
|
5
5
|
class Rewriter::InsertAction < Rewriter::Action
|
6
|
+
DO_LENGTH = ' do'.length
|
7
|
+
|
6
8
|
# Begin position to insert code.
|
7
9
|
#
|
8
10
|
# @return [Integer] begin position.
|
9
11
|
def begin_pos
|
10
|
-
|
12
|
+
case @node.type
|
13
|
+
when :block
|
14
|
+
if @node.children[1].children.empty?
|
15
|
+
@node.children[0].loc.expression.end_pos + DO_LENGTH
|
16
|
+
else
|
17
|
+
@node.children[1].loc.expression.end_pos
|
18
|
+
end
|
19
|
+
when :class
|
20
|
+
@node.children[1] ? @node.children[1].loc.expression.end_pos : @node.children[0].loc.expression.end_pos
|
21
|
+
else
|
22
|
+
@node.children.last.loc.expression.end_pos
|
23
|
+
end
|
11
24
|
end
|
12
25
|
|
13
26
|
# End position, always same to begin position.
|
@@ -17,29 +30,15 @@ module Synvert::Core
|
|
17
30
|
begin_pos
|
18
31
|
end
|
19
32
|
|
20
|
-
|
21
|
-
|
22
|
-
# Insert position.
|
23
|
-
#
|
24
|
-
# @return [Integer] insert position.
|
25
|
-
def insert_position(node)
|
26
|
-
case node.type
|
27
|
-
when :block
|
28
|
-
node.children[1].children.empty? ? node.children[0].loc.expression.end_pos + 3 : node.children[1].loc.expression.end_pos
|
29
|
-
when :class
|
30
|
-
node.children[1] ? node.children[1].loc.expression.end_pos : node.children[0].loc.expression.end_pos
|
31
|
-
else
|
32
|
-
node.children.last.loc.expression.end_pos
|
33
|
-
end
|
34
|
-
end
|
33
|
+
private
|
35
34
|
|
36
35
|
# Indent of the node.
|
37
36
|
#
|
38
37
|
# @param node [Parser::AST::Node]
|
39
38
|
# @return [String] n times whitesphace
|
40
39
|
def indent(node)
|
41
|
-
if [
|
42
|
-
' ' * (node.indent +
|
40
|
+
if %i[block class].include? node.type
|
41
|
+
' ' * (node.indent + DEFAULT_INDENT)
|
43
42
|
else
|
44
43
|
' ' * node.indent
|
45
44
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# ReplaceErbStmtWithExprAction to replace erb stmt code to expr,
|
5
5
|
# e.g. <% form_for ... %> => <%= form_for ... %>.
|
6
6
|
class Rewriter::ReplaceErbStmtWithExprAction < Rewriter::Action
|
7
|
-
def initialize(instance, code=nil)
|
7
|
+
def initialize(instance, code = nil)
|
8
8
|
super
|
9
9
|
end
|
10
10
|
|
@@ -23,7 +23,7 @@ module Synvert::Core
|
|
23
23
|
# @return [Integer] end position.
|
24
24
|
def end_pos
|
25
25
|
node_begin_pos = @node.loc.expression.begin_pos
|
26
|
-
node_begin_pos += @node.loc.expression.source.index
|
26
|
+
node_begin_pos += @node.loc.expression.source.index 'do'
|
27
27
|
while @node.loc.expression.source_buffer.source[node_begin_pos += 1] != '@'
|
28
28
|
end
|
29
29
|
node_begin_pos
|
@@ -33,7 +33,8 @@ module Synvert::Core
|
|
33
33
|
#
|
34
34
|
# @return [String] rewritten code.
|
35
35
|
def rewritten_code
|
36
|
-
@node.loc.expression.source_buffer.source[begin_pos...end_pos]
|
36
|
+
@node.loc.expression.source_buffer.source[begin_pos...end_pos]
|
37
|
+
.sub(Engine::ERUBY_STMT_SPLITTER, '@output_buffer.append= ')
|
37
38
|
.sub(Engine::ERUBY_STMT_SPLITTER, Engine::ERUBY_EXPR_SPLITTER)
|
38
39
|
end
|
39
40
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# ReplaceWithAction to replace code.
|
@@ -23,16 +23,18 @@ module Synvert::Core
|
|
23
23
|
def rewritten_code
|
24
24
|
if rewritten_source.split("\n").length > 1
|
25
25
|
new_code = []
|
26
|
-
rewritten_source
|
27
|
-
|
28
|
-
|
26
|
+
rewritten_source
|
27
|
+
.split("\n")
|
28
|
+
.each_with_index { |line, index|
|
29
|
+
new_code << (index == 0 || !@options[:autoindent] ? line : indent(@node) + line)
|
30
|
+
}
|
29
31
|
new_code.join("\n")
|
30
32
|
else
|
31
33
|
rewritten_source
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
|
37
|
+
private
|
36
38
|
|
37
39
|
# Indent of the node
|
38
40
|
#
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# IfExistCondition checks if matching node exists in the node children.
|
@@ -7,7 +7,7 @@ module Synvert::Core
|
|
7
7
|
def match?
|
8
8
|
match = false
|
9
9
|
@instance.current_node.recursive_children do |child_node|
|
10
|
-
match
|
10
|
+
match ||= (child_node&.match?(@rules))
|
11
11
|
end
|
12
12
|
match
|
13
13
|
end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# IfExistCondition checks if node has only one child node and the child node matches rules.
|
5
5
|
class Rewriter::IfOnlyExistCondition < Rewriter::Condition
|
6
6
|
# check if only have one child node and the child node matches rules.
|
7
7
|
def match?
|
8
|
-
@instance.current_node.body.size == 1 &&
|
9
|
-
@instance.current_node.body.first.match?(@rules)
|
8
|
+
@instance.current_node.body.size == 1 && @instance.current_node.body.first.match?(@rules)
|
10
9
|
end
|
11
10
|
end
|
12
11
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# UnlessExistCondition checks if matching node doesn't exist in the node children.
|
@@ -7,7 +7,7 @@ module Synvert::Core
|
|
7
7
|
def match?
|
8
8
|
match = false
|
9
9
|
@instance.current_node.recursive_children do |child_node|
|
10
|
-
match
|
10
|
+
match ||= (child_node&.match?(@rules))
|
11
11
|
end
|
12
12
|
!match
|
13
13
|
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# GemSpec checks and compares gem version.
|
5
5
|
class Rewriter::GemSpec
|
6
|
-
OPERATORS = {eq: '==', lt: '<', gt: '>', lte: '<=', gte: '>=', ne: '!='}
|
6
|
+
OPERATORS = { eq: '==', lt: '<', gt: '>', lte: '<=', gte: '>=', ne: '!=' }.freeze
|
7
7
|
|
8
8
|
# Initialize a gem_spec.
|
9
9
|
#
|
@@ -27,15 +27,15 @@ module Synvert::Core
|
|
27
27
|
# @raise [Synvert::Core::GemfileLockNotFound] raise if Gemfile.lock does not exist.
|
28
28
|
def match?
|
29
29
|
gemfile_lock_path = File.join(Configuration.instance.get(:path), 'Gemfile.lock')
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
|
31
|
+
# if Gemfile.lock does not exist, just ignore this check
|
32
|
+
return true unless File.exist?(gemfile_lock_path)
|
33
|
+
|
34
|
+
parser = Bundler::LockfileParser.new(File.read(gemfile_lock_path))
|
35
|
+
if spec = parser.specs.find { |spec| spec.name == @name }
|
36
|
+
Gem::Version.new(spec.version).send(OPERATORS[@operator], @version)
|
37
37
|
else
|
38
|
-
|
38
|
+
false
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Synvert::Core
|
4
4
|
# Rewriter::Helper provides some helper methods to make it easier to write a snippet.
|
@@ -34,9 +34,9 @@ module Synvert::Core
|
|
34
34
|
# if current_node has argument, it returns "({{arguments}})"
|
35
35
|
def add_arguments_with_parenthesis_if_necessary
|
36
36
|
if node.arguments.size > 0
|
37
|
-
|
37
|
+
'({{arguments}})'
|
38
38
|
else
|
39
|
-
|
39
|
+
''
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -65,9 +65,7 @@ module Synvert::Core
|
|
65
65
|
#
|
66
66
|
# strip_brackets("(1..100)") #=> "1..100"
|
67
67
|
def strip_brackets(code)
|
68
|
-
code.sub(/^\((.*)\)$/) { $1 }
|
69
|
-
.sub(/^\[(.*)\]$/) { $1 }
|
70
|
-
.sub(/^{(.*)}$/) { $1 }
|
68
|
+
code.sub(/^\((.*)\)$/) { $1 }.sub(/^\[(.*)\]$/) { $1 }.sub(/^{(.*)}$/) { $1 }
|
71
69
|
end
|
72
70
|
end
|
73
71
|
end
|