opal 0.8.0.beta1 → 0.8.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +0 -24
  3. data/.jshintrc +51 -0
  4. data/.travis.yml +6 -1
  5. data/CHANGELOG.md +2 -0
  6. data/Gemfile +3 -7
  7. data/README.md +2 -3
  8. data/Rakefile +1 -0
  9. data/lib/mspec/opal/rake_task.rb +1 -1
  10. data/lib/opal/builder.rb +21 -3
  11. data/lib/opal/builder_processors.rb +2 -2
  12. data/lib/opal/cli.rb +8 -5
  13. data/lib/opal/erb.rb +1 -1
  14. data/lib/opal/nodes/call.rb +1 -1
  15. data/lib/opal/nodes/call_special.rb +1 -1
  16. data/lib/opal/nodes/helpers.rb +6 -4
  17. data/lib/opal/nodes/iter.rb +2 -2
  18. data/lib/opal/nodes/singleton_class.rb +2 -1
  19. data/lib/opal/parser/lexer.rb +3 -2
  20. data/lib/opal/path_reader.rb +8 -1
  21. data/lib/opal/paths.rb +23 -16
  22. data/lib/opal/regexp_anchors.rb +5 -0
  23. data/lib/opal/sprockets/erb.rb +1 -1
  24. data/lib/opal/sprockets/processor.rb +21 -35
  25. data/lib/opal/sprockets/source_map_server.rb +5 -5
  26. data/lib/opal/version.rb +3 -1
  27. data/lib/tilt/opal.rb +11 -5
  28. data/opal.gemspec +2 -2
  29. data/opal/corelib/array.rb +196 -4
  30. data/opal/corelib/enumerable.rb +1 -1
  31. data/opal/corelib/enumerator.rb +2 -2
  32. data/opal/corelib/hash.rb +6 -6
  33. data/opal/corelib/kernel.rb +5 -3
  34. data/opal/corelib/module.rb +18 -12
  35. data/opal/corelib/regexp.rb +110 -4
  36. data/opal/corelib/runtime.js +12 -4
  37. data/opal/corelib/string.rb +51 -95
  38. data/opal/corelib/string/inheritance.rb +22 -0
  39. data/opal/corelib/variables.rb +1 -1
  40. data/spec/filters/bugs/array.rb +3 -39
  41. data/spec/filters/bugs/basic_object.rb +20 -0
  42. data/spec/filters/bugs/date.rb +5 -0
  43. data/spec/filters/bugs/enumerable.rb +9 -0
  44. data/spec/filters/bugs/enumerator.rb +0 -7
  45. data/spec/filters/bugs/hash.rb +2 -0
  46. data/spec/filters/bugs/kernel.rb +5 -0
  47. data/spec/filters/bugs/language.rb +14 -0
  48. data/spec/filters/bugs/method.rb +5 -0
  49. data/spec/filters/bugs/module.rb +23 -0
  50. data/spec/filters/bugs/regular_expressions.rb +41 -0
  51. data/spec/filters/bugs/singleton.rb +3 -0
  52. data/spec/filters/bugs/string.rb +2 -9
  53. data/spec/filters/bugs/stringscanner.rb +3 -0
  54. data/spec/filters/bugs/time.rb +19 -0
  55. data/spec/filters/unsupported/module.rb +6 -0
  56. data/spec/filters/unsupported/mutable_strings.rb +8 -0
  57. data/spec/filters/unsupported/regular_expressions.rb +90 -0
  58. data/spec/lib/builder_spec.rb +12 -6
  59. data/spec/lib/cli_spec.rb +2 -2
  60. data/spec/lib/path_reader_spec.rb +12 -0
  61. data/spec/lib/sprockets/server_spec.rb +7 -8
  62. data/spec/lib/tilt/opal_spec.rb +18 -0
  63. data/spec/opal/core/language/regexp_spec.rb +1 -1
  64. data/spec/opal/core/runtime/bridged_classes_spec.rb +48 -1
  65. data/spec/rubyspecs +88 -78
  66. data/stdlib/encoding.rb +3 -0
  67. data/stdlib/native.rb +1 -1
  68. data/stdlib/pp.rb +27 -6
  69. data/stdlib/set.rb +10 -0
  70. data/tasks/linting.rake +18 -0
  71. data/tasks/testing.rake +11 -4
  72. data/{stdlib → vendored-minitest}/minitest.rb +0 -0
  73. data/{stdlib → vendored-minitest}/minitest/assertions.rb +0 -0
  74. data/{stdlib → vendored-minitest}/minitest/autorun.rb +0 -0
  75. data/{stdlib → vendored-minitest}/minitest/benchmark.rb +0 -0
  76. data/{stdlib → vendored-minitest}/minitest/expectations.rb +0 -0
  77. data/{stdlib → vendored-minitest}/minitest/hell.rb +0 -0
  78. data/{stdlib → vendored-minitest}/minitest/mock.rb +0 -0
  79. data/{stdlib → vendored-minitest}/minitest/parallel.rb +0 -0
  80. data/{stdlib → vendored-minitest}/minitest/pride.rb +0 -0
  81. data/{stdlib → vendored-minitest}/minitest/pride_plugin.rb +0 -0
  82. data/{stdlib → vendored-minitest}/minitest/spec.rb +0 -0
  83. data/{stdlib → vendored-minitest}/minitest/test.rb +0 -0
  84. data/{stdlib → vendored-minitest}/minitest/unit.rb +0 -0
  85. data/vendored-minitest/test/unit.rb +23 -0
  86. metadata +31 -29
  87. data/stdlib/test/unit.rb +0 -10
  88. data/tasks/documentation.rake +0 -38
@@ -943,7 +943,7 @@ module Enumerable
943
943
  return enum_for :partition unless block_given?
944
944
 
945
945
  %x{
946
- var truthy = [], falsy = [];
946
+ var truthy = [], falsy = [], result;
947
947
 
948
948
  self.$each.$$p = function() {
949
949
  var param = #{Opal.destructure(`arguments`)},
@@ -59,7 +59,7 @@ class Enumerator
59
59
  return enum_for :with_index, offset unless block
60
60
 
61
61
  %x{
62
- var result, index = 0;
62
+ var result, index = offset;
63
63
 
64
64
  self.$each.$$p = function() {
65
65
  var param = #{Opal.destructure(`arguments`)},
@@ -79,7 +79,7 @@ class Enumerator
79
79
  return result;
80
80
  }
81
81
 
82
- return nil;
82
+ return self.object;
83
83
  }
84
84
  end
85
85
 
data/opal/corelib/hash.rb CHANGED
@@ -363,7 +363,7 @@ class Hash
363
363
  %x{
364
364
  var _map = self.map,
365
365
  smap = self.smap,
366
- keys = self.keys;
366
+ keys = self.keys, key, map, khash;
367
367
 
368
368
  for (var i = 0, length = keys.length; i < length; i++) {
369
369
  key = keys[i];
@@ -613,7 +613,7 @@ class Hash
613
613
 
614
614
  var key, value,
615
615
  inspect = [],
616
- keys = self.keys
616
+ keys = self.keys,
617
617
  id = self.$object_id(),
618
618
  counter = 0;
619
619
 
@@ -820,7 +820,7 @@ class Hash
820
820
  var keys = self.keys,
821
821
  _map = self.map,
822
822
  smap = self.smap,
823
- key, khash, value;
823
+ key, khash, value, map;
824
824
 
825
825
  for (var i = 0, length = keys.length; i < length; i++) {
826
826
  key = keys[i]
@@ -967,7 +967,7 @@ class Hash
967
967
  smap = self.smap,
968
968
  keys = self.keys,
969
969
  result = nil,
970
- key, khash, value, object;
970
+ key, khash, value, object, map;
971
971
 
972
972
  for (var i = 0, length = keys.length; i < length; i++) {
973
973
  key = keys[i];
@@ -1039,7 +1039,7 @@ class Hash
1039
1039
  _map = self.map,
1040
1040
  smap = self.smap,
1041
1041
  result = [],
1042
- map, key;
1042
+ map, key, khash;
1043
1043
 
1044
1044
  for (var i = 0, length = keys.length; i < length; i++) {
1045
1045
  key = keys[i];
@@ -1096,7 +1096,7 @@ class Hash
1096
1096
  smap = self.smap,
1097
1097
  keys = self.keys,
1098
1098
  result = [],
1099
- map, khash;
1099
+ map, khash, key;
1100
1100
 
1101
1101
  for (var i = 0, length = keys.length; i < length; i++) {
1102
1102
  key = keys[i];
@@ -116,7 +116,9 @@ module Kernel
116
116
  initialize_copy(other)
117
117
  end
118
118
 
119
- def define_singleton_method(name, &body)
119
+ def define_singleton_method(name, body = nil, &block)
120
+ body ||= block
121
+
120
122
  unless body
121
123
  raise ArgumentError, "tried to create Proc object without a block"
122
124
  end
@@ -180,8 +182,8 @@ module Kernel
180
182
 
181
183
  def format(format_string, *args)
182
184
  if args.length == 1 && args[0].respond_to?(:to_ary)
183
- args = args[0].to_ary
184
- args = args.to_a
185
+ ary = Opal.coerce_to?(args[0], Array, :to_ary)
186
+ args = ary.to_a unless ary.nil?
185
187
  end
186
188
 
187
189
  %x{
@@ -198,7 +198,7 @@ class Module
198
198
  raise NameError, "wrong constant name #{name}" unless name =~ /^[A-Z]\w*$/
199
199
 
200
200
  %x{
201
- scopes = [self.$$scope];
201
+ var scopes = [self.$$scope];
202
202
 
203
203
  if (inherit || self === Opal.Object) {
204
204
  var parent = self.$$super;
@@ -278,18 +278,24 @@ class Module
278
278
  value
279
279
  end
280
280
 
281
- def define_method(name, method = nil, &block)
282
- unless method || block
283
- raise ArgumentError, 'tried to create Proc object without a block'
281
+ def define_method(name, method = undefined, &block)
282
+ if `method === undefined && !#{block_given?}`
283
+ raise ArgumentError, "tried to create a Proc object without a block"
284
284
  end
285
285
 
286
- if method
287
- if Proc === method
288
- block = method
289
- else
290
- raise TypeError, "wrong argument type #{method.class} (expected Proc/Method)"
291
- end
292
- end
286
+ block ||= case method
287
+ when Proc
288
+ method
289
+ when Method
290
+ method.to_proc
291
+ when UnboundMethod
292
+ lambda do |*args|
293
+ bound = method.bind(self)
294
+ bound.call *args
295
+ end
296
+ else
297
+ raise TypeError, "wrong argument type #{block.class} (expected Proc/Method)"
298
+ end
293
299
 
294
300
  %x{
295
301
  var id = '$' + name;
@@ -536,7 +542,7 @@ class Module
536
542
  end
537
543
 
538
544
  def to_s
539
- name || "#<#{`self.$$is_mod ? 'Module' : 'Class'`}:0x#{__id__.to_s(16)}>"
545
+ `self.$$name` || "#<#{`self.$$is_mod ? 'Module' : 'Class'`}:0x#{__id__.to_s(16)}>"
540
546
  end
541
547
 
542
548
  def undef_method(symbol)
@@ -1,5 +1,9 @@
1
+ class RegexpError < StandardError; end
1
2
  class Regexp
2
- `def.$$is_regexp = true`
3
+ IGNORECASE = 1
4
+ MULTILINE = 4
5
+
6
+ `def.$$is_regexp = true`
3
7
 
4
8
  class << self
5
9
  def escape(string)
@@ -23,11 +27,77 @@ class Regexp
23
27
  alias quote escape
24
28
 
25
29
  def union(*parts)
26
- `new RegExp(parts.join(''))`
30
+ %x{
31
+ var is_first_part_array, quoted_validated, part, options, each_part_options;
32
+ if (parts.length == 0) {
33
+ return /(?!)/;
34
+ }
35
+ // cover the 2 arrays passed as arguments case
36
+ is_first_part_array = parts[0].$$is_array;
37
+ if (parts.length > 1 && is_first_part_array) {
38
+ #{raise TypeError, 'no implicit conversion of Array into String'}
39
+ }
40
+ // deal with splat issues (related to https://github.com/opal/opal/issues/858)
41
+ if (is_first_part_array) {
42
+ parts = parts[0];
43
+ }
44
+ options = undefined;
45
+ quoted_validated = [];
46
+ for (var i=0; i < parts.length; i++) {
47
+ part = parts[i];
48
+ if (part.$$is_string) {
49
+ quoted_validated.push(#{escape(`part`)});
50
+ }
51
+ else if (part.$$is_regexp) {
52
+ each_part_options = #{`part`.options};
53
+ if (options != undefined && options != each_part_options) {
54
+ #{raise TypeError, 'All expressions must use the same options'}
55
+ }
56
+ options = each_part_options;
57
+ quoted_validated.push('('+part.source+')');
58
+ }
59
+ else {
60
+ quoted_validated.push(#{escape(`part`.to_str)});
61
+ }
62
+ }
63
+ }
64
+ # Take advantage of logic that can parse options from JS Regex
65
+ new(`quoted_validated`.join('|'), `options`)
27
66
  end
28
67
 
29
- def new(regexp, options = undefined)
30
- `new RegExp(regexp, options)`
68
+ def new(regexp, options = undefined)
69
+ %x{
70
+ // Play nice with IE8
71
+ if (regexp.$$is_string && regexp.substr(regexp.length-1, 1) == "\\") {
72
+ #{raise RegexpError, "too short escape sequence: /#{regexp}/"}
73
+ }
74
+
75
+ if (options == undefined || #{!options}) {
76
+ options = undefined;
77
+ }
78
+
79
+ if (options != undefined) {
80
+ if (regexp.$$is_regexp) {
81
+ // options are already in regex
82
+ options = undefined;
83
+ }
84
+ else if (options.$$is_number) {
85
+ var result = '';
86
+ if (#{IGNORECASE} & options) {
87
+ result += 'i';
88
+ }
89
+ if (#{MULTILINE} & options) {
90
+ result += 'm';
91
+ }
92
+ options = result;
93
+ }
94
+ else {
95
+ options = 'i';
96
+ }
97
+ }
98
+
99
+ return new RegExp(regexp, options);
100
+ }
31
101
  end
32
102
  end
33
103
 
@@ -70,6 +140,7 @@ class Regexp
70
140
  }
71
141
  }
72
142
 
143
+ // global RegExp maintains state, so not using self/this
73
144
  var md, re = new RegExp(self.source, 'gm' + (self.ignoreCase ? 'i' : ''));
74
145
 
75
146
  while (true) {
@@ -93,6 +164,41 @@ class Regexp
93
164
  def source
94
165
  `self.source`
95
166
  end
167
+
168
+ def options
169
+ # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags is still experimental
170
+ # we need the flags and source does not give us that
171
+ %x{
172
+ var as_string, text_flags, result, text_flag;
173
+ as_string = self.toString();
174
+ if (as_string == "/(?:)/") {
175
+ #{raise TypeError, 'uninitialized Regexp'}
176
+ }
177
+ text_flags = as_string.replace(self.source, '').match(/\w+/);
178
+ result = 0;
179
+ // may have no flags
180
+ if (text_flags == null) {
181
+ return result;
182
+ }
183
+ // first match contains all of our flags
184
+ text_flags = text_flags[0];
185
+ for (var i=0; i < text_flags.length; i++) {
186
+ text_flag = text_flags[i];
187
+ switch(text_flag) {
188
+ case 'i':
189
+ result |= #{IGNORECASE};
190
+ break;
191
+ case 'm':
192
+ result |= #{MULTILINE};
193
+ break;
194
+ default:
195
+ #{raise "RegExp flag #{`text_flag`} does not have a match in Ruby"}
196
+ }
197
+ }
198
+
199
+ return result;
200
+ }
201
+ end
96
202
 
97
203
  alias to_s source
98
204
  end
@@ -155,7 +155,7 @@
155
155
  }
156
156
  else if (typeof(superklass) === 'function') {
157
157
  // passed native constructor as superklass, so bridge it as ruby class
158
- return bridge_class(id, superklass);
158
+ return bridge_class(id, superklass, base);
159
159
  }
160
160
  else {
161
161
  // if class doesnt exist, create a new one with given superclass
@@ -560,14 +560,22 @@
560
560
  *
561
561
  * @param [String] name the name of the ruby class to create
562
562
  * @param [Function] constructor native javascript constructor to use
563
+ * @param [Object] base where the bridge class is being created. If none is supplied, the top level scope (Opal) will be used
563
564
  * @return [Class] returns new ruby class
564
565
  */
565
- function bridge_class(name, constructor) {
566
+ function bridge_class(name, constructor, base) {
566
567
  var klass = boot_class_object(ObjectClass, constructor);
567
568
 
568
569
  klass.$$name = name;
569
570
 
570
- create_scope(Opal, klass, name);
571
+ if (base === undefined) {
572
+ base = Opal;
573
+ }
574
+ else {
575
+ base = base.$$scope;
576
+ }
577
+
578
+ create_scope(base, klass, name);
571
579
  bridged_classes.push(klass);
572
580
 
573
581
  var object_methods = BasicObjectClass.$$methods.concat(ObjectClass.$$methods);
@@ -1155,7 +1163,7 @@
1155
1163
  keys = [],
1156
1164
  _map = {},
1157
1165
  smap = {},
1158
- key, obj, length, khash;
1166
+ key, obj, length, khash, map;
1159
1167
 
1160
1168
  hash.map = _map;
1161
1169
  hash.smap = smap;
@@ -952,127 +952,83 @@ class String
952
952
  alias slice []
953
953
  alias slice! <<
954
954
 
955
- def split(pattern = $; || ' ', limit = undefined)
955
+ def split(pattern = undefined, limit = undefined)
956
956
  %x{
957
- if (pattern === nil || pattern === undefined) {
958
- pattern = #{$;};
959
- }
960
-
961
- var result = [];
962
- if (limit !== undefined) {
963
- limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
964
- }
965
-
966
957
  if (self.length === 0) {
967
958
  return [];
968
959
  }
969
960
 
970
- if (limit === 1) {
971
- return [self];
961
+ if (limit === undefined) {
962
+ limit = 0;
963
+ } else {
964
+ limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
965
+ if (limit === 1) {
966
+ return [self];
967
+ }
972
968
  }
973
969
 
974
- if (pattern && pattern.$$is_regexp) {
975
- var pattern_str = pattern.toString();
970
+ if (pattern === undefined || pattern === nil) {
971
+ pattern = #{$; || ' '};
972
+ }
976
973
 
977
- /* Opal and JS's repr of an empty RE. */
978
- var blank_pattern = (pattern_str.substr(0, 3) == '/^/') ||
979
- (pattern_str.substr(0, 6) == '/(?:)/');
974
+ var result = [],
975
+ string = self.toString(),
976
+ index = 0,
977
+ match,
978
+ i;
980
979
 
981
- /* This is our fast path */
982
- if (limit === undefined || limit === 0) {
983
- result = self.split(blank_pattern ? /(?:)/ : pattern);
980
+ if (pattern.$$is_regexp) {
981
+ pattern = new RegExp(pattern.source, 'gm' + (pattern.ignoreCase ? 'i' : ''));
982
+ } else {
983
+ pattern = #{Opal.coerce_to(pattern, String, :to_str).to_s};
984
+ if (pattern === ' ') {
985
+ pattern = /\s+/gm;
986
+ string = string.replace(/^\s+/, '');
987
+ } else {
988
+ pattern = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gm');
984
989
  }
985
- else {
986
- /* RegExp.exec only has sane behavior with global flag */
987
- if (! pattern.global) {
988
- pattern = eval(pattern_str + 'g');
989
- }
990
-
991
- var match_data;
992
- var prev_index = 0;
993
- pattern.lastIndex = 0;
994
-
995
- while ((match_data = pattern.exec(self)) !== null) {
996
- var segment = self.slice(prev_index, match_data.index);
997
- result.push(segment);
998
-
999
- prev_index = pattern.lastIndex;
1000
-
1001
- if (match_data[0].length === 0) {
1002
- if (blank_pattern) {
1003
- /* explicitly split on JS's empty RE form.*/
1004
- pattern = /(?:)/;
1005
- }
1006
-
1007
- result = self.split(pattern);
1008
- /* with "unlimited", ruby leaves a trail on blanks. */
1009
- if (limit !== undefined && limit < 0 && blank_pattern) {
1010
- result.push('');
1011
- }
1012
-
1013
- prev_index = undefined;
1014
- break;
1015
- }
990
+ }
1016
991
 
1017
- if (limit !== undefined && limit > 1 && result.length + 1 == limit) {
1018
- break;
1019
- }
1020
- }
992
+ result = string.split(pattern);
1021
993
 
1022
- if (prev_index !== undefined) {
1023
- result.push(self.slice(prev_index, self.length));
1024
- }
1025
- }
994
+ while ((i = result.indexOf(undefined)) !== -1) {
995
+ result.splice(i, 1);
1026
996
  }
1027
- else {
1028
- var splitted = 0, start = 0, lim = 0;
1029
997
 
1030
- if (pattern === nil || pattern === undefined) {
1031
- pattern = ' '
1032
- } else {
1033
- pattern = #{Opal.try_convert(pattern, String, :to_str).to_s};
998
+ if (limit === 0) {
999
+ while (result[result.length - 1] === '') {
1000
+ result.length -= 1;
1034
1001
  }
1002
+ return result;
1003
+ }
1035
1004
 
1036
- var string = (pattern == ' ') ? self.replace(/[\r\n\t\v]\s+/g, ' ')
1037
- : self;
1038
- var cursor = -1;
1039
- while ((cursor = string.indexOf(pattern, start)) > -1 && cursor < string.length) {
1040
- if (splitted + 1 === limit) {
1041
- break;
1042
- }
1043
-
1044
- if (pattern == ' ' && cursor == start) {
1045
- start = cursor + 1;
1046
- continue;
1047
- }
1048
-
1049
- result.push(string.substr(start, pattern.length ? cursor - start : 1));
1050
- splitted++;
1051
-
1052
- start = cursor + (pattern.length ? pattern.length : 1);
1053
- }
1005
+ match = pattern.exec(string);
1054
1006
 
1055
- if (string.length > 0 && (limit < 0 || string.length > start)) {
1056
- if (string.length == start) {
1007
+ if (limit < 0) {
1008
+ if (match !== null && match[0] === '' && pattern.source.indexOf('(?=') === -1) {
1009
+ for (i = 0; i < match.length; i++) {
1057
1010
  result.push('');
1058
1011
  }
1059
- else {
1060
- result.push(string.substr(start, string.length));
1061
- }
1062
1012
  }
1013
+ return result;
1063
1014
  }
1064
1015
 
1065
- if (limit === undefined || limit === 0) {
1066
- while (result[result.length-1] === '') {
1067
- result.length = result.length - 1;
1068
- }
1016
+ if (match !== null && match[0] === '') {
1017
+ result.splice(limit - 1, result.length - 1, result.slice(limit - 1).join(''));
1018
+ return result;
1069
1019
  }
1070
1020
 
1071
- if (limit > 0) {
1072
- var tail = result.slice(limit - 1).join('');
1073
- result.splice(limit - 1, result.length - 1, tail);
1021
+ i = 0;
1022
+ while (match !== null) {
1023
+ i++;
1024
+ index = pattern.lastIndex;
1025
+ if (i + 1 === limit) {
1026
+ break;
1027
+ }
1028
+ match = pattern.exec(string);
1074
1029
  }
1075
1030
 
1031
+ result.splice(limit - 1, result.length - 1, string.slice(index));
1076
1032
  return result;
1077
1033
  }
1078
1034
  end