mr_darcy 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +95 -0
- data/Guardfile +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +267 -0
- data/Rakefile +6 -0
- data/lib/mr_darcy/context.rb +71 -0
- data/lib/mr_darcy/deferred.rb +25 -0
- data/lib/mr_darcy/drivers/celluloid.rb +23 -0
- data/lib/mr_darcy/drivers/synchronous.rb +15 -0
- data/lib/mr_darcy/drivers/thread.rb +22 -0
- data/lib/mr_darcy/drivers.rb +13 -0
- data/lib/mr_darcy/promise/base.rb +117 -0
- data/lib/mr_darcy/promise/celluloid.rb +52 -0
- data/lib/mr_darcy/promise/child_promise.rb +83 -0
- data/lib/mr_darcy/promise/dsl.rb +29 -0
- data/lib/mr_darcy/promise/em.rb +29 -0
- data/lib/mr_darcy/promise/state/base.rb +43 -0
- data/lib/mr_darcy/promise/state/rejected.rb +11 -0
- data/lib/mr_darcy/promise/state/resolved.rb +11 -0
- data/lib/mr_darcy/promise/state/unresolved.rb +19 -0
- data/lib/mr_darcy/promise/state.rb +25 -0
- data/lib/mr_darcy/promise/synchronous.rb +27 -0
- data/lib/mr_darcy/promise/thread.rb +64 -0
- data/lib/mr_darcy/promise.rb +31 -0
- data/lib/mr_darcy/role.rb +45 -0
- data/lib/mr_darcy/version.rb +3 -0
- data/lib/mr_darcy.rb +26 -0
- data/mr_darcy.gemspec +29 -0
- data/spec/acceptance/dci_bank_transfer_spec.rb +62 -0
- data/spec/acceptance/em_http_request_spec.rb +22 -0
- data/spec/acceptance/open-uri_http_request_spec.rb +21 -0
- data/spec/acceptance/simple_promise_spec.rb +25 -0
- data/spec/acceptance/simple_promise_with_chained_fail.rb +25 -0
- data/spec/acceptance/simple_promise_with_fail_spec.rb +25 -0
- data/spec/acceptance/simple_promise_with_then_spec.rb +25 -0
- data/spec/lib/mr_darcy/context_spec.rb +22 -0
- data/spec/lib/mr_darcy/promise/base_spec.rb +197 -0
- data/spec/lib/mr_darcy/promise/child_promise_spec.rb +169 -0
- data/spec/lib/mr_darcy/promise/dsl_spec.rb +43 -0
- data/spec/lib/mr_darcy/promise/state/base_spec.rb +24 -0
- data/spec/lib/mr_darcy/promise/state/rejected_spec.rb +12 -0
- data/spec/lib/mr_darcy/promise/state/resolved_spec.rb +12 -0
- data/spec/lib/mr_darcy/promise/state/unresolved_spec.rb +22 -0
- data/spec/lib/mr_darcy/promise/state_spec.rb +30 -0
- data/spec/lib/mr_darcy/promise/synchronous_spec.rb +21 -0
- data/spec/lib/mr_darcy/promise_spec.rb +72 -0
- data/spec/lib/mr_darcy/role_spec.rb +89 -0
- data/spec/lib/mr_darcy_spec.rb +19 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/context_helpers.rb +19 -0
- data/spec/support/shared_examples_for_promise.rb +47 -0
- data/spec/support/shared_examples_for_thennable.rb +10 -0
- metadata +279 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class BankTransfer < MrDarcy::Context
|
4
|
+
role :money_source do
|
5
|
+
def has_available_funds? amount
|
6
|
+
available_balance >= amount
|
7
|
+
end
|
8
|
+
|
9
|
+
def subtract_funds amount
|
10
|
+
self.available_balance = available_balance - amount
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
role :money_destination do
|
15
|
+
def receive_funds amount
|
16
|
+
self.available_balance = available_balance + amount
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
action :transfer do |amount|
|
21
|
+
if money_source.has_available_funds? amount
|
22
|
+
money_source.subtract_funds amount
|
23
|
+
money_destination.receive_funds amount
|
24
|
+
else
|
25
|
+
raise "insufficient funds"
|
26
|
+
end
|
27
|
+
amount
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Account = Struct.new(:available_balance)
|
32
|
+
|
33
|
+
describe 'DCI Bank Transfer' do
|
34
|
+
let(:money_source) { Account.new source_balance }
|
35
|
+
let(:money_destination) { Account.new destination_balance }
|
36
|
+
|
37
|
+
MrDarcy.all_drivers.each do |driver|
|
38
|
+
if driver == :synchronous
|
39
|
+
describe "Driver #{driver}" do
|
40
|
+
let(:context) { BankTransfer.new money_source: money_source, money_destination: money_destination, driver: driver }
|
41
|
+
|
42
|
+
When 'the source balance is 10' do
|
43
|
+
let(:source_balance) { 10 }
|
44
|
+
|
45
|
+
And 'the destination balance is 5' do
|
46
|
+
let(:destination_balance) { 5 }
|
47
|
+
|
48
|
+
When 'I transfer 8' do
|
49
|
+
subject { context.transfer(8).final }
|
50
|
+
|
51
|
+
its('money_source.available_balance') { should eq 2 }
|
52
|
+
its('money_destination.available_balance') { should eq 13 }
|
53
|
+
its(:result) { should be_nil }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
pending "not working with driver #{driver} :("
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'em-http-request'
|
4
|
+
|
5
|
+
describe "Wrapping em-http-request" do
|
6
|
+
let(:promise) do
|
7
|
+
MrDarcy::Promise.new driver: :em do |p|
|
8
|
+
http = EM::HttpRequest.new('http://camp.ruby.org.nz/').get
|
9
|
+
http.errback do
|
10
|
+
p.reject http.error
|
11
|
+
end
|
12
|
+
http.callback do
|
13
|
+
p.resolve http.response
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
subject { promise }
|
19
|
+
|
20
|
+
it_should_behave_like 'a resolved promise'
|
21
|
+
its(:result) { should match /Rails Camp NZ/i }
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
describe "Wrapping OpenURI" do
|
5
|
+
MrDarcy.all_drivers.each do |driver|
|
6
|
+
describe "driver #{driver}" do
|
7
|
+
let(:promise) do
|
8
|
+
MrDarcy::Promise.new driver: driver do |p|
|
9
|
+
open('http://camp.ruby.org.nz') do |f|
|
10
|
+
p.resolve f.read
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { promise }
|
16
|
+
|
17
|
+
it_should_behave_like 'a resolved promise'
|
18
|
+
its(:result) { should match /Rails Camp NZ/i }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Simple promise" do
|
4
|
+
|
5
|
+
MrDarcy.all_drivers.each do |driver|
|
6
|
+
describe "with driver #{driver}" do
|
7
|
+
let(:promise) { MrDarcy::Promise.new(driver: driver, &promise_block) }
|
8
|
+
subject { promise }
|
9
|
+
|
10
|
+
When 'the promise resolves' do
|
11
|
+
let(:promise_block) { proc { |p| p.resolve 1 } }
|
12
|
+
|
13
|
+
it_should_behave_like 'a resolved promise'
|
14
|
+
its(:result) { should eq 1 }
|
15
|
+
end
|
16
|
+
|
17
|
+
When 'the promise rejects' do
|
18
|
+
let(:promise_block) { proc { |p| p.reject 1 } }
|
19
|
+
|
20
|
+
it_should_behave_like 'a rejected promise'
|
21
|
+
its(:result) { should eq 1 }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Promise with then and fail" do
|
4
|
+
|
5
|
+
MrDarcy.all_drivers.each do |driver|
|
6
|
+
describe "with driver #{driver}" do
|
7
|
+
let(:promise) { MrDarcy::Promise.new(driver: driver, &promise_block).then {}.fail { |v| v+1 } }
|
8
|
+
subject { promise }
|
9
|
+
|
10
|
+
When 'the promise resolves' do
|
11
|
+
let(:promise_block) { proc { |p| p.resolve 1 } }
|
12
|
+
|
13
|
+
it_should_behave_like 'a resolved promise'
|
14
|
+
its(:result) { should be_nil }
|
15
|
+
end
|
16
|
+
|
17
|
+
When 'the promise rejects' do
|
18
|
+
let(:promise_block) { proc { |p| p.reject 1 } }
|
19
|
+
|
20
|
+
it_should_behave_like 'a rejected promise'
|
21
|
+
its(:result) { should eq 2 }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Promise with fail" do
|
4
|
+
|
5
|
+
MrDarcy.all_drivers.each do |driver|
|
6
|
+
describe "with driver #{driver}" do
|
7
|
+
let(:promise) { MrDarcy::Promise.new(driver: driver, &promise_block).fail { |v| v+1 } }
|
8
|
+
subject { promise }
|
9
|
+
|
10
|
+
When 'the promise resolves' do
|
11
|
+
let(:promise_block) { proc { |p| p.resolve 1 } }
|
12
|
+
|
13
|
+
it_should_behave_like 'a resolved promise'
|
14
|
+
its(:result) { should eq 1 }
|
15
|
+
end
|
16
|
+
|
17
|
+
When 'the promise rejects' do
|
18
|
+
let(:promise_block) { proc { |p| p.reject 1 } }
|
19
|
+
|
20
|
+
it_should_behave_like 'a resolved promise'
|
21
|
+
its(:result) { should eq 2 }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Promise with then" do
|
4
|
+
|
5
|
+
MrDarcy.all_drivers.each do |driver|
|
6
|
+
describe "with driver #{driver}" do
|
7
|
+
let(:promise) { MrDarcy::Promise.new(driver: driver, &promise_block).then { |v| v+1 } }
|
8
|
+
subject { promise }
|
9
|
+
|
10
|
+
When 'the promise resolves' do
|
11
|
+
let(:promise_block) { proc { |p| p.resolve 1 } }
|
12
|
+
|
13
|
+
it_should_behave_like 'a resolved promise'
|
14
|
+
its(:result) { should eq 2 }
|
15
|
+
end
|
16
|
+
|
17
|
+
When 'the promise rejects' do
|
18
|
+
let(:promise_block) { proc { |p| p.reject 1 } }
|
19
|
+
|
20
|
+
it_should_behave_like 'a rejected promise'
|
21
|
+
its(:result) { should eq 1 }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Context do
|
4
|
+
context 'class methods' do
|
5
|
+
subject { described_class }
|
6
|
+
it { should respond_to :role }
|
7
|
+
it { should respond_to :action }
|
8
|
+
|
9
|
+
describe '.role' do
|
10
|
+
subject { described_class.role :test_name }
|
11
|
+
|
12
|
+
it { should be_a MrDarcy::Role }
|
13
|
+
its(:name) { should eq :test_name }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.action' do
|
17
|
+
subject { -> { described_class.action :action_name, &->{} } }
|
18
|
+
|
19
|
+
it { should change { described_class.instance_methods.member? :action_name }.from(false).to(true) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Promise::Base do
|
4
|
+
let(:promise_block) { proc {} }
|
5
|
+
let(:mock_promise) do
|
6
|
+
Class.new(MrDarcy::Promise::Base) do
|
7
|
+
def schedule_promise
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_child_promise
|
12
|
+
MrDarcy::Promise::ChildPromise.new driver: :synchronous
|
13
|
+
end
|
14
|
+
end.new(promise_block)
|
15
|
+
end
|
16
|
+
subject { mock_promise }
|
17
|
+
|
18
|
+
it_behaves_like :thenable
|
19
|
+
|
20
|
+
describe '#then' do
|
21
|
+
let(:then_block) { proc {} }
|
22
|
+
subject { mock_promise.then &then_block }
|
23
|
+
|
24
|
+
it_behaves_like :thenable
|
25
|
+
|
26
|
+
When 'the promise is already resolved' do
|
27
|
+
before { mock_promise.resolve :good }
|
28
|
+
it_behaves_like 'a resolved promise'
|
29
|
+
|
30
|
+
it 'calls the then block with the result' do
|
31
|
+
expect { |b| mock_promise.then(&b) }.to yield_with_args(:good)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'calls the then block only once' do
|
35
|
+
expect { |b| mock_promise.then(&b) }.to yield_control.once
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
When 'the promise is already rejected' do
|
40
|
+
before { mock_promise.reject :bad }
|
41
|
+
it_behaves_like 'a rejected promise'
|
42
|
+
|
43
|
+
it 'does not call the then block' do
|
44
|
+
expect { |b| mock_promise.then(&b) }.not_to yield_control
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Otherwise do
|
49
|
+
it_behaves_like 'an unresolved promise'
|
50
|
+
|
51
|
+
it 'does not call the then block' do
|
52
|
+
expect { |b| mock_promise.then(&b) }.not_to yield_control
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#fail' do
|
58
|
+
let(:fail_block) { proc {} }
|
59
|
+
subject { mock_promise.fail &fail_block }
|
60
|
+
|
61
|
+
it_behaves_like :thenable
|
62
|
+
|
63
|
+
When 'the promise is already resolved' do
|
64
|
+
before { mock_promise.resolve :good }
|
65
|
+
it_behaves_like 'a resolved promise'
|
66
|
+
|
67
|
+
it 'does not call the fail block' do
|
68
|
+
expect { |b| mock_promise.fail(&b) }.not_to yield_control
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
When 'the promise is already rejected' do
|
73
|
+
before { mock_promise.reject :bad }
|
74
|
+
|
75
|
+
When 'the fail block re-fails' do
|
76
|
+
let(:fail_block) { proc { raise :bad } }
|
77
|
+
|
78
|
+
it_behaves_like 'a rejected promise'
|
79
|
+
end
|
80
|
+
|
81
|
+
Otherwise do
|
82
|
+
it_behaves_like 'a resolved promise'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'calls the fail block with the result' do
|
86
|
+
expect { |b| mock_promise.fail(&b) }.to yield_with_args(:bad)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'calls the fail block only once' do
|
90
|
+
expect { |b| mock_promise.fail(&b) }.to yield_control.once
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
Otherwise do
|
95
|
+
it_behaves_like 'an unresolved promise'
|
96
|
+
|
97
|
+
it 'does not call the fail block' do
|
98
|
+
expect { |b| mock_promise.fail(&b) }.not_to yield_control
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#result' do
|
104
|
+
subject { -> { mock_promise.result } }
|
105
|
+
it { should raise_error }
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#final' do
|
109
|
+
subject { -> { mock_promise.final } }
|
110
|
+
it { should raise_error }
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#raise' do
|
114
|
+
before { mock_promise.stub result: :foo }
|
115
|
+
subject { -> { mock_promise.raise } }
|
116
|
+
|
117
|
+
When 'the promise is resolved' do
|
118
|
+
before { mock_promise.resolve :good }
|
119
|
+
|
120
|
+
it { should_not raise_error }
|
121
|
+
end
|
122
|
+
|
123
|
+
When 'the promise is rejected' do
|
124
|
+
before { mock_promise.reject :bad }
|
125
|
+
|
126
|
+
it { should raise_error }
|
127
|
+
end
|
128
|
+
|
129
|
+
When 'the promise is unresolved' do
|
130
|
+
it { should_not raise_error }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#resolve' do
|
135
|
+
let(:resolve_value) { :good }
|
136
|
+
subject { mock_promise.resolve resolve_value }
|
137
|
+
|
138
|
+
When 'the promise is already resolved' do
|
139
|
+
before { mock_promise.resolve :previous }
|
140
|
+
|
141
|
+
specify { expect { subject }.to raise_error }
|
142
|
+
end
|
143
|
+
|
144
|
+
When 'the promise is already rejected' do
|
145
|
+
before { mock_promise.reject :previous }
|
146
|
+
|
147
|
+
specify { expect { subject }.to raise_error }
|
148
|
+
end
|
149
|
+
|
150
|
+
Otherwise do
|
151
|
+
specify { expect { subject }.to change{ mock_promise.resolved? }.from(false).to(true) }
|
152
|
+
specify { expect { subject }.to change{ mock_promise.unresolved? }.from(true).to(false) }
|
153
|
+
|
154
|
+
When 'there is a child promise' do
|
155
|
+
let(:then_block) { proc {} }
|
156
|
+
before { mock_promise.then(&then_block) }
|
157
|
+
|
158
|
+
it 'calls the then block' do
|
159
|
+
expect(then_block).to receive(:call).with(:good).once
|
160
|
+
subject
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#reject' do
|
167
|
+
let(:reject_value) { :bad }
|
168
|
+
subject { mock_promise.reject reject_value }
|
169
|
+
|
170
|
+
When 'the promise is already resolved' do
|
171
|
+
before { mock_promise.resolve :previous }
|
172
|
+
|
173
|
+
specify { expect { subject }.to raise_error }
|
174
|
+
end
|
175
|
+
|
176
|
+
When 'the promise is already rejected' do
|
177
|
+
before { mock_promise.reject :previous }
|
178
|
+
|
179
|
+
specify { expect { subject }.to raise_error }
|
180
|
+
end
|
181
|
+
|
182
|
+
Otherwise do
|
183
|
+
specify { expect { subject }.to change{ mock_promise.rejected? }.from(false).to(true) }
|
184
|
+
specify { expect { subject }.to change{ mock_promise.unresolved? }.from(true).to(false) }
|
185
|
+
|
186
|
+
When 'there is a child promise' do
|
187
|
+
let(:fail_block) { proc {} }
|
188
|
+
before { mock_promise.fail(&fail_block) }
|
189
|
+
|
190
|
+
it 'calls the fail block' do
|
191
|
+
expect(fail_block).to receive(:call).with(:bad).once
|
192
|
+
subject
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Promise::ChildPromise do
|
4
|
+
let(:child_promise) { MrDarcy::Promise::ChildPromise.new driver: :Synchronous }
|
5
|
+
subject { child_promise }
|
6
|
+
|
7
|
+
it { should be_a MrDarcy::Deferred }
|
8
|
+
|
9
|
+
it { should respond_to :resolve_block }
|
10
|
+
it { should respond_to :reject_block }
|
11
|
+
|
12
|
+
describe '#parent_resolved' do
|
13
|
+
let(:resolve_value) { :good }
|
14
|
+
subject { child_promise.parent_resolved resolve_value }
|
15
|
+
|
16
|
+
When 'it handles success' do
|
17
|
+
let(:resolve_block) { proc {} }
|
18
|
+
before { child_promise.resolve_block = resolve_block }
|
19
|
+
|
20
|
+
it 'calls the resolve block' do
|
21
|
+
expect(resolve_block).to receive(:call).with(resolve_value)
|
22
|
+
subject
|
23
|
+
end
|
24
|
+
|
25
|
+
When 'the resolve block returns a value' do
|
26
|
+
let(:resolve_block) { proc { :new_value } }
|
27
|
+
|
28
|
+
it 'resolves' do
|
29
|
+
expect { subject }.to change { child_promise.resolved? }.from(false).to(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the new value' do
|
33
|
+
expect { subject }.to change { child_promise.result }.to(:new_value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
When 'the resolve block returns a new promise' do
|
38
|
+
let(:promise_block) { proc {} }
|
39
|
+
let(:promise) { MrDarcy::Promise::Synchronous.new promise_block }
|
40
|
+
let(:resolve_block) { proc { promise } }
|
41
|
+
|
42
|
+
it 'doesn\'t resolve' do
|
43
|
+
expect { subject }.not_to change { child_promise.resolved? }
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'doesn\'t set the value' do
|
47
|
+
expect { subject }.not_to change { child_promise.result }
|
48
|
+
end
|
49
|
+
|
50
|
+
When 'the promise eventually resolves' do
|
51
|
+
before { child_promise.parent_resolved resolve_value }
|
52
|
+
subject { promise.resolve :mr_good_stuff }
|
53
|
+
|
54
|
+
it 'resolves' do
|
55
|
+
expect { subject }.to change{ child_promise.resolved? }.from(false).to(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'has the new value' do
|
59
|
+
expect { subject }.to change { child_promise.result }.to(:mr_good_stuff)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
When 'the promise eventually rejects' do
|
64
|
+
before { child_promise.parent_resolved resolve_value }
|
65
|
+
subject { promise.reject :mr_bad_stuff }
|
66
|
+
|
67
|
+
it 'rejects' do
|
68
|
+
expect { subject }.to change{ child_promise.rejected? }.from(false).to(true)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'has the new value' do
|
72
|
+
expect { subject }.to change { child_promise.result }.to(:mr_bad_stuff)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Otherwise do
|
79
|
+
it 'is resolved' do
|
80
|
+
subject
|
81
|
+
expect(child_promise).to be_resolved
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'is resolved with the supplied value' do
|
85
|
+
subject
|
86
|
+
expect(child_promise.result).to eq(resolve_value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#parent_rejected' do
|
92
|
+
let(:reject_value) { :bad }
|
93
|
+
subject { child_promise.parent_rejected reject_value }
|
94
|
+
|
95
|
+
When 'it handles success' do
|
96
|
+
let(:reject_block) { proc {} }
|
97
|
+
before { child_promise.reject_block = reject_block }
|
98
|
+
|
99
|
+
it 'calls the reject block' do
|
100
|
+
expect(reject_block).to receive(:call).with(reject_value)
|
101
|
+
subject
|
102
|
+
end
|
103
|
+
|
104
|
+
When 'the reject block returns a value' do
|
105
|
+
let(:reject_block) { proc { :new_value } }
|
106
|
+
|
107
|
+
it 'resolves' do
|
108
|
+
expect { subject }.to change { child_promise.resolved? }.from(false).to(true)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'sets the new value' do
|
112
|
+
expect { subject }.to change { child_promise.result }.to(:new_value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
When 'the reject block returns a new promise' do
|
117
|
+
let(:promise_block) { proc {} }
|
118
|
+
let(:promise) { MrDarcy::Promise::Synchronous.new promise_block }
|
119
|
+
let(:reject_block) { proc { promise } }
|
120
|
+
|
121
|
+
it 'doesn\'t reject' do
|
122
|
+
expect { subject }.not_to change { child_promise.rejected? }
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'doesn\'t set the value' do
|
126
|
+
expect { subject }.not_to change { child_promise.result }
|
127
|
+
end
|
128
|
+
|
129
|
+
When 'the promise eventually resolves' do
|
130
|
+
before { child_promise.parent_rejected reject_value }
|
131
|
+
subject { promise.resolve :mr_good_stuff }
|
132
|
+
|
133
|
+
it 'resolves' do
|
134
|
+
expect { subject }.to change{ child_promise.resolved? }.from(false).to(true)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'has the new value' do
|
138
|
+
expect { subject }.to change { child_promise.result }.to(:mr_good_stuff)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
When 'the promise eventually rejects' do
|
143
|
+
before { child_promise.parent_rejected reject_value }
|
144
|
+
subject { promise.reject :mr_bad_stuff }
|
145
|
+
|
146
|
+
it 'rejects' do
|
147
|
+
expect { subject }.to change{ child_promise.rejected? }.from(false).to(true)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'has the new value' do
|
151
|
+
expect { subject }.to change { child_promise.result }.to(:mr_bad_stuff)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
Otherwise do
|
158
|
+
it 'is rejected' do
|
159
|
+
subject
|
160
|
+
expect(child_promise).to be_rejected
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'is rejected with the supplied value' do
|
164
|
+
subject
|
165
|
+
expect(child_promise.result).to eq(reject_value)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Promise::DSL do
|
4
|
+
|
5
|
+
let(:promise) { double :promise }
|
6
|
+
let(:dsl) { described_class.new(promise) }
|
7
|
+
subject { dsl }
|
8
|
+
|
9
|
+
it { should respond_to :resolve }
|
10
|
+
it { should respond_to :reject }
|
11
|
+
it { should respond_to :unresolved? }
|
12
|
+
it { should respond_to :resolved? }
|
13
|
+
it { should respond_to :rejected? }
|
14
|
+
it { should respond_to :then }
|
15
|
+
it { should respond_to :fail }
|
16
|
+
it { should respond_to :result }
|
17
|
+
it { should respond_to :final }
|
18
|
+
|
19
|
+
describe '.new' do
|
20
|
+
its(:promise) { should eq promise }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#resolve' do
|
24
|
+
subject { dsl.resolve :resolved_value }
|
25
|
+
before { promise.stub :value= => nil, resolve: nil }
|
26
|
+
|
27
|
+
it 'resolves the promise' do
|
28
|
+
promise.should_receive(:resolve).with(:resolved_value)
|
29
|
+
subject
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#reject' do
|
34
|
+
subject { dsl.reject :exception }
|
35
|
+
before { promise.stub :value= => nil, reject: nil }
|
36
|
+
|
37
|
+
it 'rejects the promise' do
|
38
|
+
promise.should_receive(:reject).with(:exception)
|
39
|
+
subject
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Promise::State::Base do
|
4
|
+
let(:initial_state) { 'unresolved'}
|
5
|
+
let(:stateful) { Struct.new(:state).new initial_state }
|
6
|
+
let(:state) { described_class.new stateful }
|
7
|
+
subject { state }
|
8
|
+
|
9
|
+
it { should respond_to :stateful }
|
10
|
+
it { should respond_to :stateful= }
|
11
|
+
it { should respond_to :unresolved? }
|
12
|
+
it { should respond_to :resolved? }
|
13
|
+
it { should respond_to :rejected? }
|
14
|
+
|
15
|
+
describe '#resolve' do
|
16
|
+
subject { -> { state.resolve } }
|
17
|
+
it { should raise_error }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#reject' do
|
21
|
+
subject { -> { state.reject } }
|
22
|
+
it { should raise_error }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Promise::State::Rejected do
|
4
|
+
let(:initial_state) { :unresolved }
|
5
|
+
let(:stateful) { Struct.new(:state).new initial_state }
|
6
|
+
let(:state) { described_class.new stateful }
|
7
|
+
|
8
|
+
subject { state }
|
9
|
+
|
10
|
+
it { should be_a MrDarcy::Promise::State::Base }
|
11
|
+
its(:rejected?) { should be_true }
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MrDarcy::Promise::State::Resolved do
|
4
|
+
let(:initial_state) { :unresolved }
|
5
|
+
let(:stateful) { Struct.new(:state).new initial_state }
|
6
|
+
let(:state) { described_class.new stateful }
|
7
|
+
|
8
|
+
subject { state }
|
9
|
+
|
10
|
+
it { should be_a MrDarcy::Promise::State::Base }
|
11
|
+
its(:resolved?) { should be_true }
|
12
|
+
end
|