ruby-next-core 0.8.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/README.md +85 -11
- data/bin/transform +9 -1
- data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
- data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
- data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
- data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
- data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
- data/lib/ruby-next.rb +8 -5
- data/lib/ruby-next/cli.rb +2 -2
- data/lib/ruby-next/commands/core_ext.rb +2 -2
- data/lib/ruby-next/commands/nextify.rb +64 -22
- data/lib/ruby-next/core.rb +39 -22
- data/lib/ruby-next/core/array/deconstruct.rb +9 -9
- data/lib/ruby-next/core/array/difference_union_intersection.rb +12 -12
- data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +3 -3
- data/lib/ruby-next/core/enumerable/filter.rb +8 -8
- data/lib/ruby-next/core/enumerable/filter_map.rb +25 -25
- data/lib/ruby-next/core/enumerable/tally.rb +7 -7
- data/lib/ruby-next/core/enumerator/produce.rb +12 -12
- data/lib/ruby-next/core/hash/deconstruct_keys.rb +9 -9
- data/lib/ruby-next/core/hash/except.rb +11 -0
- data/lib/ruby-next/core/hash/merge.rb +8 -8
- data/lib/ruby-next/core/kernel/then.rb +2 -2
- data/lib/ruby-next/core/proc/compose.rb +11 -11
- data/lib/ruby-next/core/string/split.rb +6 -6
- data/lib/ruby-next/core/struct/deconstruct.rb +2 -2
- data/lib/ruby-next/core/struct/deconstruct_keys.rb +17 -17
- data/lib/ruby-next/core/symbol/end_with.rb +4 -4
- data/lib/ruby-next/core/symbol/start_with.rb +4 -4
- data/lib/ruby-next/core/time/ceil.rb +6 -5
- data/lib/ruby-next/core/time/floor.rb +4 -4
- data/lib/ruby-next/core/unboundmethod/bind_call.rb +4 -4
- data/lib/ruby-next/core_ext.rb +2 -2
- data/lib/ruby-next/language.rb +31 -5
- data/lib/ruby-next/language/eval.rb +10 -8
- data/lib/ruby-next/language/proposed.rb +3 -0
- data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
- data/lib/ruby-next/language/rewriters/base.rb +2 -2
- data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
- data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
- data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
- data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
- data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
- data/lib/ruby-next/language/rewriters/pattern_matching.rb +105 -13
- data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +2 -1
- data/lib/ruby-next/language/rewriters/runtime.rb +6 -0
- data/lib/ruby-next/language/rewriters/runtime/dir.rb +32 -0
- data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
- data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
- data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
- data/lib/ruby-next/language/runtime.rb +3 -2
- data/lib/ruby-next/logging.rb +1 -1
- data/lib/ruby-next/rubocop.rb +15 -9
- data/lib/ruby-next/setup_self.rb +22 -0
- data/lib/ruby-next/utils.rb +30 -0
- data/lib/ruby-next/version.rb +1 -1
- data/lib/uby-next.rb +8 -4
- metadata +22 -7
@@ -10,8 +10,8 @@ RubyNext::Core.patch Object,
|
|
10
10
|
# we only use the contents in `ruby-next core_ext`.
|
11
11
|
supported: true,
|
12
12
|
location: [__FILE__, __LINE__ + 2] do
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
<<-RUBY
|
14
|
+
class NoMatchingPatternError < RuntimeError
|
15
|
+
end
|
16
16
|
RUBY
|
17
17
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RubyNext::Core.patch Enumerable, method: :filter, version: "2.6" do
|
4
|
-
|
5
|
-
|
4
|
+
<<-RUBY
|
5
|
+
alias filter select
|
6
6
|
RUBY
|
7
7
|
end
|
8
8
|
|
@@ -11,15 +11,15 @@ end
|
|
11
11
|
#
|
12
12
|
# Also, Array also have `filter!`
|
13
13
|
RubyNext::Core.patch Array, method: :filter!, version: "2.6" do
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
<<-RUBY
|
15
|
+
alias filter select
|
16
|
+
alias filter! select!
|
17
17
|
RUBY
|
18
18
|
end
|
19
19
|
|
20
20
|
RubyNext::Core.patch Hash, method: :filter!, version: "2.6" do
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
<<-RUBY
|
22
|
+
alias filter select
|
23
|
+
alias filter! select!
|
24
24
|
RUBY
|
25
25
|
end
|
@@ -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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
10
|
+
Enumerator.new(Float::INFINITY) do |y|
|
11
|
+
val = rest.empty? ? yield() : rest.pop
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
@@ -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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
<<-RUBY
|
5
|
+
def merge(*others)
|
6
|
+
return super if others.size == 1
|
7
|
+
return dup if others.size == 0
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
8
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
@@ -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
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
9
|
+
return to_h unless keys
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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: :start_with?, version: "2.7" do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
<<-RUBY
|
5
|
+
def start_with?(*prefixes)
|
6
|
+
to_s.start_with?(*prefixes)
|
7
|
+
end
|
8
8
|
RUBY
|
9
9
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RubyNext::Core.patch Time, method: :ceil, version: "2.7" do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
9
10
|
RUBY
|
10
11
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
<<-RUBY
|
5
|
+
def bind_call(receiver, *args, &block)
|
6
|
+
bind(receiver).call(*args, &block)
|
7
|
+
end
|
8
8
|
RUBY
|
9
9
|
end
|
data/lib/ruby-next/core_ext.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
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|
|
@@ -15,4 +15,4 @@ RubyNext::Core.patches.extensions.each do |mod, patches|
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
RubyNext::Core.strategy = :core_ext
|
18
|
+
RubyNext::Core.strategy = :core_ext unless RubyNext::Core.core_ext?
|
data/lib/ruby-next/language.rb
CHANGED
@@ -23,6 +23,8 @@ module RubyNext
|
|
23
23
|
require "ruby-next/language/parser"
|
24
24
|
require "ruby-next/language/unparser"
|
25
25
|
|
26
|
+
RewriterNotFoundError = Class.new(StandardError)
|
27
|
+
|
26
28
|
class TransformContext
|
27
29
|
attr_reader :versions, :use_ruby_next
|
28
30
|
|
@@ -83,11 +85,7 @@ module RubyNext
|
|
83
85
|
end
|
84
86
|
|
85
87
|
def runtime!
|
86
|
-
|
87
|
-
RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since Unparser doesn't support 2.7 AST yet.\n" \
|
88
|
-
"See https://github.com/mbj/unparser/pull/142"
|
89
|
-
self.mode = :rewrite
|
90
|
-
end
|
88
|
+
require "ruby-next/language/rewriters/runtime"
|
91
89
|
|
92
90
|
@runtime = true
|
93
91
|
end
|
@@ -102,6 +100,13 @@ module RubyNext
|
|
102
100
|
else
|
103
101
|
regenerate(*args, **kwargs)
|
104
102
|
end
|
103
|
+
rescue Unparser::UnknownNodeError
|
104
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
|
105
|
+
RubyNext.warn "Ruby Next fallbacks to \"rewrite\" transpiling mode since Unparser doesn't support 2.7+ AST yet.\n" \
|
106
|
+
"See https://github.com/mbj/unparser/pull/142"
|
107
|
+
self.mode = :rewrite
|
108
|
+
end
|
109
|
+
rewrite(*args, **kwargs)
|
105
110
|
end
|
106
111
|
|
107
112
|
def regenerate(source, rewriters: self.rewriters, using: RubyNext::Core.refine?, context: TransformContext.new)
|
@@ -147,6 +152,16 @@ module RubyNext
|
|
147
152
|
@current_rewriters ||= rewriters.select(&:unsupported_syntax?)
|
148
153
|
end
|
149
154
|
|
155
|
+
# This method guarantees that rewriters will be returned in order they defined in Language module
|
156
|
+
def select_rewriters(*names)
|
157
|
+
rewriters_delta = names - rewriters.map { |rewriter| rewriter::NAME }
|
158
|
+
if rewriters_delta.any?
|
159
|
+
raise RewriterNotFoundError, "Rewriters not found: #{rewriters_delta.join(",")}"
|
160
|
+
end
|
161
|
+
|
162
|
+
rewriters.select { |rewriter| names.include?(rewriter::NAME) }
|
163
|
+
end
|
164
|
+
|
150
165
|
private
|
151
166
|
|
152
167
|
attr_writer :watch_dirs
|
@@ -158,6 +173,12 @@ module RubyNext
|
|
158
173
|
|
159
174
|
require "ruby-next/language/rewriters/base"
|
160
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
|
+
|
161
182
|
require "ruby-next/language/rewriters/args_forward"
|
162
183
|
rewriters << Rewriters::ArgsForward
|
163
184
|
|
@@ -167,6 +188,11 @@ module RubyNext
|
|
167
188
|
require "ruby-next/language/rewriters/pattern_matching"
|
168
189
|
rewriters << Rewriters::PatternMatching
|
169
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
|
+
|
170
196
|
# Put endless range in the end, 'cause Parser fails to parse it in
|
171
197
|
# pattern matching
|
172
198
|
require "ruby-next/language/rewriters/endless_range"
|