cistern 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cistern.rb +7 -9
- data/lib/cistern/collection.rb +25 -24
- data/lib/cistern/formatter.rb +9 -0
- data/lib/cistern/model.rb +10 -14
- data/lib/cistern/service.rb +13 -11
- data/lib/cistern/timeout.rb +23 -0
- data/lib/cistern/version.rb +1 -1
- data/lib/cistern/wait_for.rb +26 -10
- data/spec/wait_for_spec.rb +78 -0
- metadata +5 -2
data/lib/cistern.rb
CHANGED
@@ -2,7 +2,9 @@ require 'cistern/version'
|
|
2
2
|
require 'time'
|
3
3
|
|
4
4
|
module Cistern
|
5
|
-
|
5
|
+
|
6
|
+
Error = Class.new(StandardError)
|
7
|
+
Timeout = Class.new(Error)
|
6
8
|
|
7
9
|
require 'cistern/hash'
|
8
10
|
require 'cistern/mock'
|
@@ -12,18 +14,14 @@ module Cistern
|
|
12
14
|
require 'cistern/model'
|
13
15
|
require 'cistern/service'
|
14
16
|
|
17
|
+
extend WaitFor
|
18
|
+
timeout_error = Timeout
|
19
|
+
|
15
20
|
autoload :Formatter, 'cistern/formatter'
|
16
21
|
|
17
|
-
def self.timeout=(timeout); @timeout= timeout; end
|
18
|
-
def self.timeout; @timeout || 0; end
|
19
22
|
def self.formatter=(formatter); @formatter = formatter; end
|
20
23
|
|
21
24
|
def self.formatter
|
22
|
-
@formatter ||=
|
23
|
-
Cistern::Formatter::AwesomePrint
|
24
|
-
elsif defined?(Formatador)
|
25
|
-
Cistern::Formatter::Formatador
|
26
|
-
else Cistern::Formatter::Default
|
27
|
-
end
|
25
|
+
@formatter ||= Cistern::Formatter.default
|
28
26
|
end
|
29
27
|
end
|
data/lib/cistern/collection.rb
CHANGED
@@ -25,11 +25,20 @@ class Cistern::Collection < Array
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
attr_accessor :connection
|
29
|
+
|
30
|
+
alias build initialize
|
31
|
+
|
28
32
|
def initialize(attributes = {})
|
29
33
|
@loaded = false
|
30
34
|
merge_attributes(attributes)
|
31
35
|
end
|
32
36
|
|
37
|
+
def clear
|
38
|
+
@loaded = true
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
33
42
|
def create(attributes={})
|
34
43
|
self.new(attributes).save
|
35
44
|
end
|
@@ -38,17 +47,28 @@ class Cistern::Collection < Array
|
|
38
47
|
raise NotImplementedError
|
39
48
|
end
|
40
49
|
|
41
|
-
def
|
42
|
-
@loaded
|
43
|
-
|
50
|
+
def inspect
|
51
|
+
lazy_load unless @loaded
|
52
|
+
Cistern.formatter.call(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def lazy_load
|
57
|
+
self.all
|
58
|
+
end
|
59
|
+
|
60
|
+
def load(objects)
|
61
|
+
clear
|
62
|
+
for object in objects
|
63
|
+
self << new(object)
|
64
|
+
end
|
65
|
+
self
|
44
66
|
end
|
45
67
|
|
46
68
|
def model
|
47
69
|
self.class.instance_variable_get('@model')
|
48
70
|
end
|
49
71
|
|
50
|
-
attr_accessor :connection
|
51
|
-
|
52
72
|
def new(attributes = {})
|
53
73
|
unless attributes.is_a?(::Hash)
|
54
74
|
raise(ArgumentError.new("Initialization parameters must be an attributes hash, got #{attributes.class} #{attributes.inspect}"))
|
@@ -61,28 +81,9 @@ class Cistern::Collection < Array
|
|
61
81
|
)
|
62
82
|
end
|
63
83
|
|
64
|
-
def load(objects)
|
65
|
-
clear
|
66
|
-
for object in objects
|
67
|
-
self << new(object)
|
68
|
-
end
|
69
|
-
self
|
70
|
-
end
|
71
|
-
|
72
84
|
def reload
|
73
85
|
clear
|
74
86
|
lazy_load
|
75
87
|
self
|
76
88
|
end
|
77
|
-
|
78
|
-
def inspect
|
79
|
-
lazy_load unless @loaded
|
80
|
-
Cistern.formatter.call(self)
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def lazy_load
|
86
|
-
self.all
|
87
|
-
end
|
88
89
|
end
|
data/lib/cistern/formatter.rb
CHANGED
@@ -2,4 +2,13 @@ module Cistern::Formatter
|
|
2
2
|
autoload :AwesomePrint, 'cistern/formatter/awesome_print'
|
3
3
|
autoload :Default, 'cistern/formatter/default'
|
4
4
|
autoload :Formatador, 'cistern/formatter/formatador'
|
5
|
+
|
6
|
+
def self.default
|
7
|
+
if defined?(AwesomePrint)
|
8
|
+
Cistern::Formatter::AwesomePrint
|
9
|
+
elsif defined?(Formatador)
|
10
|
+
Cistern::Formatter::Formatador
|
11
|
+
else Cistern::Formatter::Default
|
12
|
+
end
|
13
|
+
end
|
5
14
|
end
|
data/lib/cistern/model.rb
CHANGED
@@ -33,19 +33,15 @@ class Cistern::Model
|
|
33
33
|
!comparison_object.new_record?)
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise Cistern::Error.new("Reload failed, #{self.class} #{self.identity} went away.") # FIXME: pretty much assumes you are calling #ready?
|
47
|
-
end
|
48
|
-
instance_eval(&block)
|
49
|
-
end
|
36
|
+
def service
|
37
|
+
self.connection ? self.connection.class : Cistern
|
38
|
+
end
|
39
|
+
|
40
|
+
def wait_for(timeout = self.service.timeout, interval = self.service.poll_interval, &block)
|
41
|
+
service.wait_for(timeout, interval) { reload && block.call(self) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def wait_for!(timeout = self.service.timeout, interval = self.service.poll_interval, &block)
|
45
|
+
service.wait_for!(timeout, interval) { reload && block.call(self) }
|
50
46
|
end
|
51
47
|
end
|
data/lib/cistern/service.rb
CHANGED
@@ -28,10 +28,13 @@ class Cistern::Service
|
|
28
28
|
#{klass.name}
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
31
32
|
def self.service
|
32
33
|
#{klass.name}
|
33
34
|
end
|
34
35
|
EOS
|
36
|
+
|
37
|
+
klass.send(:const_set, :Timeout, Class.new(Cistern::Error))
|
35
38
|
end
|
36
39
|
|
37
40
|
def model_path(model_path)
|
@@ -82,8 +85,8 @@ class Cistern::Service
|
|
82
85
|
requests << request_name
|
83
86
|
end
|
84
87
|
|
85
|
-
def collection(collection_name)
|
86
|
-
collections << collection_name
|
88
|
+
def collection(collection_name, options={})
|
89
|
+
collections << [collection_name, options]
|
87
90
|
end
|
88
91
|
|
89
92
|
def validate_options(options={})
|
@@ -123,8 +126,8 @@ class Cistern::Service
|
|
123
126
|
EOS
|
124
127
|
end
|
125
128
|
end
|
126
|
-
collections.each do |collection|
|
127
|
-
require File.join(@model_path, collection.to_s)
|
129
|
+
collections.each do |collection, options|
|
130
|
+
require File.join(@model_path, collection.to_s) unless options[:require] == false
|
128
131
|
class_name = collection.to_s.split("_").map(&:capitalize).join
|
129
132
|
self.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
|
130
133
|
def #{collection}(attributes={})
|
@@ -140,13 +143,12 @@ class Cistern::Service
|
|
140
143
|
validate_options(options)
|
141
144
|
setup_requirements
|
142
145
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
146
|
+
klass = self.const_get(self.mocking? ? :Mock : :Real)
|
147
|
+
|
148
|
+
klass.send(:include, service::Collections)
|
149
|
+
klass.send(:extend, Cistern::WaitFor)
|
150
|
+
klass.timeout_error = service::Timeout
|
151
|
+
klass.new(options)
|
150
152
|
end
|
151
153
|
|
152
154
|
def reset!
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cistern
|
2
|
+
module WaitFor
|
3
|
+
def self.wait_for(timeout = Cistern.timeout, interval = Cistern.poll_interval, &block)
|
4
|
+
duration = 0
|
5
|
+
start = Time.now
|
6
|
+
|
7
|
+
until yield || duration > timeout
|
8
|
+
sleep(interval.to_f)
|
9
|
+
duration = Time.now - start
|
10
|
+
end
|
11
|
+
|
12
|
+
if duration > timeout
|
13
|
+
false
|
14
|
+
else
|
15
|
+
{ :duration => duration }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.wait_for!(*arg)
|
20
|
+
wait_for
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/cistern/version.rb
CHANGED
data/lib/cistern/wait_for.rb
CHANGED
@@ -1,15 +1,31 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
1
3
|
module Cistern
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module WaitFor
|
5
|
+
DEFAULT_TIMEOUT = 3 * 60 # 3 minutes
|
6
|
+
DEFAULT_POLL_INTERVAL = 10 # seconds
|
7
|
+
|
8
|
+
def timeout; @timeout || DEFAULT_TIMEOUT; end
|
9
|
+
def timeout=(timeout); @timeout = timeout; end
|
10
|
+
def poll_interval; @poll_interval || DEFAULT_POLL_INTERVAL; end
|
11
|
+
def poll_interval=(poll_interval); @poll_interval = poll_interval; end
|
12
|
+
def timeout_error=(timeout_error); @timeout_error = timeout_error; end
|
13
|
+
def timeout_error; @timeout_error || self.const_defined?(:Timeout) && self.const_get(:Timeout) || ::Timeout::Error; end
|
14
|
+
|
15
|
+
def wait_for(timeout = self.timeout, interval = self.poll_interval, &block)
|
16
|
+
duration = 0
|
17
|
+
start = Time.now
|
18
|
+
|
19
|
+
until yield || duration > timeout
|
20
|
+
sleep(interval.to_f)
|
21
|
+
duration = Time.now - start
|
22
|
+
end
|
23
|
+
|
24
|
+
duration > timeout ? false : duration
|
8
25
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
{ :duration => duration }
|
26
|
+
|
27
|
+
def wait_for!(timeout = self.timeout, interval = self.poll_interval, &block)
|
28
|
+
wait_for(timeout, interval, &block) || raise(timeout_error, "wait_for(#{timeout}) exceeded")
|
13
29
|
end
|
14
30
|
end
|
15
31
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class WaitForService < Cistern::Service
|
4
|
+
model :wait_for_model, require: false
|
5
|
+
collection :wait_for_models, require: false
|
6
|
+
|
7
|
+
class Real
|
8
|
+
def initialize(*args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class WaitForService::WaitForModel < Cistern::Model
|
14
|
+
identity :id
|
15
|
+
|
16
|
+
attribute :name
|
17
|
+
end
|
18
|
+
|
19
|
+
class WaitForService::WaitForModels < Cistern::Collection
|
20
|
+
model WaitForService::WaitForModel
|
21
|
+
|
22
|
+
def get(identity)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'Cistern#wait_for' do
|
28
|
+
it "should return false if timeout exceeded" do
|
29
|
+
Cistern.wait_for(0, 0) { false }.should be_false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'Cistern#wait_for!' do
|
34
|
+
it "should raise if timeout exceeded" do
|
35
|
+
lambda { Cistern.wait_for!(0, 0) { false } }.should raise_exception(Cistern::Timeout)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'Cistern::Model#wait_for!' do
|
40
|
+
let(:service) { WaitForService.new }
|
41
|
+
let(:model) { service.wait_for_models.new(identity: 1) }
|
42
|
+
|
43
|
+
it "should raise if timeout exceeded" do
|
44
|
+
lambda { model.wait_for!(0, 0) { false } }.should raise_exception(WaitForService::Timeout)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
describe "WaitForModel#timeout" do
|
50
|
+
let(:service) { WaitForService.new }
|
51
|
+
let(:model) { service.wait_for_models.new(identity: 1) }
|
52
|
+
|
53
|
+
it "should use service-specific timeout in #wait_for" do
|
54
|
+
service.class.timeout = 0.1
|
55
|
+
service.class.poll_interval = 0
|
56
|
+
|
57
|
+
elapsed = 0
|
58
|
+
|
59
|
+
timeout(2) do
|
60
|
+
lambda do
|
61
|
+
model.wait_for! { sleep(0.2); elapsed += 0.2; elapsed > 0.2 }
|
62
|
+
end.should raise_exception(WaitForService::Timeout)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should favor explicit timeout" do
|
67
|
+
service.class.timeout = 1
|
68
|
+
service.class.poll_interval = 0
|
69
|
+
|
70
|
+
elapsed = 0
|
71
|
+
|
72
|
+
timeout(2) do
|
73
|
+
lambda do
|
74
|
+
model.wait_for!(0.1) { sleep(0.2); elapsed += 0.2; elapsed > 0.2 }
|
75
|
+
end.should raise_exception(WaitForService::Timeout)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: cistern
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Josh Lane
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-07-
|
12
|
+
date: 2013-07-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: API client framework extracted from Fog
|
15
15
|
email:
|
@@ -37,12 +37,14 @@ files:
|
|
37
37
|
- lib/cistern/mock.rb
|
38
38
|
- lib/cistern/model.rb
|
39
39
|
- lib/cistern/service.rb
|
40
|
+
- lib/cistern/timeout.rb
|
40
41
|
- lib/cistern/version.rb
|
41
42
|
- lib/cistern/wait_for.rb
|
42
43
|
- spec/cistern_spec.rb
|
43
44
|
- spec/collection_spec.rb
|
44
45
|
- spec/model_spec.rb
|
45
46
|
- spec/spec_helper.rb
|
47
|
+
- spec/wait_for_spec.rb
|
46
48
|
homepage: http://joshualane.com/cistern
|
47
49
|
licenses: []
|
48
50
|
post_install_message:
|
@@ -72,4 +74,5 @@ test_files:
|
|
72
74
|
- spec/collection_spec.rb
|
73
75
|
- spec/model_spec.rb
|
74
76
|
- spec/spec_helper.rb
|
77
|
+
- spec/wait_for_spec.rb
|
75
78
|
has_rdoc:
|