opal 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +4 -4
- data/.github/ISSUE_TEMPLATE/bug-report.md +47 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/build.yml +11 -5
- data/.gitignore +1 -0
- data/.jshintrc +1 -1
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +95 -1
- data/Gemfile +0 -4
- data/HACKING.md +1 -1
- data/README.md +19 -15
- data/UNRELEASED.md +37 -96
- data/benchmark-ips/bm_array_unshift.rb +7 -0
- data/bin/build-browser-source-map-support +2 -3
- data/bin/opal-mspec +2 -0
- data/docs/compiler.md +1 -1
- data/examples/rack/Gemfile +0 -1
- data/examples/rack/Gemfile.lock +0 -4
- data/lib/opal/cli.rb +1 -0
- data/lib/opal/cli_options.rb +4 -0
- data/lib/opal/cli_runners/nodejs.rb +5 -1
- data/lib/opal/cli_runners/source-map-support-browser.js +8 -2
- data/lib/opal/cli_runners/source-map-support-node.js +3706 -0
- data/lib/opal/cli_runners/source-map-support.js +3 -1
- data/lib/opal/compiler.rb +2 -2
- data/lib/opal/nodes/args/arity_check.rb +1 -0
- data/lib/opal/nodes/args/parameters.rb +6 -0
- data/lib/opal/nodes/class.rb +1 -13
- data/lib/opal/nodes/literal.rb +14 -7
- data/lib/opal/nodes/module.rb +13 -9
- data/lib/opal/nodes/variables.rb +13 -4
- data/lib/opal/nodes/while.rb +54 -17
- data/lib/opal/parser.rb +1 -5
- data/lib/opal/parser/patch.rb +44 -0
- data/lib/opal/repl.rb +7 -0
- data/lib/opal/rewriter.rb +4 -0
- data/lib/opal/rewriters/arguments.rb +4 -1
- data/lib/opal/rewriters/forward_args.rb +54 -0
- data/lib/opal/rewriters/logical_operator_assignment.rb +5 -2
- data/lib/opal/rewriters/opal_engine_check.rb +5 -7
- data/lib/opal/rewriters/pattern_matching.rb +287 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +42 -20
- data/opal/corelib/array/pack.rb +6 -1
- data/opal/corelib/complex.rb +2 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/hash.rb +45 -38
- data/opal/corelib/module.rb +2 -7
- data/opal/corelib/number.rb +2 -180
- data/opal/corelib/numeric.rb +156 -0
- data/opal/corelib/object_space.rb +102 -0
- data/opal/corelib/pattern_matching.rb +159 -0
- data/opal/corelib/random.rb +31 -66
- data/opal/corelib/random/formatter.rb +122 -0
- data/opal/corelib/range.rb +50 -19
- data/opal/corelib/runtime.js +82 -21
- data/opal/corelib/string.rb +86 -52
- data/opal/corelib/string/encoding.rb +140 -25
- data/opal/corelib/string/unpack.rb +26 -40
- data/opal/opal.rb +1 -0
- data/opal/opal/full.rb +2 -0
- data/package.json +1 -1
- data/spec/filters/bugs/array.rb +0 -23
- data/spec/filters/bugs/basicobject.rb +3 -0
- data/spec/filters/bugs/encoding.rb +0 -2
- data/spec/filters/bugs/exception.rb +1 -0
- data/spec/filters/bugs/float.rb +0 -2
- data/spec/filters/bugs/hash.rb +3 -13
- data/spec/filters/bugs/integer.rb +0 -2
- data/spec/filters/bugs/kernel.rb +16 -3
- data/spec/filters/bugs/language.rb +27 -90
- data/spec/filters/bugs/marshal.rb +1 -3
- data/spec/filters/bugs/module.rb +16 -1
- data/spec/filters/bugs/numeric.rb +4 -12
- data/spec/filters/bugs/objectspace.rb +67 -0
- data/spec/filters/bugs/pack_unpack.rb +0 -9
- data/spec/filters/bugs/pathname.rb +1 -0
- data/spec/filters/bugs/proc.rb +8 -0
- data/spec/filters/bugs/random.rb +3 -6
- data/spec/filters/bugs/range.rb +83 -113
- data/spec/filters/bugs/set.rb +2 -0
- data/spec/filters/bugs/string.rb +32 -70
- data/spec/filters/bugs/struct.rb +2 -10
- data/spec/filters/bugs/time.rb +8 -2
- data/spec/filters/unsupported/float.rb +3 -0
- data/spec/filters/unsupported/freeze.rb +1 -0
- data/spec/filters/unsupported/integer.rb +3 -0
- data/spec/filters/unsupported/refinements.rb +8 -0
- data/spec/filters/unsupported/string.rb +100 -95
- data/spec/filters/unsupported/time.rb +4 -0
- data/spec/lib/compiler_spec.rb +16 -0
- data/spec/lib/rewriters/forward_args_spec.rb +61 -0
- data/spec/lib/rewriters/logical_operator_assignment_spec.rb +1 -1
- data/spec/lib/rewriters/numblocks_spec.rb +44 -0
- data/spec/lib/rewriters/opal_engine_check_spec.rb +49 -4
- data/spec/opal/core/language/forward_args_spec.rb +53 -0
- data/spec/opal/core/language/infinite_range_spec.rb +13 -0
- data/spec/opal/core/language/memoization_spec.rb +16 -0
- data/spec/opal/core/language/pattern_matching_spec.rb +124 -0
- data/spec/opal/core/module_spec.rb +38 -2
- data/spec/opal/core/number/to_i_spec.rb +28 -0
- data/spec/opal/core/runtime/bridged_classes_spec.rb +16 -0
- data/spec/opal/core/runtime/constants_spec.rb +20 -1
- data/spec/opal/core/string/subclassing_spec.rb +16 -0
- data/spec/opal/core/string/unpack_spec.rb +22 -0
- data/spec/opal/core/string_spec.rb +4 -4
- data/spec/ruby_specs +4 -1
- data/stdlib/json.rb +3 -1
- data/stdlib/promise/v1.rb +1 -0
- data/stdlib/promise/v2.rb +386 -0
- data/stdlib/securerandom.rb +55 -35
- data/tasks/releasing.rake +1 -1
- data/tasks/testing.rake +6 -4
- data/test/nodejs/test_string.rb +25 -0
- data/test/opal/promisev2/test_always.rb +63 -0
- data/test/opal/promisev2/test_error.rb +16 -0
- data/test/opal/promisev2/test_rescue.rb +59 -0
- data/test/opal/promisev2/test_then.rb +90 -0
- data/test/opal/promisev2/test_trace.rb +52 -0
- data/test/opal/promisev2/test_value.rb +16 -0
- data/test/opal/promisev2/test_when.rb +35 -0
- data/vendored-minitest/minitest/assertions.rb +2 -0
- metadata +47 -8
- data/lib/opal/parser/with_c_lexer.rb +0 -15
data/stdlib/securerandom.rb
CHANGED
@@ -1,44 +1,64 @@
|
|
1
1
|
module SecureRandom
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
2
|
+
extend Random::Formatter
|
3
|
+
|
4
|
+
%x{
|
5
|
+
var gen_random_bytes;
|
6
|
+
|
7
|
+
if ((Opal.global.crypto && Opal.global.crypto.getRandomValues) ||
|
8
|
+
(Opal.global.msCrypto && Opal.global.msCrypto.getRandomValues)) {
|
9
|
+
// This method is available in all non-ancient web browsers.
|
10
|
+
|
11
|
+
var crypto = Opal.global.crypto || Opal.global.msCrypto;
|
12
|
+
gen_random_bytes = function(count) {
|
13
|
+
var storage = new Uint8Array(count);
|
14
|
+
crypto.getRandomValues(storage);
|
15
|
+
return storage;
|
16
|
+
};
|
17
|
+
}
|
18
|
+
else if (Opal.global.crypto && Opal.global.crypto.randomBytes) {
|
19
|
+
// This method is available in Node.js
|
20
|
+
|
21
|
+
gen_random_bytes = function(count) {
|
22
|
+
return Opal.global.crypto.randomBytes(count);
|
23
|
+
};
|
24
|
+
}
|
25
|
+
else {
|
26
|
+
// Let's dangerously polyfill this interface with our MersenneTwister
|
27
|
+
// xor native JS Math.random xor something about current time...
|
28
|
+
// That's hardly secure, but the following warning should provide a person
|
29
|
+
// deploying the code a good idea on what he should do to make his deployment
|
30
|
+
// actually secure.
|
31
|
+
// It's possible to interface other libraries by adding an else if above if
|
32
|
+
// that's really desired.
|
33
|
+
|
34
|
+
#{warn 'Can\'t get a Crypto.getRandomValues interface or Crypto.randomBytes.' \
|
35
|
+
'The random values generated with SecureRandom won\'t be ' \
|
36
|
+
'cryptographically secure'}
|
37
|
+
|
38
|
+
gen_random_bytes = function(count) {
|
39
|
+
var storage = new Uint8Array(count);
|
40
|
+
for (var i = 0; i < count; i++) {
|
41
|
+
storage[i] = #{rand(0xff)} ^ Math.floor(Math.random() * 256);
|
42
|
+
storage[i] ^= +(new Date())>>#{rand(0xff)}&0xff;
|
27
43
|
}
|
28
|
-
|
44
|
+
return storage;
|
29
45
|
}
|
30
|
-
return string;
|
31
46
|
}
|
32
|
-
|
47
|
+
}
|
33
48
|
|
34
|
-
def self.
|
35
|
-
|
36
|
-
|
37
|
-
var r = Math.random() * 16 | 0,
|
38
|
-
v = ch == "x" ? r : (r & 3 | 8);
|
49
|
+
def self.bytes(bytes = nil)
|
50
|
+
gen_random(bytes)
|
51
|
+
end
|
39
52
|
|
40
|
-
|
53
|
+
def self.gen_random(count = nil)
|
54
|
+
count = Random._verify_count(count)
|
55
|
+
out = ''
|
56
|
+
%x{
|
57
|
+
var bytes = gen_random_bytes(#{count});
|
58
|
+
for (var i = 0; i < #{count}; i++) {
|
59
|
+
out += String.fromCharCode(bytes[i]);
|
41
60
|
}
|
42
|
-
|
61
|
+
}
|
62
|
+
out.encode('ASCII-8BIT')
|
43
63
|
end
|
44
64
|
end
|
data/tasks/releasing.rake
CHANGED
@@ -49,7 +49,7 @@ CHANGELOG_HEADING = <<~MARKDOWN
|
|
49
49
|
- **Security** to invite users to upgrade in case of vulnerabilities.
|
50
50
|
MARKDOWN
|
51
51
|
|
52
|
-
desc "Update CHANGELOG.md
|
52
|
+
desc "Update CHANGELOG.md using info from published GitHub releases (the first unreleased section is preserved)"
|
53
53
|
task :changelog do
|
54
54
|
changelog_path = "#{__dir__}/../CHANGELOG.md"
|
55
55
|
unreleased_path = "#{__dir__}/../UNRELEASED.md"
|
data/tasks/testing.rake
CHANGED
@@ -113,6 +113,7 @@ module Testing
|
|
113
113
|
|
114
114
|
require 'spec_helper'
|
115
115
|
require 'opal/full'
|
116
|
+
require 'securerandom'
|
116
117
|
#{enter_benchmarking_mode}
|
117
118
|
|
118
119
|
#{filter_requires}
|
@@ -354,13 +355,14 @@ task :minitest_node_nodejs do
|
|
354
355
|
files = %w[
|
355
356
|
nodejs
|
356
357
|
opal-parser
|
357
|
-
nodejs/test_file.rb
|
358
358
|
nodejs/test_dir.rb
|
359
359
|
nodejs/test_env.rb
|
360
|
-
nodejs/test_io.rb
|
361
360
|
nodejs/test_error.rb
|
361
|
+
nodejs/test_file.rb
|
362
362
|
nodejs/test_file_encoding.rb
|
363
|
+
nodejs/test_io.rb
|
363
364
|
nodejs/test_opal_builder.rb
|
365
|
+
nodejs/test_string.rb
|
364
366
|
]
|
365
367
|
|
366
368
|
filename = "tmp/minitest_node_nodejs.rb"
|
@@ -369,8 +371,9 @@ task :minitest_node_nodejs do
|
|
369
371
|
stubs = "-soptparse -sio/console -stimeout -smutex_m -srubygems -stempfile -smonitor"
|
370
372
|
includes = "-Itest -Ilib -Ivendored-minitest"
|
371
373
|
|
374
|
+
use_strict_opt = ENV['USE_STRICT'] ? ' --use-strict' : ''
|
372
375
|
sh "ruby -rbundler/setup "\
|
373
|
-
"exe/opal #{includes} #{stubs} -R#{platform} -Dwarning -A --enable-source-location #{filename}"
|
376
|
+
"exe/opal #{includes} #{stubs} -R#{platform} -Dwarning -A --enable-source-location#{use_strict_opt} #{filename}"
|
374
377
|
end
|
375
378
|
|
376
379
|
desc 'Run smoke tests with opal-rspec to see if something is broken'
|
@@ -464,4 +467,3 @@ task :test_all => [:rspec, :mspec, :minitest]
|
|
464
467
|
task(:cruby_tests) { warn "The task 'cruby_tests' has been renamed to 'minitest_cruby_nodejs'."; exit 1 }
|
465
468
|
task(:test_cruby) { warn "The task 'test_cruby' has been renamed to 'minitest_cruby_nodejs'."; exit 1 }
|
466
469
|
task(:test_nodejs) { warn "The task 'test_nodejs' has been renamed to 'minitest_node_nodejs'."; exit 1 }
|
467
|
-
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'nodejs'
|
3
|
+
|
4
|
+
class TestString < Test::Unit::TestCase
|
5
|
+
def test_should_get_bytes
|
6
|
+
assert_equal('foo'.bytesize, 3)
|
7
|
+
assert_equal('foo'.each_byte.to_a, [102, 111, 111])
|
8
|
+
assert_equal('foo'.bytes, [102, 111, 111])
|
9
|
+
assert_equal('foo'.bytes, [102, 111, 111])
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_frozen
|
13
|
+
# Commented out examples somehow work in the strict mode,
|
14
|
+
# but otherwise they don't. I have unfortunately no idea
|
15
|
+
# on where it's mangled, but then I don't see it really
|
16
|
+
# as a really harmful thing.
|
17
|
+
|
18
|
+
#assert_equal((-'x').frozen?, true)
|
19
|
+
assert_equal((+'x').frozen?, false)
|
20
|
+
#assert_equal((-+-'x').frozen?, true)
|
21
|
+
assert_equal((+-+'x').frozen?, false)
|
22
|
+
#assert_equal((`'x'`).frozen?, true)
|
23
|
+
assert_equal((+`'x'`).frozen?, false)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'promise/v2'
|
3
|
+
|
4
|
+
class TestPromiseAlways < Test::Unit::TestCase
|
5
|
+
def test_calls_the_block_when_it_was_resolved
|
6
|
+
x = 42
|
7
|
+
PromiseV2.value(23)
|
8
|
+
.then { |v| x = v }
|
9
|
+
.always { |v| x = 2 }
|
10
|
+
.then { assert_equal(x, 2) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_calls_the_block_when_it_was_rejected
|
14
|
+
x = 42
|
15
|
+
PromiseV2.error(23)
|
16
|
+
.rescue { |v| x = v }
|
17
|
+
.always { |v| x = 2 }
|
18
|
+
.always { assert_equal(x, 2) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_acts_as_resolved
|
22
|
+
x = 42
|
23
|
+
PromiseV2.error(23)
|
24
|
+
.rescue { |v| x = v }
|
25
|
+
.always { x = 2 }
|
26
|
+
.then { x = 3 }
|
27
|
+
.always { assert_equal(x, 3) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_can_be_called_multiple_times_on_resolved_promises
|
31
|
+
p = PromiseV2.value(2)
|
32
|
+
x = 1
|
33
|
+
ps = []
|
34
|
+
ps << p.then { x += 1 }
|
35
|
+
ps << p.fail { x += 2 }
|
36
|
+
ps << p.always { x += 3 }
|
37
|
+
|
38
|
+
PromiseV2.when(ps).always do
|
39
|
+
assert_equal(x, 5)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_can_be_called_multiple_times_on_rejected_promises
|
44
|
+
p = PromiseV2.error(2)
|
45
|
+
x = 1
|
46
|
+
ps = []
|
47
|
+
ps << p.then { x += 1 }.fail{}
|
48
|
+
ps << p.fail { x += 2 }
|
49
|
+
ps << p.always { x += 3 }.fail{}
|
50
|
+
|
51
|
+
PromiseV2.when(ps).then do
|
52
|
+
assert_equal(x, 6)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_raises_with_alwaysB_if_a_promise_has_already_been_chained
|
57
|
+
p = PromiseV2.new
|
58
|
+
|
59
|
+
p.then! {}
|
60
|
+
|
61
|
+
assert_raise(ArgumentError) { p.always! {} }
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'promise/v2'
|
3
|
+
|
4
|
+
class TestPromiseError < Test::Unit::TestCase
|
5
|
+
def test_rejects_the_promise_with_the_given_error
|
6
|
+
assert_equal(PromiseV2.error(23).error, 23)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_marks_the_promise_as_realized
|
10
|
+
assert_equal(PromiseV2.error(23).realized?, true)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_marks_the_promise_as_rejected
|
14
|
+
assert_equal(PromiseV2.error(23).rejected?, true)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'promise/v2'
|
3
|
+
|
4
|
+
class TestPromiseRescue < Test::Unit::TestCase
|
5
|
+
def test_calls_the_block_when_the_promise_has_already_been_rejected
|
6
|
+
x = 42
|
7
|
+
PromiseV2.error(23)
|
8
|
+
.rescue { |v| x = v }
|
9
|
+
.always { assert_equal(x, 23) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_calls_the_block_when_the_promise_is_rejected
|
13
|
+
a = PromiseV2.new
|
14
|
+
x = 42
|
15
|
+
|
16
|
+
pr = a.rescue { |v| x = v }
|
17
|
+
a.reject(23)
|
18
|
+
|
19
|
+
pr.always { assert_equal(x, 23) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_does_not_call_then_blocks_when_the_promise_is_rejected
|
23
|
+
x = 42
|
24
|
+
y = 23
|
25
|
+
|
26
|
+
PromiseV2.error(23).then { y = 42 }.rescue { |v| x = v }.always do
|
27
|
+
assert_equal(x, 23)
|
28
|
+
assert_equal(y, 23)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_does_not_call_subsequent_rescue_blocks
|
33
|
+
x = 42
|
34
|
+
PromiseV2.error(23).rescue { |v| x = v }.rescue { x = 42 }.always do
|
35
|
+
assert_equal(x, 23)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_can_be_called_multiple_times_on_the_same_promise
|
40
|
+
p = PromiseV2.error(2)
|
41
|
+
x = 1
|
42
|
+
|
43
|
+
ps = []
|
44
|
+
|
45
|
+
ps << p.then { x += 1 }.rescue{}
|
46
|
+
ps << p.rescue { x += 3 }
|
47
|
+
ps << p.rescue { x += 3 }
|
48
|
+
|
49
|
+
PromiseV2.when(ps).always { assert_equal(x, 7) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_raises_with_rescueB_if_a_promise_has_already_been_chained
|
53
|
+
p = PromiseV2.new
|
54
|
+
|
55
|
+
p.then! {}
|
56
|
+
|
57
|
+
assert_raise(ArgumentError) { p.rescue! {} }
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'promise/v2'
|
3
|
+
|
4
|
+
class TestPromiseThen < Test::Unit::TestCase
|
5
|
+
def test_calls_the_block_when_the_promise_has_already_been_resolved
|
6
|
+
x = 42
|
7
|
+
PromiseV2.value(23)
|
8
|
+
.then { |v| x = v }
|
9
|
+
.always { assert_equal(x, 23) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_calls_the_block_when_the_promise_is_resolved
|
13
|
+
a = PromiseV2.new
|
14
|
+
x = 42
|
15
|
+
|
16
|
+
p = a.then { |v| x = v }
|
17
|
+
a.resolve(23)
|
18
|
+
|
19
|
+
p.always { assert_equal(x, 23) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_works_with_multiple_chains
|
23
|
+
x = 42
|
24
|
+
PromiseV2.value(2)
|
25
|
+
.then { |v| v * 2 }
|
26
|
+
.then { |v| v * 4 }
|
27
|
+
.then { |v| x = v }
|
28
|
+
.always { assert_equal(x, 16) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_works_when_a_block_returns_a_promise
|
32
|
+
a = PromiseV2.new
|
33
|
+
b = PromiseV2.new
|
34
|
+
|
35
|
+
x = 42
|
36
|
+
p = a.then { b }.then { |v| x = v }
|
37
|
+
|
38
|
+
a.resolve(42)
|
39
|
+
b.resolve(23)
|
40
|
+
|
41
|
+
p.always { assert_equal(x, 23) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_sends_raised_exceptions_as_rejections
|
45
|
+
x = nil
|
46
|
+
|
47
|
+
PromiseV2.value(2)
|
48
|
+
.then { raise "hue" }
|
49
|
+
.rescue { |v| x = v }
|
50
|
+
.always { assert_equal(x.class, RuntimeError) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_sends_raised_exceptions_inside_rescue_blocks_as_next_errors
|
54
|
+
x = nil
|
55
|
+
|
56
|
+
PromiseV2.value(2)
|
57
|
+
.then { raise "hue" }
|
58
|
+
.rescue { raise "omg" }
|
59
|
+
.rescue { |v| x = v }
|
60
|
+
.always { assert_equal(x.class, RuntimeError) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_allows_then_to_be_called_multiple_times
|
64
|
+
pr = PromiseV2.value(2)
|
65
|
+
x = 1
|
66
|
+
|
67
|
+
ps = []
|
68
|
+
|
69
|
+
ps << pr.then { x += 1 }
|
70
|
+
ps << pr.then { x += 1 }
|
71
|
+
|
72
|
+
PromiseV2.when(ps).always { assert_equal(x, 3) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_raises_with_thenB_if_a_promise_has_already_been_chained
|
76
|
+
pr = PromiseV2.new
|
77
|
+
|
78
|
+
pr.then! {}
|
79
|
+
|
80
|
+
assert_raise(ArgumentError) { pr.then! {} }
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_should_pass_a_delayed_falsy_value
|
84
|
+
pr = PromiseV2.new.resolve(5).then { nil }
|
85
|
+
|
86
|
+
pr.always do |value|
|
87
|
+
assert_equal(value, nil)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'promise/v2'
|
3
|
+
|
4
|
+
class TestPromiseTrace < Test::Unit::TestCase
|
5
|
+
def test_calls_the_block_with_all_the_previous_results
|
6
|
+
x = 42
|
7
|
+
|
8
|
+
PromiseV2.value(1)
|
9
|
+
.then { 2 }
|
10
|
+
.then { 3 }
|
11
|
+
.trace {|a, b, c| x = a + b + c }
|
12
|
+
.always { assert_equal(x, 6) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_calls_the_then_after_the_trace
|
16
|
+
x = 42
|
17
|
+
|
18
|
+
PromiseV2.value(1)
|
19
|
+
.then { 2 }
|
20
|
+
.then { 3 }
|
21
|
+
.trace { |a, b, c| a + b + c }
|
22
|
+
.then { |v| x = v }
|
23
|
+
.always { assert_equal(x, 6) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_includes_the_first_value
|
27
|
+
x = 42
|
28
|
+
|
29
|
+
PromiseV2.value(1)
|
30
|
+
.trace { |a| x = a }
|
31
|
+
.always { assert_equal(x, 1) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_works_after_a_when
|
35
|
+
x = 42
|
36
|
+
|
37
|
+
PromiseV2.value(1).then {
|
38
|
+
PromiseV2.when PromiseV2.value(2), PromiseV2.value(3)
|
39
|
+
}.trace {|a, b|
|
40
|
+
#x = a + b[0] + b[1]
|
41
|
+
x = "#{a},#{b}"
|
42
|
+
}.always { assert_equal(x, "1,native") } # 6
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_raises_with_traceB_if_a_promise_has_already_been_chained
|
46
|
+
p = PromiseV2.new
|
47
|
+
|
48
|
+
p.then! {}
|
49
|
+
|
50
|
+
assert_raise(ArgumentError) { p.trace! {} }
|
51
|
+
end
|
52
|
+
end
|