synvert-core 0.28.1 → 0.33.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: 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.