ruby-next-core 0.10.0 → 0.10.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +3 -0
- data/bin/reparse +19 -0
- data/bin/transform +22 -8
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +4 -1
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +1 -1
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -5
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +1 -1
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +119 -92
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +117 -0
- data/lib/.rbnext/2.3/ruby-next/utils.rb +1 -1
- data/lib/ruby-next/commands/nextify.rb +3 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +1 -1
- data/lib/ruby-next/language/rewriters/base.rb +2 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +6 -2
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +117 -90
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +82 -10
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +30 -26
- data/lib/ruby-next/rubocop.rb +76 -0
- data/lib/ruby-next/version.rb +1 -1
- metadata +12 -4
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyNext
|
4
|
+
module Language
|
5
|
+
module Rewriters
|
6
|
+
class RightHandAssignment < Base
|
7
|
+
NAME = "right-hand-assignment"
|
8
|
+
SYNTAX_PROBE = "1 + 2 => a"
|
9
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
|
10
|
+
|
11
|
+
def on_rasgn(node)
|
12
|
+
context.track! self
|
13
|
+
|
14
|
+
node = super(node)
|
15
|
+
|
16
|
+
val_node, asgn_node = *node
|
17
|
+
|
18
|
+
remove(val_node.loc.expression.end.join(asgn_node.loc.expression))
|
19
|
+
insert_before(val_node.loc.expression, "#{asgn_node.loc.expression.source} = ")
|
20
|
+
|
21
|
+
asgn_node.updated(
|
22
|
+
nil,
|
23
|
+
asgn_node.children + [val_node]
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_vasgn(node)
|
28
|
+
return super(node) unless rightward?(node)
|
29
|
+
|
30
|
+
context.track! self
|
31
|
+
|
32
|
+
name, val_node = *node
|
33
|
+
|
34
|
+
remove(val_node.loc.expression.end.join(node.loc.name))
|
35
|
+
insert_before(val_node.loc.expression, "#{name} = ")
|
36
|
+
|
37
|
+
super(node)
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_casgn(node)
|
41
|
+
return super(node) unless rightward?(node)
|
42
|
+
|
43
|
+
context.track! self
|
44
|
+
|
45
|
+
scope_node, name, val_node = *node
|
46
|
+
|
47
|
+
if scope_node
|
48
|
+
scope = scope_node.type == :cbase ? scope_node.loc.expression.source : "#{scope_node.loc.expression.source}::"
|
49
|
+
name = "#{scope}#{name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
remove(val_node.loc.expression.end.join(node.loc.name))
|
53
|
+
insert_before(val_node.loc.expression, "#{name} = ")
|
54
|
+
|
55
|
+
super(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_mrasgn(node)
|
59
|
+
context.track! self
|
60
|
+
|
61
|
+
node = super(node)
|
62
|
+
|
63
|
+
lhs, rhs = *node
|
64
|
+
|
65
|
+
replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
|
66
|
+
insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
|
67
|
+
|
68
|
+
node.updated(
|
69
|
+
:masgn,
|
70
|
+
[rhs, lhs]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_masgn(node)
|
75
|
+
return super(node) unless rightward?(node)
|
76
|
+
|
77
|
+
context.track! self
|
78
|
+
|
79
|
+
rhs, lhs = *node
|
80
|
+
|
81
|
+
replace(lhs.loc.expression.end.join(rhs.loc.expression), ")")
|
82
|
+
insert_before(lhs.loc.expression, "#{rhs.loc.expression.source} = (")
|
83
|
+
|
84
|
+
node = super(node)
|
85
|
+
|
86
|
+
lhs, rhs = *node
|
87
|
+
|
88
|
+
node.updated(
|
89
|
+
nil,
|
90
|
+
[
|
91
|
+
lhs,
|
92
|
+
s(:begin, rhs)
|
93
|
+
]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def rightward?(node)
|
100
|
+
# Location could be empty for node built by rewriters
|
101
|
+
return false unless ((!node.loc.nil? || nil) && node.loc.operator)
|
102
|
+
|
103
|
+
assignee_loc =
|
104
|
+
if node.type == :masgn
|
105
|
+
node.children[0].loc.expression
|
106
|
+
else
|
107
|
+
node.loc.name
|
108
|
+
end
|
109
|
+
|
110
|
+
return false unless assignee_loc
|
111
|
+
|
112
|
+
assignee_loc.begin_pos > node.loc.operator.end_pos
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -6,7 +6,7 @@ module RubyNext
|
|
6
6
|
|
7
7
|
if $LOAD_PATH.respond_to?(:resolve_feature_path)
|
8
8
|
def resolve_feature_path(feature)
|
9
|
-
((!$LOAD_PATH.resolve_feature_path(feature).nil?
|
9
|
+
((!$LOAD_PATH.resolve_feature_path(feature).nil? || nil) && $LOAD_PATH.resolve_feature_path(feature).last)
|
10
10
|
rescue LoadError
|
11
11
|
end
|
12
12
|
else
|
@@ -148,6 +148,9 @@ module RubyNext
|
|
148
148
|
|
149
149
|
# Then, generate the source code for the next version
|
150
150
|
transpile path, contents, version: version
|
151
|
+
rescue SyntaxError, StandardError => e
|
152
|
+
warn "Failed to transpile #{path}: #{e.class} — #{e.message}"
|
153
|
+
exit 1
|
151
154
|
end
|
152
155
|
|
153
156
|
def save(contents, path, version)
|
@@ -6,7 +6,7 @@ module RubyNext
|
|
6
6
|
class ArgsForward < Base
|
7
7
|
NAME = "args-forward"
|
8
8
|
SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(1, ...); end"
|
9
|
-
MIN_SUPPORTED_VERSION = Gem::Version.new("
|
9
|
+
MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
|
10
10
|
|
11
11
|
REST = :__rest__
|
12
12
|
BLOCK = :__block__
|
@@ -16,9 +16,9 @@ module RubyNext
|
|
16
16
|
proc_or_lambda, num, body = *node.children
|
17
17
|
|
18
18
|
if proc_or_lambda.type == :lambda
|
19
|
-
insert_before(node.loc.begin, "(#{
|
19
|
+
insert_before(node.loc.begin, "(#{proc_args_str(num)})")
|
20
20
|
else
|
21
|
-
insert_after(node.loc.begin, " |#{
|
21
|
+
insert_after(node.loc.begin, " |#{proc_args_str(num)}|")
|
22
22
|
end
|
23
23
|
|
24
24
|
node.updated(
|
@@ -33,6 +33,10 @@ module RubyNext
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
+
def proc_args_str(n)
|
37
|
+
(1..n).map { |numero| "_#{numero}" }.join(", ")
|
38
|
+
end
|
39
|
+
|
36
40
|
def proc_args(n)
|
37
41
|
return s(:args, s(:procarg0, s(:arg, :_1))) if n == 1
|
38
42
|
|
@@ -244,7 +244,7 @@ module RubyNext
|
|
244
244
|
@predicates = Predicates::CaseIn.new
|
245
245
|
|
246
246
|
matchee_ast =
|
247
|
-
s(:lvasgn, MATCHEE, node.children[0])
|
247
|
+
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
248
248
|
|
249
249
|
patterns = locals.with(
|
250
250
|
matchee: MATCHEE,
|
@@ -273,7 +273,7 @@ module RubyNext
|
|
273
273
|
@predicates = Predicates::Noop.new
|
274
274
|
|
275
275
|
matchee =
|
276
|
-
s(:lvasgn, MATCHEE, node.children[0])
|
276
|
+
s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
|
277
277
|
|
278
278
|
pattern =
|
279
279
|
locals.with(
|
@@ -285,9 +285,10 @@ module RubyNext
|
|
285
285
|
:"#{node.children[1].type}_clause",
|
286
286
|
node.children[1]
|
287
287
|
).then do |node|
|
288
|
-
s(:
|
289
|
-
|
290
|
-
|
288
|
+
s(:begin,
|
289
|
+
s(:or,
|
290
|
+
node,
|
291
|
+
no_matching_pattern))
|
291
292
|
end
|
292
293
|
end
|
293
294
|
|
@@ -378,9 +379,10 @@ module RubyNext
|
|
378
379
|
predicates.const(case_eq_clause(const, right), const).then do |node|
|
379
380
|
next node if pattern.nil?
|
380
381
|
|
381
|
-
s(:
|
382
|
-
|
383
|
-
|
382
|
+
s(:begin,
|
383
|
+
s(:and,
|
384
|
+
node,
|
385
|
+
send(:"#{pattern.type}_clause", pattern)))
|
384
386
|
end
|
385
387
|
end
|
386
388
|
|
@@ -391,13 +393,14 @@ module RubyNext
|
|
391
393
|
send :"#{child.type}_clause", child
|
392
394
|
end
|
393
395
|
end
|
394
|
-
s(:or, *children)
|
396
|
+
s(:begin, s(:or, *children))
|
395
397
|
end
|
396
398
|
|
397
399
|
def match_as_clause(node, right = s(:lvar, locals[:matchee]))
|
398
|
-
s(:
|
399
|
-
|
400
|
-
|
400
|
+
s(:begin,
|
401
|
+
s(:and,
|
402
|
+
send(:"#{node.children[0].type}_clause", node.children[0], right),
|
403
|
+
match_var_clause(node.children[1], right)))
|
401
404
|
end
|
402
405
|
|
403
406
|
def match_var_clause(node, left = s(:lvar, locals[:matchee]))
|
@@ -405,9 +408,10 @@ module RubyNext
|
|
405
408
|
|
406
409
|
check_match_var_alternation! node.children[0]
|
407
410
|
|
408
|
-
s(:
|
409
|
-
s(:
|
410
|
-
|
411
|
+
s(:begin,
|
412
|
+
s(:or,
|
413
|
+
s(:begin, s(:lvasgn, node.children[0], left)),
|
414
|
+
s(:true)))
|
411
415
|
end
|
412
416
|
|
413
417
|
def pin_clause(node, right = s(:lvar, locals[:matchee]))
|
@@ -417,8 +421,8 @@ module RubyNext
|
|
417
421
|
|
418
422
|
def case_eq_clause(node, right = s(:lvar, locals[:matchee]))
|
419
423
|
predicates.terminate!
|
420
|
-
s(:send,
|
421
|
-
process(node), :===, right)
|
424
|
+
s(:begin, s(:send,
|
425
|
+
process(node), :===, right))
|
422
426
|
end
|
423
427
|
|
424
428
|
#=========== ARRAY PATTERN (START) ===============
|
@@ -429,10 +433,11 @@ module RubyNext
|
|
429
433
|
# if there is no rest or tail, match the size first
|
430
434
|
unless node.type == :array_pattern_with_tail || node.children.any? { |n| n.type == :match_rest }
|
431
435
|
size_check = predicates.array_size(
|
432
|
-
s(:
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
+
s(:begin,
|
437
|
+
s(:send,
|
438
|
+
node.children.size.to_ast_node,
|
439
|
+
:==,
|
440
|
+
s(:send, s(:lvar, locals[:arr]), :size))),
|
436
441
|
node.children.size
|
437
442
|
)
|
438
443
|
end
|
@@ -448,9 +453,10 @@ module RubyNext
|
|
448
453
|
|
449
454
|
right = s(:and, size_check, right) if size_check
|
450
455
|
|
451
|
-
s(:
|
452
|
-
|
453
|
-
|
456
|
+
s(:begin,
|
457
|
+
s(:and,
|
458
|
+
dnode,
|
459
|
+
right))
|
454
460
|
end
|
455
461
|
end
|
456
462
|
|
@@ -468,14 +474,17 @@ module RubyNext
|
|
468
474
|
predicates.array_deconstructed(
|
469
475
|
s(:and,
|
470
476
|
respond_check,
|
471
|
-
s(:
|
472
|
-
s(:
|
473
|
-
s(:
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
477
|
+
s(:begin,
|
478
|
+
s(:and,
|
479
|
+
s(:begin,
|
480
|
+
s(:or,
|
481
|
+
s(:begin, s(:lvasgn, locals[:arr], right)),
|
482
|
+
s(:true))),
|
483
|
+
s(:begin,
|
484
|
+
s(:or,
|
485
|
+
s(:send,
|
486
|
+
s(:const, nil, :Array), :===, s(:lvar, locals[:arr])),
|
487
|
+
raise_error(:TypeError, "#deconstruct must return Array"))))))
|
479
488
|
)
|
480
489
|
end
|
481
490
|
|
@@ -485,9 +494,10 @@ module RubyNext
|
|
485
494
|
send("#{head.type}_array_element", head, index).then do |node|
|
486
495
|
next node if tail.empty?
|
487
496
|
|
488
|
-
s(:
|
489
|
-
|
490
|
-
|
497
|
+
s(:begin,
|
498
|
+
s(:and,
|
499
|
+
node,
|
500
|
+
array_element(index + 1, *tail)))
|
491
501
|
end
|
492
502
|
end
|
493
503
|
|
@@ -526,15 +536,17 @@ module RubyNext
|
|
526
536
|
|
527
537
|
pattern = array_rest_element(*nodes, index).then do |needle|
|
528
538
|
next needle unless head_match
|
529
|
-
s(:
|
530
|
-
|
531
|
-
|
539
|
+
s(:begin,
|
540
|
+
s(:and,
|
541
|
+
needle,
|
542
|
+
head_match))
|
532
543
|
end.then do |headed_needle|
|
533
544
|
next headed_needle unless tail_match
|
534
545
|
|
535
|
-
s(:
|
536
|
-
|
537
|
-
|
546
|
+
s(:begin,
|
547
|
+
s(:and,
|
548
|
+
headed_needle,
|
549
|
+
tail_match))
|
538
550
|
end
|
539
551
|
|
540
552
|
s(:block,
|
@@ -550,13 +562,14 @@ module RubyNext
|
|
550
562
|
next block if match_vars.empty?
|
551
563
|
|
552
564
|
# We need to declare match vars outside of `find` block
|
553
|
-
locals_declare = s(:masgn,
|
565
|
+
locals_declare = s(:begin, s(:masgn,
|
554
566
|
s(:mlhs, *match_vars),
|
555
|
-
s(:nil))
|
567
|
+
s(:nil)))
|
556
568
|
|
557
|
-
s(:
|
558
|
-
|
559
|
-
|
569
|
+
s(:begin,
|
570
|
+
s(:or,
|
571
|
+
locals_declare,
|
572
|
+
block))
|
560
573
|
end
|
561
574
|
end
|
562
575
|
|
@@ -575,18 +588,20 @@ module RubyNext
|
|
575
588
|
|
576
589
|
return rest if tail.empty?
|
577
590
|
|
578
|
-
s(:
|
579
|
-
|
580
|
-
|
591
|
+
s(:begin,
|
592
|
+
s(:and,
|
593
|
+
rest,
|
594
|
+
array_rest_element(*tail, -(size - 1))))
|
581
595
|
end
|
582
596
|
|
583
597
|
def array_rest_element(head, *tail, index)
|
584
598
|
send("#{head.type}_array_element", head, index).then do |node|
|
585
599
|
next node if tail.empty?
|
586
600
|
|
587
|
-
s(:
|
588
|
-
|
589
|
-
|
601
|
+
s(:begin,
|
602
|
+
s(:and,
|
603
|
+
node,
|
604
|
+
array_rest_element(*tail, index + 1)))
|
590
605
|
end
|
591
606
|
end
|
592
607
|
|
@@ -610,7 +625,7 @@ module RubyNext
|
|
610
625
|
children = node.children.map do |child, i|
|
611
626
|
send :"#{child.type}_array_element", child, index
|
612
627
|
end
|
613
|
-
s(:or, *children)
|
628
|
+
s(:begin, s(:or, *children))
|
614
629
|
end
|
615
630
|
|
616
631
|
def match_var_array_element(node, index)
|
@@ -661,18 +676,20 @@ module RubyNext
|
|
661
676
|
elsif specified_key_names.empty?
|
662
677
|
hash_element(*node.children)
|
663
678
|
else
|
664
|
-
s(:
|
665
|
-
|
666
|
-
|
679
|
+
s(:begin,
|
680
|
+
s(:and,
|
681
|
+
having_hash_keys(specified_key_names),
|
682
|
+
hash_element(*node.children)))
|
667
683
|
end
|
668
684
|
|
669
685
|
predicates.pop
|
670
686
|
|
671
687
|
next dnode if right.nil?
|
672
688
|
|
673
|
-
s(:
|
674
|
-
|
675
|
-
|
689
|
+
s(:begin,
|
690
|
+
s(:and,
|
691
|
+
dnode,
|
692
|
+
right))
|
676
693
|
end
|
677
694
|
end
|
678
695
|
|
@@ -715,7 +732,7 @@ module RubyNext
|
|
715
732
|
# Duplicate the source hash when matching **rest, 'cause we mutate it
|
716
733
|
hash_dup =
|
717
734
|
if @hash_match_rest
|
718
|
-
s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup))
|
735
|
+
s(:begin, s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup)))
|
719
736
|
else
|
720
737
|
s(:true)
|
721
738
|
end
|
@@ -728,29 +745,33 @@ module RubyNext
|
|
728
745
|
key_names = keys.children.map { |node| node.children.last }
|
729
746
|
predicates.push locals[:hash]
|
730
747
|
|
731
|
-
s(:lvasgn, deconstruct_name,
|
748
|
+
s(:begin, s(:lvasgn, deconstruct_name,
|
732
749
|
s(:send,
|
733
|
-
matchee, :deconstruct_keys, keys)).then do |dnode|
|
750
|
+
matchee, :deconstruct_keys, keys))).then do |dnode|
|
734
751
|
next dnode if respond_to_checked
|
735
752
|
|
736
753
|
s(:and,
|
737
754
|
respond_check,
|
738
|
-
s(:
|
739
|
-
s(:
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
755
|
+
s(:begin,
|
756
|
+
s(:and,
|
757
|
+
s(:begin,
|
758
|
+
s(:or,
|
759
|
+
dnode,
|
760
|
+
s(:true))),
|
761
|
+
s(:begin,
|
762
|
+
s(:or,
|
763
|
+
s(:send,
|
764
|
+
s(:const, nil, :Hash), :===, s(:lvar, deconstruct_name)),
|
765
|
+
raise_error(:TypeError, "#deconstruct_keys must return Hash"))))))
|
746
766
|
end.then do |dnode|
|
747
767
|
predicates.hash_deconstructed(dnode, key_names)
|
748
768
|
end.then do |dnode|
|
749
769
|
next dnode unless @hash_match_rest
|
750
770
|
|
751
|
-
s(:
|
752
|
-
|
753
|
-
|
771
|
+
s(:begin,
|
772
|
+
s(:and,
|
773
|
+
dnode,
|
774
|
+
hash_dup))
|
754
775
|
end
|
755
776
|
end
|
756
777
|
|
@@ -780,9 +801,10 @@ module RubyNext
|
|
780
801
|
|
781
802
|
next node if right.nil?
|
782
803
|
|
783
|
-
s(:
|
784
|
-
|
785
|
-
|
804
|
+
s(:begin,
|
805
|
+
s(:and,
|
806
|
+
node,
|
807
|
+
right))
|
786
808
|
end
|
787
809
|
end
|
788
810
|
|
@@ -792,7 +814,7 @@ module RubyNext
|
|
792
814
|
end
|
793
815
|
|
794
816
|
def match_alt_hash_element(node, key)
|
795
|
-
element_node = s(:lvasgn, locals[:hash, :el], hash_value_at(key))
|
817
|
+
element_node = s(:begin, s(:lvasgn, locals[:hash, :el], hash_value_at(key)))
|
796
818
|
|
797
819
|
children = locals.with(hash_element: locals[:hash, :el]) do
|
798
820
|
node.children.map do |child, i|
|
@@ -800,11 +822,14 @@ module RubyNext
|
|
800
822
|
end
|
801
823
|
end
|
802
824
|
|
803
|
-
s(:
|
804
|
-
s(:
|
805
|
-
|
806
|
-
|
807
|
-
|
825
|
+
s(:begin,
|
826
|
+
s(:and,
|
827
|
+
s(:begin,
|
828
|
+
s(:or,
|
829
|
+
element_node,
|
830
|
+
s(:true))),
|
831
|
+
s(:begin,
|
832
|
+
s(:or, *children))))
|
808
833
|
end
|
809
834
|
|
810
835
|
def match_as_hash_element(node, key)
|
@@ -862,9 +887,10 @@ module RubyNext
|
|
862
887
|
node = predicates.hash_key(hash_has_key(key, hash), key)
|
863
888
|
|
864
889
|
keys.reduce(node) do |res, key|
|
865
|
-
s(:
|
866
|
-
|
867
|
-
|
890
|
+
s(:begin,
|
891
|
+
s(:and,
|
892
|
+
res,
|
893
|
+
predicates.hash_key(hash_has_key(key, hash), key)))
|
868
894
|
end
|
869
895
|
end
|
870
896
|
|
@@ -873,9 +899,10 @@ module RubyNext
|
|
873
899
|
def with_guard(node, guard)
|
874
900
|
return node unless guard
|
875
901
|
|
876
|
-
s(:
|
877
|
-
|
878
|
-
|
902
|
+
s(:begin,
|
903
|
+
s(:and,
|
904
|
+
node,
|
905
|
+
guard.children[0])).then do |expr|
|
879
906
|
next expr unless guard.type == :unless_guard
|
880
907
|
s(:send, expr, :!)
|
881
908
|
end
|
@@ -933,10 +960,10 @@ module RubyNext
|
|
933
960
|
deconstructed_keys[key] = :"k#{deconstructed_keys.size}"
|
934
961
|
end
|
935
962
|
|
936
|
-
# Unparser generates `do .. end`
|
963
|
+
# Unparser generates `do .. end` or `{ ... }` multiline blocks, we want to
|
937
964
|
# have single-line blocks with `{ ... }`.
|
938
965
|
def inline_blocks(source)
|
939
|
-
source.gsub(/do \|_, __i__\|\n\s*([^\n]+)\n\s*end/, '{ |_, __i__| \1 }')
|
966
|
+
source.gsub(/(?:do|{) \|_, __i__\|\n\s*([^\n]+)\n\s*(?:end|})/, '{ |_, __i__| \1 }')
|
940
967
|
end
|
941
968
|
end
|
942
969
|
end
|