ruby-next-core 0.9.2 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +15 -4
  4. data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
  5. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +201 -0
  6. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
  7. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
  8. data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
  10. data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +107 -0
  11. data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
  12. data/lib/ruby-next.rb +8 -6
  13. data/lib/ruby-next/cli.rb +2 -2
  14. data/lib/ruby-next/commands/core_ext.rb +1 -1
  15. data/lib/ruby-next/commands/nextify.rb +3 -0
  16. data/lib/ruby-next/core.rb +27 -21
  17. data/lib/ruby-next/core/array/deconstruct.rb +9 -9
  18. data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
  19. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
  20. data/lib/ruby-next/core/enumerable/filter.rb +8 -8
  21. data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
  22. data/lib/ruby-next/core/enumerable/tally.rb +7 -7
  23. data/lib/ruby-next/core/enumerator/produce.rb +12 -12
  24. data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
  25. data/lib/ruby-next/core/hash/except.rb +11 -0
  26. data/lib/ruby-next/core/hash/merge.rb +8 -8
  27. data/lib/ruby-next/core/kernel/then.rb +2 -2
  28. data/lib/ruby-next/core/proc/compose.rb +11 -11
  29. data/lib/ruby-next/core/string/split.rb +6 -6
  30. data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
  31. data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
  32. data/lib/ruby-next/core/symbol/end_with.rb +4 -4
  33. data/lib/ruby-next/core/symbol/start_with.rb +4 -4
  34. data/lib/ruby-next/core/time/ceil.rb +6 -6
  35. data/lib/ruby-next/core/time/floor.rb +4 -4
  36. data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
  37. data/lib/ruby-next/core_ext.rb +1 -1
  38. data/lib/ruby-next/language.rb +12 -1
  39. data/lib/ruby-next/language/parser.rb +0 -3
  40. data/lib/ruby-next/language/proposed.rb +3 -0
  41. data/lib/ruby-next/language/rewriters/args_forward.rb +23 -20
  42. data/lib/ruby-next/language/rewriters/base.rb +1 -1
  43. data/lib/ruby-next/language/rewriters/endless_method.rb +25 -3
  44. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  45. data/lib/ruby-next/language/rewriters/method_reference.rb +1 -1
  46. data/lib/ruby-next/language/rewriters/pattern_matching.rb +102 -12
  47. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +73 -11
  48. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  49. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  50. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  51. data/lib/ruby-next/language/unparser.rb +0 -14
  52. data/lib/ruby-next/logging.rb +1 -1
  53. data/lib/ruby-next/rubocop.rb +91 -9
  54. data/lib/ruby-next/setup_self.rb +22 -0
  55. data/lib/ruby-next/version.rb +1 -1
  56. data/lib/uby-next.rb +8 -4
  57. metadata +23 -9
@@ -3,36 +3,36 @@
3
3
  # Refine Array seprately, 'cause refining modules is vulnerable to prepend:
4
4
  # - https://bugs.ruby-lang.org/issues/13446
5
5
  RubyNext::Core.patch Enumerable, method: :filter_map, version: "2.7", refineable: [Enumerable, Array] do
6
- <<~RUBY
7
- def filter_map
8
- if block_given?
9
- result = []
10
- each do |element|
11
- res = yield element
12
- result << res if res
13
- end
14
- result
15
- else
16
- Enumerator.new do |yielder|
17
- result = []
18
- each do |element|
19
- res = yielder.yield element
20
- result << res if res
21
- end
22
- result
23
- end
6
+ <<-RUBY
7
+ def filter_map
8
+ if block_given?
9
+ result = []
10
+ each do |element|
11
+ res = yield element
12
+ result << res if res
13
+ end
14
+ result
15
+ else
16
+ Enumerator.new do |yielder|
17
+ result = []
18
+ each do |element|
19
+ res = yielder.yield element
20
+ result << res if res
24
21
  end
22
+ result
25
23
  end
24
+ end
25
+ end
26
26
  RUBY
27
27
  end
28
28
 
29
29
  RubyNext::Core.patch Enumerator::Lazy, method: :filter_map, version: "2.7" do
30
- <<~RUBY
31
- def filter_map
32
- Enumerator::Lazy.new(self) do |yielder, *values|
33
- result = yield(*values)
34
- yielder << result if result
35
- end
36
- end
30
+ <<-RUBY
31
+ def filter_map
32
+ Enumerator::Lazy.new(self) do |yielder, *values|
33
+ result = yield(*values)
34
+ yielder << result if result
35
+ end
36
+ end
37
37
  RUBY
38
38
  end
@@ -3,12 +3,12 @@
3
3
  # Refine Array seprately, 'cause refining modules is vulnerable to prepend:
4
4
  # - https://bugs.ruby-lang.org/issues/13446
5
5
  RubyNext::Core.patch Enumerable, method: :tally, version: "2.7", refineable: [Enumerable, Array] do
6
- <<~RUBY
7
- def tally
8
- each_with_object({}) do |v, acc|
9
- acc[v] ||= 0
10
- acc[v] += 1
11
- end
12
- end
6
+ <<-RUBY
7
+ def tally
8
+ each_with_object({}) do |v, acc|
9
+ acc[v] ||= 0
10
+ acc[v] += 1
11
+ end
12
+ end
13
13
  RUBY
14
14
  end
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Enumerator.singleton_class, method: :produce, singleton: Enumerator, version: "2.7" do
4
- <<~'RUBY'
5
- # Based on https://github.com/zverok/enumerator_generate
6
- def produce(*rest, &block)
7
- raise ArgumentError, "wrong number of arguments (given #{rest.size}, expected 0..1)" if rest.size > 1
8
- raise ArgumentError, "No block given" unless block
4
+ <<-'RUBY'
5
+ # Based on https://github.com/zverok/enumerator_generate
6
+ def produce(*rest, &block)
7
+ raise ArgumentError, "wrong number of arguments (given #{rest.size}, expected 0..1)" if rest.size > 1
8
+ raise ArgumentError, "No block given" unless block
9
9
 
10
- Enumerator.new(Float::INFINITY) do |y|
11
- val = rest.empty? ? yield() : rest.pop
10
+ Enumerator.new(Float::INFINITY) do |y|
11
+ val = rest.empty? ? yield() : rest.pop
12
12
 
13
- loop do
14
- y << val
15
- val = yield(val)
16
- end
17
- end
13
+ loop do
14
+ y << val
15
+ val = yield(val)
18
16
  end
17
+ end
18
+ end
19
19
  RUBY
20
20
  end
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Hash, method: :deconstruct_keys, version: "2.7" do
4
- <<~RUBY
5
- def deconstruct_keys(_)
6
- self
7
- end
4
+ <<-RUBY
5
+ def deconstruct_keys(_)
6
+ self
7
+ end
8
8
  RUBY
9
9
  end
10
10
 
11
11
  # We need to hack `respond_to?` in Ruby 2.5, since it's not working with refinements
12
12
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6")
13
13
  RubyNext::Core.patch refineable: Hash, name: "HashRespondToDeconstructKeys", method: :deconstruct_keys, version: "2.7" do
14
- <<~RUBY
15
- def respond_to?(mid, *)
16
- return true if mid == :deconstruct_keys
17
- super
18
- end
14
+ <<-RUBY
15
+ def respond_to?(mid, *)
16
+ return true if mid == :deconstruct_keys
17
+ super
18
+ end
19
19
  RUBY
20
20
  end
21
21
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ RubyNext::Core.patch Hash, method: :except, version: "3.0" do
4
+ <<-RUBY
5
+ def except(*keys)
6
+ self.dup.tap do |new_hash|
7
+ keys.each { |k| new_hash.delete(k) }
8
+ end
9
+ end
10
+ RUBY
11
+ end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Hash, method: :merge, version: "2.6", supported: {}.method(:merge).arity < 0, core_ext: :prepend do
4
- <<~RUBY
5
- def merge(*others)
6
- return super if others.size == 1
7
- return dup if others.size == 0
4
+ <<-RUBY
5
+ def merge(*others)
6
+ return super if others.size == 1
7
+ return dup if others.size == 0
8
8
 
9
- merge(others.shift).tap do |new_h|
10
- others.each { |h| new_h.merge!(h) }
11
- end
12
- end
9
+ merge(others.shift).tap do |new_h|
10
+ others.each { |h| new_h.merge!(h) }
11
+ end
12
+ end
13
13
  RUBY
14
14
  end
@@ -4,7 +4,7 @@
4
4
  # - https://bugs.ruby-lang.org/issues/13446
5
5
  # - Rails added `Kernel.prepend` in 6.1: https://github.com/rails/rails/commit/3124007bd674dcdc9c3b5c6b2964dfb7a1a0733c
6
6
  RubyNext::Core.patch Kernel, method: :then, version: "2.6", refineable: Object do
7
- <<~RUBY
8
- alias then yield_self
7
+ <<-RUBY
8
+ alias then yield_self
9
9
  RUBY
10
10
  end
@@ -2,18 +2,18 @@
2
2
 
3
3
  # rubocop:disable Style/LambdaCall
4
4
  RubyNext::Core.patch Proc, name: "ProcCompose", method: :<<, version: "2.6" do
5
- <<~RUBY
6
- def <<(other)
7
- raise TypeError, "callable object is expected" unless other.respond_to?(:call)
8
- this = self
9
- proc { |*args, &block| this.(other.(*args, &block)) }
10
- end
5
+ <<-RUBY
6
+ def <<(other)
7
+ raise TypeError, "callable object is expected" unless other.respond_to?(:call)
8
+ this = self
9
+ proc { |*args, &block| this.(other.(*args, &block)) }
10
+ end
11
11
 
12
- def >>(other)
13
- raise TypeError, "callable object is expected" unless other.respond_to?(:call)
14
- this = self
15
- proc { |*args, &block| other.(this.(*args, &block)) }
16
- end
12
+ def >>(other)
13
+ raise TypeError, "callable object is expected" unless other.respond_to?(:call)
14
+ this = self
15
+ proc { |*args, &block| other.(this.(*args, &block)) }
16
+ end
17
17
  RUBY
18
18
  end
19
19
  # rubocop:enable Style/LambdaCall
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch String, method: :split, version: "2.6", supported: ("a b".split(" ", &proc {}) == "a b"), core_ext: :prepend do
4
- <<~RUBY
5
- def split(*args, &block)
6
- return super unless block_given?
7
- super.each { |el| yield el }
8
- self
9
- end
4
+ <<-RUBY
5
+ def split(*args, &block)
6
+ return super unless block_given?
7
+ super.each { |el| yield el }
8
+ self
9
+ end
10
10
  RUBY
11
11
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Struct, method: :deconstruct, version: "2.7" do
4
- <<~'RUBY'
5
- alias deconstruct to_a
4
+ <<-RUBY
5
+ alias deconstruct to_a
6
6
  RUBY
7
7
  end
@@ -2,31 +2,31 @@
2
2
 
3
3
  # Source: https://github.com/ruby/ruby/blob/b76a21aa45fff75909a66f8b20fc5856705f7862/struct.c#L953-L980
4
4
  RubyNext::Core.patch Struct, method: :deconstruct_keys, version: "2.7" do
5
- <<~'RUBY'
6
- def deconstruct_keys(keys)
7
- raise TypeError, "wrong argument type #{keys.class} (expected Array or nil)" if keys && !keys.is_a?(Array)
5
+ <<-'RUBY'
6
+ def deconstruct_keys(keys)
7
+ raise TypeError, "wrong argument type #{keys.class} (expected Array or nil)" if keys && !keys.is_a?(Array)
8
8
 
9
- return to_h unless keys
9
+ return to_h unless keys
10
10
 
11
- keys.each_with_object({}) do |k, acc|
12
- # if k is Symbol and not a member of a Struct return {}
13
- next if (Symbol === k || String === k) && !members.include?(k.to_sym)
14
- # if k is Integer check that index is not ouf of bounds
15
- next if Integer === k && k > size - 1
16
- acc[k] = self[k]
17
- end
18
- end
11
+ keys.each_with_object({}) do |k, acc|
12
+ # if k is Symbol and not a member of a Struct return {}
13
+ next if (Symbol === k || String === k) && !members.include?(k.to_sym)
14
+ # if k is Integer check that index is not ouf of bounds
15
+ next if Integer === k && k > size - 1
16
+ acc[k] = self[k]
17
+ end
18
+ end
19
19
  RUBY
20
20
  end
21
21
 
22
22
  # We need to hack `respond_to?` in Ruby 2.5, since it's not working with refinements
23
23
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6")
24
24
  RubyNext::Core.patch refineable: Struct, name: "StructRespondToDeconstruct", method: :deconstruct_keys, version: "2.7" do
25
- <<~RUBY
26
- def respond_to?(mid, *)
27
- return true if mid == :deconstruct_keys || mid == :deconstruct
28
- super
29
- end
25
+ <<-RUBY
26
+ def respond_to?(mid, *)
27
+ return true if mid == :deconstruct_keys || mid == :deconstruct
28
+ super
29
+ end
30
30
  RUBY
31
31
  end
32
32
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Symbol, method: :end_with?, version: "2.7" do
4
- <<~RUBY
5
- def end_with?(*prefixes)
6
- to_s.end_with?(*prefixes)
7
- end
4
+ <<-RUBY
5
+ def end_with?(*prefixes)
6
+ to_s.end_with?(*prefixes)
7
+ end
8
8
  RUBY
9
9
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Symbol, method: :start_with?, version: "2.7" do
4
- <<~RUBY
5
- def start_with?(*prefixes)
6
- to_s.start_with?(*prefixes)
7
- end
4
+ <<-RUBY
5
+ def start_with?(*prefixes)
6
+ to_s.start_with?(*prefixes)
7
+ end
8
8
  RUBY
9
9
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Time, method: :ceil, version: "2.7" do
4
- <<~'RUBY'
5
- def ceil(den = 0)
6
- sceil = (subsec * 10**den).ceil.to_r / 10**den
7
- change = sceil - subsec
8
- self + change
9
- end
4
+ <<-RUBY
5
+ def ceil(den = 0)
6
+ sceil = (subsec * 10**den).ceil.to_r / 10**den
7
+ change = sceil - subsec
8
+ self + change
9
+ end
10
10
  RUBY
11
11
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch Time, method: :floor, version: "2.7" do
4
- <<~'RUBY'
5
- def floor(den = 0)
6
- self - (subsec % (10**-den))
7
- end
4
+ <<-RUBY
5
+ def floor(den = 0)
6
+ self - (subsec % (10**-den))
7
+ end
8
8
  RUBY
9
9
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RubyNext::Core.patch UnboundMethod, method: :bind_call, version: "2.7" do
4
- <<~'RUBY'
5
- def bind_call(receiver, *args, &block)
6
- bind(receiver).call(*args, &block)
7
- end
4
+ <<-RUBY
5
+ def bind_call(receiver, *args, &block)
6
+ bind(receiver).call(*args, &block)
7
+ end
8
8
  RUBY
9
9
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "core"
3
+ require "ruby-next/core"
4
4
 
5
5
  # Monkey-patch core classes using the same patches as for refinements
6
6
  RubyNext::Core.patches.extensions.each do |mod, patches|
@@ -100,7 +100,7 @@ module RubyNext
100
100
  else
101
101
  regenerate(*args, **kwargs)
102
102
  end
103
- rescue Unparser::UnknownEmitterError
103
+ rescue Unparser::UnknownNodeError
104
104
  if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
105
105
  RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since Unparser doesn't support 2.7+ AST yet.\n" \
106
106
  "See https://github.com/mbj/unparser/pull/142"
@@ -173,6 +173,12 @@ module RubyNext
173
173
 
174
174
  require "ruby-next/language/rewriters/base"
175
175
 
176
+ require "ruby-next/language/rewriters/squiggly_heredoc"
177
+ rewriters << Rewriters::SquigglyHeredoc
178
+
179
+ require "ruby-next/language/rewriters/safe_navigation"
180
+ rewriters << Rewriters::SafeNavigation
181
+
176
182
  require "ruby-next/language/rewriters/args_forward"
177
183
  rewriters << Rewriters::ArgsForward
178
184
 
@@ -182,6 +188,11 @@ module RubyNext
182
188
  require "ruby-next/language/rewriters/pattern_matching"
183
189
  rewriters << Rewriters::PatternMatching
184
190
 
191
+ # Must be added after general pattern matching rewriter to become
192
+ # no-op in Ruby <2.7
193
+ require "ruby-next/language/rewriters/find_pattern"
194
+ rewriters << Rewriters::FindPattern
195
+
185
196
  # Put endless range in the end, 'cause Parser fails to parse it in
186
197
  # pattern matching
187
198
  require "ruby-next/language/rewriters/endless_range"
@@ -6,9 +6,6 @@ module RubyNext
6
6
  module Language
7
7
  class Builder < ::Parser::Builders::Default
8
8
  modernize
9
-
10
- # FIXME: add support for 2.8 forward args
11
- self.emit_forward_arg = false
12
9
  end
13
10
 
14
11
  class << self
@@ -4,3 +4,6 @@
4
4
 
5
5
  require "ruby-next/language/rewriters/method_reference"
6
6
  RubyNext::Language.rewriters << RubyNext::Language::Rewriters::MethodReference
7
+
8
+ require "ruby-next/language/rewriters/shorthand_hash"
9
+ RubyNext::Language.rewriters << RubyNext::Language::Rewriters::ShorthandHash
@@ -5,16 +5,18 @@ module RubyNext
5
5
  module Rewriters
6
6
  class ArgsForward < Base
7
7
  NAME = "args-forward"
8
- SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(...); end"
9
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
8
+ SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(1, ...); end"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
10
10
 
11
11
  REST = :__rest__
12
12
  BLOCK = :__block__
13
13
 
14
- def on_forward_args(node)
14
+ def on_forward_arg(node)
15
15
  context.track! self
16
16
 
17
- replace(node.loc.expression, "(*#{REST}, &#{BLOCK})")
17
+ node = super(node)
18
+
19
+ replace(node.loc.expression, "*#{REST}, &#{BLOCK}")
18
20
 
19
21
  node.updated(
20
22
  :args,
@@ -26,34 +28,35 @@ module RubyNext
26
28
  end
27
29
 
28
30
  def on_send(node)
29
- return super(node) unless node.children[2]&.type == :forwarded_args
31
+ fargs = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
32
+ return super(node) unless fargs
33
+
34
+ process_fargs(node, fargs)
35
+ end
36
+
37
+ def on_super(node)
38
+ fargs = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
39
+ return super(node) unless fargs
40
+
41
+ process_fargs(node, fargs)
42
+ end
43
+
44
+ private
30
45
 
31
- replace(node.children[2].loc.expression, "*#{REST}, &#{BLOCK}")
46
+ def process_fargs(node, fargs)
47
+ replace(fargs.loc.expression, "*#{REST}, &#{BLOCK}")
32
48
 
33
49
  process(
34
50
  node.updated(
35
51
  nil,
36
52
  [
37
- *node.children[0..1],
53
+ *node.children.take(node.children.index(fargs)),
38
54
  *forwarded_args
39
55
  ]
40
56
  )
41
57
  )
42
58
  end
43
59
 
44
- def on_super(node)
45
- return super(node) unless node.children[0]&.type == :forwarded_args
46
-
47
- replace(node.children[0].loc.expression, "*#{REST}, &#{BLOCK}")
48
-
49
- node.updated(
50
- nil,
51
- forwarded_args
52
- )
53
- end
54
-
55
- private
56
-
57
60
  def forwarded_args
58
61
  [
59
62
  s(:splat, s(:lvar, REST)),