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
@@ -15,8 +15,30 @@ module ModuleSubclassIncludedSpec
|
|
15
15
|
M2 = Module2.new
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
module ModuleCVarSpec
|
19
|
+
module Mod0
|
20
|
+
def cvar0; @@cvar; end
|
21
|
+
def cvarx0; @@cvarx ||= 5; end
|
22
|
+
def cvary0; @@cvary; 0; end
|
23
|
+
def cvarz0; @@cvarz = @@cvarz || 5; end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Mod1
|
27
|
+
include Mod0
|
28
|
+
@@cvar = 10
|
29
|
+
def cvar1; @@cvar; end
|
30
|
+
def cvar1=(new); @@cvar=new; end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Mod2
|
34
|
+
include Mod1
|
35
|
+
def cvar2; @@cvar; end
|
36
|
+
def cvar2=(new); @@cvar=new; end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'Module' do
|
41
|
+
it '#included gets called in subclasses (regression for https://github.com/opal/opal/issues/1900)' do
|
20
42
|
$ScratchPad = []
|
21
43
|
klass = Class.new
|
22
44
|
klass.include ::ModuleSubclassIncludedSpec::M0
|
@@ -24,4 +46,18 @@ describe 'Module#included' do
|
|
24
46
|
klass.include ::ModuleSubclassIncludedSpec::M2
|
25
47
|
$ScratchPad.should == ['A included', 'B included']
|
26
48
|
end
|
49
|
+
|
50
|
+
it "can access ancestor's @@cvar" do
|
51
|
+
klass = Class.new
|
52
|
+
klass.include ::ModuleCVarSpec::Mod2
|
53
|
+
klass.new.cvar1.should == 10
|
54
|
+
klass.new.cvar2.should == 10
|
55
|
+
klass.new.cvar2 = 50
|
56
|
+
klass.new.cvar1.should == 50
|
57
|
+
klass.new.cvar2.should == 50
|
58
|
+
klass.new.cvarx0.should == 5
|
59
|
+
klass.new.cvary0.should == 0
|
60
|
+
->{ klass.new.cvarz0 }.should raise_error NameError
|
61
|
+
->{ klass.new.cvar0 }.should raise_error NameError
|
62
|
+
end
|
27
63
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Number#to_i' do
|
4
|
+
it "should not change huge number" do
|
5
|
+
1504642339053716000000.to_i.should == 1504642339053716000000
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should not change negative huge number" do
|
9
|
+
-1504642339053716000000.to_i.should == -1504642339053716000000
|
10
|
+
end
|
11
|
+
|
12
|
+
it "equals Number#truncate(0) with huge number" do
|
13
|
+
1504642339053716000000.to_i.should == 1504642339053716000000.truncate(0)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not change Infinity" do
|
17
|
+
`Infinity`.to_i.should == `Infinity`
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not change -Infinity" do
|
21
|
+
`-Infinity`.to_i.should == `-Infinity`
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not change NaN" do
|
25
|
+
x = `NaN`.to_i
|
26
|
+
`Number.isNaN(x)`.should be_true
|
27
|
+
end
|
28
|
+
end
|
@@ -121,3 +121,19 @@ describe 'Bridged classes in different modules' do
|
|
121
121
|
@bridged.new.some_bridged_method.should == [4, 5, 6]
|
122
122
|
end
|
123
123
|
end
|
124
|
+
|
125
|
+
|
126
|
+
describe "Invalid bridged classes" do
|
127
|
+
it "raises a TypeError when trying to extend with non-Class" do
|
128
|
+
error_msg = /superclass must be a Class/
|
129
|
+
-> { class TestClass < `""`; end }.should raise_error(TypeError, error_msg)
|
130
|
+
-> { class TestClass < `3`; end }.should raise_error(TypeError, error_msg)
|
131
|
+
-> { class TestClass < `true`; end }.should raise_error(TypeError, error_msg)
|
132
|
+
-> { class TestClass < `Math`; end }.should raise_error(TypeError, error_msg)
|
133
|
+
-> { class TestClass < `Object.create({})`; end }.should raise_error(TypeError, error_msg)
|
134
|
+
-> { class TestClass < `Object.create(null)`; end }.should raise_error(TypeError, error_msg)
|
135
|
+
-> { class TestClass < Module.new; end }.should raise_error(TypeError, error_msg)
|
136
|
+
-> { class TestClass < BasicObject.new; end }.should raise_error(TypeError, error_msg)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
@@ -4,9 +4,15 @@ module RuntimeFixtures
|
|
4
4
|
|
5
5
|
class A::B
|
6
6
|
module C
|
7
|
-
|
8
7
|
end
|
9
8
|
end
|
9
|
+
|
10
|
+
module ModuleB
|
11
|
+
end
|
12
|
+
|
13
|
+
module ModuleA
|
14
|
+
include ModuleB
|
15
|
+
end
|
10
16
|
end
|
11
17
|
|
12
18
|
describe "Constants access via .$$ with dots (regression for #1418)" do
|
@@ -14,3 +20,16 @@ describe "Constants access via .$$ with dots (regression for #1418)" do
|
|
14
20
|
`Opal.Object.$$.RuntimeFixtures.$$.A.$$.B.$$.C`.should == RuntimeFixtures::A::B::C
|
15
21
|
end
|
16
22
|
end
|
23
|
+
|
24
|
+
describe "Inclusion of modules" do
|
25
|
+
it "that have been included by other modules works" do
|
26
|
+
# here ClassC would have failed to be created due to a bug in Opal.append_features
|
27
|
+
module RuntimeFixtures
|
28
|
+
class ClassC
|
29
|
+
include ModuleA
|
30
|
+
include ModuleB
|
31
|
+
end
|
32
|
+
end
|
33
|
+
RuntimeFixtures::ClassC.new.class.should == RuntimeFixtures::ClassC
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MyStringSubclass < String
|
4
|
+
attr_reader :v
|
5
|
+
def initialize(s, v)
|
6
|
+
super(s)
|
7
|
+
@v = v
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "String subclassing" do
|
12
|
+
it "should call initialize for subclasses" do
|
13
|
+
c = MyStringSubclass.new('s', 5)
|
14
|
+
[c, c.v].should == ['s', 5]
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'String#unpack' do
|
4
|
+
it 'correctly unpacks with U* strings with latin-1 characters' do
|
5
|
+
'café'.unpack('U*').should == [99, 97, 102, 233]
|
6
|
+
[99, 97, 102, 233].pack('U*').unpack('U*').should == [99, 97, 102, 233]
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'correctly unpacks with U* strings with latin-2 characters' do
|
10
|
+
'pół'.unpack('U*').should == [112, 243, 322]
|
11
|
+
[112, 243, 322].pack('U*').unpack('U*').should == [112, 243, 322]
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'correctly unpacks with c* strings with latin-2 characters' do
|
15
|
+
'ść'.unpack('c*').should == [-59, -101, -60, -121]
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'correctly unpacks with s* binary strings' do
|
19
|
+
"\xc8\x01".unpack('s*').should == [456]
|
20
|
+
[678].pack('s').unpack('s').should == [678]
|
21
|
+
end
|
22
|
+
end
|
@@ -19,10 +19,10 @@ describe 'Encoding' do
|
|
19
19
|
Encoding.find('ascii').should == Encoding::ASCII
|
20
20
|
Encoding.find('US-ASCII').should == Encoding::ASCII
|
21
21
|
Encoding.find('us-ascii').should == Encoding::ASCII
|
22
|
-
Encoding.find('ASCII-8BIT').should == Encoding::
|
23
|
-
Encoding.find('ascii-8bit').should == Encoding::
|
24
|
-
Encoding.find('BINARY').should == Encoding::
|
25
|
-
Encoding.find('binary').should == Encoding::
|
22
|
+
Encoding.find('ASCII-8BIT').should == Encoding::BINARY
|
23
|
+
Encoding.find('ascii-8bit').should == Encoding::BINARY
|
24
|
+
Encoding.find('BINARY').should == Encoding::BINARY
|
25
|
+
Encoding.find('binary').should == Encoding::BINARY
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/spec/ruby_specs
CHANGED
@@ -106,6 +106,9 @@ ruby/core/marshal/minor_version_spec
|
|
106
106
|
ruby/core/nil
|
107
107
|
ruby/core/numeric
|
108
108
|
|
109
|
+
ruby/core/objectspace
|
110
|
+
ruby/core/objectspace/weakmap
|
111
|
+
|
109
112
|
ruby/core/proc
|
110
113
|
|
111
114
|
ruby/core/range
|
@@ -175,7 +178,7 @@ ruby/library/pathname
|
|
175
178
|
!ruby/library/pathname/empty_spec
|
176
179
|
!ruby/library/pathname/glob_spec
|
177
180
|
ruby/library/pp
|
178
|
-
ruby/library/securerandom
|
181
|
+
ruby/library/securerandom
|
179
182
|
ruby/library/set
|
180
183
|
ruby/library/singleton
|
181
184
|
ruby/library/stringio/each_line_spec
|
data/stdlib/json.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
require 'promise'
|
@@ -0,0 +1,386 @@
|
|
1
|
+
# {Promise} is used to help structure asynchronous code.
|
2
|
+
#
|
3
|
+
# It is available in the Opal standard library, and can be required in any Opal
|
4
|
+
# application:
|
5
|
+
#
|
6
|
+
# require 'promise/v2'
|
7
|
+
#
|
8
|
+
# ## Basic Usage
|
9
|
+
#
|
10
|
+
# Promises are created and returned as objects with the assumption that they
|
11
|
+
# will eventually be resolved or rejected, but never both. A {Promise} has
|
12
|
+
# a {#then} and {#fail} method (or one of their aliases) that can be used to
|
13
|
+
# register a block that gets called once resolved or rejected.
|
14
|
+
#
|
15
|
+
# promise = PromiseV2.new
|
16
|
+
#
|
17
|
+
# promise.then {
|
18
|
+
# puts "resolved!"
|
19
|
+
# }.fail {
|
20
|
+
# puts "rejected!"
|
21
|
+
# }
|
22
|
+
#
|
23
|
+
# # some time later
|
24
|
+
# promise.resolve
|
25
|
+
#
|
26
|
+
# # => "resolved!"
|
27
|
+
#
|
28
|
+
# It is important to remember that a promise can only be resolved or rejected
|
29
|
+
# once, so the block will only ever be called once (or not at all).
|
30
|
+
#
|
31
|
+
# ## Resolving Promises
|
32
|
+
#
|
33
|
+
# To resolve a promise, means to inform the {Promise} that it has succeeded
|
34
|
+
# or evaluated to a useful value. {#resolve} can be passed a value which is
|
35
|
+
# then passed into the block handler:
|
36
|
+
#
|
37
|
+
# def get_json
|
38
|
+
# promise = PromiseV2.new
|
39
|
+
#
|
40
|
+
# HTTP.get("some_url") do |req|
|
41
|
+
# promise.resolve req.json
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# promise
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# get_json.then do |json|
|
48
|
+
# puts "got some JSON from server"
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# ## Rejecting Promises
|
52
|
+
#
|
53
|
+
# Promises are also designed to handle error cases, or situations where an
|
54
|
+
# outcome is not as expected. Taking the previous example, we can also pass
|
55
|
+
# a value to a {#reject} call, which passes that object to the registered
|
56
|
+
# {#fail} handler:
|
57
|
+
#
|
58
|
+
# def get_json
|
59
|
+
# promise = PromiseV2.new
|
60
|
+
#
|
61
|
+
# HTTP.get("some_url") do |req|
|
62
|
+
# if req.ok?
|
63
|
+
# promise.resolve req.json
|
64
|
+
# else
|
65
|
+
# promise.reject req
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# promise
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# get_json.then {
|
72
|
+
# # ...
|
73
|
+
# }.fail { |req|
|
74
|
+
# puts "it went wrong: #{req.message}"
|
75
|
+
# }
|
76
|
+
#
|
77
|
+
# ## Chaining Promises
|
78
|
+
#
|
79
|
+
# Promises become even more useful when chained together. Each {#then} or
|
80
|
+
# {#fail} call returns a new {PromiseV2} which can be used to chain more and more
|
81
|
+
# handlers together.
|
82
|
+
#
|
83
|
+
# promise.then { wait_for_something }.then { do_something_else }
|
84
|
+
#
|
85
|
+
# Rejections are propagated through the entire chain, so a "catch all" handler
|
86
|
+
# can be attached at the end of the tail:
|
87
|
+
#
|
88
|
+
# promise.then { ... }.then { ... }.fail { ... }
|
89
|
+
#
|
90
|
+
# ## Composing Promises
|
91
|
+
#
|
92
|
+
# {PromiseV2.when} can be used to wait for more than one promise to resolve (or
|
93
|
+
# reject). Using the previous example, we could request two different json
|
94
|
+
# requests and wait for both to finish:
|
95
|
+
#
|
96
|
+
# PromiseV2.when(get_json, get_json2).then |first, second|
|
97
|
+
# puts "got two json payloads: #{first}, #{second}"
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
|
101
|
+
warn 'PromiseV2 is a technology preview, which means it may change its behavior ' \
|
102
|
+
'in the future until this warning is removed. If you are interested in this part, ' \
|
103
|
+
'please make sure you track the async/await/promises tag on Opal issues: ' \
|
104
|
+
'https://github.com/opal/opal/issues?q=label%3Aasync%2Fawait%2Fpromises'
|
105
|
+
|
106
|
+
class PromiseV2 < `Promise`
|
107
|
+
class << self
|
108
|
+
def allocate
|
109
|
+
ok, fail = nil, nil
|
110
|
+
|
111
|
+
prom = `new self.$$constructor(function(_ok, _fail) { #{ok} = _ok; #{fail} = _fail; })`
|
112
|
+
prom.instance_variable_set(:@type, :opal)
|
113
|
+
prom.instance_variable_set(:@resolve_proc, ok)
|
114
|
+
prom.instance_variable_set(:@reject_proc, fail)
|
115
|
+
prom
|
116
|
+
end
|
117
|
+
|
118
|
+
def when(*promises)
|
119
|
+
promises = Array(promises.length == 1 ? promises.first : promises)
|
120
|
+
`Promise.all(#{promises})`.tap do |prom|
|
121
|
+
prom.instance_variable_set(:@type, :when)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
alias all when
|
126
|
+
|
127
|
+
def all_resolved(*promises)
|
128
|
+
promises = Array(promises.length == 1 ? promises.first : promises)
|
129
|
+
`Promise.allResolved(#{promises})`.tap do |prom|
|
130
|
+
prom.instance_variable_set(:@type, :all_resolved)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def any(*promises)
|
135
|
+
promises = Array(promises.length == 1 ? promises.first : promises)
|
136
|
+
`Promise.any(#{promises})`.tap do |prom|
|
137
|
+
prom.instance_variable_set(:@type, :any)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def race(*promises)
|
142
|
+
promises = Array(promises.length == 1 ? promises.first : promises)
|
143
|
+
`Promise.race(#{promises})`.tap do |prom|
|
144
|
+
prom.instance_variable_set(:@type, :race)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def resolve(value = nil)
|
149
|
+
`Promise.resolve(#{value})`.tap do |prom|
|
150
|
+
prom.instance_variable_set(:@type, :resolve)
|
151
|
+
prom.instance_variable_set(:@realized, :resolve)
|
152
|
+
prom.instance_variable_set(:@value, value)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
alias value resolve
|
156
|
+
|
157
|
+
def reject(value = nil)
|
158
|
+
`Promise.reject(#{value})`.tap do |prom|
|
159
|
+
prom.instance_variable_set(:@type, :reject)
|
160
|
+
prom.instance_variable_set(:@realized, :reject)
|
161
|
+
prom.instance_variable_set(:@value, value)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
alias error reject
|
165
|
+
end
|
166
|
+
|
167
|
+
attr_reader :prev, :next
|
168
|
+
|
169
|
+
# Is this promise native to JavaScript? This means, that methods like resolve
|
170
|
+
# or reject won't be available.
|
171
|
+
def native?
|
172
|
+
@type != :opal
|
173
|
+
end
|
174
|
+
|
175
|
+
# Raise an exception when a non-JS-native method is called on a JS-native promise
|
176
|
+
def nativity_check!
|
177
|
+
raise ArgumentError, 'this promise is native to JavaScript' if native?
|
178
|
+
end
|
179
|
+
|
180
|
+
# Raise an exception when a non-JS-native method is called on a JS-native promise
|
181
|
+
# but permits some typed promises
|
182
|
+
def light_nativity_check!
|
183
|
+
return if %i[reject resolve trace always fail then].include? @type
|
184
|
+
raise ArgumentError, 'this promise is native to JavaScript' if native?
|
185
|
+
end
|
186
|
+
|
187
|
+
# Allow only one chain to be present, as needed by the previous implementation.
|
188
|
+
# This isn't a strict check - it's always possible on the JS side to chain a
|
189
|
+
# given block.
|
190
|
+
def there_can_be_only_one!
|
191
|
+
raise ArgumentError, 'a promise has already been chained' if @next && @next.any?
|
192
|
+
end
|
193
|
+
|
194
|
+
def gen_tracing_proc(passing, &block)
|
195
|
+
proc do |i|
|
196
|
+
res = passing.call(i)
|
197
|
+
yield(res)
|
198
|
+
res
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def resolve(value = nil)
|
203
|
+
nativity_check!
|
204
|
+
raise ArgumentError, 'this promise was already resolved' if @realized
|
205
|
+
@value = value
|
206
|
+
@realized = :resolve
|
207
|
+
@resolve_proc.call(value)
|
208
|
+
self
|
209
|
+
end
|
210
|
+
alias resolve! resolve
|
211
|
+
|
212
|
+
def reject(value = nil)
|
213
|
+
nativity_check!
|
214
|
+
raise ArgumentError, 'this promise was already resolved' if @realized
|
215
|
+
@value = value
|
216
|
+
@realized = :reject
|
217
|
+
@reject_proc.call(value)
|
218
|
+
self
|
219
|
+
end
|
220
|
+
alias reject! reject
|
221
|
+
|
222
|
+
def then(&block)
|
223
|
+
prom = nil
|
224
|
+
blk = gen_tracing_proc(block) do |val|
|
225
|
+
prom.instance_variable_set(:@realized, :resolve)
|
226
|
+
prom.instance_variable_set(:@value, val)
|
227
|
+
end
|
228
|
+
prom = `self.then(#{blk})`
|
229
|
+
prom.instance_variable_set(:@prev, self)
|
230
|
+
prom.instance_variable_set(:@type, :then)
|
231
|
+
(@next ||= []) << prom
|
232
|
+
prom
|
233
|
+
end
|
234
|
+
|
235
|
+
def then!(&block)
|
236
|
+
there_can_be_only_one!
|
237
|
+
self.then(&block)
|
238
|
+
end
|
239
|
+
|
240
|
+
alias do then
|
241
|
+
alias do! then!
|
242
|
+
|
243
|
+
def fail(&block)
|
244
|
+
prom = nil
|
245
|
+
blk = gen_tracing_proc(block) do |val|
|
246
|
+
prom.instance_variable_set(:@realized, :resolve)
|
247
|
+
prom.instance_variable_set(:@value, val)
|
248
|
+
end
|
249
|
+
prom = `self.catch(#{blk})`
|
250
|
+
prom.instance_variable_set(:@prev, self)
|
251
|
+
prom.instance_variable_set(:@type, :fail)
|
252
|
+
(@next ||= []) << prom
|
253
|
+
prom
|
254
|
+
end
|
255
|
+
|
256
|
+
def fail!(&block)
|
257
|
+
there_can_be_only_one!
|
258
|
+
fail(&block)
|
259
|
+
end
|
260
|
+
|
261
|
+
alias rescue fail
|
262
|
+
alias catch fail
|
263
|
+
alias rescue! fail!
|
264
|
+
alias catch! fail!
|
265
|
+
|
266
|
+
def always(&block)
|
267
|
+
prom = nil
|
268
|
+
blk = gen_tracing_proc(block) do |val|
|
269
|
+
prom.instance_variable_set(:@realized, :resolve)
|
270
|
+
prom.instance_variable_set(:@value, val)
|
271
|
+
end
|
272
|
+
prom = `self.finally(#{blk})`
|
273
|
+
prom.instance_variable_set(:@prev, self)
|
274
|
+
prom.instance_variable_set(:@type, :always)
|
275
|
+
(@next ||= []) << prom
|
276
|
+
prom
|
277
|
+
end
|
278
|
+
|
279
|
+
def always!(&block)
|
280
|
+
there_can_be_only_one!
|
281
|
+
always(&block)
|
282
|
+
end
|
283
|
+
|
284
|
+
alias finally always
|
285
|
+
alias ensure always
|
286
|
+
alias finally! always!
|
287
|
+
alias ensure! always!
|
288
|
+
|
289
|
+
def trace(depth = nil, &block)
|
290
|
+
prom = self.then do
|
291
|
+
values = []
|
292
|
+
prom = self
|
293
|
+
while prom && (!depth || depth > 0)
|
294
|
+
val = nil
|
295
|
+
begin
|
296
|
+
val = prom.value
|
297
|
+
rescue ArgumentError
|
298
|
+
val = :native
|
299
|
+
end
|
300
|
+
values.unshift(val)
|
301
|
+
depth -= 1 if depth
|
302
|
+
prom = prom.prev
|
303
|
+
end
|
304
|
+
yield(*values)
|
305
|
+
end
|
306
|
+
|
307
|
+
prom.instance_variable_set(:@type, :trace)
|
308
|
+
prom
|
309
|
+
end
|
310
|
+
|
311
|
+
def trace!(*args, &block)
|
312
|
+
there_can_be_only_one!
|
313
|
+
trace(*args, &block)
|
314
|
+
end
|
315
|
+
|
316
|
+
def resolved?
|
317
|
+
light_nativity_check!
|
318
|
+
@realized == :resolve
|
319
|
+
end
|
320
|
+
|
321
|
+
def rejected?
|
322
|
+
light_nativity_check!
|
323
|
+
@realized == :reject
|
324
|
+
end
|
325
|
+
|
326
|
+
def realized?
|
327
|
+
light_nativity_check!
|
328
|
+
!@realized.nil?
|
329
|
+
end
|
330
|
+
|
331
|
+
def value
|
332
|
+
if resolved?
|
333
|
+
if PromiseV2 === @value
|
334
|
+
@value.value
|
335
|
+
else
|
336
|
+
@value
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def error
|
342
|
+
light_nativity_check!
|
343
|
+
@value if rejected?
|
344
|
+
end
|
345
|
+
|
346
|
+
def and(*promises)
|
347
|
+
promises = promises.map do |i|
|
348
|
+
if PromiseV2 === i
|
349
|
+
i
|
350
|
+
else
|
351
|
+
PromiseV2.value(i)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
PromiseV2.when(self, *promises).then do |a, *b|
|
355
|
+
[*a, *b]
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def initialize(&block)
|
360
|
+
yield self if block_given?
|
361
|
+
end
|
362
|
+
|
363
|
+
alias to_n itself
|
364
|
+
|
365
|
+
def inspect
|
366
|
+
result = "#<#{self.class}"
|
367
|
+
|
368
|
+
if @type
|
369
|
+
result += ":#{@type}" unless %i[opal resolve reject].include? @type
|
370
|
+
else
|
371
|
+
result += ':native'
|
372
|
+
end
|
373
|
+
|
374
|
+
result += ":#{@realized}" if @realized
|
375
|
+
result += "(#{object_id})"
|
376
|
+
|
377
|
+
if @next && @next.any?
|
378
|
+
result += " >> #{@next.inspect}"
|
379
|
+
end
|
380
|
+
|
381
|
+
result += ": #{@value.inspect}" if @value
|
382
|
+
result += '>'
|
383
|
+
|
384
|
+
result
|
385
|
+
end
|
386
|
+
end
|