eventkit 0.0.4 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6cf7b1a61743aa9b00613324e6279acce0df9d7b
4
- data.tar.gz: c6627672c75ddd5007bc414f8f37a2f9d2565a4d
3
+ metadata.gz: 3535ba4fd3f864f13fc1a39c45eb37a560cb0aed
4
+ data.tar.gz: 5653234f81a48b182d3e6b60f3eb225cf758275b
5
5
  SHA512:
6
- metadata.gz: 6baac81b5c1c47cc2b38a759b7da5460eab177c6d1d480dca132fab014013351f4ac6273c6d1fe3abed16cd670da6f284a402b69282b0a3fdff82a95ef08e84b
7
- data.tar.gz: 15f3fdff27e494d72b37aaa40004c47bcdeef47957833b99c845d6102289b3570a65710262c23210d1f3f95c141d446ffb7153d3a2f6172c338ca87f714cc2ba
6
+ metadata.gz: e7129a03c555c04811b0aa19f4ce468130f02b57a531ea412c3d45a44e03333f4064d00c774ab2f24d04134d002bafdc388abdd4047daa527d15b4e6e721d4a3
7
+ data.tar.gz: f96f126b88c526f4318b77d0fb76b9250374f021eb20a66336f24f9de68df153f235469bac8c39d08e57edc57dfb2d71c8fea403f28970d5cd35038fefe0a906
data/README.md CHANGED
@@ -3,174 +3,21 @@
3
3
  [![Build Status](https://travis-ci.org/omartell/eventkit.svg?branch=master)](https://travis-ci.org/omartell/eventkit)
4
4
 
5
5
  A basic toolkit for asynchronous event driven applications. The
6
- current version includes an Event Loop to perform non blocking IO and
6
+ toolkit includes an Event Loop to perform non blocking IO and
7
7
  a promises A+ implementation to coordinate asychronous tasks.
8
8
 
9
+ This gem bundles up the [eventkit-eventloop](http://github.com/omartell/eventkit-eventloop) and [eventkit-promise](http://github.com/omartell/eventkit-promise) gems.
10
+
9
11
  ## Installation
10
12
 
11
13
  Add this line to your application's Gemfile:
12
14
 
13
15
  ```ruby
14
- gem 'eventkit'
15
- ```
16
-
17
- ## Event Loop Usage
18
-
19
- Eventkit provides a basic Event Loop on top of Ruby's IO.select to perform non blocking IO.
20
- Callbacks can be registered to monitor the readability or writability of IO objects.
21
- These callbacks are executed when the IO object is ready to be read or written to.
22
-
23
- Another feature is timers, which allows you to execute code at some point in the future.
24
-
25
- ```ruby
26
- require 'eventkit'
27
-
28
- # Getting notified when an IO object is ready to be read or written
29
- event_loop = Eventkit::EventLoop.new
30
-
31
- server = TCPServer.new('localhost', 9595)
32
-
33
- client = TCPSocket.new('localhost', 9595)
34
-
35
- event_loop.register_read(server) do |server|
36
- # This will be executed every time a new connection is ready to be accepted
37
- connection, _ = server.accept_nonblock
38
- event_loop.register_write(connection) do |connection|
39
- bytes_written = connection.write_nonblock('hello world')
40
- end
41
- end
42
-
43
- event_loop.start
44
-
45
-
46
- # Unsubscribing from notifications
47
- # A single read
48
- event_loop.deregister_read(io_object, handler)
49
-
50
- # A single write
51
- event_loop.deregister_write(io_object, handler)
52
-
53
- # All handlers
54
- event_loop.deregister_write(io_object)
55
- event_loop.deregister_read(io_object)
56
-
57
-
58
- # Registering a handler to be run on the next tick
59
- event_loop = Eventkit::EventLoop.new
60
-
61
- event_loop.on_next_tick do
62
- puts 'hello world'
63
- event_loop.stop
64
- end
65
-
66
- event_loop.start
67
-
68
-
69
- # Registering timers
70
-
71
- event_loop = Eventkit::EventLoop.new
72
-
73
- event_loop.register_timer(run_in: 5) do
74
- # Block executes after 5 seconds have passed
75
- puts 'hello world'
76
- event_loop.stop
77
- end
78
-
79
- event_loop.start
80
- ```
81
-
82
- ## Promises Usage
83
-
84
- Eventkit also provides an implementation of the [Promise A+ specification](https://promisesaplus.com/),
85
- which allows you coordinate different asynchronous tasks while still programming with values.
86
-
87
- If you're only interested in promises, then eventkit-promise is also available as a [separate gem] (https://rubygems.org/gems/eventkit-promise).
88
-
89
- ```ruby
90
- require 'eventkit/promise'
91
-
92
- # Resolving a promise
93
-
94
- promise = Eventkit::Promise.new
95
-
96
- promise.then(->(value) { value + 1 })
97
-
98
- promise.resolve(1)
99
-
100
- promise.value # => 1
101
-
102
- # Rejecting a promise
103
-
104
- promise = Eventkit::Promise.new
105
-
106
- promise.then(
107
- ->(value) {
108
- value + 1
109
- },
110
- ->(error) {
111
- log(error.message)
112
- }
113
- )
114
-
115
- promise.reject(NoMethodError.new('Undefined method #call'))
116
-
117
- promise.reason # => <NoMethodError: undefined method #call>
118
-
119
- # Chaining promises
120
-
121
- promise_a = Eventkit::Promise.new
122
-
123
- promise_b = promise_a
124
- .then(->(v) { v + 1 })
125
- .then(->(v) { v + 1 })
126
- .then(->(v) { v + 1 })
127
-
128
- promise_b.catch { |error|
129
- # Handle errors raised by any of the previous handlers
130
- }
131
-
132
- promise_a.resolve(1)
133
-
134
- promise_a.value # => 1
135
- promise_b.value # => 4
136
-
137
- # Resolving and fullfiling with another promise
138
-
139
- promise_a = Eventkit::Promise.new
140
- promise_b = Eventkit::Promise.new
141
-
142
- promise_a.resolve(promise_b)
143
-
144
- promise_b.resolve('foobar')
145
-
146
- promise_a.value # => foobar
147
-
148
- # Resolving and rejecting with another promise
149
-
150
- promise_a = Eventkit::Promise.new
151
- promise_b = Eventkit::Promise.new
152
-
153
- promise_a.resolve(promise_b)
154
-
155
- promise_b.reject('Ooops can not continue')
156
-
157
- promise_a.reason # => 'Ooops can not continue'
158
-
159
- # Initializing with a block
160
-
161
- promise = Promise.new do |p|
162
- p.resolve('foobar')
163
- end
164
-
165
- promise.value # => 'foobar'
166
-
16
+ gem 'eventkit', '~> 1.0'
167
17
  ```
168
18
 
169
- ## Contributing
19
+ ## Usage
170
20
 
171
- 1. Fork it ( https://github.com/[my-github-username]/eventkit/fork )
172
- 2. Create your feature branch (`git checkout -b my-new-feature`)
173
- 3. Commit your changes (`git commit -am 'Add some feature'`)
174
- 4. Push to the branch (`git push origin my-new-feature`)
175
- 5. Create a new Pull Request
176
- 6. Happy Hacking!
21
+ Check the documentation of
22
+ [eventkit-eventloop](http://github.com/omartell/eventkit-eventloop)
23
+ and [eventkit-promise](http://github.com/omartell/eventkit-promise).
@@ -1,11 +1,10 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'eventkit/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
6
  spec.name = 'eventkit'
8
- spec.version = Eventkit::VERSION
7
+ spec.version = '1.0'
9
8
  spec.authors = ['Oliver Martell']
10
9
  spec.email = ['oliver.martell@gmail.com']
11
10
  spec.summary = 'Experimental toolkit for asynchronous event driven applications'
@@ -23,5 +22,7 @@ Gem::Specification.new do |spec|
23
22
  spec.add_development_dependency 'pry', '~> 0.10.1'
24
23
  spec.add_development_dependency 'pry-doc', '~> 0.6.0'
25
24
  spec.add_development_dependency 'method_source', '~> 0.8.2'
26
- spec.add_runtime_dependency 'eventkit-promise', '~> 0.1.0'
25
+
26
+ spec.add_runtime_dependency 'eventkit-promise', '~> 1.0'
27
+ spec.add_runtime_dependency 'eventkit-eventloop', '~> 0.1.0'
27
28
  end
@@ -1,7 +1,2 @@
1
- require 'eventkit/version'
2
1
  require 'eventkit/event_loop'
3
2
  require 'eventkit/promise'
4
-
5
- module Eventkit
6
- # Your code goes here...
7
- end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: '1.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Martell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-21 00:00:00.000000000 Z
11
+ date: 2015-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -82,6 +82,20 @@ dependencies:
82
82
  version: 0.8.2
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: eventkit-promise
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: eventkit-eventloop
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - "~>"
@@ -111,12 +125,7 @@ files:
111
125
  - Rakefile
112
126
  - eventkit.gemspec
113
127
  - lib/eventkit.rb
114
- - lib/eventkit/event_loop.rb
115
- - lib/eventkit/timer.rb
116
- - lib/eventkit/version.rb
117
- - spec/eventkit/event_loop_spec.rb
118
128
  - spec/spec_helper.rb
119
- - spec/support/async_helper.rb
120
129
  homepage: http://github.com/omartell/eventkit
121
130
  licenses:
122
131
  - MIT
@@ -142,7 +151,5 @@ signing_key:
142
151
  specification_version: 4
143
152
  summary: Experimental toolkit for asynchronous event driven applications
144
153
  test_files:
145
- - spec/eventkit/event_loop_spec.rb
146
154
  - spec/spec_helper.rb
147
- - spec/support/async_helper.rb
148
155
  has_rdoc:
@@ -1,95 +0,0 @@
1
- require 'set'
2
- require 'eventkit/timer'
3
-
4
- module Eventkit
5
- class EventLoopAlreadyStartedError < StandardError; end
6
-
7
- class EventLoop
8
- attr_reader :select_interval
9
- private :select_interval
10
-
11
- def initialize(config = {})
12
- @read_handlers = Hash.new { |h, k| h[k] = [] }
13
- @write_handlers = Hash.new { |h, k| h[k] = [] }
14
- @select_interval = config.fetch(:select_interval, 1 / 100_000)
15
- @timers = SortedSet.new
16
- @stopped = false
17
- @started = false
18
- end
19
-
20
- def start(&_block)
21
- if @started
22
- fail EventLoopAlreadyStartedError, 'This event loop instance has already started running'
23
- else
24
- @started = true
25
- end
26
-
27
- loop do
28
- if stop_scheduled?
29
- @stopped = false
30
- break
31
- end
32
- tick
33
- end
34
- end
35
-
36
- def stop
37
- @stopped = true
38
- @started = false
39
- end
40
-
41
- def stop_scheduled?
42
- @stopped
43
- end
44
-
45
- def tick
46
- ready_read, ready_write, _ = IO.select(@read_handlers.keys, @write_handlers.keys, [], select_interval)
47
- ready_read.each { |io|
48
- @read_handlers.fetch(io).each { |handler| handler.call(io) }
49
- } if ready_read
50
-
51
- ready_write.each { |io|
52
- @write_handlers.fetch(io).each { |handler| handler.call(io) }
53
- } if ready_write
54
-
55
- @timers.select(&:expired?).each { |timer|
56
- timer.handler.call
57
- @timers.delete(timer)
58
- }
59
- nil
60
- end
61
-
62
- def on_next_tick(&handler)
63
- register_timer(run_in: 0, &handler)
64
- end
65
-
66
- def register_timer(options, &handler)
67
- @timers << Timer.new(options.fetch(:run_in), handler)
68
- end
69
-
70
- def register_read(io, &listener)
71
- @read_handlers[io] += [listener]
72
- end
73
-
74
- def deregister_read(io, listener = nil)
75
- if listener
76
- @read_handlers[io] -= [listener]
77
- else
78
- @read_handlers[io] = []
79
- end
80
- end
81
-
82
- def register_write(io, &listener)
83
- @write_handlers[io] += [listener]
84
- end
85
-
86
- def deregister_write(io, listener = nil)
87
- if listener
88
- @write_handlers[io] -= [listener]
89
- else
90
- @write_handlers[io] = []
91
- end
92
- end
93
- end
94
- end
95
-
@@ -1,15 +0,0 @@
1
- module Eventkit
2
- Timer = Struct.new(:expires_in, :handler) do
3
- def initialize(seconds, handler)
4
- super(Time.now.to_f + seconds, handler)
5
- end
6
-
7
- def <=>(other)
8
- expires_in <=> other.expires_in
9
- end
10
-
11
- def expired?
12
- expires_in <= Time.now.to_f
13
- end
14
- end
15
- end
@@ -1,3 +0,0 @@
1
- module Eventkit
2
- VERSION = '0.0.4'
3
- end
@@ -1,278 +0,0 @@
1
- require 'support/async_helper'
2
- require 'socket'
3
- require 'eventkit'
4
-
5
- module Eventkit
6
- RSpec.describe EventLoop do
7
- include AsyncHelper
8
-
9
- let!(:event_loop) { EventLoop.new(select_interval: 1 / 100_000) }
10
-
11
- let!(:tcp_server) { TCPServer.new('localhost', 9595) }
12
-
13
- let!(:another_tcp_server) { TCPServer.new('localhost', 9494) }
14
-
15
- let!(:tcp_socket) { TCPSocket.new('localhost', 9595) }
16
-
17
- let!(:another_tcp_socket) { TCPSocket.new('localhost', 9494) }
18
-
19
- after do
20
- tcp_server.close
21
- another_tcp_server.close
22
- tcp_socket.close
23
- another_tcp_socket.close
24
- end
25
-
26
- it 'allows to start and stop the event loop' do
27
- verifier = double(did_stop: nil)
28
-
29
- event_loop.on_next_tick do
30
- event_loop.stop
31
- verifier.did_stop
32
- end
33
-
34
- event_loop.start
35
-
36
- expect(verifier).to have_received(:did_stop)
37
- end
38
-
39
- it 'does not allow to start the event loop once it has started' do
40
- expect do
41
- listener = double(:listener)
42
-
43
- allow(listener).to receive(:handle_event) do |_io|
44
- event_loop.start
45
- end
46
-
47
- event_loop.register_write(tcp_socket, &listener.method(:handle_event))
48
-
49
- event_loop.start
50
- end.to raise_error(EventLoopAlreadyStartedError)
51
- end
52
-
53
- it 'allows to restart the event loop' do
54
- expect do |block|
55
- event_loop.register_write(tcp_socket) do |io|
56
- block.to_proc.call(:once)
57
- event_loop.stop
58
- event_loop.deregister_write(io)
59
-
60
- event_loop.register_write(another_tcp_socket) do |io|
61
- block.to_proc.call(:twice)
62
- event_loop.stop
63
- event_loop.deregister_write(io)
64
- end
65
- event_loop.start
66
- end
67
-
68
- event_loop.start
69
- end.to yield_successive_args(:once, :twice)
70
- end
71
-
72
- it 'notifies when a single read operation is ready' do
73
- fake_server = double(to_io: tcp_server, connection_read_ready: nil)
74
-
75
- event_loop.register_read(fake_server, &fake_server.method(:connection_read_ready))
76
-
77
- event_loop.tick
78
-
79
- expect(fake_server).to have_received(:connection_read_ready).once.with(fake_server)
80
- end
81
-
82
- it 'notifies when a single write operation is ready' do
83
- fake_socket = double(to_io: tcp_socket, connection_write_ready: nil)
84
-
85
- event_loop.register_write(fake_socket, &fake_socket.method(:connection_write_ready))
86
-
87
- event_loop.tick
88
-
89
- expect(fake_socket).to have_received(:connection_write_ready).once.with(fake_socket)
90
- end
91
-
92
- it 'notifies when multiple write operations are ready' do
93
- fake_socket = double(to_io: tcp_socket, connection_write_ready: nil)
94
- another_fake_socket = double(to_io: another_tcp_socket, ready_to_write: nil)
95
-
96
- event_loop.register_write(fake_socket, &fake_socket.method(:connection_write_ready))
97
- event_loop.register_write(another_fake_socket, &another_fake_socket.method(:ready_to_write))
98
-
99
- event_loop.tick
100
-
101
- expect(fake_socket).to have_received(:connection_write_ready).once.with(fake_socket)
102
- expect(another_fake_socket).to have_received(:ready_to_write).once.with(another_fake_socket)
103
- end
104
-
105
- it 'notifies when multiple read operations are ready' do
106
- fake_server = double(to_io: tcp_server, connection_read_ready: nil)
107
- another_fake_server = double(to_io: another_tcp_server, new_connection: nil)
108
-
109
- event_loop.register_read(fake_server, &fake_server.method(:connection_read_ready))
110
- event_loop.register_read(another_fake_server, &another_fake_server.method(:new_connection))
111
-
112
- event_loop.tick
113
-
114
- expect(fake_server).to have_received(:connection_read_ready).once.with(fake_server)
115
- expect(another_fake_server).to have_received(:new_connection).once.with(another_fake_server)
116
- end
117
-
118
- it 'allows an object to register reads on multiple io objects' do
119
- listener = double(connection_read_ready: nil, another_connection: nil)
120
-
121
- event_loop.register_read(tcp_server, &listener.method(:connection_read_ready))
122
- event_loop.register_read(another_tcp_server, &listener.method(:another_connection))
123
-
124
- expect(listener).to receive(:connection_read_ready).once.with(tcp_server)
125
- expect(listener).to receive(:another_connection).once.with(another_tcp_server)
126
-
127
- event_loop.tick
128
- end
129
-
130
- it 'allows an object to register writes on multiple io objects' do
131
- listener = double(one_ready_to_write: nil, another_ready_to_write: nil)
132
-
133
- event_loop.register_write(tcp_socket, &listener.method(:one_ready_to_write))
134
- event_loop.register_write(another_tcp_socket, &listener.method(:another_ready_to_write))
135
-
136
- expect(listener).to receive(:one_ready_to_write).once.with(tcp_socket)
137
- expect(listener).to receive(:another_ready_to_write).once.with(another_tcp_socket)
138
-
139
- event_loop.tick
140
- end
141
-
142
- it 'allows to register multiple read handlers on a single io object' do
143
- listener = double(connection_read_ready: nil)
144
- another_listener = double(new_connection: nil)
145
-
146
- event_loop.register_read(tcp_server, &listener.method(:connection_read_ready))
147
- event_loop.register_read(tcp_server, &another_listener.method(:new_connection))
148
-
149
- expect(listener).to receive(:connection_read_ready).once.with(tcp_server)
150
- expect(another_listener).to receive(:new_connection).once.with(tcp_server)
151
-
152
- event_loop.tick
153
- end
154
-
155
- it 'allows to register multiple write handlers on a single io object' do
156
- listener = double(connection_write_ready: nil)
157
- another_listener = double(ready_to_write: nil)
158
-
159
- event_loop.register_write(tcp_socket, &listener.method(:connection_write_ready))
160
- event_loop.register_write(tcp_socket, &another_listener.method(:ready_to_write))
161
-
162
- expect(listener).to receive(:connection_write_ready).once.with(tcp_socket)
163
- expect(another_listener).to receive(:ready_to_write).once.with(tcp_socket)
164
-
165
- event_loop.tick
166
- end
167
-
168
- it 'allows to deregister read handlers' do
169
- listener = double(connection_read_ready: nil)
170
- handler = listener.method(:connection_read_ready).to_proc
171
-
172
- event_loop.register_read(tcp_server, &handler)
173
- event_loop.deregister_read(tcp_server, &handler)
174
-
175
- expect(listener).not_to receive(:connection_read_ready)
176
-
177
- event_loop.tick
178
- end
179
-
180
- it 'allows to deregister write handlers' do
181
- listener = double(connection_write_ready: nil)
182
-
183
- handler = listener.method(:connection_write_ready).to_proc
184
-
185
- event_loop.register_write(tcp_socket, &handler)
186
- event_loop.deregister_write(tcp_socket, handler)
187
-
188
- expect(listener).not_to receive(:connection_write_ready)
189
-
190
- event_loop.tick
191
- end
192
-
193
- it 'deregisters all write handlers for an io object' do
194
- listener = double(connection_write_ready: nil, another_write_event: nil)
195
-
196
- first_handler = listener.method(:connection_write_ready).to_proc
197
- second_handler = listener.method(:another_write_event).to_proc
198
-
199
- event_loop.register_write(tcp_socket, &first_handler)
200
- event_loop.register_write(tcp_socket, &second_handler)
201
-
202
- event_loop.deregister_write(tcp_socket)
203
-
204
- expect(listener).not_to receive(:connection_write_ready)
205
- expect(listener).not_to receive(:another_event)
206
-
207
- event_loop.tick
208
- end
209
-
210
- it 'deregisters all read handlers for an io object' do
211
- listener = double(connection_read_ready: nil, another_read_event: nil)
212
-
213
- first_handler = listener.method(:connection_read_ready).to_proc
214
- second_handler = listener.method(:another_read_event).to_proc
215
-
216
- connection = tcp_server.accept
217
- connection.write('hello world')
218
-
219
- event_loop.register_read(tcp_socket, &first_handler)
220
- event_loop.register_read(tcp_socket, &second_handler)
221
-
222
- event_loop.deregister_read(tcp_socket)
223
-
224
- expect(listener).not_to receive(:connection_read_ready)
225
- expect(listener).not_to receive(:another_event)
226
-
227
- event_loop.tick
228
- end
229
-
230
- it 'allows to register timers which will executed in order' do
231
- listener = double(timer_expired_a: nil,
232
- timer_expired_b: nil,
233
- timer_expired_c: nil,
234
- timer_expired_d: nil)
235
-
236
- event_loop.register_timer(run_in: 2, &listener.method(:timer_expired_a))
237
-
238
- event_loop.register_timer(run_in: 3, &listener.method(:timer_expired_b))
239
-
240
- event_loop.register_timer(run_in: 1, &listener.method(:timer_expired_c))
241
-
242
- event_loop.register_timer(run_in: 5, &listener.method(:timer_expired_d))
243
-
244
- sleep(3.1)
245
-
246
- event_loop.tick
247
-
248
- expect(listener).to have_received(:timer_expired_c).ordered.once
249
- expect(listener).to have_received(:timer_expired_a).ordered.once
250
- expect(listener).to have_received(:timer_expired_b).ordered.once
251
- expect(listener).to_not have_received(:timer_expired_d)
252
- end
253
-
254
- it 'deregister timers as soon as they have expired' do
255
- listener = double(timer_expired: nil)
256
-
257
- event_loop.register_timer(run_in: 1, &listener.method(:timer_expired))
258
-
259
- sleep(2)
260
-
261
- event_loop.tick
262
- event_loop.tick
263
-
264
- expect(listener).to have_received(:timer_expired).once
265
- end
266
-
267
- it 'allows to schedule code to be run on the next tick' do
268
- listener = double(on_next_tick: nil)
269
-
270
- event_loop.on_next_tick(&listener.method(:on_next_tick))
271
-
272
- event_loop.tick
273
-
274
- expect(listener).to have_received(:on_next_tick).once
275
- end
276
- end
277
- end
278
-
@@ -1,40 +0,0 @@
1
- module AsyncHelper
2
- # Instead of waiting an specific an amount of time and then
3
- # asserting for a certain behaviour, this test helper polls for an assertion
4
- # success every X number of seconds (configurable as interval).
5
- # The test will fail if the assertion doesn't pass after Y number of
6
- # seconds (configurable as timeout).
7
- def eventually(options = {})
8
- timeout = options[:timeout] || 1 # seconds
9
- interval = options[:interval] || 0.0001 # seconds
10
- time_limit = Time.now + timeout
11
-
12
- loop do
13
- begin
14
- yield
15
- rescue RSpec::Expectations::ExpectationNotMetError => error
16
-
17
- end
18
-
19
- return if error.nil?
20
-
21
- fail error if Time.now >= time_limit
22
- sleep interval
23
- end
24
- end
25
-
26
- # Same behaviour as eventually, but decided to change the method name to make
27
- # it more clear that this is focused on 'synchronizing' the test with the application
28
- def wait_until(options = {}, &block)
29
- eventually(options, &block)
30
- end
31
-
32
- # Small wrapper around sleep
33
- def on_timeout(options = {})
34
- timeout = options[:timeout] || 0.5
35
-
36
- sleep timeout
37
-
38
- yield
39
- end
40
- end