ruby2js 3.5.0 → 3.6.1

Sign up to get free protection for your applications and to get access to all the features.
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
  ---