synvert-core 0.27.4 → 0.31.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: a239badccf8d5b2774e75423755123fe55393f951cb3064030800ce351e014bf
4
- data.tar.gz: df9c575e61f833873bafaa672b95977390ce5eef62ace7898f209f684fef595d
3
+ metadata.gz: bf20c3a501940025291f9e0ae6f306d1613fb4db8ee5d5cf649d0ad7da4d1c5a
4
+ data.tar.gz: 425f42f736ec76ca6ade1f881ab252bfa6659f0e2cc8f3b55dd31153a4d8853a
5
5
  SHA512:
6
- metadata.gz: fc153d13c26d917155db767031b5371df250014e1c86cfbf58e89693775e5488a02276e272a62e6c41489a744056846ff2237f74433c283dbfc5b9664c989372
7
- data.tar.gz: 4c6e8df70fc14379a871b1b187ef9ce4b3b32ec964aedf096d6db1f69ae911e005933576b1adbc01478a61b1c694a1d7002bd1d4f0647f1e421bcbe894f191f5
6
+ metadata.gz: 52c43ad9b8986c68a4c77200e84b9cf7497fec7c46f0beb74fa695ed13d986f38519c167a3242d870dca79a0e09844fe6b564d6a54c258801801d4920be2f9da
7
+ data.tar.gz: 20aeb9c56536f335d8181e307bcf96ad8b3cbc7c02471077657a1ec0fa23db6c6f10269a4477aef3007ffc0b1dd07f3bb522289ecfed2c5f8f581abd49b3ee8b
data/CHANGELOG.md CHANGED
@@ -1,20 +1,23 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.27.4 (2021-04-01)
3
+ ## 0.31.0 (2021-04-27)
4
4
 
5
- * Support `:block` in `child_node_range`
5
+ * Add `in` and `not_in` rules
6
6
 
7
- ## 0.27.3 (2021-03-31)
7
+ ## 0.30.0 (2021-04-26)
8
8
 
9
- * Support `:def` in `child_node_range`
10
- * Support `:defs` in `child_node_range`
9
+ * `goto_node` accepts multiple child node names
10
+ * Match any_value
11
11
 
12
- ## 0.27.2 (2021-03-31)
12
+ ## 0.29.0 (2021-04-25)
13
13
 
14
- * Fix selector with operator in `child_node_range`
14
+ * Make `child_name_range` support [:block, :pipe]
15
+ * Get key value for hash node
15
16
 
16
- ## 0.27.1 (2021-03-31)
17
+ ## 0.28.0 (2021-04-07)
17
18
 
19
+ * Make `child_name_range` support all dsl nodes
20
+ * Make `replace` action support multi child names
18
21
  * Fix `delete` action arguments
19
22
 
20
23
  ## 0.27.0 (2021-03-31)
@@ -101,9 +101,9 @@ module Parser::AST
101
101
  def arguments
102
102
  case type
103
103
  when :def, :block
104
- ArgumentsNode.new children[1]
104
+ ArgumentsNode.new(children[1])
105
105
  when :defs
106
- ArgumentsNode.new children[2]
106
+ ArgumentsNode.new(children[2])
107
107
  when :send
108
108
  children[2..-1]
109
109
  when :defined?
@@ -203,7 +203,7 @@ module Parser::AST
203
203
  def hash_value(key)
204
204
  if :hash == type
205
205
  value_node = children.find { |pair_node| pair_node.key.to_value == key }
206
- value_node ? value_node.value : nil
206
+ value_node&.value
207
207
  else
208
208
  raise Synvert::Core::MethodNotSupported, "hash_value is not handled for #{debug_info}"
209
209
  end
@@ -280,6 +280,31 @@ module Parser::AST
280
280
  end
281
281
  end
282
282
 
283
+ # Respond key value for hash node, e.g.
284
+ #
285
+ # Current node is s(:hash, s(:pair, s(:sym, :number), s(:int, 10)))
286
+ # node.number_value is 10
287
+ def method_missing(method_name, *args, &block)
288
+ if :hash == type && method_name.to_s.include?('_value')
289
+ key = method_name.to_s.sub('_value', '')
290
+ return hash_value(key.to_sym)&.to_value if key?(key.to_sym)
291
+ return hash_value(key.to_s)&.to_value if key?(key.to_s)
292
+
293
+ return nil
294
+ end
295
+
296
+ super
297
+ end
298
+
299
+ def respond_to_missing?(method_name, *args)
300
+ if :hash == type && method_name.to_s.include?('_value')
301
+ key = method_name.to_s.sub('_value', '')
302
+ return true if key?(key.to_sym) || key?(key.to_s)
303
+ end
304
+
305
+ super
306
+ end
307
+
283
308
  def to_s
284
309
  if :mlhs == type
285
310
  "(#{children.map(&:name).join(', ')})"
@@ -322,52 +347,56 @@ module Parser::AST
322
347
  # @param [String] name of child node.
323
348
  # @return [Parser::Source::Range] source range of child node.
324
349
  def child_node_range(child_name)
325
- case type
326
- when :block
327
- case child_name
328
- when :caller
329
- caller&.loc&.expression
330
- when :arguments
331
- arguments&.loc&.expression
332
- end
333
- when :class
334
- case child_name
335
- when :name
336
- loc.name
337
- when :parent_class
338
- parent_class&.loc&.expression
339
- end
340
- when :def
341
- case child_name
342
- when :name
343
- loc.name
344
- when :arguments
345
- Parser::Source::Range.new('(string)', arguments.first.loc.expression.begin_pos, arguments.last.loc.expression.end_pos) unless arguments.empty?
346
- end
347
- when :defs
348
- case child_name
349
- when :dot
350
- loc.operator
351
- when :self
352
- Parser::Source::Range.new('(string)', loc.operator.begin_pos - 4, loc.operator.begin_pos)
353
- when :name
354
- loc.name
355
- when :arguments
356
- Parser::Source::Range.new('(string)', arguments.first.loc.expression.begin_pos, arguments.last.loc.expression.end_pos) unless arguments.empty?
357
- end
358
- when :send
359
- case child_name
360
- when :receiver
361
- receiver&.loc&.expression
362
- when :dot
363
- loc.dot
364
- when :message
365
- loc.operator ? Parser::Source::Range.new('(string)', loc.selector.begin_pos, loc.operator.end_pos) : loc.selector
366
- when :arguments
367
- Parser::Source::Range.new('(string)', arguments.first.loc.expression.begin_pos, arguments.last.loc.expression.end_pos) unless arguments.empty?
350
+ case [type, child_name]
351
+ when %i[block pipe]
352
+ Parser::Source::Range.new('(string)', arguments.loc.expression.begin_pos, arguments.loc.expression.end_pos)
353
+ when %i[class name]
354
+ loc.name
355
+ when %i[def name]
356
+ loc.name
357
+ when %i[defs name]
358
+ loc.name
359
+ when %i[defs dot]
360
+ loc.operator
361
+ when %i[defs self]
362
+ Parser::Source::Range.new('(string)', loc.operator.begin_pos - 4, loc.operator.begin_pos)
363
+ when %i[send dot]
364
+ loc.dot
365
+ when %i[send message]
366
+ if loc.operator
367
+ Parser::Source::Range.new('(string)', loc.selector.begin_pos, loc.operator.end_pos)
368
+ else
369
+ loc.selector
368
370
  end
369
371
  else
370
- raise Synvert::Core::MethodNotSupported, "child_node_range is not handled for #{evaluated.inspect}, child_name: #{child_name}"
372
+ if respond_to?(child_name)
373
+ child_node = send(child_name)
374
+ return nil if child_node.nil?
375
+
376
+ if child_node.is_a?(Parser::AST::Node)
377
+ return(
378
+ Parser::Source::Range.new(
379
+ '(string)',
380
+ child_node.loc.expression.begin_pos,
381
+ child_node.loc.expression.end_pos
382
+ )
383
+ )
384
+ end
385
+
386
+ # arguments
387
+ return nil if child_node.empty?
388
+
389
+ return(
390
+ Parser::Source::Range.new(
391
+ '(string)',
392
+ child_node.first.loc.expression.begin_pos,
393
+ child_node.last.loc.expression.end_pos
394
+ )
395
+ )
396
+ end
397
+
398
+ raise Synvert::Core::MethodNotSupported,
399
+ "child_node_range is not handled for #{evaluated.inspect}, child_name: #{child_name}"
371
400
  end
372
401
  end
373
402
 
@@ -399,6 +428,14 @@ module Parser::AST
399
428
  actual = actual_value(self, multi_keys[0...-1])
400
429
  expected = expected_value(rules, multi_keys)
401
430
  !match_value?(actual, expected)
431
+ when :in
432
+ actual = actual_value(self, multi_keys[0...-1])
433
+ expected_values = expected_value(rules, multi_keys)
434
+ expected_values.any? { |expected| match_value?(actual, expected) }
435
+ when :not_in
436
+ actual = actual_value(self, multi_keys[0...-1])
437
+ expected_values = expected_value(rules, multi_keys)
438
+ expected_values.all? { |expected| !match_value?(actual, expected) }
402
439
  else
403
440
  actual = actual_value(self, multi_keys)
404
441
  expected = expected_value(rules, multi_keys)
@@ -438,7 +475,7 @@ module Parser::AST
438
475
  source
439
476
  end
440
477
  end
441
- when String, Symbol
478
+ when String, Symbol, Integer, Float
442
479
  evaluated
443
480
  when NilClass
444
481
  'nil'
@@ -498,6 +535,8 @@ module Parser::AST
498
535
  :false == actual.type
499
536
  when Parser::AST::Node
500
537
  actual == expected
538
+ when Synvert::Core::Rewriter::AnyValue
539
+ !actual.nil?
501
540
  else
502
541
  raise Synvert::Core::MethodNotSupported, "#{expected.class} is not handled for match_value?"
503
542
  end
@@ -44,6 +44,8 @@ module Synvert::Core
44
44
  autoload :RubyVersion, 'synvert/core/rewriter/ruby_version'
45
45
  autoload :GemSpec, 'synvert/core/rewriter/gem_spec'
46
46
 
47
+ autoload :AnyValue, 'synvert/core/rewriter/any_value'
48
+
47
49
  class << self
48
50
  # Execute the temporary rewriter without group and name.
49
51
  #
@@ -3,9 +3,9 @@
3
3
  module Synvert::Core
4
4
  # ReplaceAction to replace child node with code.
5
5
  class Rewriter::ReplaceAction < Rewriter::Action
6
- def initialize(instance, selector, with:)
6
+ def initialize(instance, *selectors, with:)
7
7
  @instance = instance
8
- @selector = selector
8
+ @selectors = selectors
9
9
  @code = with
10
10
  @node = @instance.current_node
11
11
  end
@@ -14,14 +14,14 @@ module Synvert::Core
14
14
  #
15
15
  # @return [Integer] begin position.
16
16
  def begin_pos
17
- @node.child_node_range(@selector).begin_pos
17
+ @selectors.map { |selector| @node.child_node_range(selector).begin_pos }.min
18
18
  end
19
19
 
20
20
  # End position of code to replace.
21
21
  #
22
22
  # @return [Integer] end position.
23
23
  def end_pos
24
- @node.child_node_range(@selector).end_pos
24
+ @selectors.map { |selector| @node.child_node_range(selector).end_pos }.max
25
25
  end
26
26
 
27
27
  # The rewritten source code.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synvert::Core
4
+ class Rewriter::AnyValue
5
+ end
6
+ end
@@ -181,10 +181,10 @@ module Synvert::Core
181
181
  # Parse goto_node dsl, it creates a [Synvert::Core::Rewriter::GotoScope] to go to a child node,
182
182
  # then continue operating on the child node.
183
183
  #
184
- # @param child_node_name [String] the name of the child node.
184
+ # @param *child_node_names [Array] the name of the child nodes.
185
185
  # @param block [Block] block code to continue operating on the matching nodes.
186
- def goto_node(child_node_name, &block)
187
- Rewriter::GotoScope.new(self, child_node_name, &block).process
186
+ def goto_node(*child_node_names, &block)
187
+ Rewriter::GotoScope.new(self, *child_node_names, &block).process
188
188
  end
189
189
 
190
190
  # Parse if_exist_node dsl, it creates a [Synvert::Core::Rewriter::IfExistCondition] to check
@@ -252,12 +252,12 @@ module Synvert::Core
252
252
  end
253
253
 
254
254
  # Parse replace with dsl, it creates a [Synvert::Core::Rewriter::ReplaceAction] to
255
- # replace child node with code.
255
+ # replace child nodes with code.
256
256
  #
257
- # @param selector [Symbol] selector name of child node.
257
+ # @param selectors [Array<Symbol>] selector names of child node.
258
258
  # @param with [String] code need to be replaced with.
259
- def replace(selector, with:)
260
- @actions << Rewriter::ReplaceAction.new(self, selector, with: with)
259
+ def replace(*selectors, with:)
260
+ @actions << Rewriter::ReplaceAction.new(self, *selectors, with: with)
261
261
  end
262
262
 
263
263
  # Parse replace_erb_stmt_with_expr dsl, it creates a [Synvert::Core::Rewriter::ReplaceErbStmtWithExprAction] to
@@ -285,6 +285,11 @@ module Synvert::Core
285
285
  @rewriter.add_warning Rewriter::Warning.new(self, message)
286
286
  end
287
287
 
288
+ # Any value but nil.
289
+ def any_value
290
+ Rewriter::AnyValue.new
291
+ end
292
+
288
293
  private
289
294
 
290
295
  # It changes source code from bottom to top, and it can change source code twice at the same time,
@@ -6,11 +6,11 @@ module Synvert::Core
6
6
  # Initialize a scope
7
7
  #
8
8
  # @param instance [Synvert::Core::Rewriter::Instance]
9
- # @param child_node_name [String]
9
+ # @param *child_node_names [Array]
10
10
  # @param block [Block]
11
- def initialize(instance, child_node_name, &block)
11
+ def initialize(instance, *child_node_names, &block)
12
12
  @instance = instance
13
- @child_node_name = child_node_name
13
+ @child_node_names = child_node_names
14
14
  @block = block
15
15
  end
16
16
 
@@ -19,7 +19,10 @@ module Synvert::Core
19
19
  current_node = @instance.current_node
20
20
  return unless current_node
21
21
 
22
- child_node = @child_node_name.is_a?(Parser::AST::Node) ? @child_node_name : current_node.send(@child_node_name)
22
+ child_node = current_node
23
+ @child_node_names.each do |child_node_name|
24
+ child_node = child_node_name.is_a?(Parser::AST::Node) ? child_node_name : child_node.send(child_node_name)
25
+ end
23
26
  @instance.process_with_other_node child_node do
24
27
  @instance.instance_eval(&@block)
25
28
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '0.27.4'
5
+ VERSION = '0.31.0'
6
6
  end
7
7
  end
@@ -333,6 +333,18 @@ describe Parser::AST::Node do
333
333
  end
334
334
  end
335
335
 
336
+ describe 'key value by method_missing' do
337
+ it 'gets for key value' do
338
+ node = parse('{:foo => :bar}')
339
+ expect(node.foo_value).to eq :bar
340
+
341
+ node = parse("{'foo' => 'bar'}")
342
+ expect(node.foo_value).to eq 'bar'
343
+
344
+ expect(node.bar_value).to be_nil
345
+ end
346
+ end
347
+
336
348
  describe '#recursive_children' do
337
349
  it 'iterates all children recursively' do
338
350
  node = parse('class Synvert; def current_node; @node; end; end')
@@ -392,6 +404,18 @@ describe Parser::AST::Node do
392
404
  node = parse(source)
393
405
  expect(node).not_to be_match(type: 'class', name: { not: 'Synvert' })
394
406
  end
407
+
408
+ it 'matches in' do
409
+ source = 'FactoryBot.create(:user)'
410
+ node = parse(source)
411
+ expect(node).to be_match(type: 'send', message: { in: [:create, :build] })
412
+ end
413
+
414
+ it 'matches not_in' do
415
+ source = 'FactoryBot.create(:user)'
416
+ node = parse(source)
417
+ expect(node).not_to be_match(type: 'send', message: { not_in: [:create, :build] })
418
+ end
395
419
  end
396
420
 
397
421
  describe '#child_node_range' do
@@ -405,6 +429,12 @@ describe Parser::AST::Node do
405
429
  it 'checks arguments' do
406
430
  node = parse('Factory.define :user do |user|; end')
407
431
  range = node.child_node_range(:arguments)
432
+ expect(range.to_range).to eq(25...29)
433
+ end
434
+
435
+ it 'checks pipe' do
436
+ node = parse('Factory.define :user do |user|; end')
437
+ range = node.child_node_range(:pipe)
408
438
  expect(range.to_range).to eq(24...30)
409
439
  end
410
440
  end
@@ -5,18 +5,18 @@ require 'spec_helper'
5
5
  module Synvert::Core
6
6
  describe Rewriter::DeleteAction do
7
7
  subject {
8
- source = "arr.map {}.flatten"
8
+ source = 'arr.map {}.flatten'
9
9
  node = Parser::CurrentRuby.parse(source)
10
10
  instance = double(current_node: node)
11
11
  Rewriter::DeleteAction.new(instance, :dot, :message)
12
12
  }
13
13
 
14
14
  it 'gets begin_pos' do
15
- expect(subject.begin_pos).to eq "arr.map {}".length
15
+ expect(subject.begin_pos).to eq 'arr.map {}'.length
16
16
  end
17
17
 
18
18
  it 'gets end_pos' do
19
- expect(subject.end_pos).to eq "arr.map {}.flatten".length
19
+ expect(subject.end_pos).to eq 'arr.map {}.flatten'.length
20
20
  end
21
21
 
22
22
  it 'gets rewritten_code' do
@@ -6,22 +6,22 @@ module Synvert::Core
6
6
  describe Rewriter::ReplaceAction do
7
7
  context 'replace with single line' do
8
8
  subject {
9
- source = "'slug from title'.gsub(' ', '_')"
9
+ source = 'FactoryBot.create(:user)'
10
10
  node = Parser::CurrentRuby.parse(source)
11
11
  instance = double(current_node: node)
12
- Rewriter::ReplaceAction.new(instance, :message, with: 'tr')
12
+ Rewriter::ReplaceAction.new(instance, :receiver, :dot, :message, with: 'create')
13
13
  }
14
14
 
15
15
  it 'gets begin_pos' do
16
- expect(subject.begin_pos).to eq "'slug from title'.".length
16
+ expect(subject.begin_pos).to eq 0
17
17
  end
18
18
 
19
19
  it 'gets end_pos' do
20
- expect(subject.end_pos).to eq "'slug from title'.gsub".length
20
+ expect(subject.end_pos).to eq 'FactoryBot.create'.length
21
21
  end
22
22
 
23
23
  it 'gets rewritten_code' do
24
- expect(subject.rewritten_code).to eq 'tr'
24
+ expect(subject.rewritten_code).to eq 'create'
25
25
  end
26
26
  end
27
27
  end
@@ -54,9 +54,9 @@ module Synvert::Core
54
54
  it 'parses goto_node' do
55
55
  scope = double
56
56
  block = proc {}
57
- expect(Rewriter::GotoScope).to receive(:new).with(instance, :caller, &block).and_return(scope)
57
+ expect(Rewriter::GotoScope).to receive(:new).with(instance, :caller, :receiver, &block).and_return(scope)
58
58
  expect(scope).to receive(:process)
59
- instance.goto_node(:caller, &block)
59
+ instance.goto_node(:caller, :receiver, &block)
60
60
  end
61
61
 
62
62
  it 'parses if_exist_node' do
@@ -8,12 +8,11 @@ module Synvert::Core
8
8
  rewriter = Rewriter.new('foo', 'bar')
9
9
  Rewriter::Instance.new(rewriter, 'file pattern')
10
10
  }
11
- let(:source) {
12
- '
13
- Factory.define :user do |user|
14
- end
15
- '
16
- }
11
+ let(:source) { <<~EOS }
12
+ Factory.define :user do |user|
13
+ end
14
+ EOS
15
+
17
16
  let(:node) { Parser::CurrentRuby.parse(source) }
18
17
  before do
19
18
  Rewriter::Instance.reset
@@ -25,13 +24,13 @@ end
25
24
  run = false
26
25
  type_in_scope = nil
27
26
  scope =
28
- Rewriter::GotoScope.new instance, :caller do
27
+ Rewriter::GotoScope.new instance, :caller, :receiver do
29
28
  run = true
30
29
  type_in_scope = node.type
31
30
  end
32
31
  scope.process
33
32
  expect(run).to be_truthy
34
- expect(type_in_scope).to eq :send
33
+ expect(type_in_scope).to eq :const
35
34
  expect(instance.current_node.type).to eq :block
36
35
  end
37
36
  end
data/synvert-core.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency "activesupport"
23
23
  spec.add_runtime_dependency "erubis"
24
- spec.add_runtime_dependency "parser", "~> 3.0.0"
24
+ spec.add_runtime_dependency "parser"
25
25
 
26
26
  spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "guard"
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.27.4
4
+ version: 0.31.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: 2021-04-01 00:00:00.000000000 Z
11
+ date: 2021-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: parser
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 3.0.0
47
+ version: '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: 3.0.0
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -154,6 +154,7 @@ files:
154
154
  - lib/synvert/core/rewriter/action/replace_action.rb
155
155
  - lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb
156
156
  - lib/synvert/core/rewriter/action/replace_with_action.rb
157
+ - lib/synvert/core/rewriter/any_value.rb
157
158
  - lib/synvert/core/rewriter/condition.rb
158
159
  - lib/synvert/core/rewriter/condition/if_exist_condition.rb
159
160
  - lib/synvert/core/rewriter/condition/if_only_exist_condition.rb
@@ -213,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
213
214
  - !ruby/object:Gem::Version
214
215
  version: '0'
215
216
  requirements: []
216
- rubygems_version: 3.1.4
217
+ rubygems_version: 3.1.6
217
218
  signing_key:
218
219
  specification_version: 4
219
220
  summary: convert ruby code to better syntax.