synvert-core 1.22.2 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
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