synvert-core 0.28.1 → 0.33.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: b240936c7390b45a27e9352da7acec3784499d38d5ec10aae3b3403036df36b5
4
- data.tar.gz: a9c9a91d25352ef1c5fc09bd1cc0d8751d4239513a2fcff0e2618ccec6e93855
3
+ metadata.gz: f4448b6bf2d17c1128bb6d20732de90a7458019ca33835fa9740e5d6f8102e4a
4
+ data.tar.gz: 10df72247c2d5a0e7293481096221e755322218e6031ae6b613470938e1d01c4
5
5
  SHA512:
6
- metadata.gz: 92298a2a06c408cac4aa99870f85138bc754dcb286b636976a01eb88f63613e6f81b80a1d0fc680922a394ab6b7167b9bdd70d6bf2b14a89a7f1a6faaf5292d0
7
- data.tar.gz: f5c10e0e949dd459035e92fb172ed20ad7717369923a93e77dfb8077c7ba7c45c31da3ef1756768215ccdeff8b6b36fef063fe0b6db9fa53b3b9457bd895c8ae
6
+ metadata.gz: 949edd01e98bd306456ed368bbb4fe16b9d9d320cb82aafd8ddb1a0f2367ba7b8e18ba5d43143aa51a91a64bb95c5d895ea5a7c07beea5dd6f8faf078709267d
7
+ data.tar.gz: 04ba8cfbb5d3c66855f300712417a5ad7476f4ac307d46824d8f02f8b0667c8bfcdf909ae978c8a442fee75d1f46faed15bb50cc81c0a360f8e824333fa321a0
data/CHANGELOG.md CHANGED
@@ -1,8 +1,26 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 0.28.1 (2021-04-09)
3
+ ## 0.33.0 (2021-05-10)
4
+
5
+ * Add `body` for `class` node
6
+
7
+ ## 0.32.0 (2021-05-07)
8
+
9
+ * Remove `ArgumentsNode`
10
+
11
+ ## 0.31.0 (2021-04-27)
12
+
13
+ * Add `in` and `not_in` rules
14
+
15
+ ## 0.30.0 (2021-04-26)
16
+
17
+ * `goto_node` accepts multiple child node names
18
+ * Match any_value
19
+
20
+ ## 0.29.0 (2021-04-25)
4
21
 
5
22
  * Make `child_name_range` support [:block, :pipe]
23
+ * Get key value for hash node
6
24
 
7
25
  ## 0.28.0 (2021-04-07)
8
26
 
@@ -1,29 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser::AST
4
- # ArgumentsNode allows to handle all args as one node or handle all args as an array.
5
- class ArgumentsNode
6
- # Initialize
7
- #
8
- # @param node [Parser::AST::Node] args node.
9
- def initialize(node)
10
- @node = node
11
- end
12
-
13
- # If args node responds method itself, call method on args node.
14
- # If args children (array) responds method, call method on args children.
15
- # Otherwise raise method missing error.
16
- def method_missing(meth, *args, &block)
17
- if @node.respond_to?(meth)
18
- @node.send meth, *args, &block
19
- elsif @node.children.respond_to?(meth)
20
- @node.children.send meth, *args, &block
21
- else
22
- super
23
- end
24
- end
25
- end
26
-
27
4
  # Parser::AST::Node monkey patch.
28
5
  class Node
29
6
  # Get name node of :class, :module, :const, :mlhs, :def and :defs node.
@@ -101,9 +78,9 @@ module Parser::AST
101
78
  def arguments
102
79
  case type
103
80
  when :def, :block
104
- ArgumentsNode.new(children[1])
81
+ children[1]
105
82
  when :defs
106
- ArgumentsNode.new(children[2])
83
+ children[2]
107
84
  when :send
108
85
  children[2..-1]
109
86
  when :defined?
@@ -133,7 +110,7 @@ module Parser::AST
133
110
  case type
134
111
  when :begin
135
112
  children
136
- when :def, :block
113
+ when :def, :block, :class
137
114
  return [] if children[2].nil?
138
115
 
139
116
  :begin == children[2].type ? children[2].body : children[2..-1]
@@ -203,7 +180,7 @@ module Parser::AST
203
180
  def hash_value(key)
204
181
  if :hash == type
205
182
  value_node = children.find { |pair_node| pair_node.key.to_value == key }
206
- value_node ? value_node.value : nil
183
+ value_node&.value
207
184
  else
208
185
  raise Synvert::Core::MethodNotSupported, "hash_value is not handled for #{debug_info}"
209
186
  end
@@ -280,6 +257,35 @@ module Parser::AST
280
257
  end
281
258
  end
282
259
 
260
+ # Respond key value for hash node, e.g.
261
+ #
262
+ # Current node is s(:hash, s(:pair, s(:sym, :number), s(:int, 10)))
263
+ # node.number_value is 10
264
+ def method_missing(method_name, *args, &block)
265
+ if :args == type && children.respond_to?(method_name)
266
+ return children.send(method_name, *args, &block)
267
+ elsif :hash == type && method_name.to_s.include?('_value')
268
+ key = method_name.to_s.sub('_value', '')
269
+ return hash_value(key.to_sym)&.to_value if key?(key.to_sym)
270
+ return hash_value(key.to_s)&.to_value if key?(key.to_s)
271
+
272
+ return nil
273
+ end
274
+
275
+ super
276
+ end
277
+
278
+ def respond_to_missing?(method_name, *args)
279
+ if :args == type && children.respond_to?(method_name)
280
+ return true
281
+ elsif :hash == type && method_name.to_s.include?('_value')
282
+ key = method_name.to_s.sub('_value', '')
283
+ return true if key?(key.to_sym) || key?(key.to_s)
284
+ end
285
+
286
+ super
287
+ end
288
+
283
289
  def to_s
284
290
  if :mlhs == type
285
291
  "(#{children.map(&:name).join(', ')})"
@@ -323,35 +329,57 @@ module Parser::AST
323
329
  # @return [Parser::Source::Range] source range of child node.
324
330
  def child_node_range(child_name)
325
331
  case [type, child_name]
326
- when [:block, :pipe]
332
+ when %i[block pipe]
327
333
  Parser::Source::Range.new('(string)', arguments.loc.expression.begin_pos, arguments.loc.expression.end_pos)
328
- when [:class, :name]
334
+ when %i[block arguments], %i[def arguments], %i[defs arguments]
335
+ Parser::Source::Range.new('(string)', arguments.first.loc.expression.begin_pos, arguments.last.loc.expression.end_pos)
336
+ when %i[class name]
329
337
  loc.name
330
- when [:def, :name]
338
+ when %i[def name]
331
339
  loc.name
332
- when [:defs, :name]
340
+ when %i[defs name]
333
341
  loc.name
334
- when [:defs, :dot]
342
+ when %i[defs dot]
335
343
  loc.operator
336
- when [:defs, :self]
337
- Parser::Source::Range.new('(string)', loc.operator.begin_pos - 4, loc.operator.begin_pos)
338
- when [:send, :dot]
344
+ when %i[defs self]
345
+ Parser::Source::Range.new('(string)', loc.operator.begin_pos - 'self'.length, loc.operator.begin_pos)
346
+ when %i[send dot]
339
347
  loc.dot
340
- when [:send, :message]
341
- loc.operator ? Parser::Source::Range.new('(string)', loc.selector.begin_pos, loc.operator.end_pos) : loc.selector
348
+ when %i[send message]
349
+ if loc.operator
350
+ Parser::Source::Range.new('(string)', loc.selector.begin_pos, loc.operator.end_pos)
351
+ else
352
+ loc.selector
353
+ end
342
354
  else
343
355
  if respond_to?(child_name)
344
356
  child_node = send(child_name)
345
357
  return nil if child_node.nil?
358
+
346
359
  if child_node.is_a?(Parser::AST::Node)
347
- return Parser::Source::Range.new('(string)', child_node.loc.expression.begin_pos, child_node.loc.expression.end_pos)
360
+ return(
361
+ Parser::Source::Range.new(
362
+ '(string)',
363
+ child_node.loc.expression.begin_pos,
364
+ child_node.loc.expression.end_pos
365
+ )
366
+ )
348
367
  end
368
+
349
369
  # arguments
350
370
  return nil if child_node.empty?
351
- return Parser::Source::Range.new('(string)', child_node.first.loc.expression.begin_pos, child_node.last.loc.expression.end_pos)
371
+
372
+ return(
373
+ Parser::Source::Range.new(
374
+ '(string)',
375
+ child_node.first.loc.expression.begin_pos,
376
+ child_node.last.loc.expression.end_pos
377
+ )
378
+ )
352
379
  end
353
380
 
354
- raise Synvert::Core::MethodNotSupported, "child_node_range is not handled for #{evaluated.inspect}, child_name: #{child_name}"
381
+ raise Synvert::Core::MethodNotSupported,
382
+ "child_node_range is not handled for #{evaluated.inspect}, child_name: #{child_name}"
355
383
  end
356
384
  end
357
385
 
@@ -383,6 +411,14 @@ module Parser::AST
383
411
  actual = actual_value(self, multi_keys[0...-1])
384
412
  expected = expected_value(rules, multi_keys)
385
413
  !match_value?(actual, expected)
414
+ when :in
415
+ actual = actual_value(self, multi_keys[0...-1])
416
+ expected_values = expected_value(rules, multi_keys)
417
+ expected_values.any? { |expected| match_value?(actual, expected) }
418
+ when :not_in
419
+ actual = actual_value(self, multi_keys[0...-1])
420
+ expected_values = expected_value(rules, multi_keys)
421
+ expected_values.all? { |expected| !match_value?(actual, expected) }
386
422
  else
387
423
  actual = actual_value(self, multi_keys)
388
424
  expected = expected_value(rules, multi_keys)
@@ -405,8 +441,12 @@ module Parser::AST
405
441
  evaluated = instance_eval old_code
406
442
  case evaluated
407
443
  when Parser::AST::Node
408
- evaluated.loc.expression.source
409
- when Array, ArgumentsNode
444
+ if evaluated.type == :args
445
+ evaluated.loc.expression.source[1...-1]
446
+ else
447
+ evaluated.loc.expression.source
448
+ end
449
+ when Array
410
450
  if evaluated.size > 0
411
451
  file_source = evaluated.first.loc.expression.source_buffer.source
412
452
  source = file_source[evaluated.first.loc.expression.begin_pos...evaluated.last.loc.expression.end_pos]
@@ -422,7 +462,7 @@ module Parser::AST
422
462
  source
423
463
  end
424
464
  end
425
- when String, Symbol
465
+ when String, Symbol, Integer, Float
426
466
  evaluated
427
467
  when NilClass
428
468
  'nil'
@@ -482,6 +522,8 @@ module Parser::AST
482
522
  :false == actual.type
483
523
  when Parser::AST::Node
484
524
  actual == expected
525
+ when Synvert::Core::Rewriter::AnyValue
526
+ !actual.nil?
485
527
  else
486
528
  raise Synvert::Core::MethodNotSupported, "#{expected.class} is not handled for match_value?"
487
529
  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
  #
@@ -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
@@ -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.28.1'
5
+ VERSION = '0.33.0'
6
6
  end
7
7
  end
@@ -104,17 +104,17 @@ describe Parser::AST::Node do
104
104
  describe '#arguments' do
105
105
  it 'gets for def node' do
106
106
  node = parse('def test(foo, bar); foo + bar; end')
107
- expect(node.arguments.map { |argument| argument.to_source }).to eq %w[foo bar]
107
+ expect(node.arguments.type).to eq :args
108
108
  end
109
109
 
110
110
  it 'gets for defs node' do
111
111
  node = parse('def self.test(foo, bar); foo + bar; end')
112
- expect(node.arguments.map { |argument| argument.to_source }).to eq %w[foo bar]
112
+ expect(node.arguments.type).to eq :args
113
113
  end
114
114
 
115
115
  it 'gets for block node' do
116
116
  node = parse('RSpec.configure do |config|; end')
117
- expect(node.arguments.map { |argument| argument.to_source }).to eq ['config']
117
+ expect(node.arguments.type).to eq :args
118
118
  end
119
119
 
120
120
  it 'gets for send node' do
@@ -146,6 +146,21 @@ describe Parser::AST::Node do
146
146
  expect(node.body).to eq [parse('include EmailSpec::Helpers'), parse('include EmailSpec::Matchers')]
147
147
  end
148
148
 
149
+ it 'gets empty for class node' do
150
+ node = parse('class User; end')
151
+ expect(node.body).to be_empty
152
+ end
153
+
154
+ it 'gets one line for class node' do
155
+ node = parse('class User; attr_accessor :email; end')
156
+ expect(node.body).to eq [parse('attr_accessor :email')]
157
+ end
158
+
159
+ it 'gets one line for class node' do
160
+ node = parse('class User; attr_accessor :email; attr_accessor :username; end')
161
+ expect(node.body).to eq [parse('attr_accessor :email'), parse('attr_accessor :username')]
162
+ end
163
+
149
164
  it 'gets for begin node' do
150
165
  node = parse('foo; bar')
151
166
  expect(node.body).to eq [parse('foo'), parse('bar')]
@@ -333,6 +348,18 @@ describe Parser::AST::Node do
333
348
  end
334
349
  end
335
350
 
351
+ describe 'key value by method_missing' do
352
+ it 'gets for key value' do
353
+ node = parse('{:foo => :bar}')
354
+ expect(node.foo_value).to eq :bar
355
+
356
+ node = parse("{'foo' => 'bar'}")
357
+ expect(node.foo_value).to eq 'bar'
358
+
359
+ expect(node.bar_value).to be_nil
360
+ end
361
+ end
362
+
336
363
  describe '#recursive_children' do
337
364
  it 'iterates all children recursively' do
338
365
  node = parse('class Synvert; def current_node; @node; end; end')
@@ -392,6 +419,18 @@ describe Parser::AST::Node do
392
419
  node = parse(source)
393
420
  expect(node).not_to be_match(type: 'class', name: { not: 'Synvert' })
394
421
  end
422
+
423
+ it 'matches in' do
424
+ source = 'FactoryBot.create(:user)'
425
+ node = parse(source)
426
+ expect(node).to be_match(type: 'send', message: { in: %i[create build] })
427
+ end
428
+
429
+ it 'matches not_in' do
430
+ source = 'FactoryBot.create(:user)'
431
+ node = parse(source)
432
+ expect(node).not_to be_match(type: 'send', message: { not_in: %i[create build] })
433
+ end
395
434
  end
396
435
 
397
436
  describe '#child_node_range' do
@@ -542,7 +581,7 @@ describe Parser::AST::Node do
542
581
  expect(node.rewritten_source('{{name}}')).to eq 'Synvert'
543
582
  end
544
583
 
545
- it 'rewrites for ArgumentsNode' do
584
+ it 'rewrites for arguments' do
546
585
  source = 'test { |a, b| }'
547
586
  node = parse(source)
548
587
  expect(node.rewritten_source('{{arguments}}')).to eq 'a, b'
@@ -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,7 +6,7 @@ module Synvert::Core
6
6
  describe Rewriter::ReplaceAction do
7
7
  context 'replace with single line' do
8
8
  subject {
9
- source = "FactoryBot.create(:user)"
9
+ source = 'FactoryBot.create(:user)'
10
10
  node = Parser::CurrentRuby.parse(source)
11
11
  instance = double(current_node: node)
12
12
  Rewriter::ReplaceAction.new(instance, :receiver, :dot, :message, with: 'create')
@@ -17,7 +17,7 @@ module Synvert::Core
17
17
  end
18
18
 
19
19
  it 'gets end_pos' do
20
- expect(subject.end_pos).to eq "FactoryBot.create".length
20
+ expect(subject.end_pos).to eq 'FactoryBot.create'.length
21
21
  end
22
22
 
23
23
  it 'gets rewritten_code' do
@@ -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.28.1
4
+ version: 0.33.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-09 00:00:00.000000000 Z
11
+ date: 2021-05-10 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.