visionary 0.0.1 → 0.2.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 +4 -4
- data/.rspec +1 -0
- data/README.md +181 -3
- data/lib/visionary.rb +6 -1
- data/lib/visionary/future.rb +94 -0
- data/lib/visionary/promise.rb +34 -0
- data/lib/visionary/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/visionary/future_spec.rb +256 -0
- data/spec/visionary/promise_spec.rb +75 -0
- data/spec/visionary/promised_future_spec.rb +1 -0
- data/spec/visionary_spec.rb +1 -7
- data/visionary.gemspec +2 -0
- metadata +38 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 106d4aff2ec2c6f186337b10357075980c005983
|
4
|
+
data.tar.gz: 6d322855f784a1e6fbb98e71e71c50153e9e45dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbae37b484f2fa46352527c067f0568054ae720eb64d22cb4805cbaa3fd50a3a44ac145f0b3031b9f64e4f7e78bd5ea7a39a1f0ef51a569ff6e0525939c17561
|
7
|
+
data.tar.gz: 8b86917eab4a30c9b28766499ae527c4dba05d92e0dfcf816d7c94409bf489b13fda20619d34107d3728773932b26571ac8b8a63a89b5550b6e19e8e9cb08bd8
|
data/.rspec
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Visionary
|
2
2
|
|
3
|
-
|
3
|
+
Simple futures implementation in ruby.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -18,11 +18,189 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
### Enabling `future` and `promise` helpers
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Visionary.setup!
|
25
|
+
```
|
26
|
+
|
27
|
+
### Futures
|
28
|
+
|
29
|
+
#### Deferring computation
|
30
|
+
|
31
|
+
Use `Kernel#future(&blk)` to define future and run it immediately:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
future { do_some_hard_work(some: :data) }
|
35
|
+
```
|
36
|
+
|
37
|
+
it is an alternative to:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
Visionary::Future.new { do_some_hard_work(some: :data) }.run
|
41
|
+
```
|
42
|
+
|
43
|
+
#### Checking the status of computation
|
44
|
+
|
45
|
+
You can check the status of computation inside of future by using `#state`:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
hard_work = future { do_some_hard_work(some: :data) }
|
49
|
+
hard_work.state # => :pending
|
50
|
+
sleep(5.0)
|
51
|
+
hard_work.state # => :completed
|
52
|
+
```
|
53
|
+
|
54
|
+
It can have 3 values: `:pending`, `:completed` and `:failed`.
|
55
|
+
|
56
|
+
#### Getting the result of computation
|
57
|
+
|
58
|
+
Once future has a state of `:completed`, it will hold the result of computation in `#value`:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
hard_work = future { do_some_hard_work(some: :data) }
|
62
|
+
do_something_else
|
63
|
+
hard_work.value # => 42
|
64
|
+
```
|
65
|
+
|
66
|
+
When the future is completed it becomes `frozen`:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
hard_work.state # => :completed
|
70
|
+
hard_work.frozen? # => true
|
71
|
+
hard_work.run # raises RuntimeError: can't modify frozen Visionary::Future
|
72
|
+
```
|
73
|
+
|
74
|
+
#### Explicitly awaiting for result with blocking
|
75
|
+
|
76
|
+
To block execution of until future is completed, you can use `#await` method on future:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
hard_work = future { do_some_hard_work(some: :data) }
|
80
|
+
hard_work.await
|
81
|
+
hard_work.state # => :completed
|
82
|
+
```
|
83
|
+
|
84
|
+
#### Failed computations
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
hard_work = future { raise RuntimeError }
|
88
|
+
hard_work.await
|
89
|
+
hard_work.state # => :failed
|
90
|
+
hard_work.error # => #<RuntimeError: RuntimeError>
|
91
|
+
```
|
92
|
+
|
93
|
+
#### Awaiting for result without blocking
|
94
|
+
|
95
|
+
To await for computation to complete and do something else after that without blocking use `#then` method on future:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
hard_work = future { do_some_hard_work(some: :data) }
|
99
|
+
easy_task = hard_work.then { |result| make_it_awesome(result) }
|
100
|
+
easy_task # => #<Visionary::Future:0x0000012345678 @block=...>
|
101
|
+
do_something_else
|
102
|
+
easy_task.value # => "awesome 42"
|
103
|
+
```
|
104
|
+
|
105
|
+
Under the hood it creates another future that will be run when current future is completed.
|
106
|
+
|
107
|
+
When previous future have failed, the failure will propagate to all waiting futures:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
hard_work = future { do_some_hard_work_and_loudly_fail! }
|
111
|
+
easy_task = hard_work.then { |result| make_it_awesome(result) }
|
112
|
+
easy_task.await
|
113
|
+
hard_work.state # => :failed
|
114
|
+
easy_task.state # => :failed
|
115
|
+
easy_task.error == hard_work.error # => true
|
116
|
+
```
|
117
|
+
|
118
|
+
### Promises
|
119
|
+
|
120
|
+
#### Creating a promise
|
121
|
+
|
122
|
+
You could use `Kernel#promise` or `Visionary::Promise.new`. They are the same:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
p = promise # => #<Visionary::Promise:0x0000012345678>
|
126
|
+
p2 = Visionary::Promise.new # => #<Visionary::Promise:0x0000012345679>
|
127
|
+
```
|
128
|
+
|
129
|
+
#### Obtaining a promised future
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
p = promise
|
133
|
+
p.future # => #<Visionary::Future:0x0000012345678 @block=nil, @state=:pending>
|
134
|
+
```
|
135
|
+
|
136
|
+
#### Completing a promised future
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
def hard_working_task
|
140
|
+
p = promise
|
141
|
+
|
142
|
+
call_some_external_api(
|
143
|
+
success_callback: -> { |value| p.complete(value) }
|
144
|
+
)
|
145
|
+
|
146
|
+
p
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
Promised future can be used as normal future:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
answer = hard_working_task
|
154
|
+
|
155
|
+
answer.state # => :pending
|
156
|
+
|
157
|
+
do_something_else
|
158
|
+
|
159
|
+
answer.state # => :completed
|
160
|
+
answer.value # => 42
|
161
|
+
```
|
162
|
+
|
163
|
+
#### Failing a promised future
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
def hard_working_task_often_fails
|
167
|
+
p = promise
|
168
|
+
|
169
|
+
call_some_faulty_external_api(
|
170
|
+
success_callback: -> { ... }
|
171
|
+
failure_callback: -> { |error| p.fail(error) }
|
172
|
+
)
|
173
|
+
|
174
|
+
p
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
#### Complex example what it can be used for
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
def gather_data_from_multiple_sources(user_name)
|
182
|
+
promises = {
|
183
|
+
user: promise,
|
184
|
+
friends: promise,
|
185
|
+
likes: promise
|
186
|
+
}
|
187
|
+
|
188
|
+
user_call = future { api_call(:fetch_user, user_name) }
|
189
|
+
user_call.then { |user| promises[:user].complete(user) }
|
190
|
+
|
191
|
+
friends_call = user_call.then { |user| api_call(:fetch_friends, user) }
|
192
|
+
friends_call.then { |friends| promises[:friends].complete(friends) }
|
193
|
+
|
194
|
+
likes_call = user_call.then { |user| api_call(:fetch_likes, user) }
|
195
|
+
likes_call.then { |likes| promises[:likes].complete(likes) }
|
196
|
+
|
197
|
+
promises.inject({}) { |acc, kv| acc.merge kv[0] => kv[1].future }
|
198
|
+
end
|
199
|
+
```
|
22
200
|
|
23
201
|
## Contributing
|
24
202
|
|
25
|
-
1. Fork it ( https://github.com/
|
203
|
+
1. Fork it ( https://github.com/waterlink/visionary/fork )
|
26
204
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
205
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
206
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/lib/visionary.rb
CHANGED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Visionary
|
2
|
+
class Future
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def setup!
|
6
|
+
unless setted_up
|
7
|
+
Kernel.send :define_method, :future do |&blk|
|
8
|
+
Future.new(&blk).run
|
9
|
+
end
|
10
|
+
self.setted_up = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_accessor :setted_up
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :state, :error, :value
|
20
|
+
|
21
|
+
def initialize(&blk)
|
22
|
+
@block = blk
|
23
|
+
self.state = :pending
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
raise RuntimeError, "This future have been already started" if thread
|
28
|
+
@thread = Thread.new { run! }
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def then(&blk)
|
33
|
+
fut = Future.new { blk.call(value) }
|
34
|
+
fut.waiting_for = self
|
35
|
+
|
36
|
+
case state
|
37
|
+
when :pending
|
38
|
+
callbacks << fut
|
39
|
+
when :completed
|
40
|
+
fut.run
|
41
|
+
when :failed
|
42
|
+
fut.fail_with(error)
|
43
|
+
end
|
44
|
+
|
45
|
+
fut
|
46
|
+
end
|
47
|
+
|
48
|
+
def await
|
49
|
+
waiting_for && waiting_for.await
|
50
|
+
|
51
|
+
unless thread
|
52
|
+
run
|
53
|
+
end
|
54
|
+
|
55
|
+
thread.join
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
attr_accessor :waiting_for
|
61
|
+
|
62
|
+
def complete_with(value)
|
63
|
+
@value = value
|
64
|
+
@state = :completed
|
65
|
+
callbacks.each { |callback| callback.run }
|
66
|
+
ensure
|
67
|
+
freeze
|
68
|
+
end
|
69
|
+
|
70
|
+
def fail_with(error)
|
71
|
+
@error = error
|
72
|
+
@state = :failed
|
73
|
+
callbacks.each { |callback| callback.fail_with(error) }
|
74
|
+
ensure
|
75
|
+
freeze
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_writer :state
|
81
|
+
attr_reader :block, :thread
|
82
|
+
|
83
|
+
def run!
|
84
|
+
complete_with(block.call)
|
85
|
+
rescue => e
|
86
|
+
fail_with(e)
|
87
|
+
end
|
88
|
+
|
89
|
+
def callbacks
|
90
|
+
@callbacks ||= []
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Visionary
|
2
|
+
class Promise
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def setup!
|
6
|
+
unless setted_up
|
7
|
+
Kernel.send :define_method, :promise do
|
8
|
+
Promise.new
|
9
|
+
end
|
10
|
+
self.setted_up = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_accessor :setted_up
|
17
|
+
end
|
18
|
+
|
19
|
+
def future
|
20
|
+
@future ||= Future.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def complete(computed_value)
|
24
|
+
future.instance_eval { complete_with(computed_value) }
|
25
|
+
freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
def fail(provided_error)
|
29
|
+
future.instance_eval { fail_with(provided_error) }
|
30
|
+
freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/visionary/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,256 @@
|
|
1
|
+
module Visionary
|
2
|
+
RSpec.describe Future do
|
3
|
+
|
4
|
+
describe "Kernel#future" do
|
5
|
+
it "delegates to Future.new and chains with run" do
|
6
|
+
fut = double("Future")
|
7
|
+
block = -> { :i_am_a_block }
|
8
|
+
|
9
|
+
allow(Future).to receive(:new).with(no_args) do |&blk|
|
10
|
+
if blk == block
|
11
|
+
fut
|
12
|
+
else
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
expect(fut).to receive(:run)
|
18
|
+
|
19
|
+
future(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#state" do
|
24
|
+
it "is :pending when future is still computing the result" do
|
25
|
+
fut = future { do_something; 42 }
|
26
|
+
|
27
|
+
expect(fut.state).to eq(:pending)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "is :failed when future failed to compute the result" do
|
31
|
+
fut = future { raise RuntimeError }
|
32
|
+
|
33
|
+
do_something
|
34
|
+
|
35
|
+
expect(fut.state).to eq(:failed)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "is :completed when future managed to compute the result" do
|
39
|
+
fut = future { 42 }
|
40
|
+
|
41
|
+
do_something
|
42
|
+
|
43
|
+
expect(fut.state).to eq(:completed)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#frozen?" do
|
48
|
+
it "is true when future is completed" do
|
49
|
+
fut = future { 42 }
|
50
|
+
|
51
|
+
do_something
|
52
|
+
|
53
|
+
expect(fut.frozen?).to eq(true)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "is true when future is failed" do
|
57
|
+
fut = future { raise RuntimeError }
|
58
|
+
|
59
|
+
do_something
|
60
|
+
|
61
|
+
expect(fut.frozen?).to eq(true)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "is false when future is pending" do
|
65
|
+
fut = future { do_something; 42 }
|
66
|
+
|
67
|
+
expect(fut.frozen?).to eq(false)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#value" do
|
72
|
+
it "contains computation result when future is completed" do
|
73
|
+
fut = future { 42 }
|
74
|
+
|
75
|
+
do_something
|
76
|
+
|
77
|
+
expect(fut.value).to eq(42)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "contains nil when future is pending" do
|
81
|
+
fut = future { do_something; 42 }
|
82
|
+
|
83
|
+
expect(fut.value).to eq(nil)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "contains nil when future is failed" do
|
87
|
+
fut = future { raise RuntimeError }
|
88
|
+
|
89
|
+
do_something
|
90
|
+
|
91
|
+
expect(fut.value).to eq(nil)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#error" do
|
96
|
+
it "contains error when future is failed" do
|
97
|
+
error = RuntimeError.new(description: "Great runtime error")
|
98
|
+
fut = future { raise error }
|
99
|
+
|
100
|
+
do_something
|
101
|
+
|
102
|
+
expect(fut.error).to be(error)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#then" do
|
107
|
+
it "returns a future" do
|
108
|
+
fut = future { do_something; 42 }
|
109
|
+
|
110
|
+
fut2 = fut.then { do_something; :else }
|
111
|
+
|
112
|
+
expect(fut2).to be_a(Future)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "returns a new future" do
|
116
|
+
fut = future { do_something; 42 }
|
117
|
+
|
118
|
+
fut2 = fut.then { do_something; :else }
|
119
|
+
|
120
|
+
expect(fut2).not_to be(fut)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "eventually calculates something else" do
|
124
|
+
fut = future { do_something; 42 }
|
125
|
+
fut2 = fut.then { do_something; :else }
|
126
|
+
|
127
|
+
4.times { do_something }
|
128
|
+
|
129
|
+
expect(fut2.value).to eq(:else)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "passes result to the provided block when completed" do
|
133
|
+
fut = future { do_something; 42 }
|
134
|
+
fut2 = fut.then { |answer| do_something; "answer is: #{answer}" }
|
135
|
+
|
136
|
+
4.times { do_something }
|
137
|
+
|
138
|
+
expect(fut2.value).to eq("answer is: 42")
|
139
|
+
end
|
140
|
+
|
141
|
+
it "raises the same error in a new future when failed" do
|
142
|
+
error = RuntimeError.new(description: "Great runtime error")
|
143
|
+
fut = future { do_something; raise error }
|
144
|
+
fut2 = fut.then { |answer| do_something; "answer is: #{answer}" }
|
145
|
+
|
146
|
+
2.times { do_something }
|
147
|
+
|
148
|
+
expect(fut2.error).to eq(error)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "does not start calculation of a new future until initial is completed" do
|
152
|
+
fut = future { 2.times { do_something }; 42 }
|
153
|
+
fut2 = fut.then { |answer| "answer is: #{answer}" }
|
154
|
+
|
155
|
+
do_something
|
156
|
+
|
157
|
+
expect(fut2.state).to eq(:pending)
|
158
|
+
end
|
159
|
+
|
160
|
+
context "when initial future is already completed" do
|
161
|
+
it "immediately starts computation for a new future" do
|
162
|
+
something = double("Something", notify: nil)
|
163
|
+
fut = future { 42 }
|
164
|
+
|
165
|
+
do_something
|
166
|
+
|
167
|
+
fut2 = fut.then do |answer|
|
168
|
+
something.notify(answer)
|
169
|
+
2.times { do_something }
|
170
|
+
answer + 3
|
171
|
+
end
|
172
|
+
|
173
|
+
do_something
|
174
|
+
|
175
|
+
expect(something).to have_received(:notify).with(42)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "when initial future is already failed" do
|
180
|
+
it "immediately fails a new future" do
|
181
|
+
fut = future { raise RuntimeError }
|
182
|
+
do_something
|
183
|
+
|
184
|
+
fut2 = fut.then { |answer| do_something; answer + 3 }
|
185
|
+
|
186
|
+
expect(fut2.state).to eq(:failed)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "#await" do
|
193
|
+
it "awaits for computation to complete" do
|
194
|
+
fut = future { do_something; 42 }
|
195
|
+
|
196
|
+
fut.await
|
197
|
+
|
198
|
+
expect(fut.state).to eq(:completed)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "awaits for computation to fail" do
|
202
|
+
fut = future { do_something; raise RuntimeError }
|
203
|
+
|
204
|
+
fut.await
|
205
|
+
|
206
|
+
expect(fut.state).to eq(:failed)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "awaits for computation chain to complete" do
|
210
|
+
fut = future { do_something; 42 }
|
211
|
+
fut2 = fut.then { |answer| do_something; "Answer is: #{answer}" }
|
212
|
+
fut3 = fut2.then { |report| do_something; "WTF? Why #{report.downcase}?" }
|
213
|
+
|
214
|
+
fut3.await
|
215
|
+
|
216
|
+
expect(fut3.value).to eq("WTF? Why answer is: 42?")
|
217
|
+
end
|
218
|
+
|
219
|
+
it "does not fails when awaits more than once" do
|
220
|
+
fut = future { do_something; 42 }
|
221
|
+
|
222
|
+
expect {
|
223
|
+
fut.await
|
224
|
+
fut.await
|
225
|
+
fut.await
|
226
|
+
}.not_to raise_error
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#run" do
|
231
|
+
it "runs future" do
|
232
|
+
fut = Future.new { do_something; 42 }
|
233
|
+
|
234
|
+
2.times { do_something }
|
235
|
+
|
236
|
+
fut.run
|
237
|
+
expect(fut.state).to eq(:pending)
|
238
|
+
end
|
239
|
+
|
240
|
+
it "cannot be run when it is already running" do
|
241
|
+
fut = future { do_something; 42 }
|
242
|
+
|
243
|
+
expect {
|
244
|
+
fut.run
|
245
|
+
}.to raise_error(RuntimeError)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
def do_something
|
252
|
+
sleep(0.01)
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Visionary
|
2
|
+
RSpec.describe Promise do
|
3
|
+
|
4
|
+
describe "Kernel#promise" do
|
5
|
+
it "creates new promise" do
|
6
|
+
p = double("Promise")
|
7
|
+
|
8
|
+
allow(Promise).to receive(:new) { p }
|
9
|
+
|
10
|
+
expect(promise).to be(p)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#future" do
|
15
|
+
it "returns associated future" do
|
16
|
+
expect(promise.future).to be_a(Future)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns the exact future all the times" do
|
20
|
+
p = promise
|
21
|
+
expect(p.future).to be(p.future)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns pending future" do
|
25
|
+
expect(promise.future.state).to eq(:pending)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#complete" do
|
30
|
+
it "completes future" do
|
31
|
+
p = promise
|
32
|
+
expect { p.complete(42) }
|
33
|
+
.to change { p.future.state }
|
34
|
+
.from(:pending)
|
35
|
+
.to(:completed)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "stores passed value as a value in future" do
|
39
|
+
p = promise
|
40
|
+
p.complete(42)
|
41
|
+
expect(p.future.value).to eq(42)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "becomes frozen" do
|
45
|
+
p = promise
|
46
|
+
p.complete(42)
|
47
|
+
expect(p.frozen?).to eq(true)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#fail" do
|
52
|
+
it "failes future" do
|
53
|
+
p = promise
|
54
|
+
expect { p.fail(RuntimeError.new) }
|
55
|
+
.to change { p.future.state }
|
56
|
+
.from(:pending)
|
57
|
+
.to(:failed)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "stores passed error as an error in future" do
|
61
|
+
p = promise
|
62
|
+
error = RuntimeError.new("Crazy description")
|
63
|
+
p.fail(error)
|
64
|
+
expect(p.future.error).to be(error)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "becomes frozen" do
|
68
|
+
p = promise
|
69
|
+
p.fail(RuntimeError.new)
|
70
|
+
expect(p.frozen?).to eq(true)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
RSpec.describe "something"
|
data/spec/visionary_spec.rb
CHANGED
data/visionary.gemspec
CHANGED
@@ -21,4 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.6"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "cucumber"
|
25
|
+
spec.add_development_dependency "aruba"
|
24
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: visionary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Fedorov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: cucumber
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: aruba
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
description: ''
|
56
84
|
email:
|
57
85
|
- waterlink000@gmail.com
|
@@ -67,8 +95,13 @@ files:
|
|
67
95
|
- README.md
|
68
96
|
- Rakefile
|
69
97
|
- lib/visionary.rb
|
98
|
+
- lib/visionary/future.rb
|
99
|
+
- lib/visionary/promise.rb
|
70
100
|
- lib/visionary/version.rb
|
71
101
|
- spec/spec_helper.rb
|
102
|
+
- spec/visionary/future_spec.rb
|
103
|
+
- spec/visionary/promise_spec.rb
|
104
|
+
- spec/visionary/promised_future_spec.rb
|
72
105
|
- spec/visionary_spec.rb
|
73
106
|
- visionary.gemspec
|
74
107
|
homepage: ''
|
@@ -97,4 +130,7 @@ specification_version: 4
|
|
97
130
|
summary: Simple futures implementation in ruby.
|
98
131
|
test_files:
|
99
132
|
- spec/spec_helper.rb
|
133
|
+
- spec/visionary/future_spec.rb
|
134
|
+
- spec/visionary/promise_spec.rb
|
135
|
+
- spec/visionary/promised_future_spec.rb
|
100
136
|
- spec/visionary_spec.rb
|