opal 0.7.0.beta3 → 0.7.0.rc1

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +4 -0
  3. data/.travis.yml +7 -3
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile +1 -1
  7. data/README.md +3 -12
  8. data/Rakefile +4 -150
  9. data/bin/opal-mspec +1 -1
  10. data/docs/compiler_directives.md +127 -0
  11. data/examples/rack/.gitignore +1 -0
  12. data/examples/rack/app/user.rb +1 -0
  13. data/lib/mspec/opal/special_calls.rb +15 -2
  14. data/lib/opal/builder.rb +15 -8
  15. data/lib/opal/compiler.rb +75 -4
  16. data/lib/opal/erb.rb +22 -2
  17. data/lib/opal/fragment.rb +17 -5
  18. data/lib/opal/nodes/def.rb +174 -53
  19. data/lib/opal/nodes/if.rb +14 -0
  20. data/lib/opal/nodes/module.rb +0 -1
  21. data/lib/opal/nodes/rescue.rb +10 -2
  22. data/lib/opal/nodes/scope.rb +0 -17
  23. data/lib/opal/parser.rb +83 -19
  24. data/lib/opal/parser/grammar.rb +2511 -2414
  25. data/lib/opal/parser/grammar.y +71 -9
  26. data/lib/opal/parser/lexer.rb +44 -12
  27. data/lib/opal/parser/parser_scope.rb +3 -0
  28. data/lib/opal/parser/sexp.rb +7 -1
  29. data/lib/opal/paths.rb +5 -5
  30. data/lib/opal/sprockets/environment.rb +2 -10
  31. data/lib/opal/sprockets/path_reader.rb +1 -1
  32. data/lib/opal/sprockets/processor.rb +1 -0
  33. data/lib/opal/sprockets/server.rb +2 -0
  34. data/lib/opal/util.rb +7 -2
  35. data/lib/opal/version.rb +1 -1
  36. data/opal.gemspec +1 -0
  37. data/opal/README.md +1 -1
  38. data/opal/corelib/dir.rb +1 -1
  39. data/opal/corelib/enumerable.rb +3 -1
  40. data/opal/corelib/error.rb +3 -0
  41. data/opal/corelib/file.rb +2 -0
  42. data/opal/corelib/hash.rb +3 -0
  43. data/opal/corelib/io.rb +15 -1
  44. data/opal/corelib/kernel.rb +8 -0
  45. data/opal/corelib/module.rb +42 -17
  46. data/opal/corelib/runtime.js +223 -49
  47. data/opal/corelib/string.rb +1 -1
  48. data/opal/corelib/struct.rb +1 -7
  49. data/spec/README.md +8 -0
  50. data/spec/filters/bugs/language.rb +1 -0
  51. data/spec/filters/bugs/module.rb +4 -0
  52. data/spec/filters/unsupported/frozen.rb +2 -0
  53. data/spec/lib/compiler/pre_processed_conditionals_spec.rb +87 -0
  54. data/spec/lib/compiler_spec.rb +1 -67
  55. data/spec/lib/fixtures/file_with_directives.js +2 -0
  56. data/spec/lib/fixtures/required_file.js +1 -0
  57. data/spec/lib/parser/def_spec.rb +29 -16
  58. data/spec/lib/parser/variables_spec.rb +5 -5
  59. data/spec/lib/sprockets/path_reader_spec.rb +24 -8
  60. data/spec/lib/sprockets/server_spec.rb +10 -3
  61. data/spec/opal/core/date_spec.rb +14 -0
  62. data/spec/opal/core/language/versions/def_2_0_spec.rb +62 -0
  63. data/spec/opal/core/language_spec.rb +23 -0
  64. data/spec/opal/core/runtime/donate_spec.rb +53 -0
  65. data/spec/opal/stdlib/native/native_alias_spec.rb +19 -0
  66. data/spec/opal/stdlib/native/native_class_spec.rb +18 -0
  67. data/spec/opal/stdlib/native/native_module_spec.rb +13 -0
  68. data/spec/rubyspecs +2 -0
  69. data/stdlib/buffer.rb +1 -0
  70. data/stdlib/date.rb +18 -0
  71. data/stdlib/encoding.rb +3 -2
  72. data/stdlib/minitest.rb +780 -0
  73. data/stdlib/minitest/assertions.rb +662 -0
  74. data/stdlib/minitest/autorun.rb +12 -0
  75. data/stdlib/minitest/benchmark.rb +426 -0
  76. data/stdlib/minitest/expectations.rb +281 -0
  77. data/stdlib/minitest/hell.rb +11 -0
  78. data/stdlib/minitest/mock.rb +220 -0
  79. data/stdlib/minitest/parallel.rb +65 -0
  80. data/stdlib/minitest/pride.rb +4 -0
  81. data/stdlib/minitest/pride_plugin.rb +142 -0
  82. data/stdlib/minitest/spec.rb +310 -0
  83. data/stdlib/minitest/test.rb +293 -0
  84. data/stdlib/minitest/unit.rb +45 -0
  85. data/stdlib/native.rb +12 -3
  86. data/stdlib/nodejs/process.rb +16 -2
  87. data/stdlib/promise.rb +99 -0
  88. data/stdlib/test/unit.rb +10 -0
  89. data/stdlib/thread.rb +4 -0
  90. data/tasks/building.rake +58 -0
  91. data/tasks/documentation.rake +38 -0
  92. data/tasks/documenting.rake +37 -0
  93. data/tasks/testing.rake +102 -0
  94. metadata +57 -2
@@ -47,11 +47,17 @@ class Module
47
47
 
48
48
  def alias_method(newname, oldname)
49
49
  %x{
50
- self.$$proto['$' + newname] = self.$$proto['$' + oldname];
50
+ var newjsid = '$' + newname,
51
+ body = self.$$proto['$' + oldname];
51
52
 
52
- if (self.$$methods) {
53
- Opal.donate(self, ['$' + newname ])
53
+ if (self.$$is_singleton) {
54
+ self.$$proto[newjsid] = body;
54
55
  }
56
+ else {
57
+ Opal.defn(self, newjsid, body);
58
+ }
59
+
60
+ return self;
55
61
  }
56
62
  self
57
63
  end
@@ -88,18 +94,16 @@ class Module
88
94
 
89
95
  def attr_reader(*names)
90
96
  %x{
91
- var proto = self.$$proto, cls = self;
92
97
  for (var i = 0, length = names.length; i < length; i++) {
93
98
  (function(name) {
94
- proto[name] = nil;
99
+ self.$$proto[name] = nil;
95
100
  var func = function() { return this[name] };
96
101
 
97
- if (cls.$$is_singleton) {
98
- proto.constructor.prototype['$' + name] = func;
102
+ if (self.$$is_singleton) {
103
+ self.$$proto.constructor.prototype['$' + name] = func;
99
104
  }
100
105
  else {
101
- proto['$' + name] = func;
102
- Opal.donate(self, ['$' + name ]);
106
+ Opal.defn(self, '$' + name, func);
103
107
  }
104
108
  })(names[i]);
105
109
  }
@@ -110,18 +114,16 @@ class Module
110
114
 
111
115
  def attr_writer(*names)
112
116
  %x{
113
- var proto = self.$$proto, cls = self;
114
117
  for (var i = 0, length = names.length; i < length; i++) {
115
118
  (function(name) {
116
- proto[name] = nil;
119
+ self.$$proto[name] = nil;
117
120
  var func = function(value) { return this[name] = value; };
118
121
 
119
- if (cls.$$is_singleton) {
120
- proto.constructor.prototype['$' + name + '='] = func;
122
+ if (self.$$is_singleton) {
123
+ self.$$proto.constructor.prototype['$' + name + '='] = func;
121
124
  }
122
125
  else {
123
- proto['$' + name + '='] = func;
124
- Opal.donate(self, ['$' + name + '=']);
126
+ Opal.defn(self, '$' + name + '=', func);
125
127
  }
126
128
  })(names[i]);
127
129
  }
@@ -144,6 +146,25 @@ class Module
144
146
  }
145
147
  end
146
148
 
149
+ def class_variable_get(name)
150
+ name = Opal.coerce_to!(name, String, :to_str)
151
+ raise NameError, 'class vars should start with @@' if `name.length < 3 || name.slice(0,2) !== '@@'`
152
+ %x{
153
+ var value = Opal.cvars[name.slice(2)];
154
+ #{raise NameError, 'uninitialized class variable @@a in' if `value == null`}
155
+ return value;
156
+ }
157
+ end
158
+
159
+ def class_variable_set(name, value)
160
+ name = Opal.coerce_to!(name, String, :to_str)
161
+ raise NameError if `name.length < 3 || name.slice(0,2) !== '@@'`
162
+ %x{
163
+ Opal.cvars[name.slice(2)] = value;
164
+ return value;
165
+ }
166
+ end
167
+
147
168
  def constants
148
169
  `self.$$scope.constants`
149
170
  end
@@ -241,8 +262,12 @@ class Module
241
262
  block.$$s = null;
242
263
  block.$$def = block;
243
264
 
244
- self.$$proto[jsid] = block;
245
- Opal.donate(self, [jsid]);
265
+ if (self.$$is_singleton) {
266
+ self.$$proto[jsid] = block;
267
+ }
268
+ else {
269
+ Opal.defn(self, jsid, block);
270
+ }
246
271
 
247
272
  return name;
248
273
  }
@@ -43,7 +43,22 @@
43
43
  // Globals table
44
44
  Opal.gvars = {};
45
45
 
46
- // Get constants
46
+ /**
47
+ Get a constant on the given scope. Every class and module in Opal has a
48
+ scope used to store, and inherit, constants. For example, the top level
49
+ `Object` in ruby has a scope accessible as `Opal.Object.$$scope`.
50
+
51
+ To get the `Array` class using this scope, you could use:
52
+
53
+ Opal.Object.$$scope.get("Array")
54
+
55
+ If a constant with the given name cannot be found, then a dispatch to the
56
+ class/module's `#const_method` is called, which by default will raise an
57
+ error.
58
+
59
+ @param [String] name the name of the constant to lookup
60
+ @returns [RubyObject]
61
+ */
47
62
  Opal.get = function(name) {
48
63
  var constant = this[name];
49
64
 
@@ -148,7 +163,7 @@
148
163
 
149
164
  // Copy all parent constants to child, unless parent is Object
150
165
  if (superklass !== ObjectClass && superklass !== BasicObjectClass) {
151
- Opal.donate_constants(superklass, klass);
166
+ donate_constants(superklass, klass);
152
167
  }
153
168
 
154
169
  // call .inherited() hook with new class on the superclass
@@ -253,7 +268,26 @@
253
268
  module.$$inc = [];
254
269
  }
255
270
 
256
- // Define new module (or return existing module)
271
+ /**
272
+ Define new module (or return existing module). The given `base` is basically
273
+ the current `self` value the `module` statement was defined in. If this is
274
+ a ruby module or class, then it is used, otherwise if the base is a ruby
275
+ object then that objects real ruby class is used (e.g. if the base is the
276
+ main object, then the top level `Object` class is used as the base).
277
+
278
+ If a module of the given name is already defined in the base, then that
279
+ instance is just returned.
280
+
281
+ If there is a class of the given name in the base, then an error is
282
+ generated instead (cannot have a class and module of same name in same base).
283
+
284
+ Otherwise, a new module is created in the base with the given name, and that
285
+ new instance is returned back (to be referenced at runtime).
286
+
287
+ @param [RubyModule or Class] base class or module this definition is inside
288
+ @param [String] id the name of the new (or existing) module
289
+ @returns [RubyModule]
290
+ */
257
291
  Opal.module = function(base, id) {
258
292
  var module;
259
293
 
@@ -303,11 +337,19 @@
303
337
  return module;
304
338
  }
305
339
 
306
- /*
307
- * Get (or prepare) the singleton class for the passed object.
308
- *
309
- * @param object [Ruby Object]
310
- */
340
+ /**
341
+ Return the singleton class for the passed object.
342
+
343
+ If the given object alredy has a singleton class, then it will be stored on
344
+ the object as the `$$meta` property. If this exists, then it is simply
345
+ returned back.
346
+
347
+ Otherwise, a new singleton object for the class or object is created, set on
348
+ the object at `$$meta` for future use, and then returned.
349
+
350
+ @param [RubyObject] object the ruby object
351
+ @returns [RubyClass] the singleton class for object
352
+ */
311
353
  Opal.get_singleton_class = function(object) {
312
354
  if (object.$$meta) {
313
355
  return object.$$meta;
@@ -320,11 +362,14 @@
320
362
  return build_object_singleton_class(object);
321
363
  };
322
364
 
323
- /*
324
- * Build the singleton class for an existing class.
325
- *
326
- * NOTE: Actually in MRI a class' singleton class inherits from its
327
- * superclass' singleton class which in turn inherits from Class;
365
+ /**
366
+ Build the singleton class for an existing class.
367
+
368
+ NOTE: Actually in MRI a class' singleton class inherits from its
369
+ superclass' singleton class which in turn inherits from Class.
370
+
371
+ @param [RubyClass] klass
372
+ @returns [RubyClass]
328
373
  */
329
374
  function build_class_singleton_class(klass) {
330
375
  var meta = new Opal.Class.$$alloc;
@@ -340,8 +385,11 @@
340
385
  return klass.$$meta = meta;
341
386
  }
342
387
 
343
- /*
344
- * Build the singleton class for a Ruby (non class) Object.
388
+ /**
389
+ Build the singleton class for a Ruby (non class) Object.
390
+
391
+ @param [RubyObject] object
392
+ @returns [RubyClass]
345
393
  */
346
394
  function build_object_singleton_class(object) {
347
395
  var orig_class = object.$$class,
@@ -358,9 +406,26 @@
358
406
  return object.$$meta = meta;
359
407
  }
360
408
 
361
- /*
362
- * The actual inclusion of a module into a class.
363
- */
409
+ /**
410
+ The actual inclusion of a module into a class.
411
+
412
+ ## Class `$$parent` and `iclass`
413
+
414
+ To handle `super` calls, every class has a `$$parent`. This parent is
415
+ used to resolve the next class for a super call. A normal class would
416
+ have this point to its superclass. However, if a class includes a module
417
+ then this would need to take into account the module. The module would
418
+ also have to then point its `$$parent` to the actual superclass. We
419
+ cannot modify modules like this, because it might be included in more
420
+ then one class. To fix this, we actually insert an `iclass` as the class'
421
+ `$$parent` which can then point to the superclass. The `iclass` acts as
422
+ a proxy to the actual module, so the `super` chain can then search it for
423
+ the required method.
424
+
425
+ @param [RubyModule] module the module to include
426
+ @param [RubyClass] klass the target class to include module into
427
+ @returns [null]
428
+ */
364
429
  Opal.append_features = function(module, klass) {
365
430
  var included = klass.$$inc;
366
431
 
@@ -406,10 +471,10 @@
406
471
  }
407
472
 
408
473
  if (klass.$$dep) {
409
- Opal.donate(klass, methods.slice(), true);
474
+ donate_methods(klass, methods.slice(), true);
410
475
  }
411
476
 
412
- Opal.donate_constants(module, klass);
477
+ donate_constants(module, klass);
413
478
  };
414
479
 
415
480
  // Boot a base class (makes instances).
@@ -534,30 +599,11 @@
534
599
  return base_scope[name] = value;
535
600
  };
536
601
 
537
- /*
538
- * constant get
539
- */
540
- Opal.cget = function(base_scope, path) {
541
- if (path == null) {
542
- path = base_scope;
543
- base_scope = Opal.Object;
544
- }
545
-
546
- var result = base_scope;
547
-
548
- path = path.split('::');
549
- while (path.length !== 0) {
550
- result = result.$const_get(path.shift());
551
- }
552
-
553
- return result;
554
- };
555
-
556
602
  /*
557
603
  * When a source module is included into the target module, we must also copy
558
604
  * its constants to the target.
559
605
  */
560
- Opal.donate_constants = function(source_mod, target_mod) {
606
+ function donate_constants(source_mod, target_mod) {
561
607
  var source_constants = source_mod.$$scope.constants,
562
608
  target_scope = target_mod.$$scope,
563
609
  target_constants = target_scope.constants;
@@ -844,6 +890,34 @@
844
890
  return [value];
845
891
  };
846
892
 
893
+ /**
894
+ Used to get a list of rest keyword arguments. Method takes the given
895
+ keyword args, i.e. the hash literal passed to the method containing all
896
+ keyword arguemnts passed to method, as well as the used args which are
897
+ the names of required and optional arguments defined. This method then
898
+ just returns all key/value pairs which have not been used, in a new
899
+ hash literal.
900
+
901
+ @param given_args [Hash] all kwargs given to method
902
+ @param used_args [Object<String: true>] all keys used as named kwargs
903
+ @return [Hash]
904
+ */
905
+ Opal.kwrestargs = function(given_args, used_args) {
906
+ var keys = [],
907
+ map = {},
908
+ key = null,
909
+ given_map = given_args.smap;
910
+
911
+ for (key in given_map) {
912
+ if (!used_args[key]) {
913
+ keys.push(key);
914
+ map[key] = given_map[key];
915
+ }
916
+ }
917
+
918
+ return Opal.hash2(keys, map);
919
+ };
920
+
847
921
  /*
848
922
  * Call a ruby method on a ruby object with some arguments:
849
923
  *
@@ -885,7 +959,7 @@
885
959
  /*
886
960
  * Donate methods for a class/module
887
961
  */
888
- Opal.donate = function(klass, defined, indirect) {
962
+ function donate_methods(klass, defined, indirect) {
889
963
  var methods = klass.$$methods, included_in = klass.$$dep;
890
964
 
891
965
  // if (!indirect) {
@@ -905,21 +979,121 @@
905
979
  }
906
980
 
907
981
  if (includee.$$dep) {
908
- Opal.donate(includee, defined, true);
982
+ donate_methods(includee, defined, true);
909
983
  }
910
984
  }
911
985
  }
912
986
  };
913
987
 
914
- Opal.defn = function(obj, jsid, body) {
915
- if (obj.$$is_mod) {
916
- obj.$$proto[jsid] = body;
917
- Opal.donate(obj, [jsid]);
988
+ /**
989
+ Define the given method on the module.
990
+
991
+ This also handles donating methods to all classes that include this
992
+ module. Method conflicts are also handled here, where a class might already
993
+ have defined a method of the same name, or another included module defined
994
+ the same method.
995
+
996
+ @param [RubyModule] module the module method defined on
997
+ @param [String] jsid javascript friendly method name (e.g. "$foo")
998
+ @param [Function] body method body of actual function
999
+ */
1000
+ function define_module_method(module, jsid, body) {
1001
+ module.$$proto[jsid] = body;
1002
+ body.$$owner = module;
1003
+
1004
+ module.$$methods.push(jsid);
1005
+
1006
+ if (module.$$module_function) {
1007
+ module[jsid] = body;
1008
+ }
1009
+
1010
+ var included_in = module.$$dep;
1011
+
1012
+ if (included_in) {
1013
+ for (var i = 0, length = included_in.length; i < length; i++) {
1014
+ var includee = included_in[i];
1015
+ var dest = includee.$$proto;
1016
+ var current = dest[jsid];
1017
+
1018
+
1019
+ if (dest.hasOwnProperty(jsid) && !current.$$donated && !current.$$stub) {
1020
+ // target class has already defined the same method name - do nothing
1021
+ }
1022
+ else if (dest.hasOwnProperty(jsid) && !current.$$stub) {
1023
+ // target class includes another module that has defined this method
1024
+ var klass_includees = includee.$$inc;
1025
+
1026
+ for (var j = 0, jj = klass_includees.length; j < jj; j++) {
1027
+ if (klass_includees[j] === current.$$owner) {
1028
+ var current_owner_index = j;
1029
+ }
1030
+ if (klass_includees[j] === module) {
1031
+ var module_index = j;
1032
+ }
1033
+ }
1034
+
1035
+ // only redefine method on class if the module was included AFTER
1036
+ // the module which defined the current method body. Also make sure
1037
+ // a module can overwrite a method it defined before
1038
+ if (current_owner_index <= module_index) {
1039
+ dest[jsid] = body;
1040
+ dest[jsid].$$donated = true;
1041
+ }
1042
+ }
1043
+ else {
1044
+ // neither a class, or module included by class, has defined method
1045
+ dest[jsid] = body;
1046
+ dest[jsid].$$donated = true;
1047
+ }
918
1048
 
919
- if (obj.$$module_function) {
920
- obj[jsid] = body;
1049
+ if (includee.$$dep) {
1050
+ donate_methods(includee, [jsid], true);
1051
+ }
921
1052
  }
922
1053
  }
1054
+ }
1055
+
1056
+ /**
1057
+ Used to define methods on an object. This is a helper method, used by the
1058
+ compiled source to define methods on special case objects when the compiler
1059
+ can not determine the destination object, or the object is a Module
1060
+ instance. This can get called by `Module#define_method` as well.
1061
+
1062
+ ## Modules
1063
+
1064
+ Any method defined on a module will come through this runtime helper.
1065
+ The method is added to the module body, and the owner of the method is
1066
+ set to be the module itself. This is used later when choosing which
1067
+ method should show on a class if more than 1 included modules define
1068
+ the same method. Finally, if the module is in `module_function` mode,
1069
+ then the method is also defined onto the module itself.
1070
+
1071
+ ## Classes
1072
+
1073
+ This helper will only be called for classes when a method is being
1074
+ defined indirectly; either through `Module#define_method`, or by a
1075
+ literal `def` method inside an `instance_eval` or `class_eval` body. In
1076
+ either case, the method is simply added to the class' prototype. A special
1077
+ exception exists for `BasicObject` and `Object`. These two classes are
1078
+ special because they are used in toll-free bridged classes. In each of
1079
+ these two cases, extra work is required to define the methods on toll-free
1080
+ bridged class' prototypes as well.
1081
+
1082
+ ## Objects
1083
+
1084
+ If a simple ruby object is the object, then the method is simply just
1085
+ defined on the object as a singleton method. This would be the case when
1086
+ a method is defined inside an `instance_eval` block.
1087
+
1088
+ @param [RubyObject or Class] obj the actual obj to define method for
1089
+ @param [String] jsid the javascript friendly method name (e.g. '$foo')
1090
+ @param [Function] body the literal javascript function used as method
1091
+ @returns [null]
1092
+ */
1093
+ Opal.defn = function(obj, jsid, body) {
1094
+ if (obj.$$is_mod) {
1095
+ define_module_method(obj, jsid, body);
1096
+ }
923
1097
  else if (obj.$$is_class) {
924
1098
  obj.$$proto[jsid] = body;
925
1099
 
@@ -927,7 +1101,7 @@
927
1101
  define_basic_object_method(jsid, body);
928
1102
  }
929
1103
  else if (obj === ObjectClass) {
930
- Opal.donate(obj, [jsid]);
1104
+ donate_methods(obj, [jsid]);
931
1105
  }
932
1106
  }
933
1107
  else {