synvert-core 1.22.2 → 1.23.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 125adb6c7e7a82a3b03fa233fa0d43f9beabd1c46eb620323297689e7f47826f
4
- data.tar.gz: 96118a021ba37a5723bc284b75163f7e897dc23b57d9bcc62c8203f0de9c6dbb
3
+ metadata.gz: 862649a82e2cd9d14454b65b9c3e146c2c6adff96a03441624cca7d83f42a8f5
4
+ data.tar.gz: 167e5ee0a3ae617888af58dd40ee582d3427012a5e0daea881f13ffd51a07b8a
5
5
  SHA512:
6
- metadata.gz: 7b1bd83e3d44bd056afb750005d27e3f03627f203c3d426ca7a1aca67343edb3184e46e32333532cbc0d953fd6ec56ed07985194cf6e7bf6cc3deeed20e7a91f
7
- data.tar.gz: 70cd42406792039a01298a47fbd53451de86f66d955da83174d7ee81ae271c89e8a7e98c9a317c097eddf7cac837afa15206fdfdf6b499c623b6fb721fbe2f9f
6
+ metadata.gz: dbf9a15e4e795edf0bedcb690b163b088ca00236545881744ef8ac004c68059cf90654d3804626e29e8b3ce4a9e3ccb3e9e7cf6d82ea05c98c941f6e59c77b8c
7
+ data.tar.gz: 916953fc304f4a2054b3722197ec0d194a958b2f502d5ea379411b1de970fc9691aaa3caf68c3458f99281a92d7c316bb084fe3deaba7af0fd03e0df7cbd31c9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.23.1 (2023-04-06)
4
+
5
+ * Fix array index name in `GotoScope`
6
+ * Update `node_query` to 1.12.1
7
+
8
+ ## 1.23.0 (2023-04-05)
9
+
10
+ * Add haml engine
11
+ * Add `Engine.register` and `Engine.encode` and `Engine.generate_transform_proc`
12
+ * Set NodeMutation `transform_proc`
13
+ * Update `node_mutation` to 1.14.0
14
+
3
15
  ## 1.22.2 (2023-03-31)
4
16
 
5
17
  * Do not replace white space characters in erb engine
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- synvert-core (1.22.2)
4
+ synvert-core (1.23.1)
5
5
  activesupport (< 7.0.0)
6
- node_mutation (>= 1.13.1)
7
- node_query (>= 1.12.0)
6
+ node_mutation (>= 1.14.0)
7
+ node_query (>= 1.12.1)
8
8
  parallel
9
9
  parser
10
10
  parser_node_ext (>= 1.0.0)
@@ -48,13 +48,13 @@ GEM
48
48
  method_source (1.0.0)
49
49
  minitest (5.18.0)
50
50
  nenv (0.3.0)
51
- node_mutation (1.13.1)
52
- node_query (1.12.0)
51
+ node_mutation (1.14.0)
52
+ node_query (1.12.1)
53
53
  notiffany (0.1.3)
54
54
  nenv (~> 0.1)
55
55
  shellany (~> 0.0)
56
56
  parallel (1.22.1)
57
- parser (3.2.1.1)
57
+ parser (3.2.2.0)
58
58
  ast (~> 2.4.1)
59
59
  parser_node_ext (1.0.0)
60
60
  parser
@@ -14,6 +14,11 @@ module Synvert::Core
14
14
  .sub(/%>.*?$/m) { |str| replace_all_code_but_white_space_characters(str) }
15
15
  end
16
16
 
17
+ # Generate an empty proc.
18
+ def generate_transform_proc(_encoded_source)
19
+ proc {}
20
+ end
21
+
17
22
  private
18
23
 
19
24
  def replace_all_code_but_white_space_characters(source)
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synvert::Core
4
+ module Engine
5
+ class Haml
6
+ class << self
7
+ END_LINE = "end\n"
8
+
9
+ # Encode haml string, leave only ruby code, replace other haml code with whitespace.
10
+ # And insert `end\n` for each if, unless, begin, case to make it a valid ruby code.
11
+ #
12
+ # @param source [String] haml code.
13
+ # @return [String] encoded ruby code.
14
+ def encode(source)
15
+ tab_sizes = []
16
+ lines =
17
+ source.lines.map do |line|
18
+ if line =~ /\A(\s*)(- ?)(.*)/ # match " - if currenet_user"
19
+ code = (' ' * ($1.size + $2.size)) + $3
20
+ new_line =
21
+ $3.start_with?('else', 'elsif', 'when') ? code : check_and_insert_end(code, tab_sizes, $1.size)
22
+ tab_sizes.push($1.size) if $3.start_with?('if', 'unless', 'begin', 'case') || $3.include?(' do ')
23
+
24
+ new_line
25
+ else
26
+ if line =~ /\A(\s*)([%#\.].*)?=(.*)/ # match " %span= current_user.login"
27
+ code = (' ' * ($1.size + ($2 || '').size + 1)) + $3
28
+ new_line = check_and_insert_end(code, tab_sizes, $1.size)
29
+ tab_sizes.push($1.size) if line.include?(' do ')
30
+ new_line
31
+ elsif line =~ /\A(\s*)(.*)/ # match any other line
32
+ check_and_insert_end(' ' * line.size, tab_sizes, $1.size)
33
+ end
34
+ end
35
+ end
36
+ lines.push(END_LINE) unless tab_sizes.empty?
37
+ lines.join("\n")
38
+ end
39
+
40
+ # Generate transform proc, it's used to adjust start and end position of actions.
41
+ # Due to the fact that we insert `end\n` when encode the source code, we need to adjust
42
+ # start and end position of actions to match the original source code.
43
+ # e.g. if end\n exists in position 10, action start position is 20 and end position is 30,
44
+ # then action start position should be 16 and end position should be 26.
45
+ #
46
+ # @param encoded_source [String] encoded source.
47
+ # @return [Proc] transform proc.
48
+ def generate_transform_proc(encoded_source)
49
+ proc do |actions|
50
+ start = 0
51
+ indices = []
52
+ loop do
53
+ index = encoded_source[start..-1].index(END_LINE)
54
+ break unless index
55
+
56
+ indices << (start + index)
57
+ start += index + END_LINE.length
58
+ end
59
+ indices.each do |index|
60
+ actions.each do |action|
61
+ action.start -= END_LINE.length if action.start > index
62
+ action.end -= END_LINE.length if action.end > index
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ # Check if the current tab_size is less than or equal to the last tab_size in tab_sizes.
71
+ # If so, pop the last tab_size and insert "end\n" before code.
72
+ # otherwise, return code.
73
+ def check_and_insert_end(code, tab_sizes, current_tab_size)
74
+ if !tab_sizes.empty? && current_tab_size <= tab_sizes[-1]
75
+ tab_sizes.pop
76
+ "end\n" + code
77
+ else
78
+ code
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -4,5 +4,36 @@ module Synvert::Core
4
4
  # Engine defines how to encode / decode other files (like erb).
5
5
  module Engine
6
6
  autoload :Erb, 'synvert/core/engine/erb'
7
+ autoload :Haml, 'synvert/core/engine/haml'
8
+
9
+ # Register an engine
10
+ # @param [String] extension
11
+ # @param [Class] engine
12
+ def self.register(extension, engine)
13
+ @engines ||= {}
14
+ @engines[extension] = engine
15
+ end
16
+
17
+ # Encode source code by registered engine.
18
+ # @param [String] extension
19
+ # @param [String] source
20
+ # @return [String] encoded source
21
+ def self.encode(extension, source)
22
+ engine = @engines[extension]
23
+ engine ? engine.encode(source) : source
24
+ end
25
+
26
+ # Generate a transform_proc by registered engine,
27
+ # which is used to adjust start and end position of actions.
28
+ # @param [String] extension
29
+ # @param [String] encoded_source
30
+ # @return [Proc] transform_proc
31
+ def self.generate_transform_proc(extension, encoded_source)
32
+ engine = @engines[extension]
33
+ engine ? engine.generate_transform_proc(encoded_source) : proc {}
34
+ end
7
35
  end
36
+
37
+ Engine.register('.erb', Engine::Erb)
38
+ Engine.register('.haml', Engine::Haml)
8
39
  end
@@ -44,10 +44,12 @@ module Synvert::Core
44
44
  absolute_file_path = File.join(Configuration.root_path, @file_path)
45
45
  while true
46
46
  source = read_source(absolute_file_path)
47
+ encoded_source = Engine.encode(File.extname(file_path), source)
47
48
  @current_mutation = NodeMutation.new(source)
49
+ @current_mutation.transform_proc = Engine.generate_transform_proc(File.extname(file_path), encoded_source)
48
50
  @mutation_adapter = NodeMutation.adapter
49
51
  begin
50
- node = parse_code(@file_path, source)
52
+ node = parse_code(@file_path, encoded_source)
51
53
 
52
54
  process_with_node(node) do
53
55
  instance_eval(&@block)
@@ -73,9 +75,11 @@ module Synvert::Core
73
75
  absolute_file_path = File.join(Configuration.root_path, file_path)
74
76
  source = read_source(absolute_file_path)
75
77
  @current_mutation = NodeMutation.new(source)
78
+ encoded_source = Engine.encode(File.extname(file_path), source)
79
+ @current_mutation.transform_proc = Engine.generate_transform_proc(File.extname(file_path), encoded_source)
76
80
  @mutation_adapter = NodeMutation.adapter
77
81
  begin
78
- node = parse_code(file_path, source)
82
+ node = parse_code(file_path, encoded_source)
79
83
 
80
84
  process_with_node(node) do
81
85
  instance_eval(&@block)
@@ -440,11 +444,11 @@ module Synvert::Core
440
444
  # Parse code ast node.
441
445
  #
442
446
  # @param file_path [String] file path
443
- # @param file_path [String] file path
447
+ # @param encoded_source [String] encoded source code
444
448
  # @return [Node] ast node for file
445
- def parse_code(file_path, source)
449
+ def parse_code(file_path, encoded_source)
446
450
  buffer = Parser::Source::Buffer.new file_path
447
- buffer.source = /\.erb/.match?(file_path) ? Engine::Erb.encode(source) : source
451
+ buffer.source = encoded_source
448
452
 
449
453
  parser = Parser::CurrentRuby.new
450
454
  parser.reset
@@ -20,7 +20,7 @@ module Synvert::Core
20
20
 
21
21
  child_node = current_node
22
22
  @child_node_name.to_s.split('.').each do |child_node_name|
23
- child_node = child_node_name.is_a?(Parser::AST::Node) ? child_node_name : child_node.send(child_node_name)
23
+ child_node = child_node.is_a?(Array) && child_node_name =~ /-?\d+/ ? child_node[child_node_name.to_i] : child_node.send(child_node_name)
24
24
  end
25
25
  if child_node.is_a?(Array)
26
26
  child_node.each do |child_child_node|
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.22.2'
5
+ VERSION = '1.23.1'
6
6
  end
7
7
  end
data/lib/synvert/core.rb CHANGED
@@ -28,8 +28,9 @@ module Synvert
28
28
 
29
29
  ALL_RUBY_FILES = %w[**/*.rb]
30
30
  ALL_ERB_FILES = %w[**/*.erb]
31
+ ALL_HAML_FILES = %w[**/*.haml]
31
32
  ALL_RAKE_FILES = %w[**/*.rake]
32
- ALL_FILES = ALL_RUBY_FILES + ALL_ERB_FILES + ALL_RAKE_FILES
33
+ ALL_FILES = ALL_RUBY_FILES + ALL_ERB_FILES + ALL_HAML_FILES + ALL_RAKE_FILES
33
34
 
34
35
  RAILS_APP_FILES = %w[app/**/*.rb engines/*/app/**/*.rb]
35
36
  RAILS_CONTROLLER_FILES = %w[app/controllers/**/*.rb engines/*/app/controllers/**/*.rb]
@@ -46,7 +47,7 @@ module Synvert
46
47
  engines/*/config/routes.rb
47
48
  engines/*/config/routes/**/*.rb
48
49
  ]
49
- RAILS_VIEW_FILES = %w[app/views/**/*.html.{erb}]
50
+ RAILS_VIEW_FILES = ALL_ERB_FILES + ALL_HAML_FILES
50
51
 
51
52
  RAILS_CONTROLLER_TEST_FILES = %w[
52
53
  test/functional/**/*.rb
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module Synvert::Core
6
+ describe Engine::Haml do
7
+ describe '#encode' do
8
+ it 'encodes source' do
9
+ source = <<~EOS
10
+ %p
11
+ Date/Time:
12
+ - now = DateTime.now
13
+ %strong= now
14
+ - if now > DateTime.parse("December 31, 2006")
15
+ = "Happy new " + "year!"
16
+ - else
17
+ = "Hello!"
18
+ - if current_admin?
19
+ %strong= "Admin"
20
+ - elsif current_user?
21
+ %span= "User"
22
+ #error= error_message
23
+ .form-actions
24
+ = form_for @user do |f|
25
+ Test
26
+ EOS
27
+ encoded_source = Engine::Haml.encode(source)
28
+ expect(encoded_source).to be_include 'now = DateTime.now'
29
+ expect(encoded_source).to be_include 'now'
30
+ expect(encoded_source).to be_include 'if now > DateTime.parse("December 31, 2006")'
31
+ expect(encoded_source).to be_include '"Happy new " + "year!"'
32
+ expect(encoded_source).to be_include '"Hello!"'
33
+ expect(encoded_source).to be_include 'if current_admin?'
34
+ expect(encoded_source).to be_include '"Admin"'
35
+ expect(encoded_source).to be_include 'if current_user?'
36
+ expect(encoded_source).to be_include '"User"'
37
+ expect(encoded_source).to be_include 'error_message'
38
+ expect(encoded_source).to be_include 'form_for @user do |f|'
39
+ expect(encoded_source.scan("end\n").length).to eq 3
40
+ expect(encoded_source).not_to be_include '%p'
41
+ expect(encoded_source).not_to be_include 'strong'
42
+ expect(encoded_source).not_to be_include '#error'
43
+ expect(encoded_source).not_to be_include 'Test'
44
+ expect(encoded_source).not_to be_include '.form-actions'
45
+ end
46
+ end
47
+
48
+ describe '#generate_transform_proc' do
49
+ it 'generates transform proc' do
50
+ encoded_source = <<~EOS
51
+ now = DateTime.now
52
+ if now > DateTime.parse("December 31, 2006")
53
+ else
54
+ end
55
+ if current_admin?
56
+ elsif current_user?
57
+ end
58
+ DateTime.now - now
59
+ EOS
60
+ proc = Engine::Haml.generate_transform_proc(encoded_source)
61
+ actions = [
62
+ NodeMutation::Struct::Action.new(50, 55, ''),
63
+ # first end position is 69
64
+ NodeMutation::Struct::Action.new(100, 105, ''),
65
+ # second end position is 111
66
+ NodeMutation::Struct::Action.new(120, 125, '')
67
+ ]
68
+ proc.call(actions)
69
+ expect(actions.first.start).to eq 50
70
+ expect(actions.first.end).to eq 55
71
+ expect(actions.second.start).to eq 96
72
+ expect(actions.second.end).to eq 101
73
+ expect(actions.third.start).to eq 112
74
+ expect(actions.third.end).to eq 117
75
+ end
76
+ end
77
+ end
78
+ end
@@ -365,6 +365,26 @@ module Synvert::Core
365
365
  expect(File).to receive(:write).with('./app/views/posts/_form.html.erb', output)
366
366
  instance.process
367
367
  end
368
+
369
+ it 'updates haml file' do
370
+ instance =
371
+ Rewriter::Instance.new rewriter, 'app/views/posts/_form.html.haml' do
372
+ with_node node_type: 'ivar' do
373
+ replace_with 'post'
374
+ end
375
+ end
376
+ input = <<~EOS
377
+ = form_for @post do |f|
378
+ = form_for @post do |f|
379
+ EOS
380
+ output = <<~EOS
381
+ = form_for post do |f|
382
+ = form_for post do |f|
383
+ EOS
384
+ allow(File).to receive(:read).with('./app/views/posts/_form.html.haml', encoding: 'UTF-8').and_return(input)
385
+ expect(File).to receive(:write).with('./app/views/posts/_form.html.haml', output)
386
+ instance.process
387
+ end
368
388
  end
369
389
 
370
390
  describe '#test' do
@@ -429,6 +449,34 @@ module Synvert::Core
429
449
  expect(result.file_path).to eq 'app/views/posts/_form.html.erb'
430
450
  expect(result.actions).to eq [NodeMutation::Struct::Action.new(2, 2, '=')]
431
451
  end
452
+
453
+ it 'updates haml file' do
454
+ instance =
455
+ Rewriter::Instance.new rewriter, 'app/views/posts/_form.html.haml' do
456
+ with_node node_type: 'ivar' do
457
+ replace_with 'post'
458
+ end
459
+ end
460
+ input = <<~EOS
461
+ = form_for @post do |f|
462
+ = form_for @post do |f|
463
+ EOS
464
+ output = <<~EOS
465
+ = form_for post do |f|
466
+ = form_for post do |f|
467
+ EOS
468
+ allow(File).to receive(:read).with('./app/views/posts/_form.html.haml', encoding: 'UTF-8').and_return(input)
469
+ result = instance.test
470
+ expect(result.file_path).to eq 'app/views/posts/_form.html.haml'
471
+ expect(result.actions).to eq [
472
+ NodeMutation::Struct::Action.new("= form_for ".length, "= form_for @post".length, 'post'),
473
+ NodeMutation::Struct::Action.new(
474
+ "= form_for @post do |f|\n= form_for ".length,
475
+ "= form_for @post do |f|\n= form_for @post".length,
476
+ 'post'
477
+ ),
478
+ ]
479
+ end
432
480
  end
433
481
 
434
482
  describe '#process_with_node' do
@@ -33,7 +33,21 @@ module Synvert::Core
33
33
  expect(instance.current_node.type).to eq :block
34
34
  end
35
35
 
36
- it 'calls block multiple times with blok body' do
36
+ it 'call block with child node in array' do
37
+ run = false
38
+ type_in_scope = nil
39
+ scope =
40
+ Rewriter::GotoScope.new instance, 'body.1' do
41
+ run = true
42
+ type_in_scope = node.type
43
+ end
44
+ scope.process
45
+ expect(run).to be_truthy
46
+ expect(type_in_scope).to eq :send
47
+ expect(instance.current_node.type).to eq :block
48
+ end
49
+
50
+ it 'calls block multiple times with block body' do
37
51
  count = 0
38
52
  scope =
39
53
  Rewriter::GotoScope.new instance, 'body' do
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_runtime_dependency "activesupport", "< 7.0.0"
23
- spec.add_runtime_dependency "node_query", ">= 1.12.0"
24
- spec.add_runtime_dependency "node_mutation", ">= 1.13.1"
23
+ spec.add_runtime_dependency "node_query", ">= 1.12.1"
24
+ spec.add_runtime_dependency "node_mutation", ">= 1.14.0"
25
25
  spec.add_runtime_dependency "parser"
26
26
  spec.add_runtime_dependency "parser_node_ext", ">= 1.0.0"
27
27
  spec.add_runtime_dependency "parallel"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synvert-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.22.2
4
+ version: 1.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-31 00:00:00.000000000 Z
11
+ date: 2023-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 1.12.0
33
+ version: 1.12.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 1.12.0
40
+ version: 1.12.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: node_mutation
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 1.13.1
47
+ version: 1.14.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 1.13.1
54
+ version: 1.14.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: parser
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +116,7 @@ files:
116
116
  - lib/synvert/core/configuration.rb
117
117
  - lib/synvert/core/engine.rb
118
118
  - lib/synvert/core/engine/erb.rb
119
+ - lib/synvert/core/engine/haml.rb
119
120
  - lib/synvert/core/node_ext.rb
120
121
  - lib/synvert/core/rewriter.rb
121
122
  - lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb
@@ -138,6 +139,7 @@ files:
138
139
  - spec/spec_helper.rb
139
140
  - spec/support/parser_helper.rb
140
141
  - spec/synvert/core/engine/erb_spec.rb
142
+ - spec/synvert/core/engine/haml_spec.rb
141
143
  - spec/synvert/core/node_ext_spec.rb
142
144
  - spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb
143
145
  - spec/synvert/core/rewriter/condition/if_exist_condition_spec.rb
@@ -174,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
176
  - !ruby/object:Gem::Version
175
177
  version: '0'
176
178
  requirements: []
177
- rubygems_version: 3.4.6
179
+ rubygems_version: 3.4.10
178
180
  signing_key:
179
181
  specification_version: 4
180
182
  summary: convert ruby code to better syntax.
@@ -182,6 +184,7 @@ test_files:
182
184
  - spec/spec_helper.rb
183
185
  - spec/support/parser_helper.rb
184
186
  - spec/synvert/core/engine/erb_spec.rb
187
+ - spec/synvert/core/engine/haml_spec.rb
185
188
  - spec/synvert/core/node_ext_spec.rb
186
189
  - spec/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action_spec.rb
187
190
  - spec/synvert/core/rewriter/condition/if_exist_condition_spec.rb