opal 0.8.0 → 0.8.1.rc1

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
  SHA1:
3
- metadata.gz: 04e770b579856185343a5694898109f4a3cda2b1
4
- data.tar.gz: 38d55302b728cd4268bab866932b47e49e7cb4df
3
+ metadata.gz: eef6788301853d340a407e416287b8f63e7283ce
4
+ data.tar.gz: 0b37cb315ce3c64aec0a036311325a81063ac126
5
5
  SHA512:
6
- metadata.gz: 942bad5e1f7d861f5aab1c8667c0d93fa7ea03de7650618184dc36b4c364d399a123e83ce5e1e25ec41593e3bc05a60ea73486200bc36ef207a4a8db8804df9d
7
- data.tar.gz: 3942264715c754c445657b378badbbe0550ddc49cb189da6aeae23f197efb49da63648481ef25cdc3a84168b110f63d4a08ab4daf3358f5a9a8dc336637c31ac
6
+ metadata.gz: 05109cf3b13c7f29b441c3869d1f6b8e87b1ca7525a23ab48433d31a6d5e8e320ddd7f3fdc5bc9f07c54467e646bed1646b894c0b17e2b929d3059e01ba43c74
7
+ data.tar.gz: caffd2d9b136be8f4d15778a904d199482465ee62d71a13c28740cb9bb68d622c4ce6c28d4a905a81928287ae4729b29e40d1b1d1bcaa0f1de8f80f100e2ad57
data/.gitignore CHANGED
@@ -12,3 +12,6 @@ build/
12
12
  .ruby-gemset
13
13
  .ruby-version
14
14
  .rvmrc
15
+
16
+ # Ignore the rubyspec dir from recent releases (0.9+)
17
+ /spec/rubyspec
@@ -1,3 +1,13 @@
1
+ ## 0.8.1
2
+
3
+ * Use official Sprockets processor cache keys API:
4
+ The old cache key hack has been removed.
5
+ Add `Opal::Processor.cache_key` and `Opal::Processor.reset_cache_key!` to
6
+ reset it as it’s cached but should change whenever `Opal::Config` changes.
7
+
8
+ * Fix an issue for which a Pathname was passed instead of a String to Sprockets.
9
+
10
+
1
11
  ## 0.8.0 2015-07-16
2
12
 
3
13
  * Update to Sprockets v3.0.
@@ -0,0 +1,425 @@
1
+ # Compiled Ruby Code
2
+
3
+ ## Generated Javascript
4
+
5
+ Opal is a source-to-source compiler, so there is no VM as such and the
6
+ compiled code aims to be as fast and efficient as possible, mapping
7
+ directly to underlying javascript features and objects where possible.
8
+
9
+ ### Literals
10
+
11
+ ```ruby
12
+ nil # => nil
13
+ true # => true
14
+ false # => false
15
+ self # => self
16
+ ```
17
+
18
+ **self** is mostly compiled to `this`. Methods and blocks are implemented
19
+ as javascript functions, so their `this` value will be the right
20
+ `self` value. Class bodies and the top level scope use a `self` variable
21
+ to improve readability.
22
+
23
+ **nil** is compiled to a `nil` javascript variable. `nil` is a real object
24
+ which allows methods to be called on it. Opal cannot send methods to `null`
25
+ or `undefined`, and they are considered bad values to be inside ruby code.
26
+
27
+ **true** and **false** are compiled directly into their native boolean
28
+ equivalents. This makes interaction a lot easier as there is no need
29
+ to convert values to opal specific values.
30
+
31
+ NOTE: Because `true` and `false` compile to their native
32
+ javascript equivalents, they must share the same class: `Boolean`.
33
+ For this reason, they do not belong to their respective `TrueClass`
34
+ and `FalseClass` classes from ruby.
35
+
36
+ #### Strings & Symbols
37
+
38
+ ```ruby
39
+ "hello world!" # => "hello world!"
40
+ :foo # => "foo"
41
+ <<-EOS # => "\nHello there.\n"
42
+ Hello there.
43
+ EOS
44
+ ```
45
+
46
+ Ruby strings are compiled directly into javascript strings for
47
+ performance as well as readability. This has the side effect that Opal
48
+ does not support mutable strings - i.e. all strings are immutable.
49
+
50
+ NOTE: Strings in Opal are immutable because they are compiled into regular javascript strings. This is done for performance reasons.
51
+
52
+ For performance reasons, symbols are also compiled directly into strings.
53
+ Opal supports all the symbol syntaxes, but does not have a real `Symbol`
54
+ class. Symbols and Strings can therefore be used interchangeably.
55
+
56
+ #### Numbers
57
+
58
+ In Opal there is a single class for numbers; `Numeric`. To keep opal
59
+ as performant as possible, ruby numbers are mapped to native numbers.
60
+ This has the side effect that all numbers must be of the same class.
61
+ Most relevant methods from `Integer`, `Float` and `Numeric` are
62
+ implemented on this class.
63
+
64
+ ```ruby
65
+ 42 # => 42
66
+ 3.142 # => 3.142
67
+ ```
68
+
69
+ #### Arrays
70
+
71
+ Ruby arrays are compiled directly into javascript arrays. Special
72
+ ruby syntaxes for word arrays etc are also supported.
73
+
74
+ ```ruby
75
+ [1, 2, 3, 4] # => [1, 2, 3, 4]
76
+ %w[foo bar baz] # => ["foo", "bar", "baz"]
77
+ ```
78
+
79
+ #### Hash
80
+
81
+ Inside a generated ruby script, a function `__hash` is available which
82
+ creates a new hash. This is also available in javascript as `Opal.hash`
83
+ and simply returns a new instance of the `Hash` class.
84
+
85
+ ```ruby
86
+ { :foo => 100, :baz => 700 } # => __hash("foo", 100, "baz", 700)
87
+ { foo: 42, bar: [1, 2, 3] } # => __hash("foo", 42, "bar", [1, 2, 3])
88
+ ```
89
+
90
+ #### Range
91
+
92
+ Similar to hash, there is a function `__range` available to create
93
+ range instances.
94
+
95
+ ```ruby
96
+ 1..4 # => __range(1, 4, true)
97
+ 3...7 # => __range(3, 7, false)
98
+ ```
99
+
100
+ ### Logic and conditionals
101
+
102
+ As per ruby, Opal treats only `false` and `nil` as falsy, everything
103
+ else is a truthy value including `""`, `0` and `[]`. This differs from
104
+ javascript as these values are also treated as false.
105
+
106
+ For this reason, most truthy tests must check if values are `false` or
107
+ `nil`.
108
+
109
+ Taking the following test:
110
+
111
+ ```ruby
112
+ val = 42
113
+
114
+ if val
115
+ return 3.142;
116
+ end
117
+ ```
118
+
119
+ This would be compiled into:
120
+
121
+ ```javascript
122
+ var val = 42;
123
+
124
+ if (val !== false && val !== nil) {
125
+ return 3.142;
126
+ }
127
+ ```
128
+
129
+ This makes the generated truthy tests (`if` statements, `and` checks and
130
+ `or` statements) a little more verbose in the generated code.
131
+
132
+ ### Instance variables
133
+
134
+ Instance variables in Opal work just as expected. When ivars are set or
135
+ retrieved on an object, they are set natively without the `@` prefix.
136
+ This allows real javascript identifiers to be used which is more
137
+ efficient then accessing variables by string name.
138
+
139
+ ```ruby
140
+ @foo = 200
141
+ @foo # => 200
142
+
143
+ @bar # => nil
144
+ ```
145
+
146
+ This gets compiled into:
147
+
148
+ ```javascript
149
+ this.foo = 200;
150
+ this.foo; // => 200
151
+
152
+ this.bar; // => nil
153
+ ```
154
+
155
+ NOTE: If an instance variable uses the same name as a reserved javascript keyword,
156
+ then the instance variable is wrapped using the object-key notation: `this['class']`.
157
+
158
+ ## Compiled Files
159
+
160
+ As described above, a compiled ruby source gets generated into a string
161
+ of javascript code that is wrapped inside an anonymous function. This
162
+ looks similar to the following:
163
+
164
+ ```javascript
165
+ (function($opal) {
166
+ var $klass = $opal.klass, self = $opal.top;
167
+ // generated code
168
+ })(Opal);
169
+ ```
170
+
171
+ As a complete example, assuming the following code:
172
+
173
+ ```ruby
174
+ puts "foo"
175
+ ```
176
+
177
+ This would compile directly into:
178
+
179
+ ```javascript
180
+ (function($opal) {
181
+ var $klass = $opal.klass, self = $opal.top;
182
+ self.$puts("foo");
183
+ })(Opal);
184
+ ```
185
+
186
+ Most of the helpers are no longer present as they are not used in this
187
+ example.
188
+
189
+ ### Using compiled sources
190
+
191
+ If you write the generated code as above into a file `app.js` and add
192
+ that to your HTML page, then it is obvious that `"foo"` would be
193
+ written to the browser's console.
194
+
195
+ ### Debugging and finding errors
196
+
197
+ Because Opal does not aim to be fully compatible with ruby, there are
198
+ some instances where things can break and it may not be entirely
199
+ obvious what went wrong.
200
+
201
+ ### Using javascript debuggers
202
+
203
+ As opal just generates javascript, it is useful to use a native
204
+ debugger to work through javascript code. To use a debugger, simply
205
+ add an x-string similar to the following at the place you wish to
206
+ debug:
207
+
208
+ ```ruby
209
+ # .. code
210
+ `debugger`
211
+ # .. more code
212
+ ```
213
+ The x-strings just pass the debugger statement straight through to the
214
+ javascript output.
215
+
216
+ NOTE: All local variables and method/block arguments also keep their ruby
217
+ names except in the rare cases when the name is reserved in javascript.
218
+ In these cases, a `$` suffix is added to the name
219
+ (e.g. `try` → `try$`).
220
+
221
+ ## Javascript from Ruby
222
+
223
+ Opal tries to interact as cleanly with javascript and its api as much
224
+ as possible. Ruby arrays, strings, numbers, regexps, blocks and booleans
225
+ are just javascript native equivalents. The only boxed core features are
226
+ hashes.
227
+
228
+
229
+ ### Inline Javascript
230
+
231
+ As most of the corelib deals with these low level details, opal provides
232
+ a special syntax for inlining javascript code. This is done with
233
+ x-strings or "backticks", as their ruby use has no useful translation
234
+ in the browser.
235
+
236
+ ```ruby
237
+ `window.title`
238
+ # => "Opal: Ruby to Javascript compiler"
239
+
240
+ %x{
241
+ console.log("opal version is:");
242
+ console.log(#{ RUBY_ENGINE_VERSION });
243
+ }
244
+
245
+ # => opal version is:
246
+ # => 0.6.0
247
+ ```
248
+
249
+ Even interpolations are supported, as seen here.
250
+
251
+ This feature of inlining code is used extensively, for example in
252
+ Array#length:
253
+
254
+ ```ruby
255
+ class Array
256
+ def length
257
+ `this.length`
258
+ end
259
+ end
260
+ ```
261
+
262
+ X-Strings also have the ability to automatically return their value,
263
+ as used by this example.
264
+
265
+
266
+ ### Native Module
267
+
268
+ _Reposted from: [Mikamayhem](http://dev.mikamai.com/post/79398725537/using-native-javascript-objects-from-opal)_
269
+
270
+ Opal standard lib (stdlib) includes a `Native` module, let’s see how it works and wrap `window`:
271
+
272
+ ```ruby
273
+ require 'native'
274
+
275
+ window = Native(`window`) # equivalent to Native::Object.new(`window`)
276
+ ```
277
+
278
+ Now what if we want to access one of its properties?
279
+
280
+ ```ruby
281
+ window[:location][:href] # => "http://dev.mikamai.com/"
282
+ window[:location][:href] = "http://mikamai.com/" # will bring you to mikamai.com
283
+ ```
284
+
285
+ And what about methods?
286
+
287
+ ```ruby
288
+ window.alert('hey there!')
289
+ ```
290
+
291
+ So let’s do something more interesting:
292
+
293
+ ```ruby
294
+ class << window
295
+ # A cross-browser window close method (works in IE!)
296
+ def close!
297
+ %x{
298
+ return (#@native.open('', '_self', '') && #@native.close()) ||
299
+ (#@native.opener = null && #@native.close()) ||
300
+ (#@native.opener = '' && #@native.close());
301
+ }
302
+ end
303
+
304
+ # let's assign href directly
305
+ def href= url
306
+ self[:location][:href] = url
307
+ end
308
+ end
309
+ ```
310
+
311
+ That’s all for now, bye!
312
+
313
+ ```ruby
314
+ window.close!
315
+ ```
316
+
317
+ ## Ruby from Javascript
318
+
319
+ Accessing classes and methods defined in Opal from the javascript runtime is
320
+ possible via the `Opal` js object. The following class:
321
+
322
+ ```ruby
323
+ class Foo
324
+ def bar
325
+ puts "called bar on class Foo defined in ruby code"
326
+ end
327
+ end
328
+ ```
329
+
330
+ Can be accessed from javascript like this:
331
+
332
+ ```javascript
333
+ Opal.Foo.$new().$bar();
334
+ // => "called bar on class Foo defined in ruby code"
335
+ ```
336
+
337
+ Remember that all ruby methods are prefixed with a `$`.
338
+
339
+ In the case that a method name can't be called directly due to a javascript syntax error, you will need to call the method using bracket notation. For example, you can call `foo.$merge(...)` but not `foo.$merge!(...)`, `bar.$fetch('somekey')` but not `bar.$[]('somekey')`. Instead you would write it like this: `foo['$merge!'](...)` or `bar['$[]']('somekey')`.
340
+
341
+
342
+ ### Hash
343
+
344
+ Since ruby hashes are implemented directly with an Opal class, there's no "toll-free" bridging available (unlike with strings and arrays, for example). However, it's quite possible to interact with hashes from Javascript:
345
+
346
+ ```javascript
347
+ var myHash = Opal.hash({a: 1, b: 2});
348
+ // output of $inspect: {"a"=>1, "b"=>2}
349
+ myHash.$store('a', 10);
350
+ // output of $inspect: {"a"=>10, "b"=>2}
351
+ myHash.$fetch('b','');
352
+ // 2
353
+ myHash.$fetch('z','');
354
+ // ""
355
+ myHash.$update(Opal.hash({b: 20, c: 30}));
356
+ // output of $inspect: {"a"=>10, "b"=>20, "c"=>30}
357
+ myHash.$to_n(); // provided by the Native module
358
+ // output: {"a": 10, "b": 20, "c": 30} aka a standard Javascript object
359
+ ```
360
+
361
+ NOTE: Be aware `Hash#to_n` produces a duplicate copy of the hash.
362
+
363
+ ## Advanced Compilation
364
+
365
+ ### Method Missing
366
+
367
+ Opal supports `method_missing`. This is a key feature of ruby, and opal wouldn't be much use without it! This page details the implementation of `method_missing` for Opal.
368
+
369
+ #### Method dispatches
370
+
371
+ Firstly, a ruby call `foo.bar 1, 2, 3` is compiled into the following javascript:
372
+
373
+ ```javascript
374
+ foo.$bar(1, 2, 3)
375
+ ```
376
+
377
+ This should be pretty easy to read. The `bar` method has a `$` prefix just to distinguish it from underlying javascript properties, as well as ruby ivars. Methods are compiled like this to make the generated code really readable.
378
+
379
+ #### Handling `method_missing`
380
+
381
+ Javascript does not have an equivalent of `method_missing`, so how do we handle it? If a function is missing in javascript, then a language level exception will be raised.
382
+
383
+ To get around this, we make use of our compiler. During parsing, we collect a list of all method calls made inside a ruby file, and this gives us a list of all possible method calls. We then add stub methods to the root object prototype (an opal object, not the global javascript Object) which will proxy our method missing calls for us.
384
+
385
+ For example, assume the following ruby script:
386
+
387
+ ```ruby
388
+ first 1, 2, 3
389
+ second "wow".to_sym
390
+ ```
391
+
392
+ After parsing, we know we only ever call 3 methods: `[:first, :second, :to_sym]`. So, imagine we could just add these 3 methods to `BasicObject` in ruby, we would get something like this:
393
+
394
+ ```ruby
395
+ class BasicObject
396
+ def first(*args, &block)
397
+ method_missing(:first, *args, &block)
398
+ end
399
+
400
+ def second(*args, &block)
401
+ method_missing(:second, *args, &block)
402
+ end
403
+
404
+ def to_sym(*args, &block)
405
+ method_missing(:to_sym, *args, &block)
406
+ end
407
+ end
408
+ ```
409
+
410
+ It is obvious from here, that unless an object defines any given method, it will always resort in a dispatch to `method_missing` from one of our defined stub methods. This is how we get `method_missing` in opal.
411
+
412
+ #### Optimising generated code
413
+
414
+ To optimise the generated code slightly, we reduce the code output from the compiler into the following javascript:
415
+
416
+ ```javascript
417
+ Opal.add_stubs(["first", "second", "to_sym"]);
418
+ ```
419
+
420
+ You will see this at the top of all your generated javascript files. This will add a stub method for all methods used in your file.
421
+
422
+ #### Alternative approaches
423
+
424
+ The old approach was to inline `method_missing` calls by checking for a method on **every method dispatch**. This is still supported via a parser option, but not recommended.
425
+