synvert-core 1.22.2 → 1.23.0

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: 6590e4796366364169c7e2cc65779406ce8a8f88221ffcd7f0ad05df4487f78b
4
+ data.tar.gz: b1792cc8d6744fbef809d43ba428f0d5592702c6abee4653fe8160a87261acd4
5
5
  SHA512:
6
- metadata.gz: 7b1bd83e3d44bd056afb750005d27e3f03627f203c3d426ca7a1aca67343edb3184e46e32333532cbc0d953fd6ec56ed07985194cf6e7bf6cc3deeed20e7a91f
7
- data.tar.gz: 70cd42406792039a01298a47fbd53451de86f66d955da83174d7ee81ae271c89e8a7e98c9a317c097eddf7cac837afa15206fdfdf6b499c623b6fb721fbe2f9f
6
+ metadata.gz: ec759f01184a5730b99742f28310b72df7e5c844fcafa535238dcfacaca8582e9e13b695219ec76f3b8a45194d15aec2caed1e714848cad33263de53a6a56be2
7
+ data.tar.gz: eef33bbc2623a9ff16e908722ef358cbec51188911e07e1c9c8e44bcb4029c4e73cc1c0da54581d557c3d1edde0f8ed224fff75dffca5b2fcd347e1eed3f83f8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.23.0 (2023-04-05)
4
+
5
+ * Add haml engine
6
+ * Add `Engine.register` and `Engine.encode` and `Engine.generate_transform_proc`
7
+ * Set NodeMutation `transform_proc`
8
+ * Update `node_mutation` to 1.14.0
9
+
3
10
  ## 1.22.2 (2023-03-31)
4
11
 
5
12
  * Do not replace white space characters in erb engine
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- synvert-core (1.22.2)
4
+ synvert-core (1.23.0)
5
5
  activesupport (< 7.0.0)
6
- node_mutation (>= 1.13.1)
6
+ node_mutation (>= 1.14.0)
7
7
  node_query (>= 1.12.0)
8
8
  parallel
9
9
  parser
@@ -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)
51
+ node_mutation (1.14.0)
52
52
  node_query (1.12.0)
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.22.2'
5
+ VERSION = '1.23.0'
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
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency "activesupport", "< 7.0.0"
23
23
  spec.add_runtime_dependency "node_query", ">= 1.12.0"
24
- spec.add_runtime_dependency "node_mutation", ">= 1.13.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.0
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-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,14 +44,14 @@ dependencies:
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
@@ -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