ruby-next-core 0.8.0 → 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.
- 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
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6cab7345f16f61d3a35a307024885c4b9a0055bb946f473bdd5a386b27175ec2
         | 
| 4 | 
            +
              data.tar.gz: c3b2004bf5cd1b9dd49d2a7892bd3e88472a632f4cdc8da310bdb56fb96171d9
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 4f6b3d6f64e0303e1b69fa5a99ececcf605c7413285c05feb03d9308da8449296fbea96b8779a3ac6b72dca8351ac10cd20f11161b1581ac6a60a85d951ca0d3
         | 
| 7 | 
            +
              data.tar.gz: 44e5f05020cae47e13d1a89627c94d890b07daccd1e7dcf51e2e4e0eed208fca2c946e4db674f53fa0581477a368e80c73474ffc657a53f55c1e962bd3d668ba
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -2,6 +2,51 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            ## master
         | 
| 4 4 |  | 
| 5 | 
            +
            ## 0.10.0 (2020-09-02)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            - Add proposed shorthand Hash syntax. ([@palkan][])
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            You can try it: `x = 1; y = 2; data = {x, y}`.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - Add leading argument support to args forwarding. ([@palkan][])
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            `def a(...) b(1, ...); end`.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            - Add `Hash#except`. ([@palkan][])
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            `{a: 1, b: 2}.except(:a) == {b: 2}`
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            - Add find pattern support. ([@palkan][])
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            Now you can do: `[0, 1, 2] in [*, 1 => a, *c]`.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            - Add Ruby 2.2 support. ([@palkan][])
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            With support for safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`).
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ## 0.9.2 (2020-06-24)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            - Support passing rewriters to CLI. ([@sl4vr][])
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            Use `nextify --list-rewriters` to view all available rewriters.
         | 
| 32 | 
            +
            Use `nextify` with `--rewrite=REWRITERS...` option to specify which particular rewriters to use.
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            ## 0.9.1 (2020-06-05)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            - Keep `ruby-next` version in sync with `ruby-next-core`. ([@palkan][])
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            Require `ruby-next-core` of the same version as `ruby-next`.
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            ## 0.9.0 (2020-06-04)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            - Add Ruby 2.3 support. ([@palkan][])
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            - Remove stale transpiled files when running `ruby-next nextify`. ([@palkan][])
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            - Add Ruby 2.4 support. ([@palkan][])
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            APIs for <2.5 must be backported via [backports][] gem. Refinements are not supported.
         | 
| 49 | 
            +
             | 
| 5 50 | 
             
            ## 0.8.0 (2020-05-01) 🚩
         | 
| 6 51 |  | 
| 7 52 | 
             
            - Add right-hand assignment support. ([@palkan][])
         | 
| @@ -178,3 +223,5 @@ p a #=> 1 | |
| 178 223 | 
             
            - Add `Kernel#then`. ([@palkan][])
         | 
| 179 224 |  | 
| 180 225 | 
             
            [@palkan]: https://github.com/palkan
         | 
| 226 | 
            +
            [backports]: https://github.com/marcandre/backports
         | 
| 227 | 
            +
            [@sl4vr]: https://github.com/sl4vr
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 | 
            +
            [](https://cultofmartians.com/tasks/ruby-next-cli-rewriters.html#task)
         | 
| 1 2 | 
             
            [](https://rubygems.org/gems/ruby-next) [](https://github.com/ruby-next/ruby-next/actions)
         | 
| 2 | 
            -
            [](https://github.com/ruby-next/ruby-next/actions)
         | 
| 3 | 
            +
            [](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
         | 
| 4 | 
            +
            [](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
         | 
| 3 5 |  | 
| 4 6 | 
             
            # Ruby Next
         | 
| 5 7 |  | 
| @@ -17,13 +19,31 @@ Who might be interested in Ruby Next? | |
| 17 19 | 
             
            Ruby Next also aims to help the community to assess new, _experimental_, MRI features by making it easier to play with them.
         | 
| 18 20 | 
             
            That's why Ruby Next implements the `master` features as fast as possible.
         | 
| 19 21 |  | 
| 22 | 
            +
            Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Make all Rubies quack alike](https://evilmartians.com/chronicles/ruby-next-make-all-rubies-quack-alike).
         | 
| 23 | 
            +
             | 
| 20 24 | 
             
            <a href="https://evilmartians.com/?utm_source=ruby-next">
         | 
| 21 25 | 
             
            <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
         | 
| 22 26 |  | 
| 23 | 
            -
            ##  | 
| 27 | 
            +
            ## Posts
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            - [Ruby Next: Make all Rubies quack alike](https://evilmartians.com/chronicles/ruby-next-make-all-rubies-quack-alike)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            ## Talks
         | 
| 24 32 |  | 
| 25 33 | 
             
            - [Ruby Next: Make old Rubies quack like a new one](https://noti.st/palkan/j3i2Dr/ruby-next-make-old-rubies-quack-like-a-new-one) (RubyConf 2019)
         | 
| 26 34 |  | 
| 35 | 
            +
            ## Examples
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            - Ruby gems
         | 
| 38 | 
            +
              - [anyway_config](https://github.com/palkan/anyway_config)
         | 
| 39 | 
            +
              - [graphql-fragment_cache](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache)
         | 
| 40 | 
            +
            - Rails applications
         | 
| 41 | 
            +
              - [anycable_rails_demo](https://github.com/anycable/anycable_rails_demo)
         | 
| 42 | 
            +
            - mruby
         | 
| 43 | 
            +
              - [ACLI](https://github.com/palkan/acli)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            _Please, submit a PR to add your project to the list!_
         | 
| 46 | 
            +
             | 
| 27 47 | 
             
            ## Table of contents
         | 
| 28 48 |  | 
| 29 49 | 
             
            - [Overview](#overview)
         | 
| @@ -38,6 +58,7 @@ That's why Ruby Next implements the `master` features as fast as possible. | |
| 38 58 | 
             
              - [`ruby -ruby-next`](#uby-next)
         | 
| 39 59 | 
             
              - [Logging & Debugging](#logging-and-debugging)
         | 
| 40 60 | 
             
            - [RuboCop](#rubocop)
         | 
| 61 | 
            +
            - [Using with EOL Rubies](#using-with-eol-rubies)
         | 
| 41 62 | 
             
            - [Proposed & edge features](#proposed-and-edge-features)
         | 
| 42 63 |  | 
| 43 64 | 
             
            ## Overview
         | 
| @@ -49,8 +70,9 @@ Core provides **polyfills** for Ruby core classes APIs via Refinements (default | |
| 49 70 | 
             
            Language is responsible for **transpiling** edge Ruby syntax into older versions. It could be done
         | 
| 50 71 | 
             
            programmatically or via CLI. It also could be done in runtime.
         | 
| 51 72 |  | 
| 52 | 
            -
            Currently, Ruby Next supports Ruby versions 2. | 
| 53 | 
            -
             | 
| 73 | 
            +
            Currently, Ruby Next supports Ruby versions 2.2+, including JRuby 9.2.8+ and TruffleRuby 20.1+ (with some limitations). Support for EOL versions (<2.5) slightly differs though ([see below](#using-with-eol-rubies)).
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            Please, [open an issue](https://github.com/ruby-next/ruby-next/issues/new/choose) or join the discussion in the existing ones if you would like us to support older Ruby versions.
         | 
| 54 76 |  | 
| 55 77 | 
             
            ## Quick start
         | 
| 56 78 |  | 
| @@ -119,6 +141,8 @@ The following _rule of thumb_ is recommended when choosing between refinements a | |
| 119 141 | 
             
            - Using core extensions could be considered for application development (no need to think about `using RubyNext`); this approach could potentially lead to conflicts with dependencies (if these dependencies are not using refinements 🙂)
         | 
| 120 142 | 
             
            - Use core extensions if refinements are not supported by your platform
         | 
| 121 143 |  | 
| 144 | 
            +
            **NOTE:** _Edge_ APIs (i.e., from the Ruby's master branch) are included by default.
         | 
| 145 | 
            +
             | 
| 122 146 | 
             
            [**The list of supported APIs.**][features_core]
         | 
| 123 147 |  | 
| 124 148 | 
             
            ## Transpiling
         | 
| @@ -179,12 +203,15 @@ It has the following interface: | |
| 179 203 | 
             
            ```sh
         | 
| 180 204 | 
             
            $ ruby-next nextify
         | 
| 181 205 | 
             
            Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
         | 
| 182 | 
            -
                -o, --output=OUTPUT              Specify output directory or file or stdout | 
| 206 | 
            +
                -o, --output=OUTPUT              Specify output directory or file or stdout
         | 
| 183 207 | 
             
                    --min-version=VERSION        Specify the minimum Ruby version to support
         | 
| 184 208 | 
             
                    --single-version             Only create one version of a file (for the earliest Ruby version)
         | 
| 185 | 
            -
                    -- | 
| 209 | 
            +
                    --edge                       Enable edge (master) Ruby features
         | 
| 210 | 
            +
                    --proposed                   Enable proposed/experimental Ruby features
         | 
| 186 211 | 
             
                    --transpile-mode=MODE        Transpiler mode (ast or rewrite). Default: ast
         | 
| 187 212 | 
             
                    --[no-]refine                Do not inject `using RubyNext`
         | 
| 213 | 
            +
                    --list-rewriters             List available rewriters
         | 
| 214 | 
            +
                    --rewrite=REWRITERS...       Specify particular Ruby features to rewrite
         | 
| 188 215 | 
             
                -h, --help                       Print help
         | 
| 189 216 | 
             
                -V                               Turn on verbose mode
         | 
| 190 217 | 
             
                    --dry-run                    Print verbose output without generating files
         | 
| @@ -297,17 +324,17 @@ If you're using [runtime mode](#runtime-usage) a long with `setup_gem_load_path` | |
| 297 324 |  | 
| 298 325 | 
             
            \* Ruby Next avoids storing duplicates; instead, only the code for the earlier version is created and is assumed to be used with other versions. For example, if the transpiled code is the same for Ruby 2.5 and Ruby 2.6, only the `.rbnext/2.7/path/to/file.rb` is kept. That's why multiple entries are added to the `$LOAD_PATH` (`.rbnext/2.6` and `.rbnext/2.7` in the specified order for Ruby 2.5 and only `.rbnext/2.7` for Ruby 2.6).
         | 
| 299 326 |  | 
| 300 | 
            -
            ### Transpiled files vs.  | 
| 327 | 
            +
            ### Transpiled files vs. VCS vs. installing from source
         | 
| 301 328 |  | 
| 302 329 | 
             
            It's a best practice to not keep generated files in repositories. In case of Ruby Next, it's a `lib/.rbnext` folder.
         | 
| 303 330 |  | 
| 304 | 
            -
            We recommend adding this folder only to the gem package (i.e., it should be added to your `spec.files`) and ignore it in your  | 
| 331 | 
            +
            We recommend adding this folder only to the gem package (i.e., it should be added to your `spec.files`) and ignore it in your VCS (e.g., `echo ".rbnext/" >> .gitignore`). That would make transpiled files available in releases without polluting your repository.
         | 
| 305 332 |  | 
| 306 | 
            -
            What if someone decides to install your gem from the  | 
| 333 | 
            +
            What if someone decides to install your gem from the VCS source? They would likely face some syntax errors due to the missing transpiled files.
         | 
| 307 334 |  | 
| 308 335 | 
             
            To solve this problem, Ruby Next _tries_ to transpile the source code when you call `#setup_gem_load_path`. It does this by calling `bundle exec ruby-next nextify <lib_dir> -o <next_dir>`. We make the following assumptions:
         | 
| 309 336 |  | 
| 310 | 
            -
            - We in the Bundler context (since that's the most common way of installing gems from source).
         | 
| 337 | 
            +
            - We are in the Bundler context (since that's the most common way of installing gems from source).
         | 
| 311 338 | 
             
            - Our Gemfile contains `ruby-next` gem.
         | 
| 312 339 | 
             
            - We use [`.rbnextrc`](#CLI-configuration-file) for transpiling options.
         | 
| 313 340 |  | 
| @@ -372,6 +399,12 @@ RUBYOPT="-ruby-next" ruby my_ruby_script.rb | |
| 372 399 | 
             
            ruby -ruby-next -e "puts [2, 4, 5].tally"
         | 
| 373 400 | 
             
            ```
         | 
| 374 401 |  | 
| 402 | 
            +
            **NOTE:** running Ruby scripts directly or executing code via `-e` option is not supported in TruffleRuby. You can still use `-ruby-next` to transpile required files, e.g.:
         | 
| 403 | 
            +
             | 
| 404 | 
            +
            ```sh
         | 
| 405 | 
            +
            ruby -ruby-next -r my_ruby_script.rb -e "puts my_method"
         | 
| 406 | 
            +
            ```
         | 
| 407 | 
            +
             | 
| 375 408 | 
             
            ## Logging and debugging
         | 
| 376 409 |  | 
| 377 410 | 
             
            Ruby Next prints some debugging information when fails to load a file in the runtime mode (and fallbacks to the built-in loading mechanism).
         | 
| @@ -396,6 +429,8 @@ You must set `TargetRubyVersion: next` to make RuboCop use a Ruby Next parser. | |
| 396 429 |  | 
| 397 430 | 
             
            Alternatively, you can load the patch from the command line by running: `rubocop -r ruby-next/rubocop ...`.
         | 
| 398 431 |  | 
| 432 | 
            +
            We recommend using the latest RuboCop version, 'cause it has support for new nodes built-in.
         | 
| 433 | 
            +
             | 
| 399 434 | 
             
            Also, when pre-transpiling source code with `ruby-next nextify`, we suggest ignoring the transpiled files:
         | 
| 400 435 |  | 
| 401 436 | 
             
            ```yml
         | 
| @@ -406,6 +441,40 @@ AllCops: | |
| 406 441 |  | 
| 407 442 | 
             
            **NOTE:** you need `ruby-next` gem available in the environment where you run RuboCop (having `ruby-next-core` is not enough).
         | 
| 408 443 |  | 
| 444 | 
            +
            ## Using with EOL Rubies
         | 
| 445 | 
            +
             | 
| 446 | 
            +
            We currently provide support for Ruby 2.2, 2.3 and 2.4.
         | 
| 447 | 
            +
             | 
| 448 | 
            +
            Ruby Next itself relies on 2.5 features and contains polyfills only for version 2.5+ (and that won't change).
         | 
| 449 | 
            +
            Thus, to make it work with <2.5 we need to backport some APIs ourselves.
         | 
| 450 | 
            +
             | 
| 451 | 
            +
            The recommended way of doing this is to use [backports][] gem. You need to load backports **before Ruby Next**.
         | 
| 452 | 
            +
             | 
| 453 | 
            +
            When using runtime features, you should do the following:
         | 
| 454 | 
            +
             | 
| 455 | 
            +
            ```ruby
         | 
| 456 | 
            +
            # first, require backports upto 2.5
         | 
| 457 | 
            +
            require "backports/2.5"
         | 
| 458 | 
            +
            # then, load Ruby Next
         | 
| 459 | 
            +
            require "ruby-next"
         | 
| 460 | 
            +
            # if you need 2.6+ APIs, add Ruby Next core_ext
         | 
| 461 | 
            +
            require "ruby-next/core_ext"
         | 
| 462 | 
            +
            # then, load runtime transpiling
         | 
| 463 | 
            +
            require "ruby-next/language/runtime"
         | 
| 464 | 
            +
            # or
         | 
| 465 | 
            +
            require "ruby-next/language/bootsnap"
         | 
| 466 | 
            +
            ```
         | 
| 467 | 
            +
             | 
| 468 | 
            +
            To load backports while using `ruby-next nextify` command, you must configure the environment variable:
         | 
| 469 | 
            +
             | 
| 470 | 
            +
            ```sh
         | 
| 471 | 
            +
            RUBY_NEXT_CORE_STRATEGY=backports ruby-next nextify lib/
         | 
| 472 | 
            +
            ```
         | 
| 473 | 
            +
             | 
| 474 | 
            +
            **NOTE:** Make sure you have `backports` gem installed globally or added to your bundle (if you're using `bundle exec ruby-next ...`).
         | 
| 475 | 
            +
             | 
| 476 | 
            +
            **NOTE:** For Ruby 2.2, safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`) support is provided.
         | 
| 477 | 
            +
             | 
| 409 478 | 
             
            ## Proposed and edge features
         | 
| 410 479 |  | 
| 411 480 | 
             
            Ruby Next aims to bring edge and proposed features to Ruby community before they (hopefully) reach an official Ruby release.
         | 
| @@ -439,12 +508,16 @@ require "ruby-next/language/runtime" | |
| 439 508 |  | 
| 440 509 | 
             
            - "Endless" method definition (`def foo() = 42`) ([#16746](https://bugs.ruby-lang.org/issues/16746)).
         | 
| 441 510 |  | 
| 442 | 
            -
            - Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921))
         | 
| 511 | 
            +
            - Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921)).
         | 
| 512 | 
            +
             | 
| 513 | 
            +
            - Find pattern (`[0, 1, 2] in [*, 1 => a, *c]`) ([#16828](https://bugs.ruby-lang.org/issues/16828)).
         | 
| 443 514 |  | 
| 444 515 | 
             
            ### Supported proposed features
         | 
| 445 516 |  | 
| 446 517 | 
             
            - _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
         | 
| 447 518 |  | 
| 519 | 
            +
            - Shorthand Hash notation (`data = {x, y}`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
         | 
| 520 | 
            +
             | 
| 448 521 | 
             
            ## Contributing
         | 
| 449 522 |  | 
| 450 523 | 
             
            Bug reports and pull requests are welcome on GitHub at [https://github.com/ruby-next/ruby-next](ttps://github.com/ruby-next/ruby-next).
         | 
| @@ -470,3 +543,4 @@ The gem is available as open source under the terms of the [MIT License](https:/ | |
| 470 543 | 
             
            [next_parser]: https://github.com/ruby-next/parser
         | 
| 471 544 | 
             
            [Bootsnap]: https://github.com/Shopify/bootsnap
         | 
| 472 545 | 
             
            [rubocop]: https://github.com/rubocop-hq/rubocop
         | 
| 546 | 
            +
            [backports]: https://github.com/marcandre/backports
         | 
    
        data/bin/transform
    CHANGED
    
    | @@ -9,6 +9,7 @@ ENV["RUBY_NEXT_EDGE"] = "1" | |
| 9 9 | 
             
            ENV["RUBY_NEXT_PROPOSED"] = "1"
         | 
| 10 10 |  | 
| 11 11 | 
             
            require "ruby-next/language"
         | 
| 12 | 
            +
            require "ruby-next/language/rewriters/runtime"
         | 
| 12 13 |  | 
| 13 14 | 
             
            contents =
         | 
| 14 15 | 
             
              if File.exist?(ARGV[0])
         | 
| @@ -17,4 +18,11 @@ contents = | |
| 17 18 | 
             
                ARGV[0]
         | 
| 18 19 | 
             
              end
         | 
| 19 20 |  | 
| 20 | 
            -
             | 
| 21 | 
            +
            opts =
         | 
| 22 | 
            +
              if ARGV[1] && ARGV[1] == "--current"
         | 
| 23 | 
            +
                {rewriters: RubyNext::Language.current_rewriters}
         | 
| 24 | 
            +
              else
         | 
| 25 | 
            +
                {}
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            puts RubyNext::Language.transform(contents, **opts)
         | 
| @@ -0,0 +1,167 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "fileutils"
         | 
| 4 | 
            +
            require "pathname"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module RubyNext
         | 
| 7 | 
            +
              module Commands
         | 
| 8 | 
            +
                class CoreExt < Base
         | 
| 9 | 
            +
                  using RubyNext
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  attr_reader :out_path, :min_version, :names, :list, :filter, :original_command
         | 
| 12 | 
            +
                  alias list? list
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def run
         | 
| 15 | 
            +
                    log "Select core extensions for Ruby v#{min_version}" \
         | 
| 16 | 
            +
                        "#{filter ? " and matching #{filter.inspect}" : ""}"
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    matching_patches.then do |patches|
         | 
| 19 | 
            +
                      next print_list(patches) if list?
         | 
| 20 | 
            +
                      generate_core_ext(patches)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def parse!(args)
         | 
| 25 | 
            +
                    print_help = false
         | 
| 26 | 
            +
                    @min_version = MIN_SUPPORTED_VERSION
         | 
| 27 | 
            +
                    @original_command = "ruby-next core_ext #{args.join(" ")}"
         | 
| 28 | 
            +
                    @names = []
         | 
| 29 | 
            +
                    @list = false
         | 
| 30 | 
            +
                    @out_path = File.join(Dir.pwd, "core_ext.rb")
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    optparser = base_parser do |opts|
         | 
| 33 | 
            +
                      opts.banner = "Usage: ruby-next core_ext [options]"
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      opts.on("-o", "--output=OUTPUT", "Specify output file or stdout (default: ./core_ext.rb)") do |val|
         | 
| 36 | 
            +
                        @out_path = val
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                      opts.on("-l", "--list", "List all available extensions") do
         | 
| 40 | 
            +
                        @list = true
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      opts.on("--min-version=VERSION", "Specify the minimum Ruby version to support") do |val|
         | 
| 44 | 
            +
                        @min_version = Gem::Version.new(val)
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                      opts.on("-n", "--name=NAME", "Filter extensions by name") do |val|
         | 
| 48 | 
            +
                        names << val
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      opts.on("-h", "--help", "Print help") do
         | 
| 52 | 
            +
                        print_help = true
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    optparser.parse!(args)
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    if print_help
         | 
| 59 | 
            +
                      $stdout.puts optparser.help
         | 
| 60 | 
            +
                      exit 0
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    @filter = /(#{names.join("|")})/i unless names.empty?
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  private
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def matching_patches
         | 
| 69 | 
            +
                    RubyNext::Core.patches.extensions
         | 
| 70 | 
            +
                      .values
         | 
| 71 | 
            +
                      .flatten
         | 
| 72 | 
            +
                      .select do |patch|
         | 
| 73 | 
            +
                      next if min_version && Gem::Version.new(patch.version) <= min_version
         | 
| 74 | 
            +
                      next if filter && !filter.match?(patch.name)
         | 
| 75 | 
            +
                      true
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def print_list(patches)
         | 
| 80 | 
            +
                    grouped_patches = patches.group_by(&:version).sort_by(&:first)
         | 
| 81 | 
            +
                    grouped_patches.each do |(group, patches)|
         | 
| 82 | 
            +
                      $stdout.puts "#{group} extensions:\n"
         | 
| 83 | 
            +
                      $stdout.puts patches.sort_by(&:name).map { |patch| "  - #{patch.name}" }.join("\n")
         | 
| 84 | 
            +
                      $stdout.puts "\n"
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  def generate_core_ext(patches)
         | 
| 89 | 
            +
                    grouped_patches = patches.group_by(&:mod).sort_by { |(mod, patch)| mod.singleton_class? ? mod.inspect : mod.name }
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    buffer = []
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    buffer << "# frozen_string_literal: true\n"
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    buffer << generation_meta
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    grouped_patches.each do |mod, patches|
         | 
| 98 | 
            +
                      singleton = mod.singleton_class?
         | 
| 99 | 
            +
                      extend_name = singleton ? patches.first.singleton.name : mod.name
         | 
| 100 | 
            +
                      prepend_name = singleton ? "#{patches.first.singleton.name}.singleton_class" : mod.name
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                      prepended, extended = patches.partition(&:prepend?)
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                      prepended.map do |patch|
         | 
| 105 | 
            +
                        name = "RubyNext::Core::#{patch.name}"
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                        buffer << <<-RUBY
         | 
| 108 | 
            +
                         module #{name}
         | 
| 109 | 
            +
                         #{indent_and_trim(patch.body)}
         | 
| 110 | 
            +
                         end
         | 
| 111 | 
            +
                         #{prepend_name}.prepend #{name}
         | 
| 112 | 
            +
                        RUBY
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                        name
         | 
| 115 | 
            +
                      end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                      class_or_module = mod.is_a?(Class) ? "class" : "module"
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      buffer << "#{class_or_module} #{extend_name}"
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                      buffer << "  class << self" if singleton
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                      indent_size = singleton ? 4 : 2
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                      buffer << extended.map do |patch|
         | 
| 126 | 
            +
                        indent_and_trim(patch.body, indent_size)
         | 
| 127 | 
            +
                      end.join("\n\n")
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                      buffer << "  end" if singleton
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                      buffer << "end\n"
         | 
| 132 | 
            +
                    end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    contents = buffer.join("\n")
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    return $stdout.puts(contents) if out_path == "stdout"
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    unless CLI.dry_run?
         | 
| 139 | 
            +
                      FileUtils.mkdir_p File.dirname(out_path)
         | 
| 140 | 
            +
                      File.write(out_path, contents)
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    log "Generated: #{out_path}"
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                  def generation_meta
         | 
| 147 | 
            +
                    <<-MSG
         | 
| 148 | 
            +
            # Generated by Ruby Next v#{RubyNext::VERSION} using the following command:
         | 
| 149 | 
            +
            #
         | 
| 150 | 
            +
            #   #{original_command}
         | 
| 151 | 
            +
            #
         | 
| 152 | 
            +
                    MSG
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  def indent_and_trim(src, size = 2)
         | 
| 156 | 
            +
                    new_src = src.dup
         | 
| 157 | 
            +
                    # indent code using <size> spaces
         | 
| 158 | 
            +
                    new_src.gsub!(/^/, " " * size)
         | 
| 159 | 
            +
                    # remove empty lines
         | 
| 160 | 
            +
                    new_src.gsub!(/^\s+$/, "")
         | 
| 161 | 
            +
                    # remove traling blank lines
         | 
| 162 | 
            +
                    new_src.delete_suffix!("\n")
         | 
| 163 | 
            +
                    new_src
         | 
| 164 | 
            +
                  end
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
            end
         |