opal 0.9.0.beta1 → 0.9.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -20
  3. data/CONTRIBUTING.md +8 -16
  4. data/lib/opal/compiler.rb +5 -0
  5. data/lib/opal/nodes/def.rb +10 -3
  6. data/lib/opal/nodes/definitions.rb +14 -3
  7. data/lib/opal/paths.rb +21 -4
  8. data/lib/opal/version.rb +1 -1
  9. data/opal/corelib/array.rb +1 -1
  10. data/opal/corelib/basic_object.rb +9 -9
  11. data/opal/corelib/constants.rb +2 -2
  12. data/opal/corelib/enumerable.rb +79 -2
  13. data/opal/corelib/error.rb +18 -0
  14. data/opal/corelib/helpers.rb +2 -8
  15. data/opal/corelib/kernel.rb +7 -7
  16. data/opal/corelib/module.rb +10 -10
  17. data/opal/corelib/number.rb +1 -1
  18. data/opal/corelib/regexp.rb +31 -30
  19. data/opal/corelib/string.rb +1 -1
  20. data/opal/corelib/struct.rb +8 -10
  21. data/opal/corelib/time.rb +4 -2
  22. data/opal/corelib/unsupported.rb +2 -2
  23. data/spec/filters/bugs/enumerable.rb +0 -19
  24. data/spec/filters/bugs/exception.rb +0 -4
  25. data/spec/filters/bugs/language.rb +7 -5
  26. data/spec/filters/bugs/module.rb +18 -19
  27. data/spec/filters/bugs/regexp.rb +0 -3
  28. data/spec/filters/bugs/set.rb +0 -8
  29. data/spec/filters/bugs/struct.rb +2 -2
  30. data/spec/filters/unsupported/bignum.rb +1 -0
  31. data/spec/filters/unsupported/language.rb +7 -0
  32. data/spec/lib/compiler_spec.rb +5 -0
  33. data/spec/lib/parser/undef_spec.rb +4 -0
  34. data/spec/lib/paths_spec.rb +11 -0
  35. data/spec/lib/spec_helper.rb +1 -1
  36. data/spec/opal/core/arity_spec.rb +142 -0
  37. data/spec/rubyspecs +1 -0
  38. data/stdlib/json.rb +13 -8
  39. data/stdlib/native.rb +1 -1
  40. data/stdlib/observer.rb +1 -1
  41. data/stdlib/ostruct.rb +1 -1
  42. data/stdlib/set.rb +33 -0
  43. data/tasks/testing.rake +8 -7
  44. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1af59805d32671acdce95d17776362c714d82c6d
4
- data.tar.gz: cbe41468214bfdd99902f85301182b37a4281a8a
3
+ metadata.gz: a71d75efaebcd98ae54fc1221323252c84d83725
4
+ data.tar.gz: 81f52eea1fb84c5bb009ccd90bdf4cbc2f8344a8
5
5
  SHA512:
6
- metadata.gz: 280f8bc25e11405068eb0794bc3c094885f2b43266b9dfa8716e1f25009118a5c0556ed8b8d489f1f406cc862817753fb70065eda5e744c3f2f0de142607611b
7
- data.tar.gz: d4ad1d81debb785c025807f6d18281dd191df37efef3afb2d548e611aa36ecb44bbe43846a8fe900f03cec73ec1be0159483d3c18f373eda3beda498bb16aa2b
6
+ metadata.gz: c9fa8aa94a8350f9ec2bde008a844c4fb09d5b05917abc21045791b584adef85951106190840e50b29e047ad0f464e0acfbee5e8ac5ba2872ffac59574efc136
7
+ data.tar.gz: 3744c8d573fa179442543ad09d876ee64d56b2d0114c151cc2789821741c52206edce4c0ba798658d91ee3c25679aa37b37c3eb5548e6d1611a3a68b42409f5c
data/CHANGELOG.md CHANGED
@@ -1,43 +1,62 @@
1
1
  ## 0.9.0 (edge)
2
2
 
3
- * A `console` wrapper has been added to the stdlib, requiring it will make available the `$console` global variable.
3
+ * A `console` wrapper has been added to the stdlib, requiring it will make available the `$console` global variable.
4
4
 
5
- * `Kernel#pp` no longer forwards arguments directly to `console.log`, this behavior has been replaced by stdlib's own `console.rb` (see above).
5
+ * `Kernel#pp` no longer forwards arguments directly to `console.log`, this behavior has been * `replaced by stdlib's own `console.rb` (see above).
6
6
 
7
- * `method_added`, `method_removed` and `method_undefined` reflection now works.
7
+ * `method_added`, `method_removed` and `method_undefined` reflection now works.
8
8
 
9
- * `singleton_method_added`, `singleton_method_removed` and `singleton_method_undefined` reflection now works.
9
+ * `singleton_method_added`, `singleton_method_removed` and `singleton_method_undefined` reflection now works.
10
10
 
11
- * Now you can bridge a native class to a Ruby class that inherits from another Ruby class
11
+ * Now you can bridge a native class to a Ruby class that inherits from another Ruby class
12
12
 
13
- * `Numeric` semantics are now compliant with Ruby.
13
+ * `Numeric` semantics are now compliant with Ruby.
14
14
 
15
- * `Complex` has been fully implemented.
15
+ * `Complex` has been fully implemented.
16
16
 
17
- * `Rational` has been fully implemented.
17
+ * `Rational` has been fully implemented.
18
18
 
19
- * `Opal::Sprockets.javascript_include_tag` has been add to allow easy debug mode (i.e. with source maps) when including a sprockets asset into an HTML page.
19
+ * `Opal::Sprockets.javascript_include_tag` has been add to allow easy debug mode (i.e. with source maps) when including a sprockets asset into an HTML page.
20
20
 
21
- * `Opal::Processor.load_asset_code(sprockets, name)` has been deprecated in favor of `Opal::Sprockets.load_asset(name, sprockets)`.
21
+ * `Opal::Processor.load_asset_code(sprockets, name)` has been deprecated in favor of `Opal::Sprockets.load_asset(name, sprockets)`.
22
22
 
23
- * `Struct#hash` now works properly based on struct contents
23
+ * `Struct#hash` now works properly based on struct contents
24
24
 
25
- * No longer crashes when calling a method with an opt arg followed by an optional kwarg when called without the kwarg
25
+ * No longer crashes when calling a method with an opt arg followed by an optional kwarg when called without the kwarg
26
26
 
27
- * Newly compliant with RubySpec:
28
- * `Enumerable#chunk`
27
+ * Newly compliant with RubySpec:
28
+ * `Enumerable#chunk`
29
+ * `Enumerable#each_cons`
30
+ * `Enumerable#minmax`
31
+
32
+ * Operator methods (e.g. `+`, `<`, etc.) can be handled by `method_missing`
33
+
34
+ * `OpenStruct` - fixed `#method missing`, `#inspect`, `#to_s`, `#delete_field`. Fully compliant except for frozen and marshal behavior.
35
+
36
+ * Fix issue where passing a block after a parameter and a hash was causing block to not be passed (e.g. `method1 some_param, 'a' => 1, &block`)
37
+
38
+ * `Kernel#raise` now properly re-raises exceptions (regardless of how many levels deep you are) and works properly if supplied a class that has an exception method.
39
+
40
+ * `Exception#exception`, `Exception::exception`, `Exception#message`, and `Exception#to_s` are fully implemented
41
+
42
+ * Method defs issued inside `Module#instance_eval` and `Class#instance_eval`, and the respective `exec` now create class methods
43
+
44
+ * You can make direct JavaScript method calls on using the `recv.JS.method`syntax. Has support for method calls, final callback (as a block), property getter and setter (via `#[]` and `#[]=`), splats, JavaScript keywords (via the `::JS` module) and global functions (after `require "js"`).
45
+
46
+ * `Set#superset?`, `Set#subset?`, and the respective 'proper_' variant of each are now implemented
47
+
48
+ * Now with enabled arity checks calling a method with more arguments than those supported by its signature raises an `ArgumentError` as well.
29
49
 
30
- * Operator methods (e.g. `+`, `<`, etc.) can be handled by `method_missing`
50
+ * `NameError` and `NoMethodError` - add `#name` and `#args` attributes
31
51
 
32
- * `OpenStruct` - fixed `#method missing`, `#inspect`, `#to_s`, `#delete_field`. Fully compliant except for frozen and marshal behavior.
52
+ * Previously arity checks would raise an error without clearing the block for a method, the could lead to strange bugs in case the error was rescued.
33
53
 
34
- * Fix issue where passing a block after a parameter and a hash was causing block to not be passed (e.g. `method1 some_param, 'a' => 1, &block`)
54
+ * `RegExp#match` now works correctly in multiline mode with white space
35
55
 
36
- * `Kernel#raise` now properly re-raises exceptions (regardless of how many levels deep you are) and works properly if supplied a class that has an exception method.
56
+ * `Regexp#===` returns false when the right hand side of the expression cannot be coereced to a string (instead of throwing a TypeError)
37
57
 
38
- * `Exception#exception`, `Exception::exception`, `Exception#message`, and `Exception#to_s` are fully implemented
58
+ * `Regexp#options` has been optimized and correctly returns 0 when called on a Regexp literal without any options (e.g. //)
39
59
 
40
- * Method defs issued inside `Module#instance_eval` and `Class#instance_eval`, and the respective `exec` now create class methods
41
60
 
42
61
  ## 0.8.1 2015-10-12
43
62
 
data/CONTRIBUTING.md CHANGED
@@ -38,7 +38,7 @@ $ bundle install
38
38
  $ npm install -g jshint
39
39
  ```
40
40
 
41
- RubySpec related repos must be cloned as a git submodules:
41
+ RubySpec related repos must be cloned as git submodules:
42
42
 
43
43
  ```
44
44
  $ git submodule update --init
@@ -58,22 +58,22 @@ You are now ready to make your first contribution to Opal! At a high level, your
58
58
 
59
59
  ## Down The Rabbit Hole
60
60
 
61
- Before making changes to Opal source, you need to understand a little about how the test suite works. Every spec that Opal test suite executes is listed in `spec/rubyspecs` file. Each line in that file is a path to either a spec file or a directory full of spec files. If it's a path to a directory, all spec files in that directory will be executed when you run the test suite. All paths are relative to the top-level `specs` directory. Let's follow one of these paths - `rubyspec/core/string/sub_spec` - and see where it goes.
61
+ Before making changes to Opal source, you need to understand a little about how the test suite works. Every spec that Opal test suite executes is listed in `spec/rubyspecs` file. Each line in that file is a path to either a spec file or a directory full of spec files. If it's a path to a directory, all spec files in that directory will be executed when you run the test suite. Lines starting with a `!` represent files that are excluded (i.e. "execute all files in a given directory, *except* this file"), and lines starting with a `#` are ignored as comments. All paths are relative to the top-level `specs` directory. Let's follow one of these paths - `rubyspec/core/string/sub_spec` - and see where it goes.
62
62
 
63
- Navigating to `spec/rubyspec/core` directory, you see that it contains multiple sub-directories, usually named after the Ruby class or module. Drilling further down into `spec/rubyspec/core/string` you see all the spec files for the various `String` behaviors under test, usually named by a method name followed by `_spec.rb`. Opening `spec/rubyspec/core/string/sub_spec.rb` you finally see the code that checks the correctness of Opal's implementation of `String#sub` method's behavior.
63
+ Navigating to `spec/rubyspec/core` directory, you see that it contains multiple sub-directories, usually named after the Ruby class or module. Drilling further down into `spec/rubyspec/core/string` you see all the spec files for the various `String` behaviors under test, usually named by a method name followed by `_spec.rb`. Opening `spec/rubyspec/core/string/sub_spec.rb` you finally see the code that checks the correctness of Opal's implementation of the `String#sub` method's behavior.
64
64
 
65
65
  When you execute `$ bundle exec rake`, the code in this file is executed, along with all the other specs in the entire test suite. It's a good idea to run the entire test suite when you feel you reached a certain milestone in the course of making your changes (exactly what that means is up to you), and definitely do `$ bundle exec rake` before commiting your changes to make sure they have not introduced regressions or other unintended side effects.
66
66
 
67
67
  But you will want to run tests as often as possible, after every small change, and running the entire test suite will slow you down. You need to be able to execute a single spec that is concerned with the feature you are currently working on. To accomplish this, just add `PATTERN` to your spec invocation command, like this:
68
68
 
69
69
  ```
70
- $ env PATTERN="spec/rubyspec/core/string/sub_spec.rb" bundle exec rake mspec_node
70
+ $ bundle exec rake mspec_rubyspec_node PATTERN=spec/rubyspec/core/string/sub_spec.rb
71
71
  ```
72
72
 
73
73
  This will make sure that only `spec/rubyspec/core/string/sub_spec.rb` is run, and no other specs are executed. Globs can be used too:
74
74
 
75
75
  ```
76
- $ env PATTERN="spec/rubyspec/core/string/*_spec.rb" bundle exec rake mspec_node
76
+ $ bundle exec rake mspec_rubyspec_node PATTERN=spec/rubyspec/core/string/*_spec.rb
77
77
  ```
78
78
 
79
79
  Another way to quickly validate ideas and play with your changes is to use `opal-repl`, a tool similar to `irb`. Running `opal-repl` drops you into an interactive environment with your current version of Opal loaded, including any changes you have made.
@@ -168,17 +168,9 @@ If I were to continue running benchmarks, more columns would be added to the rep
168
168
  This type of benchmarking relies on a feature of MSpec whereby you can ask it to execute every example in a given spec multiple times. Adding `BM=<number of times>` to your regular spec suite invocation command will hook into this MSpec functionality, collect timing information, and dump the results into the benchmarking workspace, making them available for reporting. Below is an example run with a single spec and `BM` set to `100`, meaning each example in the spec would be run 100 times.
169
169
 
170
170
  ```
171
- $ bundle exec rake mspec_node PATTERN=spec/rubyspec/core/array/permutation_spec.rb BM=100
172
- [44, :filters]
173
- [1, :custom]
174
- mkdir -p tmp
175
- mkdir -p tmp/bench
176
- ruby -rbundler/setup -rmspec/opal/special_calls bin/opal -gmspec -Ispec -Ilib -smspec/helpers/tmp -smspec/helpers/environment -smspec/guards/block_device -smspec/guards/endian -rnodejs/io -rnodejs/kernel -Dwarning -A tmp/mspec_node.rb -c > tmp/mspec_node.js
177
- jshint --verbose tmp/mspec_node.js
178
- NODE_PATH=stdlib/nodejs/node_modules node tmp/mspec_node.js
179
- ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
180
- Finished
181
- 1200 examples, 0 failures (time taken: 0.7880001068115234)
171
+ $ bundle exec rake mspec_rubyspec_node PATTERN=spec/rubyspec/core/array/permutation_spec.rb BM=100
172
+
173
+ ...
182
174
 
183
175
  Benchmark results have been written to tmp/bench/Spec1
184
176
  To view the results, run bundle exec rake bench:report
data/lib/opal/compiler.rb CHANGED
@@ -318,6 +318,11 @@ module Opal
318
318
  return returns s(:nil) unless sexp
319
319
 
320
320
  case sexp.type
321
+ # Undefs go from 1 ruby undef a,b,c to multiple JS Opal.udef() calls, so need to treat them as individual statements
322
+ # and put the return on the last one
323
+ when :undef
324
+ last = sexp.pop
325
+ sexp << returns(last)
321
326
  when :break, :next, :redo
322
327
  sexp
323
328
  when :yield
@@ -80,9 +80,10 @@ module Opal
80
80
  end
81
81
 
82
82
  unshift "\n#{current_indent}", scope.to_vars
83
- line stmt_code
84
83
 
85
- unshift arity_code if arity_code
84
+ line arity_code if arity_code
85
+
86
+ line stmt_code
86
87
 
87
88
  unshift "var $zuper = $slice.call(arguments, 0);" if scope.uses_zuper
88
89
 
@@ -243,7 +244,13 @@ module Opal
243
244
  aritycode = "var $arity = arguments.length;"
244
245
 
245
246
  if arity < 0 # splat or opt args
246
- aritycode + "if ($arity < #{-(arity + 1)}) { Opal.ac($arity, #{arity}, this, #{meth}); }"
247
+ min_arity = -(arity + 1)
248
+ max_arity = args.size - 1
249
+ max_arity -= 1 if block_name
250
+ checks = []
251
+ checks << "$arity < #{min_arity}" if min_arity > 0
252
+ checks << "$arity > #{max_arity}" if max_arity and not(splat)
253
+ aritycode + "if (#{checks.join(' || ')}) { Opal.ac($arity, #{arity}, this, #{meth}); }" if checks.size > 0
247
254
  else
248
255
  aritycode + "if ($arity !== #{arity}) { Opal.ac($arity, #{arity}, this, #{meth}); }"
249
256
  end
@@ -16,10 +16,21 @@ module Opal
16
16
  class UndefNode < Base
17
17
  handle :undef
18
18
 
19
- children :mid
20
-
21
19
  def compile
22
- push "Opal.udef(self, '$#{mid[1].to_s}');"
20
+ children.each do |child|
21
+ value = child[1]
22
+ statements = []
23
+ if child[0] == :js_return
24
+ value = value[1]
25
+ statements << expr(s(:js_return))
26
+ end
27
+ statements << "Opal.udef(self, '$#{value.to_s}');"
28
+ if children.length > 1 && child != children.first
29
+ line *statements
30
+ else
31
+ push *statements
32
+ end
33
+ end
23
34
  end
24
35
  end
25
36
 
data/lib/opal/paths.rb CHANGED
@@ -18,15 +18,25 @@ module Opal
18
18
  def self.append_path(path)
19
19
  append_paths(path)
20
20
  end
21
+
22
+ # Same as #append_path but can take multiple paths.
21
23
  def self.append_paths(*paths)
22
- self.paths.concat(paths)
24
+ @paths.concat(paths)
25
+ nil
23
26
  end
24
27
 
25
28
  module UseGem
29
+ # Adds the "require_paths" (usually `lib/`) of gem with the given name to
30
+ # Opal paths. By default will include the "require_paths" from all the
31
+ # dependent gems.
32
+ #
33
+ # @param gem_name [String] the name of the gem
34
+ # @param include_dependencies [Boolean] whether or not to add recursively
35
+ # the gem's dependencies
26
36
  def use_gem(gem_name, include_dependencies = true)
27
37
  append_paths(*require_paths_for_gem(gem_name, include_dependencies))
28
38
  end
29
-
39
+
30
40
  private
31
41
 
32
42
  def require_paths_for_gem(gem_name, include_dependencies)
@@ -48,8 +58,15 @@ module Opal
48
58
 
49
59
  extend UseGem
50
60
 
51
- # Private, don't add to these directly (use .append_path instead).
52
61
  def self.paths
53
- @paths ||= [core_dir.untaint, std_dir.untaint, gem_dir.untaint]
62
+ @paths.dup.freeze
54
63
  end
64
+
65
+ # Resets Opal.paths to the default value (includes `corelib`, `stdlib`, `opal/lib`)
66
+ def self.reset_paths!
67
+ @paths = [core_dir.untaint, std_dir.untaint, gem_dir.untaint]
68
+ nil
69
+ end
70
+
71
+ reset_paths!
55
72
  end
data/lib/opal/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Opal
2
2
  # WHEN RELEASING:
3
3
  # Remember to update RUBY_ENGINE_VERSION in opal/corelib/constants.rb too!
4
- VERSION = '0.9.0.beta1'
4
+ VERSION = '0.9.0.beta2'
5
5
  end
@@ -1262,7 +1262,7 @@ class Array < `Array`
1262
1262
  }
1263
1263
  }
1264
1264
 
1265
- #{raise NoMethodError, "#{Opal.inspect(`item`)} doesn't respond to #to_str, #to_ary or #to_s"};
1265
+ #{raise NoMethodError.new("#{Opal.inspect(`item`)} doesn't respond to #to_str, #to_ary or #to_s", 'to_str')};
1266
1266
  }
1267
1267
 
1268
1268
  if (sep === nil) {
@@ -52,12 +52,12 @@ class BasicObject
52
52
  result;
53
53
 
54
54
  block.$$s = null;
55
-
55
+
56
56
  // need to pass $$eval so that method definitions know if this is being done on a class/module. Cannot be compiler driven since
57
57
  // send(:instance_eval) needs to work
58
58
  if (self.$$is_class || self.$$is_module) {
59
59
  self.$$eval = true;
60
- try {
60
+ try {
61
61
  result = block.call(self, self);
62
62
  }
63
63
  finally {
@@ -67,7 +67,7 @@ class BasicObject
67
67
  else {
68
68
  result = block.call(self, self);
69
69
  }
70
-
70
+
71
71
  block.$$s = old;
72
72
 
73
73
  return result;
@@ -80,17 +80,17 @@ class BasicObject
80
80
  %x{
81
81
  var block_self = block.$$s,
82
82
  result;
83
-
83
+
84
84
  block.$$s = null;
85
-
85
+
86
86
  if (self.$$is_class || self.$$is_module) {
87
87
  self.$$eval = true;
88
- try {
88
+ try {
89
89
  result = block.apply(self, args);
90
90
  }
91
91
  finally {
92
92
  self.$$eval = false;
93
- }
93
+ }
94
94
  }
95
95
  else {
96
96
  result = block.apply(self, args);
@@ -112,8 +112,8 @@ class BasicObject
112
112
  end
113
113
 
114
114
  def method_missing(symbol, *args, &block)
115
- Kernel.raise NoMethodError, `self.$inspect && !self.$inspect.$$stub` ?
115
+ Kernel.raise NoMethodError.new(`self.$inspect && !self.$inspect.$$stub` ?
116
116
  "undefined method `#{symbol}' for #{inspect}:#{`self.$$class`}" :
117
- "undefined method `#{symbol}' for #{`self.$$class`}"
117
+ "undefined method `#{symbol}' for #{`self.$$class`}", symbol)
118
118
  end
119
119
  end
@@ -1,8 +1,8 @@
1
1
  RUBY_PLATFORM = 'opal'
2
2
  RUBY_ENGINE = 'opal'
3
3
  RUBY_VERSION = '2.1.5'
4
- RUBY_ENGINE_VERSION = '0.9.0.beta1'
5
- RUBY_RELEASE_DATE = '2015-10-19'
4
+ RUBY_ENGINE_VERSION = '0.9.0.beta2'
5
+ RUBY_RELEASE_DATE = '2015-11-06'
6
6
  RUBY_PATCHLEVEL = 0
7
7
  RUBY_REVISION = 0
8
8
  RUBY_COPYRIGHT = 'opal - Copyright (C) 2013-2015 Adam Beynon'
@@ -341,7 +341,52 @@ module Enumerable
341
341
  end
342
342
 
343
343
  def each_cons(n, &block)
344
- raise NotImplementedError
344
+ if `arguments.length != 1`
345
+ raise ArgumentError, "wrong number of arguments (#{`arguments.length`} for 1)"
346
+ end
347
+
348
+ n = Opal.try_convert n, Integer, :to_int
349
+
350
+ if `n <= 0`
351
+ raise ArgumentError, 'invalid size'
352
+ end
353
+
354
+ unless block_given?
355
+ return enum_for(:each_cons, n) {
356
+ enum_size = self.enumerator_size
357
+ if enum_size.nil?
358
+ nil
359
+ elsif enum_size == 0 || enum_size < n
360
+ 0
361
+ else
362
+ enum_size - n + 1
363
+ end
364
+ }
365
+ end
366
+
367
+ %x{
368
+ var buffer = [], result = nil;
369
+
370
+ self.$each.$$p = function() {
371
+ var element = #{Opal.destructure(`arguments`)};
372
+ buffer.push(element);
373
+ if (buffer.length > n) {
374
+ buffer.shift();
375
+ }
376
+ if (buffer.length == n) {
377
+ var value = Opal.yield1(block, buffer.slice(0, n));
378
+
379
+ if (value == $breaker) {
380
+ result = $breaker.$v;
381
+ return $breaker;
382
+ }
383
+ }
384
+ }
385
+
386
+ self.$each();
387
+
388
+ return result;
389
+ }
345
390
  end
346
391
 
347
392
  def each_entry(&block)
@@ -900,7 +945,39 @@ module Enumerable
900
945
  end
901
946
 
902
947
  def minmax(&block)
903
- raise NotImplementedError
948
+ block ||= proc { |a,b| a <=> b }
949
+
950
+ %x{
951
+ var min = nil, max = nil, first_time = true;
952
+
953
+ self.$each.$$p = function() {
954
+ var element = #{Opal.destructure(`arguments`)};
955
+ if (first_time) {
956
+ min = max = element;
957
+ first_time = false;
958
+ } else {
959
+ var min_cmp = #{block.call(`min`, `element`)};
960
+
961
+ if (min_cmp === nil) {
962
+ #{raise ArgumentError, 'comparison failed'}
963
+ } else if (min_cmp > 0) {
964
+ min = element;
965
+ }
966
+
967
+ var max_cmp = #{block.call(`max`, `element`)};
968
+
969
+ if (max_cmp === nil) {
970
+ #{raise ArgumentError, 'comparison failed'}
971
+ } else if (max_cmp < 0) {
972
+ max = element;
973
+ }
974
+ }
975
+ }
976
+
977
+ self.$each();
978
+
979
+ return [min, max];
980
+ }
904
981
  end
905
982
 
906
983
  def minmax_by(&block)