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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.md +85 -11
  4. data/bin/transform +9 -1
  5. data/lib/.rbnext/2.3/ruby-next/commands/core_ext.rb +167 -0
  6. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +198 -0
  7. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +66 -0
  8. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +121 -0
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +63 -0
  10. data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +944 -0
  11. data/lib/.rbnext/2.3/ruby-next/utils.rb +65 -0
  12. data/lib/ruby-next.rb +8 -5
  13. data/lib/ruby-next/cli.rb +2 -2
  14. data/lib/ruby-next/commands/core_ext.rb +2 -2
  15. data/lib/ruby-next/commands/nextify.rb +64 -22
  16. data/lib/ruby-next/core.rb +39 -22
  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 -5
  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 +2 -2
  38. data/lib/ruby-next/language.rb +31 -5
  39. data/lib/ruby-next/language/eval.rb +10 -8
  40. data/lib/ruby-next/language/proposed.rb +3 -0
  41. data/lib/ruby-next/language/rewriters/args_forward.rb +24 -20
  42. data/lib/ruby-next/language/rewriters/base.rb +2 -2
  43. data/lib/ruby-next/language/rewriters/endless_method.rb +26 -3
  44. data/lib/ruby-next/language/rewriters/endless_range.rb +1 -0
  45. data/lib/ruby-next/language/rewriters/find_pattern.rb +44 -0
  46. data/lib/ruby-next/language/rewriters/method_reference.rb +2 -1
  47. data/lib/ruby-next/language/rewriters/numbered_params.rb +1 -0
  48. data/lib/ruby-next/language/rewriters/pattern_matching.rb +105 -13
  49. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +2 -1
  50. data/lib/ruby-next/language/rewriters/runtime.rb +6 -0
  51. data/lib/ruby-next/language/rewriters/runtime/dir.rb +32 -0
  52. data/lib/ruby-next/language/rewriters/safe_navigation.rb +87 -0
  53. data/lib/ruby-next/language/rewriters/shorthand_hash.rb +47 -0
  54. data/lib/ruby-next/language/rewriters/squiggly_heredoc.rb +36 -0
  55. data/lib/ruby-next/language/runtime.rb +3 -2
  56. data/lib/ruby-next/logging.rb +1 -1
  57. data/lib/ruby-next/rubocop.rb +15 -9
  58. data/lib/ruby-next/setup_self.rb +22 -0
  59. data/lib/ruby-next/utils.rb +30 -0
  60. data/lib/ruby-next/version.rb +1 -1
  61. data/lib/uby-next.rb +8 -4
  62. metadata +22 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb8a0798576c2859d40a9bcc7ce9b4c5a9bcdd2560a603bdf14b897a1248edc2
4
- data.tar.gz: 38f599eb8a725bb52ad7a7ae66342c3450da6906366a8d6e53304484ca60a1b5
3
+ metadata.gz: 6cab7345f16f61d3a35a307024885c4b9a0055bb946f473bdd5a386b27175ec2
4
+ data.tar.gz: c3b2004bf5cd1b9dd49d2a7892bd3e88472a632f4cdc8da310bdb56fb96171d9
5
5
  SHA512:
6
- metadata.gz: abdb54f137bce953dfe24eaa29dcb35c057534821ac2dbd5a4b3092608aa88b6a2e8ac81032f86766d2eda29c70bbc0b2acd79f72dcd2490d230cd92a34af00b
7
- data.tar.gz: ff4087bb6b6ecf1cc159166b0a4a8bcc650d2ef1ee315f3eeee5e8b8b1f3005ce560c4fcd7ffd5673cef206fbaf7f3fca2e0f0a594fda042b5f5178d43ea2a5b
6
+ metadata.gz: 4f6b3d6f64e0303e1b69fa5a99ececcf605c7413285c05feb03d9308da8449296fbea96b8779a3ac6b72dca8351ac10cd20f11161b1581ac6a60a85d951ca0d3
7
+ data.tar.gz: 44e5f05020cae47e13d1a89627c94d890b07daccd1e7dcf51e2e4e0eed208fca2c946e4db674f53fa0581477a368e80c73474ffc657a53f55c1e962bd3d668ba
@@ -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
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/ruby-next-cli-rewriters.html#task)
1
2
  [![Gem Version](https://badge.fury.io/rb/ruby-next.svg)](https://rubygems.org/gems/ruby-next) [![Build](https://github.com/ruby-next/ruby-next/workflows/Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions)
2
- [![JRuby Build](https://github.com/ruby-next/ruby-next/workflows/JRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions)
3
+ [![JRuby Build](https://github.com/ruby-next/ruby-next/workflows/JRuby%20Build/badge.svg)](https://github.com/ruby-next/ruby-next/actions?query=workflow%3A%22TruffleRuby+Build%22)
4
+ [![TruffleRuby Build](https://github.com/ruby-next/ruby-next/workflows/TruffleRuby%20Build/badge.svg)](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
- ## Links
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.5+ (including JRuby 9.2.8+).
53
- Please, [open an issue](https://github.com/ruby-next/ruby-next/issues/new/choose) if you would like us to support older Ruby versions.
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 (use -o stdout for that)
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
- --enable-method-reference Enable reverted method reference syntax (requires custom parser)
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. VSC vs. installing from source
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 VSC (e.g., `echo ".rbnext/" >> .gitignore`). That would make transpiled files available in releases without polluting your repository.
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 VSC source? They would likely face some syntax errors due to the missing transpiled files.
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
@@ -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
- puts RubyNext::Language.transform(contents)
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