synvert-core 1.15.0 → 1.17.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: 82066eb2fad44d4ca47831672a31195bba96aa07206aa23e449d5c519b94b23d
4
- data.tar.gz: 70a8566412fc3cf9004441a07e706c79807820003155b4bafdc03cf4c62042cc
3
+ metadata.gz: dd4c09ec590b3d1bce36465a50c319c4530aa2d28c08906fa008f33e72cccc55
4
+ data.tar.gz: 5591fd4d4a992ac99d5d523b958d7aa4e2caf56deaa5203dc8cf4ad537c3be32
5
5
  SHA512:
6
- metadata.gz: b33e55196d7d3e13097e7b101ee693111e5a49ad6d93015eba85886214abb125b5a938908ca0feadef772ef6eecdaa8874fc2d8d6ecff044d5506f685db8bc8d
7
- data.tar.gz: aa7f05784e6e8710903c47e6616dac0a18bca66b0f04fc6b7089ff8d47f8c5c6d073bdb45fccb04b25765feed6b28313ea08dd80d166e0283cd931e2d9482229
6
+ metadata.gz: 5c24a6a380ad2b418639b834818100c8ebcb19255e33c78a19b99b299d2cf6ad24024a0e6d4543d205bee939f53646f3180db257cad05c96bb011334849e79ec
7
+ data.tar.gz: 1025d6ebfab92f0a04d939aa57b5e2170e957fec465d7996b36abc5f4e334996f615e5cfc5b7db0b9a9ae076de2eac3108fa6e7a7446640b5de1fd4929fc48a5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.17.0 (2023-01-21)
4
+
5
+ * Add `add_action` dsl
6
+ * Remove `any_value`
7
+ * No access to `Instance#current_mutation`
8
+
9
+ ## 1.16.0 (2022-12-29)
10
+
11
+ * Add `Instance#query_adapter` and `Instance#mutation_adapter`
12
+ * One instance handle only one file
13
+ * Use `instance.file_path` instead of `instance.current_file`
14
+
3
15
  ## 1.15.0 (2022-11-30)
4
16
 
5
17
  * Load snippet from github
data/Gemfile.lock CHANGED
@@ -1,19 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- synvert-core (1.15.0)
4
+ synvert-core (1.17.0)
5
5
  activesupport (< 7.0.0)
6
6
  erubis
7
- node_mutation (>= 1.7.1)
8
- node_query (>= 1.10.0)
7
+ node_mutation (>= 1.8.2)
8
+ node_query (>= 1.12.0)
9
9
  parallel
10
10
  parser
11
- parser_node_ext (>= 0.4.1)
11
+ parser_node_ext (>= 0.6.1)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
- activesupport (6.1.7)
16
+ activesupport (6.1.7.1)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
18
  i18n (>= 1.6, < 2)
19
19
  minitest (>= 5.1)
@@ -48,20 +48,18 @@ GEM
48
48
  rb-inotify (~> 0.9, >= 0.9.10)
49
49
  lumberjack (1.2.8)
50
50
  method_source (1.0.0)
51
- minitest (5.16.3)
51
+ minitest (5.17.0)
52
52
  nenv (0.3.0)
53
- node_mutation (1.7.1)
54
- activesupport (< 7.0.0)
53
+ node_mutation (1.8.2)
55
54
  erubis
56
- node_query (1.10.0)
57
- activesupport (< 7.0.0)
55
+ node_query (1.12.0)
58
56
  notiffany (0.1.3)
59
57
  nenv (~> 0.1)
60
58
  shellany (~> 0.0)
61
59
  parallel (1.22.1)
62
- parser (3.1.3.0)
60
+ parser (3.2.0.0)
63
61
  ast (~> 2.4.1)
64
- parser_node_ext (0.4.1)
62
+ parser_node_ext (0.6.1)
65
63
  parser
66
64
  pry (0.14.1)
67
65
  coderay (~> 1.1)
data/README.md CHANGED
@@ -59,43 +59,51 @@ Want to use the CLI, check out [synvert-ruby](https://github.com/xinminlabs/synv
59
59
 
60
60
  DSLs are as follows
61
61
 
62
- * [configure](./Synvert/Core/Rewriter.html#configure-instance_method) - configure the rewriter
63
- * [description](./Synvert/Core/Rewriter.html#description-instance_method) - set description of the rewriter
64
- * [if_ruby](./Synvert/Core/Rewriter.html#if_ruby-instance_method) - check if ruby version is greater than or equal to the specified ruby version
65
- * [if_gem](./Synvert/Core/Rewriter.html#if_gem-instance_method) - compare version of specified gem
66
- * [within_files](./Synvert/Core/Rewriter.html#within_files-instance_method) - find specified files
67
- * [within_file](./Synvert/Core/Rewriter.html#within_file-instance_method) - alias to within_files
68
- * [add_file](./Synvert/Core/Rewriter.html#add_file-instance_method) - add a new file
69
- * [remove_file](./Synvert/Core/Rewriter.html#remove_file-instance_method) - remove a file
70
- * [helper_method](./Synvert/Core/Rewriter.html#helper_method-instance_method) - define a helper method
71
- * [add_snippet](./Synvert/Core/Rewriter.html#add_snippet-instance_method) - call another rewriter
72
- * [todo](./Synvert/Core/Rewriter.html#todo-instance_method) - set todo
73
- * [redo_until_no_change](./Synvert/Core/Rewriter.html#redo_until_no_change-instance_method) - run the snippet until no change
62
+ * [configure](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#configure-instance_method) - configure the rewriter
63
+ * [description](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#description-instance_method) - describe what the rewriter does
64
+ * [if_ruby](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#if_ruby-instance_method) - check if ruby version is greater than or equal to the specified ruby version
65
+ * [if_gem](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#if_gem-instance_method) - compare version of specified gem
66
+ * [within_files](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#within_files-instance_method) - find specified files
67
+ * [within_file](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#within_file-instance_method) - alias to within_files
68
+ * [add_file](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#add_file-instance_method) - add a new file
69
+ * [remove_file](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#remove_file-instance_method) - remove a file
70
+ * [helper_method](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#helper_method-instance_method) - define a helper method
71
+ * [add_snippet](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#add_snippet-instance_method) - call another rewriter
72
+ * [todo](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#todo-instance_method) - set todo
73
+ * [redo_until_no_change](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter.html#redo_until_no_change-instance_method) - run the snippet until no change
74
74
 
75
75
  Scopes:
76
76
 
77
- * [within_node](./Synvert/Core/Rewriter/Instance.html#within_node-instance_method) - recursively find matching ast nodes
78
- * [with_node](./Synvert/Core/Rewriter/Instance.html#with_node-instance_method) - alias to within_node
79
- * [find_node](./Synvert/Core/Rewriter/Instance.html#find_node-instance_method) - alias to within_node
80
- * [goto_node](./Synvert/Core/Rewriter/Instance.html#goto_node-instance_method) - go to a child node
77
+ * [within_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#within_node-instance_method) - recursively find matching ast nodes
78
+ * [with_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#with_node-instance_method) - alias to within_node
79
+ * [find_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#find_node-instance_method) - alias to within_node
80
+ * [goto_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#goto_node-instance_method) - go to a child node
81
81
 
82
82
  Conditions:
83
83
 
84
- * [if_exist_node](./Synvert/Core/Rewriter/Instance.html#if_exist_node-instance_method) - check if matching node exist in the child nodes
85
- * [unless_exist_node](./Synvert/Core/Rewriter/Instance.html#unless_exist_node-instance_method) - check if matching node doesn't exist in the child nodes
86
- * [if_only_exist_node](./Synvert/Core/Rewriter/Instance.html#if_only_exist_node-instance_method) - check if current node has only one child node and the child node matches
84
+ * [if_exist_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#if_exist_node-instance_method) - check if matching node exist in the child nodes
85
+ * [unless_exist_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#unless_exist_node-instance_method) - check if matching node doesn't exist in the child nodes
86
+ * [if_only_exist_node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#if_only_exist_node-instance_method) - check if current node has only one child node and the child node matches
87
87
 
88
88
  Actions:
89
89
 
90
- * [append](./Synvert/Core/Rewriter/Instance.html#append-instance_method) - append the code to the bottom of current node body
91
- * [prepend](./Synvert/Core/Rewriter/Instance.html#prepend-instance_method) - prepend the code to the bottom of current node body
92
- * [insert](./Synvert/Core/Rewriter/Instance.html#insert-instance_method) - insert code
93
- * [insert_after](./Synvert/Core/Rewriter/Instance.html#insert_after-instance_method) - insert the code next to the current node
94
- * [insert_before](./Synvert/Core/Rewriter/Instance.html#insert_before-instance_method) - insert the code previous to the current node
95
- * [replace](./Synvert/Core/Rewriter/Instance.html#replace-instance_method) - replace the code of specified child nodes
96
- * [delete](./Synvert/Core/Rewriter/Instance.html#delete-instance_method) - delete the code specified child nodes
97
- * [wrap](./Synvert/Core/Rewriter/Instance.html#wrap-instance_method) - wrap the current node with code
98
- * [replace_with](./Synvert/Core/Rewriter/Instance.html#replace_with-instance_method) - replace the whole code of current node
99
- * [warn](./Synvert/Core/Rewriter/Instance.html#warn-instance_method) - warn message
100
- * [replace_erb_stmt_with_expr](./Synvert/Core/Rewriter/Instance.html#replace_erb_stmt_with_expr-instance_method) - replace erb stmt code to expr code
101
- * [noop](./Synvert/Core/Rewriter/Instance.html#noop-instance_method) - no operation
90
+ * [append](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#append-instance_method) - append the code to the bottom of current node body
91
+ * [prepend](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#prepend-instance_method) - prepend the code to the bottom of current node body
92
+ * [insert](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#insert-instance_method) - insert code
93
+ * [insert_after](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#insert_after-instance_method) - insert the code next to the current node
94
+ * [insert_before](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#insert_before-instance_method) - insert the code previous to the current node
95
+ * [replace](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#replace-instance_method) - replace the code of specified child nodes
96
+ * [delete](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#delete-instance_method) - delete the code specified child nodes
97
+ * [wrap](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#wrap-instance_method) - wrap the current node with code
98
+ * [replace_with](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#replace_with-instance_method) - replace the whole code of current node
99
+ * [warn](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#warn-instance_method) - warn message
100
+ * [replace_erb_stmt_with_expr](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#replace_erb_stmt_with_expr-instance_method) - replace erb stmt code to expr code
101
+ * [noop](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#noop-instance_method) - no operation
102
+ * [add_action](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#add_action-instance_method) - add custom action
103
+
104
+ Attributes:
105
+
106
+ * [file_path](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#file_path-instance_method) - current file path
107
+ * [node](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#node-instance_method) - current ast node
108
+ * [query_adapter](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#query_adapter-instance_method) - [query adapter](https://xinminlabs.github.io/node-query-ruby/NodeQuery/Adapter.html) to get some helper methods
109
+ * [mutation_adapter](https://xinminlabs.github.io/synvert-core-ruby/Synvert/Core/Rewriter/Instance.html#mutation_adapter-instance_method) - [mutation adapter](https://xinminlabs.github.io/node-mutation-ruby/NodeMutation/Adapter.html) to get some helper methods
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'parallel'
3
2
 
4
3
  module Synvert::Core
5
4
  # Instance is an execution unit, it finds specified ast nodes,
@@ -12,12 +11,12 @@ module Synvert::Core
12
11
  # Initialize an Instance.
13
12
  #
14
13
  # @param rewriter [Synvert::Core::Rewriter]
15
- # @param file_patterns [Array<String>] pattern list to find files, e.g. ['spec/**/*_spec.rb']
14
+ # @param file_path [Array<String>]
16
15
  # @yield block code to find nodes, match conditions and rewrite code.
17
- def initialize(rewriter, file_patterns, &block)
16
+ def initialize(rewriter, file_path, &block)
18
17
  @rewriter = rewriter
19
18
  @actions = []
20
- @file_patterns = file_patterns
19
+ @file_path = file_path
21
20
  @block = block
22
21
  strategy = NodeMutation::Strategy::KEEP_RUNNING
23
22
  if rewriter.options[:strategy] == Strategy::ALLOW_INSERT_AT_SAME_POSITION
@@ -27,20 +26,54 @@ module Synvert::Core
27
26
  rewriter.helpers.each { |helper| singleton_class.send(:define_method, helper[:name], &helper[:block]) }
28
27
  end
29
28
 
29
+ # @!attribute [r] file_path
30
+ # @return file path
30
31
  # @!attribute [rw] current_node
31
- # @return current parsing node
32
- # @!attribute [rw] current_file
33
- # @return current filename
34
- # @!attribute [rw] current_mutation
35
- # @return current mutation
36
- attr_accessor :current_node, :current_file, :current_mutation
32
+ # @return current ast node
33
+ # @!attribute [r] query_adapter
34
+ # @return NodeQuery Adapter
35
+ # @!attribute [r] mutation_adapter
36
+ # @return NodeMutation Adapter
37
+ attr_reader :file_path, :current_node, :query_adapter, :mutation_adapter
38
+ attr_accessor :current_node
37
39
 
38
40
  # Process the instance.
39
41
  # It finds specified files, for each file, it executes the block code, rewrites the original code,
40
42
  # then writes the code back to the original file.
41
43
  def process
42
- get_file_paths.each do |file_path|
43
- process_file(file_path)
44
+ puts @file_path if Configuration.show_run_process
45
+
46
+ absolute_file_path = File.join(Configuration.root_path, @file_path)
47
+ while true
48
+ source = read_source(absolute_file_path)
49
+ @current_mutation = NodeMutation.new(source)
50
+ @mutation_adapter = NodeMutation.adapter
51
+ @query_adapter = NodeQuery.adapter
52
+ begin
53
+ node = parse_code(@file_path, source)
54
+
55
+ process_with_node(node) do
56
+ instance_eval(&@block)
57
+ rescue NoMethodError => e
58
+ puts [
59
+ "error: #{e.message}",
60
+ "file: #{file_path}",
61
+ "source: #{source}",
62
+ "line: #{current_node.line}"
63
+ ].join("\n")
64
+ raise
65
+ end
66
+
67
+ result = @current_mutation.process
68
+ if result.affected?
69
+ @rewriter.add_affected_file(file_path)
70
+ write_source(absolute_file_path, result.new_source)
71
+ end
72
+ break unless result.conflicted?
73
+ rescue Parser::SyntaxError
74
+ puts "[Warn] file #{file_path} was not parsed correctly."
75
+ # do nothing, iterate next file
76
+ end
44
77
  end
45
78
  end
46
79
 
@@ -48,17 +81,36 @@ module Synvert::Core
48
81
  # It finds specified files, for each file, it executes the block code, tests the original code,
49
82
  # then returns the actions.
50
83
  def test
51
- if Configuration.number_of_workers > 1
52
- Parallel.map(get_file_paths, in_processes: Configuration.number_of_workers) do |file_path|
53
- test_file(file_path)
54
- end
55
- else
56
- get_file_paths.map do |file_path|
57
- test_file(file_path)
84
+ absolute_file_path = File.join(Configuration.root_path, file_path)
85
+ source = read_source(absolute_file_path)
86
+ @current_mutation = NodeMutation.new(source)
87
+ @mutation_adapter = NodeMutation.adapter
88
+ @query_adapter = NodeQuery.adapter
89
+ begin
90
+ node = parse_code(file_path, source)
91
+
92
+ process_with_node(node) do
93
+ instance_eval(&@block)
94
+ rescue NoMethodError => e
95
+ puts [
96
+ "error: #{e.message}",
97
+ "file: #{file_path}",
98
+ "source: #{source}",
99
+ "line: #{current_node.line}"
100
+ ].join("\n")
101
+ raise
58
102
  end
103
+
104
+ result = @current_mutation.test
105
+ result.file_path = file_path
106
+ result
107
+ rescue Parser::SyntaxError
108
+ puts "[Warn] file #{file_path} was not parsed correctly."
109
+ # do nothing, iterate next file
59
110
  end
60
111
  end
61
112
 
113
+
62
114
  # Gets current node, it allows to get current node in block code.
63
115
  #
64
116
  # @return [Parser::AST::Node]
@@ -343,6 +395,15 @@ module Synvert::Core
343
395
  @current_mutation.noop(@current_node)
344
396
  end
345
397
 
398
+ # Add a custom action.
399
+ # @example
400
+ # remover_action = NodeMutation::RemoveAction.new(node)
401
+ # add_action(remover_action)
402
+ # @param action [Synvert::Core::Rewriter::Action] action
403
+ def add_action(action)
404
+ @current_mutation.actions << action.process
405
+ end
406
+
346
407
  # It creates a {Synvert::Core::Rewriter::Warning} to save warning message.
347
408
  # @example
348
409
  # within_files 'vendor/plugins' do
@@ -353,115 +414,8 @@ module Synvert::Core
353
414
  @rewriter.add_warning Rewriter::Warning.new(self, message)
354
415
  end
355
416
 
356
- # Match any value but nil.
357
- # @example
358
- # type: 'hash', nothing_value: 'true', status_value: any_value
359
- # @return [NodeQuery::AnyValue]
360
- def any_value
361
- NodeQuery::AnyValue.new
362
- end
363
-
364
417
  private
365
418
 
366
- # Process one file.
367
- #
368
- # @param file_path [String]
369
- def process_file(file_path)
370
- puts file_path if Configuration.show_run_process
371
-
372
- @current_file = File.join(Configuration.root_path, file_path)
373
- while true
374
- source = read_source(@current_file)
375
- @current_mutation = NodeMutation.new(source)
376
- begin
377
- node = parse_code(file_path, source)
378
-
379
- process_with_node(node) do
380
- instance_eval(&@block)
381
- rescue NoMethodError => e
382
- puts [
383
- "error: #{e.message}",
384
- "file: #{file_path}",
385
- "source: #{source}",
386
- "line: #{current_node.line}"
387
- ].join("\n")
388
- raise
389
- end
390
-
391
- result = @current_mutation.process
392
- if result.affected?
393
- @rewriter.add_affected_file(file_path)
394
- write_source(@current_file, result.new_source)
395
- end
396
- break unless result.conflicted?
397
- rescue Parser::SyntaxError
398
- puts "[Warn] file #{file_path} was not parsed correctly."
399
- # do nothing, iterate next file
400
- end
401
- end
402
- end
403
-
404
- # Test one file.
405
- #
406
- # @param file_path [String]
407
- def test_file(file_path)
408
- @current_file = File.join(Configuration.root_path, file_path)
409
- source = read_source(@current_file)
410
- @current_mutation = NodeMutation.new(source)
411
- begin
412
- node = parse_code(@current_file, source)
413
-
414
- process_with_node(node) do
415
- instance_eval(&@block)
416
- rescue NoMethodError => e
417
- puts [
418
- "error: #{e.message}",
419
- "file: #{file_path}",
420
- "source: #{source}",
421
- "line: #{current_node.line}"
422
- ].join("\n")
423
- raise
424
- end
425
-
426
- result = @current_mutation.test
427
- result.file_path = file_path
428
- result
429
- rescue Parser::SyntaxError
430
- puts "[Warn] file #{file_path} was not parsed correctly."
431
- # do nothing, iterate next file
432
- end
433
- end
434
-
435
- # Get file paths.
436
- # @return [Array<String>] file paths
437
- def get_file_paths
438
- Dir.chdir(Configuration.root_path) do
439
- only_paths = Configuration.only_paths.size > 0 ? Configuration.only_paths : ["."]
440
- only_paths.flat_map do |only_path|
441
- @file_patterns.flat_map do |file_pattern|
442
- pattern = only_path == "." ? file_pattern : File.join(only_path, file_pattern)
443
- Dir.glob(pattern)
444
- end
445
- end - get_skip_files
446
- end
447
- end
448
-
449
- # Get skip files.
450
- # @return [Array<String>] skip files
451
- def get_skip_files
452
- Configuration.skip_paths.flat_map do |skip_path|
453
- if File.directory?(skip_path)
454
- Dir.glob(File.join(skip_path, "**/*"))
455
- elsif File.file?(skip_path)
456
- [skip_path]
457
- elsif skip_path.end_with?("**") || skip_path.end_with?("**/")
458
- Dir.glob(File.join(skip_path, "*"))
459
- else
460
- Dir.glob(skip_path)
461
- end
462
- end
463
- end
464
-
465
419
  # Read file source.
466
420
  # @param file_path [String] file path
467
421
  # @return [String] file source
@@ -8,7 +8,7 @@ module Synvert::Core
8
8
  # @param instance [Synvert::Core::Rewriter::Instance]
9
9
  # @param message [String] warning message.
10
10
  def initialize(instance, message)
11
- @file_path = instance.current_file
11
+ @file_path = instance.file_path
12
12
  @line = instance.current_node.loc.expression.line
13
13
  @message = message
14
14
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'parallel'
3
4
  require 'fileutils'
4
5
 
5
6
  module Synvert::Core
@@ -223,11 +224,16 @@ module Synvert::Core
223
224
  return if @ruby_version && !@ruby_version.match?
224
225
  return if @gem_spec && !@gem_spec.match?
225
226
 
226
- instance = Rewriter::Instance.new(self, Array(file_patterns), &block)
227
227
  if @options[:write_to_file]
228
- instance.process
228
+ handle_one_file(Array(file_patterns)) do |file_path|
229
+ instance = Rewriter::Instance.new(self, file_path, &block)
230
+ instance.process
231
+ end
229
232
  else
230
- results = instance.test
233
+ results = handle_one_file(Array(file_patterns)) do |file_path|
234
+ instance = Rewriter::Instance.new(self, file_path, &block)
235
+ instance.test
236
+ end
231
237
  merge_test_results(results)
232
238
  end
233
239
  end
@@ -357,6 +363,52 @@ module Synvert::Core
357
363
 
358
364
  private
359
365
 
366
+ # Handle one file.
367
+ # @param file_patterns [String] file patterns to find files.
368
+ # @yield [file_path] block to handle file.
369
+ # @yieldparam file_path [String] file path.
370
+ def handle_one_file(file_patterns)
371
+ if Configuration.number_of_workers > 1
372
+ Parallel.map(get_file_paths(file_patterns), in_processes: Configuration.number_of_workers) do |file_path|
373
+ yield(file_path)
374
+ end
375
+ else
376
+ get_file_paths(file_patterns).map do |file_path|
377
+ yield(file_path)
378
+ end
379
+ end
380
+ end
381
+
382
+ # Get file paths.
383
+ # @return [Array<String>] file paths
384
+ def get_file_paths(file_patterns)
385
+ Dir.chdir(Configuration.root_path) do
386
+ only_paths = Configuration.only_paths.size > 0 ? Configuration.only_paths : ["."]
387
+ only_paths.flat_map do |only_path|
388
+ file_patterns.flat_map do |file_pattern|
389
+ pattern = only_path == "." ? file_pattern : File.join(only_path, file_pattern)
390
+ Dir.glob(pattern)
391
+ end
392
+ end - get_skip_files
393
+ end
394
+ end
395
+
396
+ # Get skip files.
397
+ # @return [Array<String>] skip files
398
+ def get_skip_files
399
+ Configuration.skip_paths.flat_map do |skip_path|
400
+ if File.directory?(skip_path)
401
+ Dir.glob(File.join(skip_path, "**/*"))
402
+ elsif File.file?(skip_path)
403
+ [skip_path]
404
+ elsif skip_path.end_with?("**") || skip_path.end_with?("**/")
405
+ Dir.glob(File.join(skip_path, "*"))
406
+ else
407
+ Dir.glob(skip_path)
408
+ end
409
+ end
410
+ end
411
+
360
412
  def merge_test_results(results)
361
413
  @test_results += results.select { |result| result.affected? }
362
414
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.15.0'
5
+ VERSION = '1.17.0'
6
6
  end
7
7
  end
data/lib/synvert/core.rb CHANGED
@@ -7,6 +7,7 @@ require 'parser/current'
7
7
  require 'parser_node_ext'
8
8
  require_relative './core/node_ext'
9
9
  require 'active_support'
10
+ require 'active_support/core_ext'
10
11
  require 'erubis'
11
12
  require 'set'
12
13
  require 'node_query'
@@ -6,7 +6,7 @@ module Synvert::Core
6
6
  describe Rewriter::Instance do
7
7
  let(:instance) {
8
8
  rewriter = Rewriter.new('foo', 'bar')
9
- Rewriter::Instance.new(rewriter, ['file pattern'])
9
+ Rewriter::Instance.new(rewriter, 'code.rb')
10
10
  }
11
11
 
12
12
  it 'parses find_node' do
@@ -97,98 +97,98 @@ module Synvert::Core
97
97
  end
98
98
 
99
99
  it 'parses append' do
100
- instance.current_mutation = double
100
+ instance.instance_variable_set(:@current_mutation, double)
101
101
  instance.current_node = double
102
- expect(instance.current_mutation).to receive(:append).with(instance.current_node, 'Foobar')
102
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:append).with(instance.current_node, 'Foobar')
103
103
  instance.append 'Foobar'
104
104
  end
105
105
 
106
106
  it 'parses prepend' do
107
- instance.current_mutation = double
107
+ instance.instance_variable_set(:@current_mutation, double)
108
108
  instance.current_node = double
109
- expect(instance.current_mutation).to receive(:prepend).with(instance.current_node, 'Foobar')
109
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:prepend).with(instance.current_node, 'Foobar')
110
110
  instance.prepend 'Foobar'
111
111
  end
112
112
 
113
113
  it 'parses insert at end' do
114
- instance.current_mutation = double
114
+ instance.instance_variable_set(:@current_mutation, double)
115
115
  instance.current_node = double
116
- expect(instance.current_mutation).to receive(:insert).with(instance.current_node, 'Foobar', at: 'end', to: 'receiver')
116
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:insert).with(instance.current_node, 'Foobar', at: 'end', to: 'receiver')
117
117
  instance.insert 'Foobar', to: 'receiver'
118
118
  end
119
119
 
120
120
  it 'parses insert at beginning' do
121
- instance.current_mutation = double
121
+ instance.instance_variable_set(:@current_mutation, double)
122
122
  instance.current_node = double
123
- expect(instance.current_mutation).to receive(:insert).with(instance.current_node, 'Foobar', at: 'beginning', to: nil)
123
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:insert).with(instance.current_node, 'Foobar', at: 'beginning', to: nil)
124
124
  instance.insert 'Foobar', at: 'beginning'
125
125
  end
126
126
 
127
127
  it 'parses insert_after' do
128
- instance.current_mutation = double
128
+ instance.instance_variable_set(:@current_mutation, double)
129
129
  instance.current_node = double
130
130
  expect(NodeMutation).to receive_message_chain(:adapter, :get_start_loc, :column).and_return(2)
131
- expect(instance.current_mutation).to receive(:insert).with(instance.current_node, "\n Foobar", at: 'end', to: nil)
131
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:insert).with(instance.current_node, "\n Foobar", at: 'end', to: nil)
132
132
  instance.insert_after 'Foobar'
133
133
  end
134
134
 
135
135
  it 'parses insert_before' do
136
- instance.current_mutation = double
136
+ instance.instance_variable_set(:@current_mutation, double)
137
137
  instance.current_node = double
138
138
  expect(NodeMutation).to receive_message_chain(:adapter, :get_start_loc, :column).and_return(2)
139
- expect(instance.current_mutation).to receive(:insert).with(instance.current_node, "Foobar\n ", at: 'beginning', to: nil)
139
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:insert).with(instance.current_node, "Foobar\n ", at: 'beginning', to: nil)
140
140
  instance.insert_before 'Foobar'
141
141
  end
142
142
 
143
143
  it 'parses replace_erb_stmt_with_expr' do
144
- instance.current_mutation = double
144
+ instance.instance_variable_set(:@current_mutation, double)
145
145
  instance.current_node = double
146
146
  action = double
147
- expect(instance.current_mutation).to receive(:actions).and_return([])
147
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:actions).and_return([])
148
148
  expect(Rewriter::ReplaceErbStmtWithExprAction).to receive(:new).with(instance.current_node).and_return(action)
149
149
  expect(action).to receive(:process)
150
150
  instance.replace_erb_stmt_with_expr
151
151
  end
152
152
 
153
153
  it 'parses replace_with' do
154
- instance.current_mutation = double
154
+ instance.instance_variable_set(:@current_mutation, double)
155
155
  instance.current_node = double
156
- expect(instance.current_mutation).to receive(:replace_with).with(instance.current_node, 'Foobar')
156
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:replace_with).with(instance.current_node, 'Foobar')
157
157
  instance.replace_with 'Foobar'
158
158
  end
159
159
 
160
160
  it 'parses replace with' do
161
- instance.current_mutation = double
161
+ instance.instance_variable_set(:@current_mutation, double)
162
162
  instance.current_node = double
163
- expect(instance.current_mutation).to receive(:replace).with(instance.current_node, :message, with: 'Foobar')
163
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:replace).with(instance.current_node, :message, with: 'Foobar')
164
164
  instance.replace :message, with: 'Foobar'
165
165
  end
166
166
 
167
167
  it 'parses remove' do
168
- instance.current_mutation = double
168
+ instance.instance_variable_set(:@current_mutation, double)
169
169
  instance.current_node = double
170
- expect(instance.current_mutation).to receive(:remove).with(instance.current_node, and_comma: true)
170
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:remove).with(instance.current_node, and_comma: true)
171
171
  instance.remove and_comma: true
172
172
  end
173
173
 
174
174
  it 'parses delete' do
175
- instance.current_mutation = double
175
+ instance.instance_variable_set(:@current_mutation, double)
176
176
  instance.current_node = double
177
- expect(instance.current_mutation).to receive(:delete).with(instance.current_node, :dot, :message, and_comma: true)
177
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:delete).with(instance.current_node, :dot, :message, and_comma: true)
178
178
  instance.delete :dot, :message, and_comma: true
179
179
  end
180
180
 
181
181
  it 'parses wrap with' do
182
- instance.current_mutation = double
182
+ instance.instance_variable_set(:@current_mutation, double)
183
183
  instance.current_node = double
184
- expect(instance.current_mutation).to receive(:wrap).with(instance.current_node, with: 'module Foobar')
184
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:wrap).with(instance.current_node, with: 'module Foobar')
185
185
  instance.wrap with: 'module Foobar'
186
186
  end
187
187
 
188
188
  it 'parses noop' do
189
- instance.current_mutation = double
189
+ instance.instance_variable_set(:@current_mutation, double)
190
190
  instance.current_node = double
191
- expect(instance.current_mutation).to receive(:noop).with(instance.current_node)
191
+ expect(instance.instance_variable_get(:@current_mutation)).to receive(:noop).with(instance.current_node)
192
192
  instance.noop
193
193
  end
194
194
 
@@ -197,8 +197,13 @@ module Synvert::Core
197
197
  instance.warn 'foobar'
198
198
  end
199
199
 
200
- it 'parses any_value' do
201
- expect(instance.any_value).to be_instance_of NodeQuery::AnyValue
200
+ it 'adds action' do
201
+ mutation = NodeMutation.new("")
202
+ instance.instance_variable_set(:@current_mutation, mutation)
203
+ action = double
204
+ expect(action).to receive(:process).and_return(action)
205
+ instance.add_action(action)
206
+ expect(mutation.actions).to eq [action]
202
207
  end
203
208
 
204
209
  describe '#process' do
@@ -206,7 +211,7 @@ module Synvert::Core
206
211
 
207
212
  it 'writes new code to file' do
208
213
  instance =
209
- Rewriter::Instance.new rewriter, ['spec/**/*_spec.rb'] do
214
+ Rewriter::Instance.new rewriter, 'spec/models/post_spec.rb' do
210
215
  with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
211
216
  replace_with 'create {{arguments}}'
212
217
  end
@@ -225,7 +230,6 @@ module Synvert::Core
225
230
  assert post.valid?
226
231
  end
227
232
  EOS
228
- expect(Dir).to receive(:glob).with('spec/**/*_spec.rb').and_return(['spec/models/post_spec.rb'])
229
233
  expect(File).to receive(:read).with('./spec/models/post_spec.rb', encoding: 'UTF-8').and_return(input)
230
234
  expect(File).to receive(:write).with('./spec/models/post_spec.rb', output)
231
235
  instance.process
@@ -233,7 +237,7 @@ module Synvert::Core
233
237
 
234
238
  it 'does not write if file content is not changed' do
235
239
  instance =
236
- Rewriter::Instance.new rewriter, ['spec/spec_helper.rb'] do
240
+ Rewriter::Instance.new rewriter, 'spec/spec_helper.rb' do
237
241
  with_node type: 'block', caller: { receiver: 'RSpec', message: 'configure' } do
238
242
  unless_exist_node type: 'send', message: 'include', arguments: ['FactoryGirl::Syntax::Methods'] do
239
243
  insert '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
@@ -250,7 +254,6 @@ module Synvert::Core
250
254
  config.include FactoryGirl::Syntax::Methods
251
255
  end
252
256
  EOS
253
- expect(Dir).to receive(:glob).with('spec/spec_helper.rb').and_return(['spec/spec_helper.rb'])
254
257
  expect(File).to receive(:read).with('./spec/spec_helper.rb', encoding: 'UTF-8').and_return(input)
255
258
  expect(File).not_to receive(:write).with('./spec/spec_helper.rb', output)
256
259
  instance.process
@@ -258,7 +261,7 @@ module Synvert::Core
258
261
 
259
262
  it 'updates file_source and file_ast when writing a file' do
260
263
  instance =
261
- Rewriter::Instance.new rewriter, ['spec/**/*_spec.rb'] do
264
+ Rewriter::Instance.new rewriter, 'spec/models/post_spec.rb' do
262
265
  with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
263
266
  replace_with 'create {{arguments}}'
264
267
  end
@@ -277,7 +280,6 @@ module Synvert::Core
277
280
  assert post.valid?
278
281
  end
279
282
  EOS
280
- expect(Dir).to receive(:glob).with('spec/**/*_spec.rb').and_return(['spec/models/post_spec.rb']).twice
281
283
  expect(File).to receive(:read).with('./spec/models/post_spec.rb', encoding: 'UTF-8').and_return(input)
282
284
  expect(File).to receive(:write).with('./spec/models/post_spec.rb', output)
283
285
  expect(File).to receive(:read).with('./spec/models/post_spec.rb', encoding: 'UTF-8').and_return(output)
@@ -292,7 +294,7 @@ module Synvert::Core
292
294
 
293
295
  it 'writes new code to file' do
294
296
  instance =
295
- Rewriter::Instance.new rewriter, ['spec/**/*_spec.rb'] do
297
+ Rewriter::Instance.new rewriter, 'spec/models/post_spec.rb' do
296
298
  with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
297
299
  replace_with 'create {{arguments}}'
298
300
  end
@@ -304,11 +306,10 @@ module Synvert::Core
304
306
  assert post.valid?
305
307
  end
306
308
  EOS
307
- expect(Dir).to receive(:glob).with('spec/**/*_spec.rb').and_return(['spec/models/post_spec.rb'])
308
309
  expect(File).to receive(:read).with('./spec/models/post_spec.rb', encoding: 'UTF-8').and_return(input)
309
310
  results = instance.test
310
- expect(results[0].file_path).to eq 'spec/models/post_spec.rb'
311
- expect(results[0].actions).to eq [
311
+ expect(results.file_path).to eq 'spec/models/post_spec.rb'
312
+ expect(results.actions).to eq [
312
313
  OpenStruct.new(start: 35, end: 59, new_code: 'create :user'),
313
314
  OpenStruct.new(start: 69, end: 105, new_code: 'create :post, user: user')
314
315
  ]
@@ -316,7 +317,7 @@ module Synvert::Core
316
317
 
317
318
  it 'does not write if file content is not changed' do
318
319
  instance =
319
- Rewriter::Instance.new rewriter, ['spec/spec_helper.rb'] do
320
+ Rewriter::Instance.new rewriter, 'spec/spec_helper.rb' do
320
321
  with_node type: 'block', caller: { receiver: 'RSpec', message: 'configure' } do
321
322
  unless_exist_node type: 'send', message: 'include', arguments: ['FactoryGirl::Syntax::Methods'] do
322
323
  insert '{{arguments.first}}.include FactoryGirl::Syntax::Methods'
@@ -328,11 +329,10 @@ module Synvert::Core
328
329
  config.include FactoryGirl::Syntax::Methods
329
330
  end
330
331
  EOS
331
- expect(Dir).to receive(:glob).with('spec/spec_helper.rb').and_return(['spec/spec_helper.rb'])
332
332
  expect(File).to receive(:read).with('./spec/spec_helper.rb', encoding: 'UTF-8').and_return(input)
333
333
  result = instance.test
334
- expect(result[0].file_path).to eq 'spec/spec_helper.rb'
335
- expect(result[0].actions).to eq []
334
+ expect(result.file_path).to eq 'spec/spec_helper.rb'
335
+ expect(result.actions).to eq []
336
336
  end
337
337
  end
338
338
 
@@ -7,7 +7,7 @@ module Synvert::Core
7
7
  subject {
8
8
  source = "def test\n debugger\nend"
9
9
  send_node = Parser::CurrentRuby.parse(source).body.first
10
- instance = double(current_node: send_node, current_file: 'app/test.rb')
10
+ instance = double(current_node: send_node, file_path: 'app/test.rb')
11
11
  Rewriter::Warning.new(instance, 'remove debugger')
12
12
  }
13
13
 
@@ -86,6 +86,7 @@ module Synvert::Core
86
86
  end
87
87
 
88
88
  it 'delegates process to instances if if_ruby matches' do
89
+ expect(Dir).to receive(:glob).with('config/routes.rb').and_return(['config/routes.rb'])
89
90
  expect(File).to receive(:exist?).with('./.ruby-version').and_return(true)
90
91
  expect(File).to receive(:read).with('./.ruby-version').and_return('2.0.0')
91
92
  expect_any_instance_of(Rewriter::Instance).to receive(:process)
@@ -111,6 +112,7 @@ module Synvert::Core
111
112
  end
112
113
 
113
114
  it 'delegates process to instances if if_gem matches' do
115
+ expect(Dir).to receive(:glob).with('config/routes.rb').and_return(['config/routes.rb'])
114
116
  expect_any_instance_of(Rewriter::GemSpec).to receive(:match?).and_return(true)
115
117
  expect_any_instance_of(Rewriter::Instance).to receive(:process)
116
118
  rewriter =
@@ -123,6 +125,7 @@ module Synvert::Core
123
125
  end
124
126
 
125
127
  it 'delegates process to instances if if_ruby and if_gem do not exist' do
128
+ expect(Dir).to receive(:glob).with('config/routes.rb').and_return(['config/routes.rb'])
126
129
  expect_any_instance_of(Rewriter::Instance).to receive(:process)
127
130
  rewriter =
128
131
  Rewriter.new 'group', 'name' do
@@ -221,7 +224,8 @@ module Synvert::Core
221
224
  end
222
225
 
223
226
  it 'adds snippet by http url' do
224
- expect_any_instance_of(URI).to receive(:open).and_return(StringIO.new("Rewriter.new 'group', 'sub_rewriter' do\nend"))
227
+ expect(Utils).to receive(:remote_snippet_exists?).with(URI.parse('http://synvert.net/foo/bar.rb')).and_return(true)
228
+ expect_any_instance_of(URI::HTTP).to receive(:open).and_return(StringIO.new("Rewriter.new 'group', 'sub_rewriter' do\nend"))
225
229
  rewriter = Rewriter.new 'group', 'rewriter' do
226
230
  add_snippet 'http://synvert.net/foo/bar.rb'
227
231
  end
@@ -21,9 +21,9 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency "activesupport", "< 7.0.0"
23
23
  spec.add_runtime_dependency "erubis"
24
- spec.add_runtime_dependency "node_query", ">= 1.10.0"
25
- spec.add_runtime_dependency "node_mutation", ">= 1.7.1"
24
+ spec.add_runtime_dependency "node_query", ">= 1.12.0"
25
+ spec.add_runtime_dependency "node_mutation", ">= 1.8.2"
26
26
  spec.add_runtime_dependency "parser"
27
- spec.add_runtime_dependency "parser_node_ext", ">= 0.4.1"
27
+ spec.add_runtime_dependency "parser_node_ext", ">= 0.6.1"
28
28
  spec.add_runtime_dependency "parallel"
29
29
  end
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.15.0
4
+ version: 1.17.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: 2022-11-30 00:00:00.000000000 Z
11
+ date: 2023-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 1.10.0
47
+ version: 1.12.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.10.0
54
+ version: 1.12.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: node_mutation
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 1.7.1
61
+ version: 1.8.2
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 1.7.1
68
+ version: 1.8.2
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: parser
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 0.4.1
89
+ version: 0.6.1
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: 0.4.1
96
+ version: 0.6.1
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: parallel
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -188,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
188
  - !ruby/object:Gem::Version
189
189
  version: '0'
190
190
  requirements: []
191
- rubygems_version: 3.3.26
191
+ rubygems_version: 3.4.1
192
192
  signing_key:
193
193
  specification_version: 4
194
194
  summary: convert ruby code to better syntax.