opal 0.6.0 → 0.6.1

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitmodules +27 -3
  4. data/.rspec +1 -1
  5. data/CHANGELOG.md +29 -5
  6. data/README.md +30 -3
  7. data/Rakefile +18 -9
  8. data/bin/opal +1 -1
  9. data/bin/opal-build +70 -7
  10. data/lib/mspec/opal/rake_task.rb +6 -9
  11. data/lib/opal/cli.rb +2 -2
  12. data/lib/opal/nodes/call.rb +1 -1
  13. data/lib/opal/nodes/def.rb +1 -1
  14. data/lib/opal/nodes/rescue.rb +19 -18
  15. data/lib/opal/parser/grammar.rb +8 -3
  16. data/lib/opal/parser/grammar.y +4 -0
  17. data/lib/opal/parser/lexer.rb +10 -5
  18. data/lib/opal/sprockets/environment.rb +9 -0
  19. data/lib/opal/sprockets/erb.rb +9 -1
  20. data/lib/opal/sprockets/processor.rb +10 -14
  21. data/lib/opal/util.rb +50 -15
  22. data/lib/opal/version.rb +1 -1
  23. data/opal.gemspec +4 -4
  24. data/opal/corelib/enumerable.rb +1 -1
  25. data/opal/corelib/enumerator.rb +7 -3
  26. data/opal/corelib/hash.rb +38 -14
  27. data/opal/corelib/kernel.rb +6 -2
  28. data/opal/corelib/module.rb +15 -1
  29. data/opal/corelib/runtime.js +32 -2
  30. data/opal/corelib/string.rb +103 -53
  31. data/opal/corelib/variables.rb +3 -3
  32. data/spec/cli/fixtures/sprockets_file.js.rb +3 -0
  33. data/spec/cli/parser/literal_spec.rb +5 -0
  34. data/spec/cli/sprockets/environment_spec.rb +14 -0
  35. data/spec/cli/sprockets/erb_spec.rb +25 -0
  36. data/spec/cli/sprockets/processor_spec.rb +28 -0
  37. data/spec/{opal/filters → filters}/bugs/array.rb +29 -9
  38. data/spec/{opal/filters → filters}/bugs/basic_object.rb +2 -0
  39. data/spec/{opal/filters → filters}/bugs/class.rb +1 -0
  40. data/spec/{opal/filters → filters}/bugs/enumerable.rb +6 -2
  41. data/spec/filters/bugs/enumerator.rb +3 -0
  42. data/spec/{opal/filters → filters}/bugs/hash.rb +6 -24
  43. data/spec/{opal/filters → filters}/bugs/kernel.rb +0 -0
  44. data/spec/{opal/filters → filters}/bugs/language.rb +14 -3
  45. data/spec/{opal/filters → filters}/bugs/math.rb +3 -1
  46. data/spec/{opal/filters → filters}/bugs/module.rb +0 -0
  47. data/spec/{opal/filters → filters}/bugs/nil.rb +0 -0
  48. data/spec/{opal/filters → filters}/bugs/numeric.rb +1 -1
  49. data/spec/{opal/filters → filters}/bugs/opal.rb +0 -0
  50. data/spec/filters/bugs/regexp.rb +7 -0
  51. data/spec/{opal/filters → filters}/bugs/set.rb +0 -0
  52. data/spec/{opal/filters → filters}/bugs/singleton.rb +0 -0
  53. data/spec/{opal/filters → filters}/bugs/string.rb +40 -13
  54. data/spec/{opal/filters → filters}/bugs/stringscanner.rb +1 -0
  55. data/spec/{opal/filters → filters}/bugs/struct.rb +7 -2
  56. data/spec/{opal/filters → filters}/bugs/symbol.rb +0 -0
  57. data/spec/{opal/filters → filters}/bugs/time.rb +23 -0
  58. data/spec/{opal/filters → filters}/bugs/unknown.rb +0 -0
  59. data/spec/{opal/filters → filters}/unsupported/encoding.rb +34 -1
  60. data/spec/{opal/filters → filters}/unsupported/enumerator.rb +1 -0
  61. data/spec/{opal/filters → filters}/unsupported/float.rb +0 -0
  62. data/spec/{opal/filters → filters}/unsupported/frozen.rb +3 -2
  63. data/spec/{opal/filters → filters}/unsupported/hash_compare_by_identity.rb +0 -0
  64. data/spec/{opal/filters → filters}/unsupported/integer_size.rb +0 -0
  65. data/spec/{opal/filters → filters}/unsupported/method_added.rb +0 -0
  66. data/spec/{opal/filters → filters}/unsupported/mutable_strings.rb +14 -0
  67. data/spec/{opal/filters → filters}/unsupported/private_constants.rb +0 -0
  68. data/spec/{opal/filters → filters}/unsupported/private_methods.rb +0 -0
  69. data/spec/{opal/filters → filters}/unsupported/random.rb +0 -0
  70. data/spec/{opal/filters → filters}/unsupported/ruby_exe.rb +0 -0
  71. data/spec/{opal/filters → filters}/unsupported/tainted.rb +11 -0
  72. data/spec/{opal/filters → filters}/unsupported/time.rb +0 -0
  73. data/spec/{opal/filters → filters}/unsupported/trusted.rb +14 -0
  74. data/spec/opal/core/kernel/methods_spec.rb +16 -2
  75. data/spec/opal/core/language/string_spec.rb +29 -1
  76. data/spec/opal/core/module/remove_const_spec.rb +1 -1
  77. data/spec/opal/stdlib/erb/erb_spec.rb +3 -3
  78. data/spec/opal/stdlib/native/native_reader_spec.rb +22 -0
  79. data/spec/opal/stdlib/native/native_writer_spec.rb +30 -0
  80. data/spec/rubyspecs +285 -0
  81. data/spec/{opal/spec_helper.rb → spec_helper.rb} +0 -0
  82. data/stdlib/native.rb +21 -0
  83. data/stdlib/opal-parser.rb +14 -3
  84. data/stdlib/promise.rb +2 -2
  85. metadata +113 -97
  86. data/spec/opal/filters/bugs/regexp.rb +0 -5
  87. data/spec/opal/rubyspecs +0 -285
@@ -271,7 +271,7 @@ class Module
271
271
  self._proto[jsid] = block;
272
272
  $opal.donate(self, [jsid]);
273
273
 
274
- return null;
274
+ return name;
275
275
  }
276
276
  end
277
277
 
@@ -304,6 +304,20 @@ class Module
304
304
  self
305
305
  end
306
306
 
307
+ def include?(mod)
308
+ %x{
309
+ for (var cls = self; cls; cls = cls.parent) {
310
+ for (var i = 0; i != cls.__inc__.length; i++) {
311
+ var mod2 = cls.__inc__[i];
312
+ if (mod === mod2) {
313
+ return true;
314
+ }
315
+ }
316
+ }
317
+ return false;
318
+ }
319
+ end
320
+
307
321
  def instance_method(name)
308
322
  %x{
309
323
  var meth = self._proto['$' + name];
@@ -602,6 +602,24 @@
602
602
  return block.apply(null, args);
603
603
  };
604
604
 
605
+ // Finds the corresponding exception match in candidates. Each candidate can
606
+ // be a value, or an array of values. Returns null if not found.
607
+ Opal.$rescue = function(exception, candidates) {
608
+ for (var i = 0; i != candidates.length; i++) {
609
+ var candidate = candidates[i];
610
+ if (candidate._isArray) {
611
+ var subresult;
612
+ if (subresult = Opal.$rescue(exception, candidate)) {
613
+ return subresult;
614
+ }
615
+ }
616
+ else if (candidate['$==='](exception)) {
617
+ return candidate;
618
+ }
619
+ }
620
+ return null;
621
+ };
622
+
605
623
  Opal.is_a = function(object, klass) {
606
624
  if (object.__meta__ === klass) {
607
625
  return true;
@@ -762,7 +780,14 @@
762
780
  var args = arguments[0];
763
781
 
764
782
  for (var i = 0, length = args.length; i < length; i++) {
765
- var key = args[i][0], obj = args[i][1];
783
+ var pair = args[i];
784
+
785
+ if (pair.length !== 2) {
786
+ throw Opal.ArgumentError.$new("value not of length 2: " + pair.$inspect());
787
+ }
788
+
789
+ var key = pair[0],
790
+ obj = pair[1];
766
791
 
767
792
  if (assocs[key] == null) {
768
793
  keys.push(key);
@@ -780,7 +805,12 @@
780
805
  }
781
806
  }
782
807
  else {
783
- for (var i = 0, length = arguments.length; i < length; i++) {
808
+ var length = arguments.length;
809
+ if (length % 2 !== 0) {
810
+ throw Opal.ArgumentError.$new("odd number of arguments for Hash");
811
+ }
812
+
813
+ for (var i = 0; i < length; i++) {
784
814
  var key = arguments[i],
785
815
  obj = arguments[++i];
786
816
 
@@ -599,71 +599,126 @@ class String
599
599
 
600
600
  def split(pattern = $; || ' ', limit = undefined)
601
601
  %x{
602
+ if (pattern === nil || pattern === undefined) {
603
+ pattern = #{$;};
604
+ }
605
+
606
+ var result = [];
607
+ if (limit !== undefined) {
608
+ limit = #{Opal.coerce_to!(limit, Integer, :to_int)};
609
+ }
610
+
611
+ if (self.length === 0) {
612
+ return [];
613
+ }
614
+
615
+ if (limit === 1) {
616
+ return [self];
617
+ }
618
+
602
619
  if (pattern && pattern._isRegexp) {
603
- return self.split(pattern, limit);
604
- } else {
605
- result = [],
606
- splitted = start = lim = 0,
607
- splat = #{Opal.try_convert(pattern, String, :to_str).to_s};
620
+ var pattern_str = pattern.toString();
608
621
 
609
- if (undefined !== limit) {
610
- lim = #{Opal.try_convert(limit, Integer, :to_int)};
611
- }
622
+ /* Opal and JS's repr of an empty RE. */
623
+ var blank_pattern = (pattern_str.substr(0, 3) == '/^/') ||
624
+ (pattern_str.substr(0, 6) == '/(?:)/');
612
625
 
613
- if (pattern === nil) {
614
- if (#{$;} === undefined || #{$;} === nil) {
615
- splat = ' ';
616
- }
617
- else {
618
- splat = #{$;};
619
- }
626
+ /* This is our fast path */
627
+ if (limit === undefined || limit === 0) {
628
+ result = self.split(blank_pattern ? /(?:)/ : pattern);
629
+ }
630
+ else {
631
+ /* RegExp.exec only has sane behavior with global flag */
632
+ if (! pattern.global) {
633
+ pattern = eval(pattern_str + 'g');
620
634
  }
621
635
 
622
- if (lim == 1) {
623
- if (self.length == 0) {
624
- return [];
625
- }
626
- else {
627
- return [self];
628
- }
629
- }
636
+ var match_data;
637
+ var prev_index = 0;
638
+ pattern.lastIndex = 0;
630
639
 
631
- string = (splat == ' ') ? self.replace(/[\r\n\t\v]\s+/g, ' ')
632
- : self;
640
+ while ((match_data = pattern.exec(self)) !== null) {
641
+ var segment = self.slice(prev_index, match_data.index);
642
+ result.push(segment);
633
643
 
634
- while ((cursor = string.indexOf(splat, start)) > -1 && cursor < string.length) {
635
- if (splitted + 1 == lim) {
644
+ prev_index = pattern.lastIndex;
645
+
646
+ if (match_data[0].length === 0) {
647
+ if (blank_pattern) {
648
+ /* explicitly split on JS's empty RE form.*/
649
+ pattern = /(?:)/;
650
+ }
651
+
652
+ result = self.split(pattern);
653
+ /* with "unlimited", ruby leaves a trail on blanks. */
654
+ if (limit !== undefined && limit < 0 && blank_pattern) {
655
+ result.push('');
656
+ }
657
+
658
+ prev_index = undefined;
636
659
  break;
637
660
  }
638
661
 
639
- if (splat == ' ' && cursor == start) {
640
- start = cursor + 1;
641
- continue;
662
+ if (limit !== undefined && limit > 1 && result.length + 1 == limit) {
663
+ break;
642
664
  }
665
+ }
643
666
 
644
- result.push(string.substr(start, splat.length ? cursor - start : 1));
645
- splitted++;
667
+ if (prev_index !== undefined) {
668
+ result.push(self.slice(prev_index, self.length));
669
+ }
670
+ }
671
+ }
672
+ else {
673
+ var splitted = 0, start = 0, lim = 0;
674
+
675
+ if (pattern === nil || pattern === undefined) {
676
+ pattern = ' '
677
+ } else {
678
+ pattern = #{Opal.try_convert(pattern, String, :to_str).to_s};
679
+ }
646
680
 
647
- start = cursor + (splat.length ? splat.length : 1);
681
+ var string = (pattern == ' ') ? self.replace(/[\r\n\t\v]\s+/g, ' ')
682
+ : self;
683
+ var cursor = -1;
684
+ while ((cursor = string.indexOf(pattern, start)) > -1 && cursor < string.length) {
685
+ if (splitted + 1 === limit) {
686
+ break;
648
687
  }
649
688
 
650
- if (string.length > 0 && (limit || lim < 0 || string.length > start)) {
651
- if (string.length == start) {
652
- result.push('');
653
- }
654
- else {
655
- result.push(string.substr(start, string.length));
656
- }
689
+ if (pattern == ' ' && cursor == start) {
690
+ start = cursor + 1;
691
+ continue;
657
692
  }
658
693
 
659
- if (limit === undefined || lim == 0) {
660
- while (result.length > 0 && result[result.length - 1].length == 0) {
661
- result.pop();
662
- }
694
+ result.push(string.substr(start, pattern.length ? cursor - start : 1));
695
+ splitted++;
696
+
697
+ start = cursor + (pattern.length ? pattern.length : 1);
698
+ }
699
+
700
+ if (string.length > 0 && (limit < 0 || string.length > start)) {
701
+ if (string.length == start) {
702
+ result.push('');
663
703
  }
704
+ else {
705
+ result.push(string.substr(start, string.length));
706
+ }
707
+ }
708
+ }
664
709
 
665
- return result;
710
+ if (limit === undefined || limit === 0) {
711
+ while (result[result.length-1] === '') {
712
+ result.length = result.length - 1;
666
713
  }
714
+ }
715
+
716
+ if (limit > 0) {
717
+ var tail = result.slice(limit - 1).join('');
718
+ result.splice(limit - 1, result.length - 1, tail);
719
+ }
720
+
721
+ return result;
667
722
  }
668
723
  end
669
724
 
@@ -825,14 +880,9 @@ class String
825
880
  end
826
881
 
827
882
  def to_proc
828
- %x{
829
- var name = '$' + self;
830
-
831
- return function(arg) {
832
- var meth = arg[name];
833
- return meth ? meth.call(arg) : arg.$method_missing(name);
834
- };
835
- }
883
+ proc do |recv, *args|
884
+ recv.send(self, *args)
885
+ end
836
886
  end
837
887
 
838
888
  def to_s
@@ -19,6 +19,6 @@ $SAFE = 0
19
19
 
20
20
  RUBY_PLATFORM = 'opal'
21
21
  RUBY_ENGINE = 'opal'
22
- RUBY_VERSION = '2.0.0'
23
- RUBY_ENGINE_VERSION = '0.6.0'
24
- RUBY_RELEASE_DATE = '2014-03-05'
22
+ RUBY_VERSION = '2.1.1'
23
+ RUBY_ENGINE_VERSION = '0.6.1'
24
+ RUBY_RELEASE_DATE = '2014-04-15'
@@ -0,0 +1,3 @@
1
+ require 'opal'
2
+ require 'native'
3
+ puts 'sprockets!'
@@ -94,6 +94,11 @@ describe Opal::Parser do
94
94
  parsed("{ a: 1 }").should == [:hash, [:sym, :a], [:int, 1]]
95
95
  parsed("{ a: 1, b: 2 }").should == [:hash, [:sym, :a], [:int, 1], [:sym, :b], [:int, 2]]
96
96
  end
97
+
98
+ it "parses hash arrows without spaces around arguments" do
99
+ parsed("{1=>2}").should == [:hash, [:int, 1], [:int, 2]]
100
+ parsed("{:foo=>2}").should == [:hash, [:sym, :foo], [:int, 2]]
101
+ end
97
102
  end
98
103
 
99
104
  describe "parsing regexps" do
@@ -0,0 +1,14 @@
1
+ require 'cli/spec_helper'
2
+ require 'opal/sprockets/environment'
3
+
4
+ describe Opal::Environment do
5
+ let(:env) { described_class.new }
6
+ let(:logical_path) { 'sprockets_file' }
7
+
8
+ before { env.append_path File.expand_path('../../fixtures/', __FILE__) }
9
+
10
+ it 'compiles Ruby to JS' do
11
+ expect(env[logical_path].source).to include('$puts(')
12
+ expect(env[logical_path+'.js'].source).to include('$puts(')
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ require 'cli/spec_helper'
2
+ require 'opal/sprockets/erb'
3
+
4
+ describe Opal::ERB::Processor do
5
+ let(:pathname) { Pathname("/Code/app/mylib/opal/foo.#{ext}") }
6
+ let(:_context) { double('_context', :logical_path => "foo.#{ext}", :pathname => pathname) }
7
+ let(:required_assets) { [] }
8
+ let(:template) { described_class.new { |t| %Q{<a href="<%= url %>"><%= name %></a>} } }
9
+ before { _context.stub(:require_asset) {|asset| required_assets << asset } }
10
+
11
+ let(:ext) { 'opalerb' }
12
+
13
+ it "is registered for '.opalerb' files" do
14
+ expect(Tilt["test.#{ext}"]).to eq(described_class)
15
+ end
16
+
17
+ it 'renders the template' do
18
+ expect(template.render(_context)).to include('"<a href=\""')
19
+ end
20
+
21
+ it 'implicitly requires "erb"' do
22
+ template.render(_context)
23
+ expect(required_assets).to eq(['erb'])
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require 'cli/spec_helper'
2
+ require 'opal/sprockets/processor'
3
+
4
+ describe Opal::Processor do
5
+ let(:pathname) { Pathname("/Code/app/mylib/opal/foo.#{ext}") }
6
+ let(:_context) { double('_context', :logical_path => "foo.#{ext}", :pathname => pathname, :resolve => pathname.expand_path) }
7
+
8
+ %w[rb js.rb opal js.opal].each do |ext|
9
+ let(:ext) { ext }
10
+
11
+ describe %Q{with extension ".#{ext}"} do
12
+ it "is registered for '.#{ext}' files" do
13
+ expect(Tilt["test.#{ext}"]).to eq(described_class)
14
+ end
15
+
16
+ it "compiles and evaluates the template on #render" do
17
+ template = described_class.new { |t| "puts 'Hello, World!'\n" }
18
+ expect(template.render(_context)).to include('"Hello, World!"')
19
+ end
20
+
21
+ it "can be rendered more than once" do
22
+ template = described_class.new(_context) { |t| "puts 'Hello, World!'\n" }
23
+ 3.times { expect(template.render(_context)).to include('"Hello, World!"') }
24
+ end
25
+ end
26
+ end
27
+
28
+ end
@@ -130,17 +130,29 @@ opal_filter "Array" do
130
130
  fails "Array#rotate does not mutate the receiver"
131
131
  fails "Array#rotate returns a copy of the array when its length is one or zero"
132
132
 
133
- fails "Array#sample raises a RangeError if the value is equal to the Array size"
134
- fails "Array#sample raises a RangeError if the value is less than zero"
135
- fails "Array#sample calls #to_int on the Object returned by #rand"
136
- fails "Array#sample ignores an Object passed for the RNG if it does not define #rand"
137
133
  fails "Array#sample calls #rand on the Object passed by the :random key in the arguments Hash"
138
- fails "Array#sample calls #to_int on the first argument and #to_hash on the second when passed Objects"
139
134
  fails "Array#sample calls #to_hash to convert the passed Object"
140
- fails "Array#sample raises ArgumentError when passed a negative count"
135
+ fails "Array#sample calls #to_int on the Object returned by #rand"
136
+ fails "Array#sample calls #to_int on the first argument and #to_hash on the second when passed Objects"
141
137
  fails "Array#sample calls #to_int to convert the count when passed an Object"
142
138
  fails "Array#sample does not return the same value if the Array has unique values"
139
+ fails "Array#sample ignores an Object passed for the RNG if it does not define #rand"
140
+ fails "Array#sample raises ArgumentError when passed a negative count"
141
+ fails "Array#sample raises a RangeError if the value is equal to the Array size"
142
+ fails "Array#sample raises a RangeError if the value is less than zero"
143
143
  fails "Array#sample returns at most the number of elements in the Array"
144
+ fails "Array#sample when the object returned by #rand is not a Fixnum but responds to #to_int calls #to_int on the Object"
145
+ fails "Array#sample when the object returned by #rand is not a Fixnum but responds to #to_int raises a RangeError if the value is equal to the Array size"
146
+ fails "Array#sample when the object returned by #rand is not a Fixnum but responds to #to_int raises a RangeError if the value is less than zero"
147
+ fails "Array#sample with options calls #rand on the Object passed by the :random key in the arguments Hash"
148
+ fails "Array#sample with options calls #to_hash to convert the passed Object"
149
+ fails "Array#sample with options calls #to_int on the first argument and #to_hash on the second when passed Objects"
150
+ fails "Array#sample with options ignores an Object passed for the RNG if it does not define #rand"
151
+ fails "Array#sample with options when the object returned by #rand is a Fixnum raises a RangeError if the value is equal to the Array size"
152
+ fails "Array#sample with options when the object returned by #rand is a Fixnum raises a RangeError if the value is less than zero"
153
+ fails "Array#sample with options when the object returned by #rand is a Fixnum uses the fixnum as index"
154
+
155
+ fails "Array#select returns a new array of elements for which block is true"
144
156
 
145
157
  fails "Array#shuffle attempts coercion via #to_hash"
146
158
  fails "Array#shuffle is not destructive"
@@ -154,11 +166,12 @@ opal_filter "Array" do
154
166
 
155
167
  fails "Array#shuffle! returns the same values, in a usually different order"
156
168
 
157
- fails "Array#slice! does not expand array with negative indices out of bounds"
158
- fails "Array#slice! does not expand array with indices out of bounds"
159
169
  fails "Array#slice! calls to_int on range arguments"
160
- fails "Array#slice! removes and return elements in range"
161
170
  fails "Array#slice! calls to_int on start and length arguments"
171
+ fails "Array#slice! does not expand array with indices out of bounds"
172
+ fails "Array#slice! does not expand array with negative indices out of bounds"
173
+ fails "Array#slice! removes and return elements in range"
174
+ fails "Array#slice! removes and returns elements in end-exclusive ranges"
162
175
 
163
176
  fails "Array#sort_by! makes some modification even if finished sorting when it would break in the given block"
164
177
  fails "Array#sort_by! returns the specified value when it would break in the given block"
@@ -170,7 +183,9 @@ opal_filter "Array" do
170
183
 
171
184
  fails "Array#uniq compares elements based on the value returned from the block"
172
185
  fails "Array#uniq compares elements with matching hash codes with #eql?"
186
+ fails "Array#uniq handles nil and false like any other values"
173
187
  fails "Array#uniq uses eql? semantics"
188
+ fails "Array#uniq yields items in order"
174
189
 
175
190
  fails "Array#uniq! compares elements based on the value returned from the block"
176
191
 
@@ -179,6 +194,11 @@ opal_filter "Array" do
179
194
  fails "Array#values_at returns an array of elements in the ranges when passes ranges"
180
195
  fails "Array#values_at calls to_int on arguments of ranges when passes ranges"
181
196
  fails "Array#values_at does not return subclass instance on Array subclasses"
197
+ fails "Array#values_at when passed ranges returns an array of elements in the ranges"
198
+ fails "Array#values_at when passed ranges calls to_int on arguments of ranges"
199
+ fails "Array#values_at when passed a range fills with nil if the index is out of the range"
200
+ fails "Array#values_at when passed a range on an empty array fills with nils if the index is out of the range"
201
+
182
202
 
183
203
  fails "Array#zip calls #to_ary to convert the argument to an Array"
184
204
  fails "Array#zip uses #each to extract arguments' elements when #to_ary fails"