opal 0.8.0.beta1 → 0.8.0.rc1

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