opal 0.3.19 → 0.3.20

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.
Files changed (66) hide show
  1. data/.gitignore +3 -1
  2. data/Gemfile +3 -2
  3. data/README.md +304 -48
  4. data/Rakefile +1 -2
  5. data/core/alpha.rb +2 -1
  6. data/core/array.rb +92 -96
  7. data/core/basic_object.rb +1 -10
  8. data/core/boolean.rb +6 -18
  9. data/core/class.rb +9 -10
  10. data/core/comparable.rb +1 -1
  11. data/core/enumerable.rb +11 -11
  12. data/core/enumerator.rb +2 -10
  13. data/core/error.rb +16 -31
  14. data/core/hash.rb +32 -36
  15. data/core/json.rb +50 -0
  16. data/core/kernel.rb +48 -57
  17. data/core/load_order +3 -5
  18. data/core/module.rb +37 -35
  19. data/core/nil_class.rb +4 -0
  20. data/core/numeric.rb +10 -30
  21. data/core/proc.rb +1 -1
  22. data/core/range.rb +3 -4
  23. data/core/regexp.rb +21 -6
  24. data/core/runtime.js +278 -370
  25. data/core/string.rb +21 -37
  26. data/core/struct.rb +11 -3
  27. data/core/time.rb +44 -37
  28. data/lib/opal.rb +3 -3
  29. data/lib/opal/builder.rb +48 -27
  30. data/lib/opal/builder_task.rb +3 -20
  31. data/lib/opal/grammar.rb +18 -13
  32. data/lib/opal/grammar.y +7 -4
  33. data/lib/opal/parser.rb +290 -199
  34. data/lib/opal/scope.rb +187 -176
  35. data/lib/opal/version.rb +1 -1
  36. data/test/core/kernel/define_singleton_method_spec.rb +21 -0
  37. data/test/core/time/at_spec.rb +7 -0
  38. data/test/core/time/day_spec.rb +5 -0
  39. data/test/core/time/friday_spec.rb +9 -0
  40. data/test/core/time/hour_spec.rb +5 -0
  41. data/test/core/time/min_spec.rb +5 -0
  42. data/test/core/time/monday_spec.rb +9 -0
  43. data/test/core/time/month_spec.rb +5 -0
  44. data/test/core/time/now_spec.rb +5 -0
  45. data/test/core/time/saturday_spec.rb +9 -0
  46. data/test/index.html +2 -1
  47. data/test/language/singleton_class_spec.rb +0 -16
  48. data/test/opal/array/to_json_spec.rb +7 -0
  49. data/test/opal/boolean/singleton_class_spec.rb +9 -0
  50. data/test/opal/boolean/to_json_spec.rb +9 -0
  51. data/test/opal/hash/to_json_spec.rb +9 -0
  52. data/test/opal/json/parse_spec.rb +31 -0
  53. data/test/opal/kernel/to_json_spec.rb +5 -0
  54. data/test/opal/nil/to_json_spec.rb +5 -0
  55. data/test/opal/numeric/to_json_spec.rb +6 -0
  56. data/test/opal/runtime/call_spec.rb +16 -0
  57. data/test/opal/runtime/defined_spec.rb +11 -0
  58. data/test/opal/runtime/super_spec.rb +16 -0
  59. data/test/opal/string/to_json_spec.rb +6 -0
  60. data/test/spec_helper.rb +1 -3
  61. metadata +48 -15
  62. data/core/dir.rb +0 -89
  63. data/core/file.rb +0 -85
  64. data/core/match_data.rb +0 -35
  65. data/core/rational.rb +0 -16
  66. data/test/core/file/expand_path_spec.rb +0 -20
data/.gitignore CHANGED
@@ -11,4 +11,6 @@ pkg/*
11
11
  /*.js
12
12
  /docs/gh-pages
13
13
  /examples/**/*.js
14
- /build
14
+ /build
15
+ Gemfile.local
16
+ Gemfile.local.lock
data/Gemfile CHANGED
@@ -3,8 +3,9 @@ source :rubygems
3
3
  gemspec
4
4
 
5
5
  gem "rake"
6
- # gem "racc"
6
+ gem "racc"
7
7
 
8
8
  group :browser do
9
- gem "opal-spec", "0.1.6"
9
+ gem "opal-spec"
10
+ gem 'opal-dom'
10
11
  end
data/README.md CHANGED
@@ -19,7 +19,7 @@ is a Freenode IRC channel at `#opal`.
19
19
  The Opal runtime and corelib are distributed here, and are required to
20
20
  run any code generated by opal.
21
21
 
22
- [Opal version 0.3.19](http://opalrb.org/opal.js) _(14.9kb Minified And Gzipped)_
22
+ [Opal version 0.3.20](http://opalrb.org/opal.js) _(14.3kb Minified And Gzipped)_
23
23
 
24
24
  ## Installation
25
25
 
@@ -41,18 +41,22 @@ method which returns a string of javascript code:
41
41
  ```ruby
42
42
  require 'opal'
43
43
 
44
- Opal.parse("[1, 2, 3, 4].each { |a| puts a }")
44
+ Opal.parse("puts 'hello world'")
45
45
  ```
46
46
 
47
- This will return a string of javascript similar to the following:
47
+ This will return a string of javascript:
48
48
 
49
49
  ```javascript
50
50
  (function() {
51
- // compiled ruby
52
- }).call(Opal.top);
51
+ var self = Opal.top;
52
+ self.$puts('hello world');
53
+ })();
53
54
  ```
54
55
 
55
- This can then be written to a file and run in any browser.
56
+ This can then be written to a file and run in any browser. `Opal` is a
57
+ global javascript variable that holds all the ruby classes and methods.
58
+ `Opal.top` points to the top object in opal, so any file will have this
59
+ object as its top level `self` variable.
56
60
 
57
61
  ### Creating a rake task
58
62
 
@@ -98,7 +102,7 @@ opal runtime needs to be loaded first (you can download that above).
98
102
 
99
103
  When using `Opal.parse()` as above, the generated code will be run
100
104
  as soon as the page loads. Open the browsers console and you should
101
- see the 4 numbers printed to the console.
105
+ see the message printed to the console.
102
106
 
103
107
  ## Builder and Dependency Builder
104
108
 
@@ -114,7 +118,7 @@ require 'opal'
114
118
 
115
119
  Opal::BuilderTask.new do |t|
116
120
  t.name = 'my-first-app'
117
- t.dependencies = ['opal-json']
121
+ t.dependencies = ['opal-spec']
118
122
  t.files = ['app.rb']
119
123
  end
120
124
  ```
@@ -124,16 +128,16 @@ dependencies.
124
128
 
125
129
  ### Building dependencies
126
130
 
127
- To build the opal runtime `opal.js`, as well as `opal-json` into
131
+ To build the opal runtime `opal.js`, as well as `opal-spec` into
128
132
  `build/`, run the simple rake task:
129
133
 
130
134
  ```ruby
131
135
  rake dependencies
132
136
  ```
133
137
 
134
- This will try and find the `opal-json` gem installed, so either
135
- install globally with `gem install opal-json`, or add it to your
136
- Gemfile as `gem "opal-json"` and run `bundle install`.
138
+ This will try and find the `opal-spec` gem installed, so either
139
+ install globally with `gem install opal-spec`, or add it to your
140
+ Gemfile as `gem "opal-spec"` and run `bundle install`.
137
141
 
138
142
  ### Building app
139
143
 
@@ -158,28 +162,12 @@ You should now be able to run the built app using a standard HTML page.
158
162
  </head>
159
163
  <body>
160
164
  <script src="build/opal.js"></script>
161
- <script src="build/opal-json.js"></script>
165
+ <script src="build/opal-spec.js"></script>
162
166
  <script src="build/my-first-app.js"></script>
163
167
  </body>
164
168
  </html>
165
169
  ```
166
170
 
167
- ### Main file
168
-
169
- When using the `BuilderTask`, the files generated will not be run
170
- automatically on page load. The files are registered so they can be
171
- loaded using `require()`. Builder is clever enough though that it will,
172
- by default, automatically require the first file in the app to be
173
- loaded. This will appear at the bottom of `my-first-app.js`:
174
-
175
- ```javascript
176
- Opal.define('app', function() {
177
- // app.rb code
178
- });
179
-
180
- Opal.require('app');
181
- ```
182
-
183
171
  ## Features And Implementation
184
172
 
185
173
  Opal is a source-to-source compiler, so there is no VM as such and the
@@ -274,6 +262,49 @@ range instances.
274
262
  3...7 # => __range(3, 7, false)
275
263
  ```
276
264
 
265
+ ### Classes and Modules
266
+
267
+ A compiled class or module body is simply just an anonymous javascript
268
+ function that is used to keep any internal variables from escaping.
269
+
270
+ Classes and modules themselves are created as named functions (which is
271
+ used purely to aid debugging). Classes and modules may also be
272
+ re-opended, so they are put through a runtime function `__klass` for
273
+ classes, and `__module` for modules.
274
+
275
+ For example, the `Kernel` module is generated as:
276
+
277
+ ```javascript
278
+ (function(__base) {
279
+ function Kernel(){};
280
+ Kernel = __module(__base, "Kernel", Kernel);
281
+ var Kernel_prototype = Kernel.prototype;
282
+
283
+ Kernel_prototype.$class = function() {
284
+ return this._real;
285
+ };
286
+
287
+ Kernel._donate('$class', ...);
288
+ })(__scope);
289
+ ```
290
+
291
+ Firstly, the `__scope` is passed into the function which is used
292
+ for defining constants, which means that if `Kernel` gets defined
293
+ here then it can be set as a constant for the given scope. The
294
+ named function (Kernel) follows, as well as the `__module()` call
295
+ which simply sets the up the module if it doesn't already exist. If
296
+ it does exist, then it overwrites the previous `Kernel` local variable.
297
+
298
+ Next, `Kernel_prototype` is made a local variable to improve
299
+ minimization of the code, then all the Kernel methods are set on the
300
+ prototype. Modules cannot be instantiated in Opal (just like ruby), but
301
+ the use of prototypes is to improve performance.
302
+
303
+ Finally, the call to `Kernel._donate()` is to pass any defined methods
304
+ into classes that have included `Kernel`, which is just `Object`. This
305
+ method then adds all of Kernels methods into Objects prototype as this
306
+ is the most efficient way to try and emulate rubys method chain.
307
+
277
308
  ### Methods
278
309
 
279
310
  A ruby method is just compiled directly into a function definition.
@@ -352,12 +383,12 @@ def to_s
352
383
  end
353
384
  ```
354
385
 
355
- This would generate the following javascript. (`def.` is a local
356
- variable set to be the class's instance prototype. It is used
357
- for minimization of code as well as trying to be readable).
386
+ This would generate the following javascript. (`Array_prototype` is
387
+ an example and would assume this method definition is inside the
388
+ `Array` class body).
358
389
 
359
390
  ```javascript
360
- def.$to_s = function() {
391
+ Array_prototype.$to_s = function() {
361
392
  return this.$inspect();
362
393
  };
363
394
  ```
@@ -384,16 +415,16 @@ end
384
415
  The generated code reads as expected:
385
416
 
386
417
  ```javascript
387
- def.$norm = function(a, b, c) {
418
+ Array_prototype.$norm = function(a, b, c) {
388
419
  return nil;
389
420
  };
390
421
 
391
- def.$opt = function(a, b) {
422
+ Array_prototype.$opt = function(a, b) {
392
423
  if (b == null) b = 100;
393
424
  return nil;
394
425
  };
395
426
 
396
- def.$rest = function(a, b) {
427
+ Array_prototype.$rest = function(a, b) {
397
428
  b = __slice.call(arguments, 1);
398
429
  return nil;
399
430
  };
@@ -404,6 +435,144 @@ the correct number of arguments get passed to a function. This can be
404
435
  enabled in debug mode, but is not included in production builds as it
405
436
  adds a lot of overhead to **every** method call.
406
437
 
438
+ ### Logic and conditionals
439
+
440
+ As per ruby, Opal treats only `false` and `nil` as falsy, everything
441
+ else is a truthy value including `""`, `0` and `[]`. This differs from
442
+ javascript as these values are also treated as false.
443
+
444
+ For this reason, most truthy tests must check if values are `false` or
445
+ `nil`.
446
+
447
+ Taking the following test:
448
+
449
+ ```javascript
450
+ val = 42
451
+
452
+ if val
453
+ return 3.142;
454
+ end
455
+ ```
456
+
457
+ This would be compiled into:
458
+
459
+ ```ruby
460
+ val = 42;
461
+
462
+ if (val !== false && val !== nil) {
463
+ return 3.142;
464
+ }
465
+ ```
466
+
467
+ This makes the generated truthy tests (`if` statements, `and` checks and
468
+ `or` statements) a litle more verbose in the generated code.
469
+
470
+ ### Instance variables
471
+
472
+ Instance variables in Opal work just as expected. When ivars are set or
473
+ retrieved on an object, they are set natively without the `@` prefix.
474
+ This allows real javascript identifiers to be used which is more
475
+ efficient then accessing variables by string name.
476
+
477
+ ```ruby
478
+ @foo = 200
479
+ @foo # => 200
480
+
481
+ @bar # => nil
482
+ ```
483
+
484
+ This gets compiled into:
485
+
486
+ ```javascript
487
+ this.foo = 200;
488
+ this.foo; // => 200
489
+
490
+ this.bar; // => nil
491
+ ```
492
+
493
+ The only point of warning is that when variables are used for the
494
+ first time in ruby, they default to `nil`. In javascript, they default
495
+ to `undefined`/`null`.
496
+
497
+ To keep things working in opal, ivars must be preset to `nil` before
498
+ they can be used. In the top scope and other corner cases, this needs
499
+ to be done on a per scope basis, which can add overhead.
500
+
501
+ To improve performance, once a class body is compiled, all ivars used
502
+ within methods in that class are preset on the prototype of the class
503
+ to be `nil`. This means that all known ivars are already set to nil,
504
+ and this is done just once during the lifespan of the app.
505
+
506
+ ```ruby
507
+ class Foo
508
+ def bar
509
+ @lol
510
+ end
511
+
512
+ def woosh
513
+ @kapow
514
+ end
515
+ end
516
+ ```
517
+
518
+ This example gets compiled into something similar to:
519
+
520
+ ```javascript
521
+ (function() {
522
+ function Foo(){}
523
+ // ...
524
+
525
+ Foo.prototype.lol = Foo.prototype.woosh = nil;
526
+
527
+ Foo.prototype.$bar = function() {
528
+ return this.lol;
529
+ };
530
+
531
+ // etc ...
532
+ })()
533
+ ```
534
+
535
+ ### Interacting with javascript
536
+
537
+ Opal tries to interact as cleanly with javascript and its api as much
538
+ as possible. Ruby arrays, strings, numbers, regexps, blocks and booleans
539
+ are just javascript native equivalents. The only boxed core features are
540
+ hashes and nil.
541
+
542
+ As most of the corelib deals with these low level details, opal provides
543
+ a special syntax for inlining javascript code. This is done with
544
+ x-strings or "backticks", as their ruby use has no useful translation
545
+ in the browser.
546
+
547
+ ```ruby
548
+ `window.title`
549
+ # => "Opal: ruby to javascript compiler"
550
+
551
+ %x{
552
+ console.log("ruby version is:");
553
+ console.log(#{ OPAL_VERSION });
554
+ }
555
+
556
+ # => ruby version is:
557
+ # => 0.3.19
558
+ ```
559
+
560
+ Even interpolations are supported, as seen here.
561
+
562
+ This feature of inlining code is used extensively, for example in
563
+ Array#length:
564
+
565
+ ```ruby
566
+ class Array
567
+ def length
568
+ `this.length`
569
+ end
570
+ end
571
+ ```
572
+
573
+ X-Strings also have the ability to automatically return their value,
574
+ as used by this example.
575
+
407
576
  ### Compiled Files
408
577
 
409
578
  As described above, a compiled ruby source gets generated into a string
@@ -411,17 +580,16 @@ of javascript code that is wrapped inside an anonymous function. This
411
580
  looks similar to the following:
412
581
 
413
582
  ```javascript
414
- (function(undefined) {
415
- var nil = Opal.nil, __slice = Opal.slice, __klass = Opal.klass;
583
+ (function() {
584
+ var nil = Opal.nil, self = Opal.top;
416
585
  // generated code
417
- }).call(Opal.top);
586
+ })();
418
587
  ```
419
588
 
420
- This function gets called with `Opal.top` as the context, which is the
421
- top level object available to ruby (`main`). Inside the function, `nil`
422
- is assigned to ensure a local copy is available, as well as all the
423
- helper methods used within the generated file. There is no return value
424
- from these functions as they are not used anywhere.
589
+ Inside the function, `nil` is assigned to ensure a local copy is
590
+ available, as well as all the helper methods used within the
591
+ generated file. There is no return value from these functions as they
592
+ are not used anywhere.
425
593
 
426
594
  As a complete example, assuming the following code:
427
595
 
@@ -432,10 +600,10 @@ puts "foo"
432
600
  This would compile directly into:
433
601
 
434
602
  ```javascript
435
- (function(undefined) {
436
- var nil = Opal.nil;
437
- this.$puts("foo");
438
- }).call(Opal.top);
603
+ (function() {
604
+ var nil = Opal.nil, self = Opal.top;
605
+ self.$puts("foo");
606
+ })();
439
607
  ```
440
608
 
441
609
  Most of the helpers are no longer present as they are not used in this
@@ -447,12 +615,100 @@ If you write the generated code as above into a file `app.js` and add
447
615
  that to your HTML page, then it is obvious that `"foo"` would be
448
616
  written to the browser's console.
449
617
 
618
+ ### JSON
619
+
620
+ The opal corelib includes JSON support instead of treating it as an
621
+ external lib. The `JSON` module provides the usual parsing methods.
622
+
623
+ ```ruby
624
+ JSON.parse '{"a": 10, "b": [1, 2, 3], "c": null}'
625
+ # => { "a" => 10, "b" => [1, 2, 3], "c" => nil }
626
+ ```
627
+
628
+ Opal expects `JSON` to be present in the browser, so older browsers
629
+ may require a shim (json2.js) to work with opal. Most mobile browsers
630
+ and modern desktop browsers include json support natively.
631
+
632
+ ## Debugging and finding errors
633
+
634
+ Because Opal does not aim to be fully compatible with ruby, there are
635
+ some instances where things can break and it may not be entirely
636
+ obvious what went wrong.
637
+
638
+ ### Undefined methods
639
+
640
+ By default, opal aims to be as fast as possible, so `method_missing` is
641
+ not turned on by default. Instead, when calling a method that doesn't
642
+ exist, a native error will be raised.
643
+
644
+ ```ruby
645
+ self.do_something()
646
+ ```
647
+
648
+ Might raise an error similar to:
649
+
650
+ ```
651
+ Error: 'undefined' is not a function (evaluating 'this.$do_something()')
652
+ ```
653
+
654
+ As described above, all ruby methods will have a `$` prefix which gives
655
+ a good indication that it is a opal method that doesnt exist, and most
656
+ js engines output the missing function name.
657
+
658
+ ### Undefined constants
659
+
660
+ If trying to access a constant that doesn't exist, there is no runtime
661
+ error. Instead, the value of that expression is just `undefined` as
662
+ constants are retrieved from objects that hold all constants in the
663
+ scope. Trying to send a method to an undefined constant will therefore
664
+ just raise an ugly javascript `TypeError`.
665
+
666
+ If you are using the constant as a reference, it may not be until much
667
+ later that the error occurs.
668
+
669
+ ### Using javascript debuggers
670
+
671
+ As opal just generates javascript, it is useful to use a native
672
+ debugger to work through javascript code. To use a debugger, simply
673
+ add an x-string similar to the following at the place you wish to
674
+ debug:
675
+
676
+ ```ruby
677
+ # .. code
678
+ `debugger`
679
+ # .. more code
680
+ ```
681
+ The x-strings just pass the debugger statement straight through to the
682
+ javascript output.
683
+
684
+ Inside methods and blocks, the current `self` value is always the
685
+ native `this` value. You will not see `self` inside debuggers as it is
686
+ never used to refer to the actual ruby self value.
687
+
688
+ All local variables and method/block arguments also keep their ruby
689
+ names except in the rare cases when the name is reserved in javascript.
690
+ In these cases, a `$` suffix is added to the name (e.g. `try` =>
691
+ `try$`).
692
+
450
693
  ## License
451
694
 
452
695
  Opal is released under the MIT license.
453
696
 
454
697
  ## Change Log
455
698
 
699
+ **0.3.20** _(23 June 2012)_
700
+
701
+ * Merge JSON into core. JSON module and various #to_json methods are
702
+ now included as part of corelib
703
+ * Make `Time` class bridge to native `Date` constructor
704
+ * Use named functions as class constuctors to make debugging easier
705
+ * Classes are now real functions with prototypes. Bridged classes are
706
+ now directly corresponding to the ruby class (e.g. Array === Opal.Array)
707
+ * Set ivars used inside methods in class to `nil` inside class definition
708
+ to avoid doing it everytime method is called
709
+ * Add debug comments to output for def, class and module stating the file
710
+ and line number the given code was generated from
711
+
456
712
  **0.3.19** _(30 May 2012)_
457
713
 
458
714
  * Add BasicObject as the root class