vapir-common 1.7.0.rc1

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.
@@ -0,0 +1,141 @@
1
+ require 'vapir-common/exceptions'
2
+
3
+ module Vapir
4
+
5
+ def wait_until(*args)
6
+ Waiter.wait_until(*args) {yield}
7
+ end
8
+
9
+ class TimeKeeper
10
+ attr_reader :sleep_time
11
+ def initialize
12
+ @sleep_time = 0.0
13
+ end
14
+ def sleep seconds
15
+ @sleep_time += Kernel.sleep seconds
16
+ end
17
+ def now
18
+ Time.now
19
+ end
20
+ end
21
+
22
+ class Waiter
23
+ # This is an interface to a TimeKeeper which proxies
24
+ # calls to "sleep" and "Time.now".
25
+ # Useful for unit testing Waiter.
26
+ attr_accessor :timer
27
+
28
+ # How long to wait between each iteration through the wait_until
29
+ # loop. In seconds.
30
+ attr_accessor :polling_interval
31
+
32
+ # Timeout for wait_until.
33
+ attr_accessor :timeout
34
+
35
+ @@default_polling_interval = 0.5
36
+ @@default_timeout = 60.0
37
+
38
+ def initialize(timeout=@@default_timeout,
39
+ polling_interval=@@default_polling_interval)
40
+ @timeout = timeout
41
+ @polling_interval = polling_interval
42
+ @timer = TimeKeeper.new
43
+ end
44
+
45
+ # Execute the provided block until either (1) it returns true, or
46
+ # (2) the timeout (in seconds) has been reached. If the timeout is reached,
47
+ # a TimeOutException will be raised. The block will always
48
+ # execute at least once.
49
+ #
50
+ # waiter = Waiter.new(5)
51
+ # waiter.wait_until {puts 'hello'}
52
+ #
53
+ # This code will print out "hello" for five seconds, and then raise a
54
+ # Vapir::TimeOutException.
55
+ def wait_until # block
56
+ start_time = now
57
+ until yield do
58
+ if (duration = now - start_time) > @timeout
59
+ raise Vapir::Exception::TimeOutException.new(duration, @timeout),
60
+ "Timed out after #{duration} seconds."
61
+ end
62
+ sleep @polling_interval
63
+ end
64
+ end
65
+
66
+ # Execute the provided block until either (1) it returns true, or
67
+ # (2) the timeout (in seconds) has been reached. If the timeout is reached,
68
+ # a TimeOutException will be raised. The block will always
69
+ # execute at least once.
70
+ #
71
+ # Waiter.wait_until(5) {puts 'hello'}
72
+ #
73
+ # This code will print out "hello" for five seconds, and then raise a
74
+ # Vapir::TimeOutException.
75
+
76
+ # IDEA: wait_until: remove defaults from Waiter.wait_until
77
+ def self.wait_until(timeout=@@default_timeout,
78
+ polling_interval=@@default_polling_interval)
79
+ waiter = new(timeout, polling_interval)
80
+ waiter.wait_until { yield }
81
+ end
82
+
83
+ private
84
+ def sleep seconds
85
+ @timer.sleep seconds
86
+ end
87
+ def now
88
+ @timer.now
89
+ end
90
+ end
91
+
92
+ end # module
93
+
94
+ require 'vapir-common/handle_options'
95
+
96
+ class WaiterError < StandardError; end
97
+ class Waiter
98
+ # Tries for +time+ seconds to get the desired result from the given block. Stops when either:
99
+ # 1. The :condition option (which should be a proc) returns true (that is, not false or nil)
100
+ # 2. The block returns true (that is, anything but false or nil) if no :condition option is given
101
+ # 3. The specified amount of time has passed. By default a WaiterError is raised.
102
+ # If :exception option is given, then if it is nil, no exception is raised; otherwise it should be
103
+ # an exception class or an exception instance which will be raised instead of WaiterError
104
+ #
105
+ # Returns the value of the block, which can be handy for things that return nil on failure and some
106
+ # other object on success, like Enumerable#detect for example:
107
+ # found_thing=Waiter.try_for(30) { all_things().detect {|thing| thing.name=="Bill" } }
108
+ #
109
+ # Examples:
110
+ # Waiter.try_for(30) do
111
+ # Time.now.year == 2015
112
+ # end
113
+ # Raises a WaiterError unless it is called between the last 30 seconds of December 31, 2014 and the end of 2015
114
+ #
115
+ # Waiter.try_for(365.242199*24*60*60, :interval => 0.1, :exception => nil, :condition => proc{ 2+2==5 }) do
116
+ # STDERR.puts "any decisecond now ..."
117
+ # end
118
+ # Complains to STDERR for one year, every tenth of a second, as long as 2+2 does not equal 5. Does not
119
+ # raise an exception if 2+2 does not become equal to 5.
120
+ def self.try_for(time, options={})
121
+ unless time.is_a?(Numeric) && options.is_a?(Hash)
122
+ raise TypeError, "expected arguments are time (a numeric) and, optionally, options (a Hash). received arguments #{time.inspect} (#{time.class}), #{options.inspect} (#{options.class})"
123
+ end
124
+ options=handle_options(options, {:interval => 0.5, :condition => proc{|_ret| _ret}, :exception => WaiterError})
125
+ started=Time.now
126
+ begin
127
+ ret=yield
128
+ break if options[:condition].call(ret)
129
+ sleep options[:interval]
130
+ end while Time.now < started+time && !options[:condition].call(ret)
131
+ if options[:exception] && !options[:condition].call(ret)
132
+ ex=if options[:exception].is_a?(Class)
133
+ options[:exception].new("Waiter waited #{time} seconds and condition was not met")
134
+ else
135
+ options[:exception]
136
+ end
137
+ raise ex
138
+ end
139
+ ret
140
+ end
141
+ end