synvert-core 0.63.1 → 0.64.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -1
  3. data/README.md +73 -33
  4. data/lib/synvert/core/configuration.rb +12 -0
  5. data/lib/synvert/core/exceptions.rb +0 -4
  6. data/lib/synvert/core/node_ext.rb +188 -87
  7. data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
  8. data/lib/synvert/core/rewriter/action/delete_action.rb +13 -6
  9. data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
  10. data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
  11. data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
  12. data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
  13. data/lib/synvert/core/rewriter/action/replace_action.rb +13 -5
  14. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
  15. data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
  16. data/lib/synvert/core/rewriter/action/wrap_action.rb +13 -5
  17. data/lib/synvert/core/rewriter/action.rb +20 -9
  18. data/lib/synvert/core/rewriter/any_value.rb +1 -0
  19. data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
  20. data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
  21. data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
  22. data/lib/synvert/core/rewriter/condition.rb +11 -3
  23. data/lib/synvert/core/rewriter/gem_spec.rb +6 -3
  24. data/lib/synvert/core/rewriter/helper.rb +2 -2
  25. data/lib/synvert/core/rewriter/instance.rb +195 -94
  26. data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
  27. data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
  28. data/lib/synvert/core/rewriter/scope/within_scope.rb +9 -4
  29. data/lib/synvert/core/rewriter/scope.rb +8 -0
  30. data/lib/synvert/core/rewriter/warning.rb +1 -1
  31. data/lib/synvert/core/rewriter.rb +90 -43
  32. data/lib/synvert/core/version.rb +1 -1
  33. data/lib/synvert/core.rb +0 -1
  34. data/spec/synvert/core/node_ext_spec.rb +0 -7
  35. data/spec/synvert/core/rewriter/action_spec.rb +0 -4
  36. data/spec/synvert/core/rewriter/gem_spec_spec.rb +1 -1
  37. data/spec/synvert/core/rewriter/instance_spec.rb +7 -17
  38. data/synvert-core-ruby.gemspec +2 -1
  39. metadata +20 -6
@@ -5,18 +5,8 @@ require 'fileutils'
5
5
  module Synvert::Core
6
6
  # Rewriter is the top level namespace in a snippet.
7
7
  #
8
- # One Rewriter can contain one or many [Synvert::Core::Rewriter::Instance],
8
+ # One Rewriter checks if the depndency version matches, and it can contain one or many {Synvert::Core::Rewriter::Instance},
9
9
  # which define the behavior what files and what codes to detect and rewrite to what code.
10
- #
11
- # Synvert::Rewriter.new 'factory_girl_short_syntax', 'use FactoryGirl short syntax' do
12
- # if_gem 'factory_girl', '>= 2.0.0'
13
- #
14
- # within_files 'spec/**/*.rb' do
15
- # with_node type: 'send', receiver: 'FactoryGirl', message: 'create' do
16
- # replace_with "create({{arguments}})"
17
- # end
18
- # end
19
- # end
20
10
  class Rewriter
21
11
  autoload :Action, 'synvert/core/rewriter/action'
22
12
  autoload :AppendAction, 'synvert/core/rewriter/action/append_action'
@@ -53,7 +43,7 @@ module Synvert::Core
53
43
  class << self
54
44
  # Execute the temporary rewriter without group and name.
55
45
  #
56
- # @param block [Block] a block defines the behaviors of the rewriter.
46
+ # @yield defines the behaviors of the rewriter.
57
47
  def execute(&block)
58
48
  rewriter = Rewriter.new('', '', &block)
59
49
  rewriter.process
@@ -91,7 +81,7 @@ module Synvert::Core
91
81
  #
92
82
  # @param group [String] the rewriter group.
93
83
  # @param name [String] the rewriter name.
94
- # @param sandbox [Boolean] if run in sandbox mode, default is false.
84
+ # @param sandbox [Boolean] if run in sandbox mode.
95
85
  # @return [Synvert::Core::Rewriter] the registered rewriter.
96
86
  # @raise [Synvert::Core::RewriterNotFound] if the registered rewriter is not found.
97
87
  def call(group, name, sandbox = false)
@@ -141,13 +131,12 @@ module Synvert::Core
141
131
  # @return [Rewriter::GemSpec] the gem spec
142
132
  attr_reader :group, :name, :sub_snippets, :helpers, :warnings, :affected_files, :ruby_version, :gem_spec
143
133
 
144
- # Initialize a rewriter.
145
- # When a rewriter is initialized, it is also registered.
134
+ # Initialize a Rewriter.
135
+ # When a rewriter is initialized, it is already registered.
146
136
  #
147
137
  # @param group [String] group of the rewriter.
148
138
  # @param name [String] name of the rewriter.
149
- # @param block [Block] a block defines the behaviors of the rewriter, block code won't be called when initialization.
150
- # @return [Synvert::Core::Rewriter]
139
+ # @yield defines the behaviors of the rewriter, block code won't be called when initialization.
151
140
  def initialize(group, name, &block)
152
141
  @group = group
153
142
  @name = name
@@ -198,9 +187,12 @@ module Synvert::Core
198
187
  # DSL #
199
188
  #######
200
189
 
201
- # Parse description dsl, it sets description of the rewrite.
190
+ # Parse +description+ dsl, it sets description of the rewrite.
202
191
  # Or get description.
203
- #
192
+ # @example
193
+ # Synvert::Rewriter.new 'rspec', 'use_new_syntax' do
194
+ # description 'It converts rspec code to new syntax, it calls all rspec sub snippets.'
195
+ # end
204
196
  # @param description [String] rewriter description.
205
197
  # @return rewriter description.
206
198
  def description(description = nil)
@@ -211,24 +203,34 @@ module Synvert::Core
211
203
  end
212
204
  end
213
205
 
214
- # Parse if_ruby dsl, it checks if ruby version if greater than or equal to the specified ruby version.
215
- #
216
- # @param version, [String] specified ruby version.
206
+ # Parse +if_ruby+ dsl, it checks if ruby version is greater than or equal to the specified ruby version.
207
+ # @example
208
+ # Synvert::Rewriter.new 'ruby', 'new_safe_navigation_operator' do
209
+ # if_ruby '2.3.0'
210
+ # end
211
+ # @param version [String] specified ruby version.
217
212
  def if_ruby(version)
218
213
  @ruby_version = Rewriter::RubyVersion.new(version)
219
214
  end
220
215
 
221
- # Parse if_gem dsl, it compares version of the specified gem.
222
- #
216
+ # Parse +if_gem+ dsl, it compares version of the specified gem.
217
+ # @example
218
+ # Synvert::Rewriter.new 'rails', 'upgrade_5_2_to_6_0' do
219
+ # if_gem 'rails', '>= 6.0'
220
+ # end
223
221
  # @param name [String] gem name.
224
222
  # @param version [String] equal, less than or greater than specified version, e.g. '>= 2.0.0',
225
223
  def if_gem(name, version)
226
224
  @gem_spec = Rewriter::GemSpec.new(name, version)
227
225
  end
228
226
 
229
- # Parse within_files dsl, it finds specified files.
230
- # It creates a [Synvert::Core::Rewriter::Instance] to rewrite code.
231
- #
227
+ # Parse +within_files+ dsl, it finds specified files.
228
+ # It creates a {Synvert::Core::Rewriter::Instance} to rewrite code.
229
+ # @example
230
+ # Synvert::Rewriter.new 'rspec', 'be_close_to_be_within' do
231
+ # within_files '**/*.rb' do
232
+ # end
233
+ # end
232
234
  # @param file_patterns [String|Array<String>] string pattern or list of string pattern to find files, e.g. ['spec/**/*_spec.rb']
233
235
  # @param block [Block] the block to rewrite code in the matching files.
234
236
  def within_files(file_patterns, &block)
@@ -240,11 +242,18 @@ module Synvert::Core
240
242
  Rewriter::Instance.new(self, Array(file_patterns), &block).process
241
243
  end
242
244
 
243
- # Parse within_file dsl, it finds a specifiled file.
245
+ # Parse +within_file+ dsl, it finds a specifiled file.
244
246
  alias within_file within_files
245
247
 
246
- # Parses add_file dsl, it adds a new file.
247
- #
248
+ # Parses +add_file+ dsl, it adds a new file.
249
+ # @example
250
+ # Synvert::Rewriter.new 'rails', 'add_application_record' do
251
+ # add_file 'app/models/application_record.rb', <<~EOS
252
+ # class ApplicationRecord < ActiveRecord::Base
253
+ # self.abstract_class = true
254
+ # end
255
+ # EOS
256
+ # end
248
257
  # @param filename [String] file name of newly created file.
249
258
  # @param content [String] file body of newly created file.
250
259
  def add_file(filename, content)
@@ -257,13 +266,14 @@ module Synvert::Core
257
266
  end
258
267
 
259
268
  FileUtils.mkdir_p File.dirname(filepath)
260
- File.open filepath, 'w' do |file|
261
- file.write content
262
- end
269
+ File.write(filepath, content)
263
270
  end
264
271
 
265
- # Parses remove_file dsl, it removes a file.
266
- #
272
+ # Parses +remove_file+ dsl, it removes a file.
273
+ # @example
274
+ # Synvert::Rewriter.new 'rails', 'upgrade_4_0_to_4_1' do
275
+ # remove_file 'config/initializers/secret_token.rb'
276
+ # end
267
277
  # @param filename [String] file name.
268
278
  def remove_file(filename)
269
279
  return if @sandbox
@@ -272,26 +282,59 @@ module Synvert::Core
272
282
  File.delete(file_path) if File.exist?(file_path)
273
283
  end
274
284
 
275
- # Parse add_snippet dsl, it calls anther rewriter.
276
- #
285
+ # Parse +add_snippet+ dsl, it calls anther rewriter.
286
+ # @example
287
+ # Synvert::Rewriter.new 'minitest', 'better_syntax' do
288
+ # add_snippet 'minitest', 'assert_empty'
289
+ # add_snippet 'minitest', 'assert_equal_arguments_order'
290
+ # add_snippet 'minitest', 'assert_includes'
291
+ # add_snippet 'minitest', 'assert_instance_of'
292
+ # add_snippet 'minitest', 'assert_kind_of'
293
+ # add_snippet 'minitest', 'assert_match'
294
+ # add_snippet 'minitest', 'assert_nil'
295
+ # add_snippet 'minitest', 'assert_operator'
296
+ # add_snippet 'minitest', 'assert_path_exists'
297
+ # add_snippet 'minitest', 'assert_predicate'
298
+ # add_snippet 'minitest', 'assert_respond_to'
299
+ # add_snippet 'minitest', 'assert_silent'
300
+ # add_snippet 'minitest', 'assert_truthy'
301
+ # end
277
302
  # @param group [String] group of another rewriter.
278
303
  # @param name [String] name of another rewriter.
279
304
  def add_snippet(group, name)
280
305
  @sub_snippets << self.class.call(group.to_s, name.to_s, @sandbox)
281
306
  end
282
307
 
283
- # Parse helper_method dsl, it defines helper method for [Synvert::Core::Rewriter::Instance].
284
- #
308
+ # Parse +helper_method+ dsl, it defines helper method for {Synvert::Core::Rewriter::Instance}.
309
+ # @example
310
+ # Synvert::Rewriter.new 'rails', 'convert_active_record_dirty_5_0_to_5_1' do
311
+ # helper_method :find_callbacks_and_convert do |callback_names, callback_changes|
312
+ # # do anything, method find_callbacks_and_convert can be reused later.
313
+ # end
314
+ # within_files Synvert::RAILS_MODEL_FILES + Synvert::RAILS_OBSERVER_FILES do
315
+ # find_callbacks_and_convert(before_callback_names, before_callback_changes)
316
+ # find_callbacks_and_convert(after_callback_names, after_callback_changes)
317
+ # end
318
+ # end
285
319
  # @param name [String] helper method name.
286
- # @param block [Block] helper method block.
320
+ # @yield helper method block.
287
321
  def helper_method(name, &block)
288
322
  @helpers << { name: name, block: block }
289
323
  end
290
324
 
291
- # Parse todo dsl, it sets todo of the rewriter.
325
+ # Parse +todo+ dsl, it sets todo of the rewriter.
292
326
  # Or get todo.
293
- #
294
- # @param todo_list [String] rewriter todo.
327
+ # @example
328
+ # Synvert::Rewriter.new 'rails', 'upgrade_3_2_to_4_0' do
329
+ # todo <<~EOS
330
+ # 1. Rails 4.0 no longer supports loading plugins from vendor/plugins. You must replace any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, lib/my_plugin/* and add an appropriate initializer in config/initializers/my_plugin.rb.
331
+ # 2. Make the following changes to your Gemfile.
332
+ # gem 'sass-rails', '~> 4.0.0'
333
+ # gem 'coffee-rails', '~> 4.0.0'
334
+ # gem 'uglifier', '>= 1.3.0'
335
+ # EOS
336
+ # end
337
+ # @param todo [String] rewriter todo.
295
338
  # @return [String] rewriter todo.
296
339
  def todo(todo = nil)
297
340
  if todo
@@ -302,6 +345,10 @@ module Synvert::Core
302
345
  end
303
346
 
304
347
  # Rerun the snippet until no change.
348
+ # @example
349
+ # Synvert::Rewriter.new 'ruby', 'nested_class_definition' do
350
+ # redo_until_no_change
351
+ # end
305
352
  def redo_until_no_change
306
353
  @redo_until_no_change = true
307
354
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '0.63.1'
5
+ VERSION = '0.64.0'
6
6
  end
7
7
  end
data/lib/synvert/core.rb CHANGED
@@ -18,7 +18,6 @@ module Synvert
18
18
  autoload :Rewriter, 'synvert/core/rewriter'
19
19
  autoload :Engine, 'synvert/core/engine'
20
20
  autoload :RewriterNotFound, 'synvert/core/exceptions'
21
- autoload :GemfileLockNotFound, 'synvert/core/exceptions'
22
21
  autoload :MethodNotSupported, 'synvert/core/exceptions'
23
22
  end
24
23
  end
@@ -404,13 +404,6 @@ describe Parser::AST::Node do
404
404
  end
405
405
  end
406
406
 
407
- describe '#to_s' do
408
- it 'gets for mlhs node' do
409
- node = parse('var.each { |(param1, param2)| }')
410
- expect(node.arguments.first.to_s).to eq '(param1, param2)'
411
- end
412
- end
413
-
414
407
  describe '#filename' do
415
408
  it 'gets file name' do
416
409
  source = 'foobar'
@@ -10,9 +10,5 @@ module Synvert::Core
10
10
  instance = double(current_node: block_node)
11
11
  Rewriter::Action.new(instance, source)
12
12
  }
13
-
14
- it 'gets line' do
15
- expect(subject.line).to eq 2
16
- end
17
13
  end
18
14
  end
@@ -46,7 +46,7 @@ module Synvert::Core
46
46
  expect(gem_spec).not_to be_match
47
47
  end
48
48
 
49
- it 'raise Synvert::Core::GemfileLockNotFound if Gemfile.lock does not exist' do
49
+ it 'returns true if Gemfile.lock does not exist' do
50
50
  expect(File).to receive(:exist?).with(lock_path).and_return(false)
51
51
  gem_spec = Rewriter::GemSpec.new('ast', '1.1.0')
52
52
  expect(gem_spec).to be_match
@@ -15,7 +15,7 @@ module Synvert::Core
15
15
  scope = double
16
16
  block = proc {}
17
17
  expect(Rewriter::WithinScope).to receive(:new)
18
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false }, &block)
18
+ .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false, direct: false }, &block)
19
19
  .and_return(scope)
20
20
  expect(scope).to receive(:process)
21
21
  instance.within_node(type: 'send', message: 'create', &block)
@@ -25,40 +25,30 @@ module Synvert::Core
25
25
  scope = double
26
26
  block = proc {}
27
27
  expect(Rewriter::WithinScope).to receive(:new)
28
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false }, &block)
28
+ .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false, direct: false }, &block)
29
29
  .and_return(scope)
30
30
  expect(scope).to receive(:process)
31
31
  instance.with_node(type: 'send', message: 'create', &block)
32
32
  end
33
33
 
34
- it 'parses within_node with recursive false' do
34
+ it 'parses within_node with stop_when_match true' do
35
35
  scope = double
36
36
  block = proc {}
37
37
  expect(Rewriter::WithinScope).to receive(:new)
38
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: true }, &block)
38
+ .with(instance, { type: 'send', message: 'create' }, { stop_when_match: true, direct: false }, &block)
39
39
  .and_return(scope)
40
40
  expect(scope).to receive(:process)
41
41
  instance.within_node({ type: 'send', message: 'create' }, { stop_when_match: true }, &block)
42
42
  end
43
43
 
44
- it 'parses within_direct_node' do
44
+ it 'parses within_node with direct true' do
45
45
  scope = double
46
46
  block = proc {}
47
47
  expect(Rewriter::WithinScope).to receive(:new)
48
- .with(instance, { type: 'send', message: 'create' }, { direct: true }, &block)
48
+ .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false, direct: true }, &block)
49
49
  .and_return(scope)
50
50
  expect(scope).to receive(:process)
51
- instance.within_direct_node(type: 'send', message: 'create', &block)
52
- end
53
-
54
- it 'parses with_direct_node' do
55
- scope = double
56
- block = proc {}
57
- expect(Rewriter::WithinScope).to receive(:new)
58
- .with(instance, { type: 'send', message: 'create' }, { direct: true }, &block)
59
- .and_return(scope)
60
- expect(scope).to receive(:process)
61
- instance.with_direct_node(type: 'send', message: 'create', &block)
51
+ instance.within_node({ type: 'send', message: 'create' }, { direct: true }, &block)
62
52
  end
63
53
 
64
54
  it 'parses goto_node' do
@@ -27,5 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "guard"
28
28
  spec.add_development_dependency "guard-rspec"
29
29
  spec.add_development_dependency "rake"
30
- spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "rspec", "3.10.0"
31
+ spec.add_development_dependency "rspec-mocks", "3.10.2" # rspec-mocks 3.10.3 breaks tests
31
32
  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: 0.63.1
4
+ version: 0.64.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-02-26 00:00:00.000000000 Z
11
+ date: 2022-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -112,16 +112,30 @@ dependencies:
112
112
  name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: 3.10.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: 3.10.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec-mocks
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '='
130
+ - !ruby/object:Gem::Version
131
+ version: 3.10.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '='
137
+ - !ruby/object:Gem::Version
138
+ version: 3.10.2
125
139
  description: convert ruby code to better syntax automatically.
126
140
  email:
127
141
  - flyerhzm@gmail.com