ruby2js 3.5.0 → 3.6.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cdffdc5d823c8e1c36abf924de9f8a489508a5c472b5ac0dd11c42f14dfa7e1
4
- data.tar.gz: bfa2a41e9865fef31f7502ab1027f5486103434050aec92d3fe4d6044d165d52
3
+ metadata.gz: 11ab4e0003f0ff4f5821c00d5e282dead1201f97d4a2cc9ffb5c1188687f6079
4
+ data.tar.gz: fe572bef6b5ee55d46d72bb71dfcca5793fff8dd29a44243da637a7b91264bf7
5
5
  SHA512:
6
- metadata.gz: ce19f04471f515a27d5cd13fb8beea734fb88246efe03a7ac746e29b07a96696afc5751de9d5f73f0d81d761de7bd8d853197e7d0ce7ce68eab0796524f2b222
7
- data.tar.gz: 70080cf83c65cfe8d8c6aaa6067ae191df714dfe838977e5be50902624f4e516a680ee0459fcdd8d7274834e29841df5ec592e6ee31dfe990dea305f457c1707
6
+ metadata.gz: f865febd41bda90440a03b5ccdb7ac921eb794701c61d120b28a850d03001ee95ae0403bd50123d96c0f85b5e3a0e672066fcde7da68216c28f6f5895b2f9398
7
+ data.tar.gz: cf4811376c6daf78e951f688b6b2ee78e1b627135ed365e7197a6a318601b8b6313d2c4123031fd395cf5342707914e56bc29d946e15dc8f1dc7d53d35b053c6
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Ruby2js
1
+ Ruby2JS
2
2
  =======
3
3
 
4
4
  Minimal yet extensible Ruby to JavaScript conversion.
@@ -7,62 +7,14 @@ Minimal yet extensible Ruby to JavaScript conversion.
7
7
  [![Gem Version](https://badge.fury.io/rb/ruby2js.svg)](https://badge.fury.io/rb/ruby2js)
8
8
  [![Gitter](https://badges.gitter.im/ruby2js/community.svg)](https://gitter.im/ruby2js/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
9
9
 
10
- Description
11
- ---
12
-
13
- The base package maps Ruby syntax to JavaScript semantics.
14
- For example:
15
-
16
- * a Ruby Hash literal becomes a JavaScript Object literal
17
- * Ruby symbols become JavaScript strings.
18
- * Ruby method calls become JavaScript function calls IF
19
- there are either one or more arguments passed OR
20
- parenthesis are used
21
- * otherwise Ruby method calls become JavaScript property accesses.
22
- * by default, methods and procs return `undefined`
23
- * splats mapped to spread syntax when ES2015 or later is selected, and
24
- to equivalents using `apply`, `concat`, `slice`, and `arguments` otherwise.
25
- * ruby string interpolation is expanded into string + operations
26
- * `and` and `or` become `&&` and `||`
27
- * `a ** b` becomes `Math.pow(a,b)`
28
- * `<< a` becomes `.push(a)`
29
- * `unless` becomes `if !`
30
- * `until` becomes `while !`
31
- * `case` and `when` becomes `switch` and `case`
32
- * ruby for loops become js for loops
33
- * `(1...4).step(2){` becomes `for (var i = 1; i < 4; i += 2) {`
34
- * `x.forEach { next }` becomes `x.forEach(function() {return})`
35
- * `lambda {}` and `proc {}` becomes `function() {}`
36
- * `class Person; end` becomes `function Person() {}`
37
- * instance methods become prototype methods
38
- * instance variables become underscored, `@name` becomes `this._name`
39
- * self is assigned to this is if used
40
- * Any block becomes and explicit argument `new Promise do; y(); end` becomes `new Promise(function() {y()})`
41
- * regular expressions are mapped to js
42
- * `raise` becomes `throw`
43
- * expressions enclosed in backtick operators (\`\`) and `%x{}` literals are
44
- evaluated in the context of the caller and the results are inserted
45
- into the generated JavaScript.
46
10
 
47
- Ruby attribute accessors, methods defined with no parameters and no
48
- parenthesis, as well as setter method definitions, are
49
- mapped to
50
- [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2FdefineProperty),
51
- so avoid these if you wish to target users running IE8 or lower.
11
+ Documentation
12
+ ---
52
13
 
53
- While both Ruby and JavaScript have open classes, Ruby unifies the syntax for
54
- defining and extending an existing class, whereas JavaScript does not. This
55
- means that Ruby2JS needs to be told when a class is being extended, which is
56
- done by prepending the `class` keyword with two plus signs, thus:
57
- `++class C; ...; end`.
14
+ * Visit **[ruby2js.com](https://www.ruby2js.com)** for detailed setup instructions and API reference.
58
15
 
59
- Filters may be provided to add Ruby-specific or framework specific
60
- behavior. Filters are essentially macro facilities that operate on
61
- an AST representation of the code.
16
+ * [Try Ruby2JS online](https://whimsy.apache.org/ruby2js)
62
17
 
63
- See
64
- [notimplemented_spec](https://github.com/rubys/ruby2js/blob/master/spec/notimplemented_spec.rb)
65
- for a list of Ruby features _known_ to be not implemented.
66
18
 
67
19
  Synopsis
68
20
  ---
@@ -81,621 +33,24 @@ require 'ruby2js/filter/functions'
81
33
  puts Ruby2JS.convert('"2A".to_i(16)')
82
34
  ```
83
35
 
84
- Enable ES2015 support:
85
-
86
- ```ruby
87
- puts Ruby2JS.convert('"#{a}"', eslevel: 2015)
88
- ```
89
-
90
- Enable strict support:
91
-
92
- ```ruby
93
- puts Ruby2JS.convert('a=1', strict: true)
94
- ```
95
-
96
- Emit strict equality comparisons:
97
-
98
- ```ruby
99
- puts Ruby2JS.convert('a==1', comparison: :identity)
100
- ```
101
-
102
- Emit nullish coalescing operators:
103
-
104
- ```ruby
105
- puts Ruby2JS.convert('a || 1', or: :nullish)
106
- ```
107
-
108
- Emit underscored private fields (allowing subclass access):
109
-
110
- ```ruby
111
- puts Ruby2JS.convert('class C; def initialize; @f=1; end; end',
112
- eslevel: 2020, underscored_private: true)
113
- ```
114
-
115
- With [ExecJS](https://github.com/sstephenson/execjs):
116
- ```ruby
117
- require 'ruby2js/execjs'
118
- require 'date'
119
-
120
- context = Ruby2JS.compile(Date.today.strftime('d = new Date(%Y, %-m-1, %-d)'))
121
- puts context.eval('d.getYear()')+1900
122
- ```
123
-
124
- Conversions can be explored interactively using the
125
- [demo](https://github.com/rubys/ruby2js/blob/master/demo/ruby2js.rb) provided.
126
-
127
- Introduction
128
- ---
129
-
130
- JavaScript is a language where `0` is considered `false`, strings are
131
- immutable, and the behaviors for operators like `==` are, at best,
132
- [convoluted](http://zero.milosz.ca/).
133
-
134
- Any attempt to bridge the semantics of Ruby and JavaScript will involve
135
- trade-offs. Consider the following expression:
36
+ Host variable substitution:
136
37
 
137
38
  ```ruby
138
- a[-1]
39
+ puts Ruby2JS.convert("@name", ivars: {:@name => "Joe"})
139
40
  ```
140
41
 
141
- Programmers who are familiar with Ruby will recognize that this returns the
142
- last element (or character) of an array (or string). However, the meaning is
143
- quite different if `a` is a Hash.
144
-
145
- One way to resolve this is to change the way indexing operators are evaluated,
146
- and to provide a runtime library that adds properties to global JavaScript
147
- objects to handle this. This is the approach that [Opal](http://opalrb.com/)
148
- takes. It is a fine approach, with a number of benefits. It also has some
149
- notable drawbacks. For example,
150
- [readability](http://opalrb.com/try/#code:a%20%3D%20%22abc%22%3B%20puts%20a[-1])
151
- and
152
- [compatibility with other frameworks](https://github.com/opal/opal/issues/400).
153
-
154
- Another approach is to simply accept JavaScript semantics for what they are.
155
- This would mean that negative indexes would return `undefined` for arrays
156
- and strings. This is the base approach provided by ruby2js.
157
-
158
- A third approach would be to do static transformations on the source in order
159
- to address common usage patterns or idioms. These transformations can even be
160
- occasionally unsafe, as long as the transformations themselves are opt-in.
161
- ruby2js provides a number of such filters, including one that handles negative
162
- indexes when passed as a literal. As indicated above, this is unsafe in that
163
- it will do the wrong thing when it encounters a hash index which is expressed
164
- as a literal constant negative one. My experience is that such is rare enough
165
- to be safely ignored, but YMMV. More troublesome, this also won’t work when
166
- the index is not a literal (e.g., `a[n]`) and the index happens to be
167
- negative at runtime.
168
-
169
- This quickly gets into gray areas. `each` in Ruby is a common method that
170
- facilitates iteration over arrays. `forEach` is the JavaScript equivalent.
171
- Mapping this is fine until you start using a framework like jQuery which
172
- provides a function named [each](http://api.jquery.com/jQuery.each/).
173
-
174
- Fortunately, Ruby provides `?` and `!` as legal suffixes for method names,
175
- Ruby2js filters do an exact match, so if you select a filter that maps `each`
176
- to `forEach`, `each!` will pass through the filter. The final code that emits
177
- JavaScript function calls and parameter accesses will strip off these
178
- suffixes.
179
-
180
- This approach works well if it is an occasional change, but if the usage is
181
- pervasive, most filters support options to `exclude` a list of mappings,
182
- for example:
183
-
42
+ Host expression evalution -- potentially unsafe, use only if you trust
43
+ the source being converted::
184
44
  ```ruby
185
- puts Ruby2JS.convert('jQuery("li").each {|index| ...}', exclude: :each)
45
+ i = 7; puts Ruby2JS.convert("i = `i`", binding: binding)
186
46
  ```
187
47
 
188
- Alternatively, you can change the default:
48
+ Enable ES2015 support:
189
49
 
190
50
  ```ruby
191
- Ruby2JS::Filter.exclude :each
51
+ puts Ruby2JS.convert('"#{a}"', eslevel: 2015)
192
52
  ```
193
53
 
194
- Static transformations and runtime libraries aren't aren’t mutually exclusive.
195
- With enough of each, one could reproduce any functionality desired. Just be
196
- forewarned, that implementing a function like `method_missing` would require a
197
- _lot_ of work.
198
-
199
- Integrations
200
- ---
201
-
202
- While this is a low level library suitable for DIY integration, one of the
203
- obvious uses of a tool that produces JavaScript is by web servers. Ruby2JS
204
- includes several integrations:
205
-
206
- * [CGI](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/cgi.rb)
207
- * [Sinatra](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/sinatra.rb)
208
- * [Rails](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/rails.rb)
209
- * [Haml](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/haml.rb)
210
-
211
- As you might expect, CGI is a bit sluggish. By contrast, Sinatra and Rails
212
- are quite speedy as the bulk of the time is spent on the initial load of the
213
- required libraries.
214
-
215
- For easy integration with Webpack (and Webpacker in Rails 5+), you can use the
216
- [rb2js-loader](https://github.com/whitefusionhq/rb2js-loader) plugin.
217
-
218
- Filters
219
- ---
220
-
221
- In general, making use of a filter is as simple as requiring it. If multiple
222
- filters are selected, they will all be applied in parallel in one pass through
223
- the script.
224
-
225
- * <a id="return" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/return.rb">return</a>
226
- adds `return` to the last expression in functions.
227
-
228
- * <a id="require" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/require.rb">require</a>
229
- supports `require` and `require_relative` statements. Contents of files
230
- that are required are converted to JavaScript and expanded inline.
231
- `require` function calls in expressions are left alone.
232
-
233
- * <a id="camelCase" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/camelCase.rb">camelCase</a>
234
- converts `underscore_case` to `camelCase`. See
235
- [camelcase_spec](https://github.com/rubys/ruby2js/blob/master/spec/camelcase_spec.rb)
236
- for examples.
237
-
238
- * <a id="functions" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/functions.rb">functions</a>
239
-
240
- * `.all?` becomes `.every`
241
- * `.any?` becomes `.some`
242
- * `.chr` becomes `fromCharCode`
243
- * `.clear` becomes `.length = 0`
244
- * `.delete` becomes `delete target[arg]`
245
- * `.downcase` becomes `.toLowerCase`
246
- * `.each` becomes `.forEach`
247
- * `.each_key` becomes `for (i in ...) {}`
248
- * `.each_pair` becomes `for (var key in item) {var value = item[key]; ...}`
249
- * `.each_value` becomes `.forEach`
250
- * `.each_with_index` becomes `.forEach`
251
- * `.end_with?` becomes `.slice(-arg.length) == arg`
252
- * `.empty?` becomes `.length == 0`
253
- * `.find_index` becomes `findIndex`
254
- * `.first` becomes `[0]`
255
- * `.first(n)` becomes `.slice(0, n)`
256
- * `.gsub` becomes `replace(//g)`
257
- * `.include?` becomes `.indexOf() != -1`
258
- * `.inspect` becomes `JSON.stringify()`
259
- * `.keys()` becomes `Object.keys()`
260
- * `.last` becomes `[*.length-1]`
261
- * `.last(n)` becomes `.slice(*.length-1, *.length)`
262
- * `.lstrip` becomes `.replace(/^\s+/, "")`
263
- * `.max` becomes `Math.max.apply(Math)`
264
- * `.merge` becomes `Object.assign({}, ...)`
265
- * `.merge!` becomes `Object.assign()`
266
- * `.min` becomes `Math.min.apply(Math)`
267
- * `.nil?` becomes `== null`
268
- * `.ord` becomes `charCodeAt(0)`
269
- * `puts` becomes `console.log`
270
- * `.replace` becomes `.length = 0; ...push.apply(*)`
271
- * `.respond_to?` becomes `right in left`
272
- * `.rstrip` becomes `.replace(/s+$/, "")`
273
- * `.scan` becomes `.match(//g)`
274
- * `.start_with?` becomes `.substring(0, arg.length) == arg`
275
- * `.upto(lim)` becomes `for (var i=num; i<=lim; i+=1)`
276
- * `.downto(lim)` becomes `for (var i=num; i>=lim; i-=1)`
277
- * `.step(lim, n).each` becomes `for (var i=num; i<=lim; i+=n)`
278
- * `.step(lim, -n).each` becomes `for (var i=num; i>=lim; i-=n)`
279
- * `(0..a).to_a` becomes `Array.apply(null, {length: a}).map(Function.call, Number)`
280
- * `(b..a).to_a` becomes `Array.apply(null, {length: (a-b+1)}).map(Function.call, Number).map(function (idx) { return idx+b })`
281
- * `(b...a).to_a` becomes `Array.apply(null, {length: (a-b)}).map(Function.call, Number).map(function (idx) { return idx+b })`
282
- * `.strip` becomes `.trim`
283
- * `.sub` becomes `.replace`
284
- * `.tap {|n| n}` becomes `(function(n) {n; return n})(...)`
285
- * `.to_f` becomes `parseFloat`
286
- * `.to_i` becomes `parseInt`
287
- * `.to_s` becomes `.to_String`
288
- * `.upcase` becomes `.toUpperCase`
289
- * `.yield_self {|n| n}` becomes `(function(n) {return n})(...)`
290
- * `[-n]` becomes `[*.length-n]` for literal values of `n`
291
- * `[n...m]` becomes `.slice(n,m)`
292
- * `[n..m]` becomes `.slice(n,m+1)`
293
- * `[/r/, n]` becomes `.match(/r/)[n]`
294
- * `[/r/, n]=` becomes `.replace(/r/, ...)`
295
- * `(1..2).each {|i| ...}` becomes `for (var i=1 i<=2; i+=1)`
296
- * `"string" * length` becomes `new Array(length + 1).join("string")`
297
- * `.sub!` and `.gsub!` become equivalent `x = x.replace` statements
298
- * `.map!`, `.reverse!`, and `.select` become equivalent
299
- `.splice(0, .length, *.method())` statements
300
- * `@foo.call(args)` becomes `this._foo(args)`
301
- * `@@foo.call(args)` becomes `this.constructor._foo(args)`
302
- * `Array(x)` becomes `Array.prototype.slice.call(x)`
303
- * `delete x` becomes `delete x` (note lack of parenthesis)
304
- * `setInterval` and `setTimeout` allow block to be treated as the
305
- first parameter on the call
306
- * for the following methods, if the block consists entirely of a simple
307
- expression (or ends with one), a `return` is added prior to the
308
- expression: `sub`, `gsub`, `any?`, `all?`, `map`, `find`, `find_index`.
309
- * New classes subclassed off of `Exception` will become subclassed off
310
- of `Error` instead; and default constructors will be provided
311
- * `loop do...end` will be replaced with `while (true) {...}`
312
- * `raise Exception.new(...)` will be replaced with `throw new Error(...)`
313
- * `block_given?` will check for the presence of optional argument `_implicitBlockYield` which is a function made accessible through the use of `yield` in a method body.
314
-
315
- Additionally, there is one mapping that will only be done if explicitly
316
- included (pass `include: :class` as a `convert` option to enable):
317
-
318
- * `.class` becomes `.constructor`
319
-
320
- * <a id="tagged_templates" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/tagged_templates.rb">tagged_templates</a>
321
-
322
- Allows you to turn certain method calls with a string argument into tagged
323
- template literals. By default it supports html and css, so you can write
324
- `html "<div>#{1+2}</div>"` which converts to `` html`<div>${1+2}</div>` ``.
325
- Works nicely with squiggly heredocs for multi-line templates as well. If you
326
- need to configure the tag names yourself, pass a `template_literal_tags`
327
- option to `convert` with an array of tag name symbols.
328
-
329
- Note: these conversions are only done if eslevel >= 2015
330
-
331
- * <a href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/esm.rb">esm</a>
332
-
333
- Provides conversion of import and export statements for use with modern ES builders like Webpack.
334
-
335
- Examples:
336
-
337
- **import**
338
-
339
- ```ruby
340
- import "./index.scss"
341
- # => import "./index.scss"
342
-
343
- import Something from "./lib/something"
344
- # => import Something from "./lib/something"
345
-
346
- import Something, "./lib/something"
347
- # => import Something from "./lib/something"
348
-
349
- import [ LitElement, html, css ], from: "lit-element"
350
- # => import { LitElement, html, css } from "lit-element"
351
-
352
- import React, from: "react"
353
- # => import React from "react"
354
-
355
- import React, as: "*", from: "react"
356
- # => import React as * from "react"
357
- ```
358
-
359
- **export**
360
-
361
- ```ruby
362
- export hash = { ab: 123 }
363
- # => export const hash = {ab: 123};
364
-
365
- export func = ->(x) { x * 10 }
366
- # => export const func = x => x * 10;
367
-
368
- export def multiply(x, y)
369
- return x * y
370
- end
371
- # => export function multiply(x, y) {
372
- # return x * y
373
- # }
374
-
375
- export default class MyClass
376
- end
377
- # => export default class MyClass {
378
- # };
379
-
380
- # or final export statement:
381
- export [ one, two, default: three ]
382
- # => export { one, two, three as default }
383
- ```
384
-
385
- * <a id="node" href="https://github.com/rubys/ruby2js/blob/master/spec/node_spec.rb">node</a>
386
-
387
- * `` `command` `` becomes `child_process.execSync("command", {encoding: "utf8"})`
388
- * `ARGV` becomes `process.argv.slice(2)`
389
- * `__dir__` becomes `__dirname`
390
- * `Dir.chdir` becomes `process.chdir`
391
- * `Dir.entries` becomes `fs.readdirSync`
392
- * `Dir.mkdir` becomes `fs.mkdirSync`
393
- * `Dir.mktmpdir` becomes `fs.mkdtempSync`
394
- * `Dir.pwd` becomes `process.cwd`
395
- * `Dir.rmdir` becomes `fs.rmdirSync`
396
- * `ENV` becomes `process.env`
397
- * `__FILE__` becomes `__filename`
398
- * `File.chmod` becomes `fs.chmodSync`
399
- * `File.chown` becomes `fs.chownSync`
400
- * `File.cp` becomes `fs.copyFileSync`
401
- * `File.exist?` becomes `fs.existsSync`
402
- * `File.lchmod` becomes `fs.lchmodSync`
403
- * `File.link` becomes `fs.linkSync`
404
- * `File.ln` becomes `fs.linkSync`
405
- * `File.lstat` becomes `fs.lstatSync`
406
- * `File.read` becomes `fs.readFileSync`
407
- * `File.readlink` becomes `fs.readlinkSync`
408
- * `File.realpath` becomes `fs.realpathSync`
409
- * `File.rename` becomes `fs.renameSync`
410
- * `File.stat` becomes `fs.statSync`
411
- * `File.symlink` becomes `fs.symlinkSync`
412
- * `File.truncate` becomes `fs.truncateSync`
413
- * `File.unlink` becomes `fs.unlinkSync`
414
- * `FileUtils.cd` becomes `process.chdir`
415
- * `FileUtils.cp` becomes `fs.copyFileSync`
416
- * `FileUtils.ln` becomes `fs.linkSync`
417
- * `FileUtils.ln_s` becomes `fs.symlinkSync`
418
- * `FileUtils.mkdir` becomes `fs.mkdirSync`
419
- * `FileUtils.mv` becomes `fs.renameSync`
420
- * `FileUtils.pwd` becomes `process.cwd`
421
- * `FileUtils.rm` becomes `fs.unlinkSync`
422
- * `IO.read` becomes `fs.readFileSync`
423
- * `IO.write` becomes `fs.writeFileSync`
424
- * `system` becomes `child_process.execSync(..., {stdio: "inherit"})`
425
-
426
- * <a id="nokogiri" href="https://github.com/rubys/ruby2js/blob/master/spec/nokogiri.rb">nokogiri</a>
427
- * `add_child` becomes `appendChild`
428
- * `add_next_sibling` becomes `node.parentNode.insertBefore(sibling, node.nextSibling)`
429
- * `add_previous_sibling` becomes `node.parentNode.insertBefore(sibling, node)`
430
- * `after` becomes `node.parentNode.insertBefore(sibling, node.nextSibling)`
431
- * `at` becomes `querySelector`
432
- * `attr` becomes `getAttribute`
433
- * `attribute` becomes `getAttributeNode`
434
- * `before` becomes `node.parentNode.insertBefore(sibling, node)`
435
- * `cdata?` becomes `node.nodeType === Node.CDATA_SECTION_NODE`
436
- * `children` becomes `childNodes`
437
- * `comment?` becomes `node.nodeType === Node.COMMENT_NODE`
438
- * `content` becomes `textContent`
439
- * `create_element` becomes `createElement`
440
- * `document` becomes `ownerDocument`
441
- * `element?` becomes `node.nodeType === Node.ELEMENT_NODE`
442
- * `fragment?` becomes `node.nodeType === Node.FRAGMENT_NODE`
443
- * `get_attribute` becomes `getAttribute`
444
- * `has_attribute` becomes `hasAttribute`
445
- * `inner_html` becomes `innerHTML`
446
- * `key?` becomes `hasAttribute`
447
- * `name` becomes `nextSibling`
448
- * `next` becomes `nodeName`
449
- * `next=` becomes `node.parentNode.insertBefore(sibling,node.nextSibling)`
450
- * `next_element` becomes `nextElement`
451
- * `next_sibling` becomes `nextSibling`
452
- * `Nokogiri::HTML5` becomes `new JSDOM().window.document`
453
- * `Nokogiri::HTML5.parse` becomes `new JSDOM().window.document`
454
- * `Nokogiri::HTML` becomes `new JSDOM().window.document`
455
- * `Nokogiri::HTML.parse` becomes `new JSDOM().window.document`
456
- * `Nokogiri::XML::Node.new` becomes `document.createElement()`
457
- * `parent` becomes `parentNode`
458
- * `previous=` becomes `node.parentNode.insertBefore(sibling, node)`
459
- * `previous_element` becomes `previousElement`
460
- * `previous_sibling` becomes `previousSibling`
461
- * `processing_instruction?` becomes `node.nodeType === Node.PROCESSING_INSTRUCTION_NODE`
462
- * `remove_attribute` becomes `removeAttribute`
463
- * `root` becomes `documentElement`
464
- * `search` becomes `querySelectorAll`
465
- * `set_attribute` becomes `setAttribute`
466
- * `text?` becomes `node.nodeType === Node.TEXT_NODE`
467
- * `text` becomes `textContent`
468
- * `to_html` becomes `outerHTML`
469
-
470
- * <a id="underscore" href="https://github.com/rubys/ruby2js/blob/master/spec/underscore.rb">underscore</a>
471
-
472
- * `.clone()` becomes `_.clone()`
473
- * `.compact()` becomes `_.compact()`
474
- * `.count_by {}` becomes `_.countBy {}`
475
- * `.find {}` becomes `_.find {}`
476
- * `.find_by()` becomes `_.findWhere()`
477
- * `.flatten()` becomes `_.flatten()`
478
- * `.group_by {}` becomes `_.groupBy {}`
479
- * `.has_key?()` becomes `_.has()`
480
- * `.index_by {}` becomes `_.indexBy {}`
481
- * `.invert()` becomes `_.invert()`
482
- * `.invoke(&:n)` becomes `_.invoke(, :n)`
483
- * `.map(&:n)` becomes `_.pluck(, :n)`
484
- * `.merge!()` becomes `_.extend()`
485
- * `.merge()` becomes `_.extend({}, )`
486
- * `.reduce {}` becomes `_.reduce {}`
487
- * `.reduce()` becomes `_.reduce()`
488
- * `.reject {}` becomes `_.reject {}`
489
- * `.sample()` becomes `_.sample()`
490
- * `.select {}` becomes `_.select {}`
491
- * `.shuffle()` becomes `_.shuffle()`
492
- * `.size()` becomes `_.size()`
493
- * `.sort()` becomes `_.sort_by(, _.identity)`
494
- * `.sort_by {}` becomes `_.sortBy {}`
495
- * `.times {}` becomes `_.times {}`
496
- * `.values()` becomes `_.values()`
497
- * `.where()` becomes `_.where()`
498
- * `.zip()` becomes `_.zip()`
499
- * `(n...m)` becomes `_.range(n, m)`
500
- * `(n..m)` becomes `_.range(n, m+1)`
501
- * `.compact!`, `.flatten!`, `shuffle!`, `reject!`, `sort_by!`, and
502
- `.uniq` become equivalent `.splice(0, .length, *.method())` statements
503
- * for the following methods, if the block consists entirely of a simple
504
- expression (or ends with one), a `return` is added prior to the
505
- expression: `reduce`, `sort_by`, `group_by`, `index_by`, `count_by`,
506
- `find`, `select`, `reject`.
507
- * `is_a?` and `kind_of?` map to `Object.prototype.toString.call() ===
508
- "[object #{type}]" for the following types: `Arguments`, `Boolean`,
509
- `Date`, `Error`, `Function`, `Number`, `Object`, `RegExp`, `String`; and
510
- maps Ruby names to JavaScript equivalents for `Exception`, `Float`,
511
- `Hash`, `Proc`, and `Regexp`. Additionally, `is_a?` and `kind_of?` map
512
- to `Array.isArray()` for `Array`.
513
-
514
- * <a id="jquery" href="https://github.com/rubys/ruby2js/blob/master/spec/jquery.rb">jquery</a>
515
-
516
- * maps Ruby unary operator `~` to jQuery `$` function
517
- * maps Ruby attribute syntax to jquery attribute syntax
518
- * `.to_a` becomes `toArray`
519
- * maps `$$` to jQuery `$` function
520
- * defaults the fourth parameter of $$.post to `"json"`, allowing Ruby block
521
- syntax to be used for the success function.
522
-
523
- * <a id="minitest-jasmine" href="https://github.com/rubys/ruby2js/blob/master/spec/minitest-jasmine.rb">minitest-jasmine</a>
524
- * maps subclasses of `Minitest::Test` to `describe` calls
525
- * maps `test_` methods inside subclasses of `Minitest::Test` to `it` calls
526
- * maps `setup`, `teardown`, `before`, and `after` calls to `beforeEach`
527
- and `afterEach` calls
528
- * maps `assert` and `refute` calls to `expect`...`toBeTruthy()` and
529
- `toBeFalsy` calls
530
- * maps `assert_equal`, `refute_equal`, `.must_equal` and `.cant_equal`
531
- calls to `expect`...`toBe()` calls
532
- * maps `assert_in_delta`, `refute_in_delta`, `.must_be_within_delta`,
533
- `.must_be_close_to`, `.cant_be_within_delta`, and `.cant_be_close_to`
534
- calls to `expect`...`toBeCloseTo()` calls
535
- * maps `assert_includes`, `refute_includes`, `.must_include`, and
536
- `.cant_include` calls to `expect`...`toContain()` calls
537
- * maps `assert_match`, `refute_match`, `.must_match`, and `.cant_match`
538
- calls to `expect`...`toMatch()` calls
539
- * maps `assert_nil`, `refute_nil`, `.must_be_nil`, and `.cant_be_nill` calls
540
- to `expect`...`toBeNull()` calls
541
- * maps `assert_operator`, `refute_operator`, `.must_be`, and `.cant_be`
542
- calls to `expect`...`toBeGreaterThan()` or `toBeLessThan` calls
543
-
544
- * <a id="cjs" href="https://github.com/rubys/ruby2js/blob/master/spec/cjs">cjs</a>
545
- * maps `export def f` to `exports.f =`
546
- * maps `export async def f` to `exports.f = async`
547
- * maps `export v =` to `exports.v =`
548
- * maps `export default proc` to `module.exports =`
549
- * maps `export default async proc` to `module.exports = async`
550
- * maps `export default` to `module.exports =`
551
-
552
- * <a id="matchAll" href="https://github.com/rubys/ruby2js/blob/master/spec/matchAll">matchAll</a>
553
-
554
- For ES level < 2020:
555
-
556
- * maps `str.matchAll(pattern).forEach {}` to
557
- `while (match = pattern.exec(str)) {}`
558
-
559
- Note `pattern` must be a simple variable with a value of a regular
560
- expression with the `g` flag set at runtime.
561
-
562
- [Wunderbar](https://github.com/rubys/wunderbar) includes additional demos:
563
-
564
- * [chat](https://github.com/rubys/wunderbar/blob/master/demo/chat.rb),
565
- [diskusage](https://github.com/rubys/wunderbar/blob/master/demo/diskusage.rb),
566
- and [wiki](https://github.com/rubys/wunderbar/blob/master/demo/wiki.rb) make
567
- use of the jquery filter.
568
-
569
- ES2015 support
570
- ---
571
-
572
- When option `eslevel: 2015` is provided, the following additional
573
- conversions are made:
574
-
575
- * `"#{a}"` becomes <code>\`${a}\`</code>
576
- * `a = 1` becomes `let a = 1`
577
- * `A = 1` becomes `const A = 1`
578
- * `a, b = b, a` becomes `[a, b] = [b, a]`
579
- * `a, (foo, *bar) = x` becomes `let [a, [foo, ...bar]] = x`
580
- * `def f(a, (foo, *bar))` becomes `function f(a, [foo, ...bar])`
581
- * `def a(b=1)` becomes `function a(b=1)`
582
- * `def a(*b)` becomes `function a(...b)`
583
- * `.each_value` becomes `for (i of ...) {}`
584
- * `a(*b)` becomes `a(...b)`
585
- * `"#{a}"` becomes <code>\`${a}\`</code>
586
- * `lambda {|x| x}` becomes `(x) => {return x}`
587
- * `proc {|x| x}` becomes `(x) => {x}`
588
- * `a {|x|}` becomes `a((x) => {})`
589
- * `class Person; end` becomes `class Person {}`
590
- * `(0...a).to_a` becomes `[...Array(a).keys()]`
591
- * `(0..a).to_a` becomes `[...Array(a+1).keys()]`
592
- * `(b..a).to_a` becomes `Array.from({length: (a-b+1)}, (_, idx) => idx+b)`
593
-
594
- ES2015 class support includes constructors, super, methods, class methods,
595
- instance methods, instance variables, class variables, getters, setters,
596
- attr_accessor, attr_reader, attr_writer, etc.
597
-
598
- Additionally, the `functions` filter will provide the following conversion:
599
-
600
- * `Array(x)` becomes `Array.from(x)`
601
- * `.inject(n) {}` becomes `.reduce(() => {}, n)`
602
-
603
- Finally, keyword arguments and optional keyword arguments will be mapped to
604
- parameter detructuring.
605
-
606
- ES2016 support
607
- ---
608
-
609
- When option `eslevel: 2016` is provided, the following additional
610
- conversion is made:
611
-
612
- * `a ** b` becomes `a ** b`
613
- * `.include?` becomes `.includes`
614
-
615
- ES2017 support
616
- ---
617
-
618
- When option `eslevel: 2017` is provided, the following additional
619
- conversions are made by the `functions` filter:
620
-
621
- * `.values()` becomes `Object.values()`
622
- * `.entries()` becomes `Object.entries()`
623
- * `.each_pair {}` becomes `for (let [key, value] of Object.entries()) {}'
624
-
625
- async support:
626
-
627
- * `async def` becomes `async function`
628
- * `async lambda` becomes `async =>`
629
- * `async proc` becomes `async =>`
630
- * `async ->` becomes `async =>`
631
- * `foo bar, async do...end` becomes `foo(bar, async () => {})`
632
-
633
- ES2018 support
634
- ---
635
-
636
- When option `eslevel: 2018` is provided, the following additional
637
- conversion is made by the `functions` filter:
638
-
639
- * `.merge` becomes `{...a, ...b}`
640
-
641
- Additionally, rest arguments can now be used with keyword arguments and
642
- optional keyword arguments.
643
-
644
- ES2019 support
645
- ---
646
-
647
- When option `eslevel: 2019` is provided, the following additional
648
- conversion is made by the `functions` filter:
649
-
650
- * `.flatten` becomes `.flat(Infinity)`
651
- * `.lstrip` becomes `.trimEnd
652
- * `.rstrip` becomes `.trimStart
653
- * `a.to_h` becomes `Object.fromEntries(a)`
654
- * `Hash[a]` becomes `Object.fromEntries(a)`
655
-
656
- Additionally, `rescue` without a variable will map to `catch` without a
657
- variable.
658
-
659
- ES2020 support
660
- ---
661
-
662
- When option `eslevel: 2020` is provided, the following additional
663
- conversions are made:
664
-
665
- * `@x` becomes `this.#x`
666
- * `@@x` becomes `ClassName.#x`
667
- * `a&.b` becomes `a?.b`
668
- * `.scan` becomes `Array.from(str.matchAll(/.../g), s => s.slice(1))`
669
-
670
- ES2021 support
671
- ---
672
-
673
- When option `eslevel: 2021` is provided, the following additional
674
- conversions are made:
675
-
676
- * `x ||= 1` becomes `x ||= 1`
677
- * `x &&= 1` becomes `x &&= 1`
678
-
679
- Picking a Ruby to JS mapping tool
680
- ---
681
-
682
- > dsl — A domain specific language, where code is written in one language and
683
- > errors are given in another.
684
- > -- [Devil’s Dictionary of Programming](http://programmingisterrible.com/post/65781074112/devils-dictionary-of-programming)
685
-
686
- If you simply want to get a job done, and would like a mature and tested
687
- framework, and only use one of the many integrations that
688
- [Opal](http://opalrb.com/) provides, then Opal is the way to go right now.
689
-
690
- ruby2js is for those that want to produce JavaScript that looks like it
691
- wasn’t machine generated, and want the absolute bare minimum in terms of
692
- limitations as to what JavaScript can be produced.
693
-
694
- [Try](http://intertwingly.net/projects/ruby2js.cgi/all) for yourself.
695
- [Compare](http://opalrb.com/try/#code:).
696
-
697
- And, of course, the right solution might be to use
698
- [CoffeeScript](http://coffeescript.org/) instead.
699
54
 
700
55
  License
701
56
  ---