ruby-next-core 0.9.2 → 0.10.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +14 -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 +198 -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/utils.rb +65 -0
  11. data/lib/ruby-next.rb +8 -6
  12. data/lib/ruby-next/cli.rb +2 -2
  13. data/lib/ruby-next/commands/core_ext.rb +1 -1
  14. data/lib/ruby-next/core.rb +27 -21
  15. data/lib/ruby-next/core/array/deconstruct.rb +9 -9
  16. data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
  17. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
  18. data/lib/ruby-next/core/enumerable/filter.rb +8 -8
  19. data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
  20. data/lib/ruby-next/core/enumerable/tally.rb +7 -7
  21. data/lib/ruby-next/core/enumerator/produce.rb +12 -12
  22. data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
  23. data/lib/ruby-next/core/hash/except.rb +11 -0
  24. data/lib/ruby-next/core/hash/merge.rb +8 -8
  25. data/lib/ruby-next/core/kernel/then.rb +2 -2
  26. data/lib/ruby-next/core/proc/compose.rb +11 -11
  27. data/lib/ruby-next/core/string/split.rb +6 -6
  28. data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
  29. data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
  30. data/lib/ruby-next/core/symbol/end_with.rb +4 -4
  31. data/lib/ruby-next/core/symbol/start_with.rb +4 -4
  32. data/lib/ruby-next/core/time/ceil.rb +6 -6
  33. data/lib/ruby-next/core/time/floor.rb +4 -4
  34. data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
  35. data/lib/ruby-next/core_ext.rb +1 -1
  36. data/lib/ruby-next/language.rb +12 -1
  37. data/lib/ruby-next/language/parser.rb +0 -3
  38. data/lib/ruby-next/language/proposed.rb +3 -0
  39. data/lib/ruby-next/language/rewriters/args_forward.rb +23 -20
  40. data/lib/ruby-next/language/rewriters/base.rb +1 -1
  41. data/lib/ruby-next/language/rewriters/endless_method.rb +25 -3
  42. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  43. data/lib/ruby-next/language/rewriters/method_reference.rb +1 -1
  44. data/lib/ruby-next/language/rewriters/pattern_matching.rb +102 -12
  45. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +1 -1
  46. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  47. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  48. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  49. data/lib/ruby-next/language/unparser.rb +0 -14
  50. data/lib/ruby-next/logging.rb +1 -1
  51. data/lib/ruby-next/rubocop.rb +15 -9
  52. data/lib/ruby-next/setup_self.rb +22 -0
  53. data/lib/ruby-next/version.rb +1 -1
  54. data/lib/uby-next.rb +8 -4
  55. metadata +20 -7
@@ -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("2.7.2")
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)),
@@ -34,7 +34,7 @@ module RubyNext
34
34
  end
35
35
 
36
36
  def key?(name)
37
- !!fetch(name) { false }
37
+ !!fetch(name) { false } # rubocop:disable Style/RedundantFetchBlock
38
38
  end
39
39
 
40
40
  def fetch(name)
@@ -6,7 +6,14 @@ module RubyNext
6
6
  class EndlessMethod < Base
7
7
  NAME = "endless-method"
8
8
  SYNTAX_PROBE = "obj = Object.new; def obj.foo() = 42"
9
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.8.0")
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
10
+
11
+ unless Parser::Meta::NODE_TYPES.include?(:def_e)
12
+ def on_def(node)
13
+ return on_def_e(node) if node.loc.end.nil?
14
+ super(node)
15
+ end
16
+ end
10
17
 
11
18
  def on_def_e(node)
12
19
  context.track! self
@@ -14,24 +21,39 @@ module RubyNext
14
21
  replace(node.loc.assignment, "; ")
15
22
  insert_after(node.loc.expression, "; end")
16
23
 
24
+ new_loc = node.loc.dup
25
+ new_loc.instance_variable_set(:@end, node.loc.expression)
26
+
17
27
  process(
18
28
  node.updated(
19
29
  :def,
20
- node.children
30
+ node.children,
31
+ location: new_loc
21
32
  )
22
33
  )
23
34
  end
24
35
 
36
+ unless Parser::Meta::NODE_TYPES.include?(:def_e)
37
+ def on_defs(node)
38
+ return on_defs_e(node) if node.loc.end.nil?
39
+ super(node)
40
+ end
41
+ end
42
+
25
43
  def on_defs_e(node)
26
44
  context.track! self
27
45
 
28
46
  replace(node.loc.assignment, "; ")
29
47
  insert_after(node.loc.expression, "; end")
30
48
 
49
+ new_loc = node.loc.dup
50
+ new_loc.instance_variable_set(:@end, node.loc.expression)
51
+
31
52
  process(
32
53
  node.updated(
33
54
  :defs,
34
- node.children
55
+ node.children,
56
+ location: new_loc
35
57
  )
36
58
  )
37
59
  end