synvert-core 0.27.4 → 0.31.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: 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.