ruby-next-core 0.14.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -0
  3. data/README.md +163 -56
  4. data/bin/mspec +11 -0
  5. data/lib/.rbnext/2.1/ruby-next/commands/nextify.rb +295 -0
  6. data/lib/.rbnext/2.1/ruby-next/core.rb +12 -4
  7. data/lib/.rbnext/2.1/ruby-next/language.rb +62 -12
  8. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +97 -3
  9. data/lib/.rbnext/2.3/ruby-next/config.rb +79 -0
  10. data/lib/.rbnext/2.3/ruby-next/core/data.rb +163 -0
  11. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
  12. data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/args_forward.rb +134 -0
  13. data/lib/.rbnext/2.3/ruby-next/language/rewriters/2.7/pattern_matching.rb +122 -47
  14. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +6 -32
  15. data/lib/.rbnext/2.3/ruby-next/utils.rb +3 -22
  16. data/lib/.rbnext/2.6/ruby-next/core/data.rb +163 -0
  17. data/lib/.rbnext/2.7/ruby-next/core/data.rb +163 -0
  18. data/lib/.rbnext/2.7/ruby-next/core.rb +12 -4
  19. data/lib/.rbnext/2.7/ruby-next/language/paco_parsers/string_literals.rb +109 -0
  20. data/lib/.rbnext/2.7/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
  21. data/lib/.rbnext/2.7/ruby-next/language/rewriters/text.rb +132 -0
  22. data/lib/.rbnext/3.2/ruby-next/commands/base.rb +55 -0
  23. data/lib/.rbnext/3.2/ruby-next/language/rewriters/2.7/pattern_matching.rb +1095 -0
  24. data/lib/.rbnext/3.2/ruby-next/rubocop.rb +210 -0
  25. data/lib/ruby-next/cli.rb +10 -15
  26. data/lib/ruby-next/commands/nextify.rb +99 -3
  27. data/lib/ruby-next/config.rb +31 -4
  28. data/lib/ruby-next/core/data.rb +163 -0
  29. data/lib/ruby-next/core/matchdata/deconstruct.rb +9 -0
  30. data/lib/ruby-next/core/matchdata/deconstruct_keys.rb +20 -0
  31. data/lib/ruby-next/core/matchdata/named_captures.rb +11 -0
  32. data/lib/ruby-next/core/proc/compose.rb +0 -1
  33. data/lib/ruby-next/core/refinement/import.rb +44 -36
  34. data/lib/ruby-next/core/time/deconstruct_keys.rb +30 -0
  35. data/lib/ruby-next/core.rb +11 -3
  36. data/lib/ruby-next/irb.rb +24 -0
  37. data/lib/ruby-next/language/bootsnap.rb +2 -25
  38. data/lib/ruby-next/language/eval.rb +4 -4
  39. data/lib/ruby-next/language/paco_parser.rb +7 -0
  40. data/lib/ruby-next/language/paco_parsers/base.rb +47 -0
  41. data/lib/ruby-next/language/paco_parsers/comments.rb +26 -0
  42. data/lib/ruby-next/language/paco_parsers/string_literals.rb +109 -0
  43. data/lib/ruby-next/language/parser.rb +31 -6
  44. data/lib/ruby-next/language/rewriters/2.5/rescue_within_block.rb +41 -0
  45. data/lib/ruby-next/language/rewriters/2.7/args_forward.rb +57 -0
  46. data/lib/ruby-next/language/rewriters/2.7/pattern_matching.rb +120 -45
  47. data/lib/ruby-next/language/rewriters/3.0/args_forward_leading.rb +2 -2
  48. data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +1 -1
  49. data/lib/ruby-next/language/rewriters/3.1/shorthand_hash.rb +2 -1
  50. data/lib/ruby-next/language/rewriters/3.2/anonymous_restargs.rb +104 -0
  51. data/lib/ruby-next/language/rewriters/abstract.rb +57 -0
  52. data/lib/ruby-next/language/rewriters/base.rb +6 -32
  53. data/lib/ruby-next/language/rewriters/edge/it_param.rb +58 -0
  54. data/lib/ruby-next/language/rewriters/edge.rb +12 -0
  55. data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +3 -0
  56. data/lib/ruby-next/language/rewriters/proposed/method_reference.rb +9 -20
  57. data/lib/ruby-next/language/rewriters/text.rb +132 -0
  58. data/lib/ruby-next/language/runtime.rb +9 -86
  59. data/lib/ruby-next/language/setup.rb +5 -2
  60. data/lib/ruby-next/language/unparser.rb +5 -0
  61. data/lib/ruby-next/language.rb +62 -12
  62. data/lib/ruby-next/pry.rb +90 -0
  63. data/lib/ruby-next/rubocop.rb +2 -0
  64. data/lib/ruby-next/utils.rb +3 -22
  65. data/lib/ruby-next/version.rb +1 -1
  66. data/lib/uby-next/irb.rb +3 -0
  67. data/lib/uby-next/pry.rb +3 -0
  68. data/lib/uby-next.rb +2 -2
  69. metadata +70 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55cb050984d91ee15965b5629bd998d7300af1051e7ad721f1dfed77fc6df7c7
4
- data.tar.gz: ac68a6f7da2b826b11a40be72948601f2d12ce8b1cd27ab9953977238c1e488d
3
+ metadata.gz: 5ad6cea22baa599e5a6edeb4fdda1fb7c9bd0b0d5617fb01cf4450374c7d9443
4
+ data.tar.gz: fa39e7d51cdf70c32dc2067000023e42dbe3b23bd1d8bf520e06a3a028d63b8d
5
5
  SHA512:
6
- metadata.gz: 7cd0de569637973ff61866733a6a3d59dac4e317bf5e522b23a88fc5af8941e4a072ae1dfbf26163a661c71911f7801545cad00134cd826d4913f684b03473e8
7
- data.tar.gz: 2ebf8f0a42bd0771cfbc2f2240a3844436d3f108e5cec292ec2cd7e3bebef0a332088357a892cf3cd2b0c6835e9f39679cc472cd6a0ac4d120850194d8f2e783
6
+ metadata.gz: e7df787bfa90d036bbd45c7bd2dbc85f3513d922b3606dfde12ade0196f7ccb6789aba263d407fc012c7b4625d38b4aa6d7df05a5ac4e784b3b9cfc5db356b0f
7
+ data.tar.gz: a4c364d32b19003c219670096dd6420d415663445d68deb7e92c8d9763cc705884e7ab7e43fed81c6c084450f6a32e14bd8b50f24629256cb064c84a8bb87583
data/CHANGELOG.md CHANGED
@@ -2,6 +2,74 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.0.1 (2024-01-28)
6
+
7
+ - Fix using Data with inheritance (`class X < Data.define(...)`). ([@palkan][])
8
+
9
+ ## 1.0.0 🎄
10
+
11
+ - Add `it` block parameter support. ([@palkan][])
12
+
13
+ - Deprecate AST transpiling mode. ([@palkan][])
14
+
15
+ - Add `Data` backport. ([@palkan][], [@saturnflyer][])
16
+
17
+ - Add `Time#deconstruct_keys` support. ([@palkan][])
18
+
19
+ - Add anonymous rest and keyword rest arguments forwarding support (`def foo(*, **); bar(*, **) end`). ([@palkan][])
20
+
21
+ - Add `MatchData#{deconstruct,deconstruct_keys}` and `#named_captures(symbolize_names: true)` support. ([@palkan][])
22
+
23
+ - Add stats to `nextify`. ([@palkan][])
24
+
25
+ - Add text rewriters and `--import-rewriter` option to `nextify`. ([@palkan][])
26
+
27
+ - Use `.rbnextrc` arguments for runtime transpiling and auto-transpiling gems. ([@palkan][])
28
+
29
+ - Stop testing Ruby <2.4. ([@palkan][])
30
+
31
+ - Extract `require-hooks`. ([@palkan][])
32
+
33
+ - Support passing --overwrite to CLI. ([@prog-supdex][])
34
+
35
+ Use `nextify original_file --single-version --overwrite` to overwrite the original file without create new one.
36
+
37
+ ## 0.15.3 (2022-10-16)
38
+
39
+ - Fix handling nested const patterns. ([@palkan][])
40
+
41
+ ## 0.15.2 (2022-08-02)
42
+
43
+ - Fix loading transpiled in TruffleRuby. ([@palkan][])
44
+
45
+ TruffleRuby doesn't support all Ruby 3.0 features yet, so we should treat as an older Ruby.
46
+
47
+ - Use `ruby2_keywords` when transpiling arguments forwarding. ([@palkan][])
48
+
49
+ That makes transpiled code forward compatible with the modern Ruby versions.
50
+
51
+ ## 0.15.1 (2022-04-05)
52
+
53
+ - Fix transpiling `rescue` within nested blocks. ([@palkan][])
54
+
55
+ ## 0.15.0 (2022-03-21)
56
+
57
+ - Support IRB ([@palkan][])
58
+
59
+ - Create empty `.rbnext` folder during `nextify` if nothing to transpile. ([@palkan][])
60
+
61
+ This would prevent from auto-transpiling a library every time when no files should be transpiled.
62
+
63
+ - Auto-transpile using the current Ruby version. ([@palkan][])
64
+
65
+ - Support Pry. ([@baygeldin][])
66
+
67
+ - Add `rescue/ensure/else` within block rewriter for Ruby < 2.5. ([@fargelus][])
68
+
69
+ ## 0.14.1 (2022-01-21)
70
+
71
+ - Fix nested find patterns transpiling. ([@palkan][])
72
+
5
73
  ## 0.14.0 🎄
6
74
 
7
75
  - Add `Integer.try_convert`. ([@palkan][])
@@ -328,3 +396,5 @@ p a #=> 1
328
396
  [backports]: https://github.com/marcandre/backports
329
397
  [@sl4vr]: https://github.com/sl4vr
330
398
  [@skryukov]: https://github.com/skryukov
399
+ [@prog-supdex]: https://github.com/prog-supdex
400
+ [@saturnflyer]: https://github.com/saturnflyer
data/README.md CHANGED
@@ -14,11 +14,14 @@ Who might be interested in Ruby Next?
14
14
 
15
15
  - **Ruby gems maintainers** who want to write code using the latest Ruby version but still support older ones.
16
16
  - **Application developers** who want to give new features a try without waiting for the final release (or, more often, for the first patch).
17
- - **Users of non-MRI implementations** such as [mruby][], [JRuby][], [TruffleRuby][], [Opal][], [RubyMotion][], [Artichoke][], [Prism][].
17
+ - **Users of non-MRI implementations** such as [mruby][], [JRuby][], [TruffleRuby][], [Natalie][], [Opal][], [RubyMotion][], [Artichoke][], [Prism][].
18
+ - **Ruby syntax enthusiasts** who want to experiment with [custom syntax extensions](#custom-syntax-rewriters) 👩‍🔬👨‍🔬.
18
19
 
19
20
  Ruby Next also aims to help the community to assess new, _experimental_, MRI features by making it easier to play with them.
20
21
  That's why Ruby Next implements the `master` features as fast as possible.
21
22
 
23
+ See also a companion library (extracted from Ruby Next) that provides **code loading hooks** for your needs—[require-hooks][require-hooks].
24
+
22
25
  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
26
 
24
27
  <table style="border:none;">
@@ -60,19 +63,21 @@ _Please, submit a PR to add your project to the list!_
60
63
  ## Table of contents
61
64
 
62
65
  - [Overview](#overview)
63
- - [Quick Start](#quick-start)
66
+ - [Quick start](#quick-start)
64
67
  - [Polyfills](#using-only-polyfills)
65
68
  - [Transpiling](#transpiling)
66
69
  - [Modes](#transpiler-modes)
67
70
  - [CLI](#cli)
68
71
  - [Using in gems](#integrating-into-a-gem-development)
69
72
  - [Runtime usage](#runtime-usage)
70
- - [Bootsnap integration](#using-with-bootsnap)
71
73
  - [`ruby -ruby-next`](#uby-next)
72
74
  - [Logging & Debugging](#logging-and-debugging)
73
75
  - [RuboCop](#rubocop)
76
+ - [Using with IRB](#irb)
77
+ - [Using with Pry](#pry)
74
78
  - [Using with EOL Rubies](#using-with-eol-rubies)
75
79
  - [Proposed & edge features](#proposed-and-edge-features)
80
+ - [Custom syntax rewriters](#custom-syntax-rewriters)
76
81
  - [Known limitations](#known-limitations)
77
82
 
78
83
  ## Overview
@@ -84,9 +89,9 @@ Core provides **polyfills** for Ruby core classes APIs via Refinements (default
84
89
  Language is responsible for **transpiling** edge Ruby syntax into older versions. It could be done
85
90
  programmatically or via CLI. It also could be done in runtime.
86
91
 
87
- 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)).
92
+ Currently, Ruby Next supports Ruby versions 2.3+, including JRuby 9.2.8+ and TruffleRuby 20.1+ (with some limitations). Support for older versions (<2.5) slightly differs though ([see below](#using-with-eol-rubies)). Versions between 2.0 and 2.3 may work but we no longer test against them.
88
93
 
89
- 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.
94
+ Please [open an issue](https://github.com/ruby-next/ruby-next/issues/new/choose) or join the discussion in the existing issues if you would like us to support older Ruby versions.
90
95
 
91
96
  ## Quick start
92
97
 
@@ -98,15 +103,16 @@ $ gem install ruby-next
98
103
 
99
104
  # Call ruby with -ruby-next flag
100
105
  $ ruby -ruby-next -e "
101
- def greet(val) =
102
- case val
106
+ greet = proc do
107
+ case it
103
108
  in hello: hello if hello =~ /human/i
104
109
  '🙂'
105
110
  in hello: 'martian'
106
111
  '👽'
107
112
  end
113
+ end
108
114
 
109
- puts greet(hello: 'martian')
115
+ puts greet.call(hello: 'martian')
110
116
  "
111
117
 
112
118
  => 👽
@@ -121,12 +127,12 @@ First, install a gem:
121
127
  gem "ruby-next-core"
122
128
 
123
129
  # gemspec
124
- spec.add_dependency "ruby-next-core"
130
+ spec.add_dependency "ruby-next-core", "~> 1.0"
125
131
  ```
126
132
 
127
- **NOTE:** we use the different _distribution_ gem, `ruby-next-core`, to provide zero-dependency, polyfills-only version.
133
+ **NOTE:** we use a different gem for _distribution_, `ruby-next-core`, to provide a zero-dependency, polyfills-only version.
128
134
 
129
- Then, all you need is to load the Ruby Next:
135
+ Then, all you need is to load Ruby Next:
130
136
 
131
137
  ```ruby
132
138
  require "ruby-next"
@@ -138,9 +144,9 @@ And activate the refinement in every file where you want to use it\*:
138
144
  using RubyNext
139
145
  ```
140
146
 
141
- Ruby Next only refines core classes if necessary; thus, this line wouldn't have any effect in the edge Ruby.
147
+ Ruby Next only refines core classes if necessary; thus, this line wouldn't have any effect in edge Ruby.
142
148
 
143
- **NOTE:** Even if the runtime already contains a monkey-patch with the backported functionality, we consider the method as _dirty_ and activate the refinement for it. Thus, you always have predictable behaviour. That's why we recommend using refinements for gems development.
149
+ **NOTE:** Even if the runtime already contains a monkey-patch with the backported functionality, we consider the method as _dirty_ and activate the refinement for it. Thus, you always have predictable behaviour. That's why we recommend using refinements for gem development.
144
150
 
145
151
  Alternatively, you can go with monkey-patches. Just add this line:
146
152
 
@@ -150,7 +156,7 @@ require "ruby-next/core_ext"
150
156
 
151
157
  The following _rule of thumb_ is recommended when choosing between refinements and monkey-patches:
152
158
 
153
- - Use refinements for libraries development (to avoid conflicts with others code)
159
+ - Use refinements for library development (to avoid conflicts with others' code)
154
160
  - 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 🙂)
155
161
  - Use core extensions if refinements are not supported by your platform
156
162
 
@@ -158,6 +164,16 @@ The following _rule of thumb_ is recommended when choosing between refinements a
158
164
 
159
165
  [**The list of supported APIs.**][features_core]
160
166
 
167
+ ### Data backport
168
+
169
+ Ruby 3.2 has introduced a new core class—[Data](https://bugs.ruby-lang.org/issues/16122). Ruby Next provides a backport functionality which is automatically activated when you `require "ruby-next"` **if and only if the constant is undefined**. If you want to use a custom backport, make sure you loaded it first.
170
+
171
+ If you want to opt-out from loading Data backport, you must set the `RUBY_NEXT_DISABLE_DATA` env variable to `true`.
172
+
173
+ #### Known limitations
174
+
175
+ Currently, passing Hash as a last positional argument to `Data.new` is not supported in Ruby <3.0 (due to the difference in keyword arguments handling). We recommend always using keyword arguments when initializing Data objects.
176
+
161
177
  ## Transpiling
162
178
 
163
179
  Ruby Next allows you to transpile\* edge Ruby syntax to older versions.
@@ -170,10 +186,10 @@ Installation:
170
186
 
171
187
  ```ruby
172
188
  # Gemfile
173
- gem "ruby-next"
189
+ gem "ruby-next", "~> 1.0"
174
190
 
175
191
  # gemspec
176
- spec.add_dependency "ruby-next"
192
+ spec.add_dependency "ruby-next", "~> 1.0"
177
193
  ```
178
194
 
179
195
  ```sh
@@ -185,21 +201,11 @@ gem install ruby-next
185
201
 
186
202
  ### Transpiler modes
187
203
 
188
- Ruby Next currently provides two different modes of generating transpiled code: _AST_ and _rewrite_.
189
-
190
- In the AST mode, we parse the source code into AST, modifies this AST and **generate a new code from AST** (using [unparser][unparser]). Thus, the transpiled code being identical in terms of functionality has different formatting.
191
-
192
- In the rewrite mode, we apply changes to the source code itself, thus, keeping the original formatting of the unaffected code (in a similar way to RuboCop's autocorrect feature).
204
+ Since v1.0, Ruby Next only support the _rewrite_ mode, i.e., the code transformations are applied directly to the original source code. This allows us to keep formatting as close as possible to the original code.
193
205
 
194
206
  The main benefit of the rewrite mode is that it preserves the original code line numbers and layout, which is especially useful in debugging.
195
207
 
196
- By default, we use the rewrite mode. If you found a bug with rewrite mode which is not reproducible in the AST mode, please, let us know.
197
-
198
- You can change the transpiler mode:
199
-
200
- - From code by setting `RubyNext::Language.mode = :ast` or `RubyNext::Language.mode = :rewrite`.
201
- - Via environmental variable `RUBY_NEXT_TRANSPILE_MODE=ast`.
202
- - Via CLI option ([see below](#cli)).
208
+ The legacy AST mode (regenerating source code from the modified abstract syntax tree) is deprecated (though still supported).
203
209
 
204
210
  ## CLI
205
211
 
@@ -217,12 +223,13 @@ Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
217
223
  -o, --output=OUTPUT Specify output directory or file or stdout
218
224
  --min-version=VERSION Specify the minimum Ruby version to support
219
225
  --single-version Only create one version of a file (for the earliest Ruby version)
226
+ --overwrite Overwrites the original file with one version of --single-version (works only with --single-version or --rewrite)
220
227
  --edge Enable edge (master) Ruby features
221
228
  --proposed Enable proposed/experimental Ruby features
222
- --transpile-mode=MODE Transpiler mode (ast or rewrite). Default: ast
223
229
  --[no-]refine Do not inject `using RubyNext`
224
230
  --list-rewriters List available rewriters
225
231
  --rewrite=REWRITERS... Specify particular Ruby features to rewrite
232
+ --import-rewriter=PATHS... Specify the paths to custom rewriters to load
226
233
  -h, --help Print help
227
234
  -V Turn on verbose mode
228
235
  --dry-run Print verbose output without generating files
@@ -230,7 +237,7 @@ Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
230
237
 
231
238
  The behaviour depends on whether you transpile a single file or a directory:
232
239
 
233
- - When transpiling a directory, the `.rbnext` subfolder is created within the target folder with subfolders for each supported Ruby versions (e.g., `.rbnext/2.6`, `.rbnext/2.7`, `.rbnext/3.0`, etc.). If you want to create only a single version (the smallest), you can also pass `--single-version` flag. In that case, no version directory is created (i.e., transpiled files go into `.rbnext`).
240
+ - When transpiling a directory, the `.rbnext` subfolder is created within the target folder with subfolders for each supported Ruby versions (e.g., `.rbnext/2.7`, `.rbnext/3.1`, `.rbnext/3.4`, etc.). If you want to create only a single version (the smallest), you can also pass `--single-version` flag. In that case, no version directory is created (i.e., transpiled files go into `.rbnext`).
234
241
 
235
242
  - When transpiling a file and providing the output path as a _file_ path, only a single version is created. For example:
236
243
 
@@ -296,10 +303,12 @@ Configuration file is a YAML with commands as keys and options as multiline stri
296
303
  # ./.rbnextrc
297
304
 
298
305
  nextify: |
299
- --transpiler-mode=rewrite
306
+ --min-version=2.7
300
307
  --edge
301
308
  ```
302
309
 
310
+ **NOTE:** The `nextify` section is also used by auto-transpiling when installing the gem from source and by runtime transpiling.
311
+
303
312
  ## Integrating into a gem development
304
313
 
305
314
  We recommend _pre-transpiling_ source code to work with older versions before releasing it.
@@ -359,19 +368,21 @@ This feature, _auto-transpiling_, is **disabled** by default (will likely be ena
359
368
 
360
369
  It is also possible to transpile Ruby source code in run-time via Ruby Next.
361
370
 
362
- All you need is to `require "ruby-next/language/runtime"` as early as possible to hijack `Kernel#require` and friends.
363
- You can also automatically inject `using RubyNext` to every\* loaded file by also adding `require "ruby-next/core/runtime"`.
371
+ All you need is to `require "ruby-next/language/runtime"` to hijack `Kernel#require` and friends before loading the files you want to transpile. You can also automatically inject `using RubyNext` to every\* loaded file by also adding `require "ruby-next/core/runtime"`.
364
372
 
365
- Since the runtime mode requires Kernel monkey-patching, it should be used carefully. For example, we use it in Ruby Next tests—works perfectly. But think twice before enabling it in production.
373
+ Runtime mode is backed by [require-hooks][require-hooks]—a standalone gem which has been extracted from Ruby Next. Depending on the current runtime, it picks an optimal strategy for hijacking the loading mechanism. Please, refer to its documentation for more details.
366
374
 
367
- Consider using [Bootsnap](#using-with-bootsnap) integration, 'cause its monkey-patching has been bullet-proofed 😉.
375
+ \* Ruby Next doesn't hijack every required file but only the configured directories: `./app/`, `./lib/`, `./spec/`, `./test/` (relative to the `pwd`). It also excludes the `./vendor/bundle` directory by default.
368
376
 
369
- \* Ruby Next doesn't hijack every required file but _watches_ only the configured directories: `./app/`, `./lib/`, `./spec/`, `./test/` (relative to the `pwd`). You can configure the watch dirs:
377
+ You can customize target files via the `include_patterns` and `exclude_patterns` configuration options:
370
378
 
371
379
  ```ruby
372
- RubyNext::Language.watch_dirs << "path/to/other/dir"
380
+ RubyNext::Language.include_patterns << "path/to/other/dir/*.rb"
381
+ RubyNext::Language.exclude_patterns << "path/to/other/dir/subdir/*"
373
382
  ```
374
383
 
384
+ **NOTE:** Directories MUST be configured before requiring `ruby-next/language/runtime`.
385
+
375
386
  ### Eval & similar
376
387
 
377
388
  By default, we do not hijack `Kernel.eval` and similar methods due to some limitations (e.g., there is no easy and efficient way to access the caller's scope, or _binding_, and some evaluations relies on local variables).
@@ -382,20 +393,6 @@ If you want to support transpiling in `eval`-like methods, opt-in explicitly by
382
393
  using RubyNext::Language::Eval
383
394
  ```
384
395
 
385
- ## Using with Bootsnap
386
-
387
- [Bootsnap][] is a great tool to speed-up your application load and it's included into the default Rails Gemfile. It patches Ruby mechanism of loading source files to make it possible to cache the intermediate representation (_iseq_).
388
-
389
- Ruby Next provides a specific integration which allows to add a transpiling step to this process, thus making the transpiler overhead as small as possible, because the cached and **already transpiled** version is used if no changes were made.
390
-
391
- To enable this integration, add the following line after the `require "bootsnap/setup"`:
392
-
393
- ```ruby
394
- require "ruby-next/language/bootsnap"
395
- ```
396
-
397
- **NOTE:** There is no way to invalidate the cache when you upgrade Ruby Next (e.g., due to the bug fixes), so you should do this manually.
398
-
399
396
  ## `uby-next`
400
397
 
401
398
  _This is [not a typo](https://github.com/ruby-next/ruby-next/pull/8), that’s the way `ruby -ruby-next` works: it’s equal to `ruby -r uby-next`, and [`uby-next.rb`](https://github.com/ruby-next/ruby-next/blob/master/lib/uby-next.rb) is a special file that activates the runtime mode._
@@ -454,9 +451,41 @@ AllCops:
454
451
 
455
452
  **NOTE:** you need `ruby-next` gem available in the environment where you run RuboCop (having `ruby-next-core` is not enough).
456
453
 
454
+ ## IRB
455
+
456
+ Ruby Next supports IRB. In order to enable edge Ruby features for your REPL, add the following line to your `.irbrc`:
457
+
458
+ ```ruby
459
+ require "ruby-next/irb"
460
+ ```
461
+
462
+ Alternatively, you can require it at startup:
463
+
464
+ ```sh
465
+ irb -r ruby-next/irb
466
+ # or
467
+ irb -ruby-next/irb
468
+ ```
469
+
470
+ ## Pry
471
+
472
+ Ruby Next supports Pry. In order to enable edge Ruby features for your REPL, add the following line to your `.pryrc`:
473
+
474
+ ```ruby
475
+ require "ruby-next/pry"
476
+ ```
477
+
478
+ Alternatively, you can require it at startup:
479
+
480
+ ```sh
481
+ pry -r ruby-next/pry
482
+ # or
483
+ pry -ruby-next/pry
484
+ ```
485
+
457
486
  ## Using with EOL Rubies
458
487
 
459
- We currently provide support for Ruby 2.2, 2.3 and 2.4.
488
+ We currently provide support for Ruby 2.3+.
460
489
 
461
490
  **NOTE:** By "support" here we mean using `ruby-next` CLI and runtime transpiling. Transpiled code may run on Ruby 2.0+.
462
491
 
@@ -510,9 +539,9 @@ These features are disabled by default, you must opt-in in one of the following
510
539
  # It's important to load language module first
511
540
  require "ruby-next/language"
512
541
 
513
- require "ruby-next/language/edge"
542
+ require "ruby-next/language/rewriters/edge"
514
543
  # or
515
- require "ruby-next/language/proposed"
544
+ require "ruby-next/language/rewriters/proposed"
516
545
 
517
546
  # and then activate the runtime mode
518
547
  require "ruby-next/language/runtime"
@@ -523,13 +552,85 @@ require "ruby-next/language/runtime"
523
552
 
524
553
  ### Supported edge features
525
554
 
526
- It's too early, Ruby 3.1 has just been released. See its features in the [supported features list](./SUPPORTED_FEATURES.md).
555
+ - Implicit `it` block parameter ([#19890](https://bugs.ruby-lang.org/issues/18980)).
527
556
 
528
557
  ### Supported proposed features
529
558
 
530
559
  - _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
531
560
  - Binding non-local variables in pattern matching (`42 => @v`) ([#18408](https://bugs.ruby-lang.org/issues/18408)).
532
561
 
562
+ ## Custom syntax rewriters
563
+
564
+ Wonder what would happen if Ruby get a null coalescing operator (`??=`) or some other syntactic feature you want to try out? Ruby Next is here to help you!
565
+
566
+ Ruby Next allows you to write your own syntax rewriters. Full-featured rewriters (used by Ruby Next itself) operate on AST and usually require parser modifications. However, we also support text-based rewriters which can be used to experiment with new syntax much quicker without dealing with grammars, parsers and syntax trees.
567
+
568
+ To implement a text-based rewriter, you need to create a new class inherited from `RubyNext::Language::Rewriters::Text` and implementing either `#rewrite` or `#safe_rewrite` method. For example, the method reference operator (`.:`) could be implemented as follows:
569
+
570
+ ```ruby
571
+ class MethodReferenceRewriter < RubyNext::Language::Rewriters::Text
572
+ # Rewriter configuration includes its name, a syntax probe and a minimum supported Ruby version.
573
+ # The latter two are used to determine whether the rewriter should be activated for the current file in runtime or when running `ruby-next nextify`.
574
+ NAME = "method-reference"
575
+ SYNTAX_PROBE = "Language.:transform"
576
+ MIN_SUPPORTED_VERSION = Gem::Version.new(RubyNext::NEXT_VERSION)
577
+
578
+ def safe_rewrite(source)
579
+ source.gsub(/\.:([\w_]+)/) do |match|
580
+ context.track! self
581
+
582
+ ".method(:#{$1})"
583
+ end
584
+ end
585
+ end
586
+
587
+ # Add the rewriter to the list of rewriters
588
+ RubyNext::Language.rewriters << MethodReferenceRewriter
589
+ ```
590
+
591
+ The `context` object is responsible for tracking if the rewriter was used for the current file. You must call the `context.track!` method to mark the file as _dirty_ (i.e., it should be transpiled). The input parameter (`source`) is the Ruby source code of the file being transpiled and the output must be the transpiled source code.
592
+
593
+ The `#safe_rewrite` method operates on the normalized source code (i.e., without comments and string literals). It's useful when you want to avoid transpiling inside strings or comments. If you want to transpile the original contents, you can use the `#rewrite` method instead.
594
+
595
+ Under the hood, `#safe_rewrite` uses [Paco][] to parse the source and separate string literals from the rest of the code. You can also leverage [Paco][] in your text rewriters, if you want more control on the parsing process. For better experience, we provide a DSL to define a custom parser and the `#parse` method to use it. Here is an example of implementing the `.:` operator using a Paco parser:
596
+
597
+ ```ruby
598
+ class MethodReferenceRewriter < RubyNext::Language::Rewriters::Text
599
+ NAME = "method-reference"
600
+ SYNTAX_PROBE = "Language.:transform"
601
+
602
+ parser do
603
+ def default
604
+ many(
605
+ alt(
606
+ method_ref,
607
+ any_char
608
+ )
609
+ )
610
+ end
611
+
612
+ def method_ref
613
+ seq(
614
+ string(".:").result(""),
615
+ method_name
616
+ # IMPORTANT: Use `#track!` method to mark the file as dirty
617
+ ).fmap { track! }.fmap { ".method(:#{_1})" }
618
+ end
619
+
620
+ def method_name = regexp(/[\w_]+/)
621
+ end
622
+
623
+ def safe_rewrite(source)
624
+ parse(source).join
625
+ end
626
+ end
627
+
628
+ # Add the rewriter to the list of rewriters
629
+ RubyNext::Language.rewriters << MethodReferenceRewriter
630
+ ```
631
+
632
+ When using the `ruby-next nextify` command, you can load custom rewriters via the `--import-rewriter` option.
633
+
533
634
  ## Known limitations
534
635
 
535
636
  Ruby Next aims to be _reasonably compatible_ with MRI. That means, some edge cases could be uncovered. Below is the list of known limitations.
@@ -562,6 +663,10 @@ Bug reports and pull requests are welcome on GitHub at [https://github.com/ruby-
562
663
 
563
664
  See also the [development guide](./DEVELOPMENT.md).
564
665
 
666
+ ## Acknowledgments
667
+
668
+ - Thanks to [Jim Gay](https://github.com/saturnflyer) for the original Data polyfill implementation ([polyfill-data](https://github.com/saturnflyer/polyfill-data))
669
+
565
670
  ## License
566
671
 
567
672
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -579,6 +684,8 @@ The gem is available as open source under the terms of the [MIT License](https:/
579
684
  [parser]: https://github.com/whitequark/parser
580
685
  [unparser]: https://github.com/mbj/unparser
581
686
  [next_parser]: https://github.com/ruby-next/parser
582
- [Bootsnap]: https://github.com/Shopify/bootsnap
583
687
  [rubocop]: https://github.com/rubocop-hq/rubocop
584
688
  [backports]: https://github.com/marcandre/backports
689
+ [require-hooks]: https://github.com/ruby-next/require-hooks
690
+ [Natalie]: https://natalie-lang.org
691
+ [Paco]: https://github.com/ruby-next/paco
data/bin/mspec ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless File.directory?('mspec/lib')
4
+ $stdout.puts "Cloning mspec..."
5
+ system("git clone https://github.com/ruby/mspec.git mspec")
6
+ end
7
+
8
+ $:.unshift File.expand_path(File.join(__dir__, '..', 'mspec', 'lib'))
9
+
10
+ require 'mspec/commands/mspec'
11
+ MSpecMain.main(false)