cistern 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,9 @@ require 'cistern/version'
2
2
  require 'time'
3
3
 
4
4
  module Cistern
5
- Error = Class.new(StandardError)
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 ||= if defined?(AwesomePrint)
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
@@ -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 clear
42
- @loaded = true
43
- super
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
@@ -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
@@ -33,19 +33,15 @@ class Cistern::Model
33
33
  !comparison_object.new_record?)
34
34
  end
35
35
 
36
- def wait_for(timeout=Cistern.timeout, interval=1, &block)
37
- reload
38
- retries = 3
39
- Cistern.wait_for(timeout, interval) do
40
- if reload
41
- retries = 3
42
- elsif retries > 0
43
- retries -= 1
44
- sleep(1)
45
- elsif retries == 0
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
@@ -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
- if self.mocking?
144
- self.const_get(:Mock).send(:include, self.const_get(:Collections))
145
- self.const_get(:Mock).new(options)
146
- else
147
- self.const_get(:Real).send(:include, self.const_get(:Collections))
148
- self.const_get(:Real).new(options)
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
@@ -1,3 +1,3 @@
1
1
  module Cistern
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,15 +1,31 @@
1
+ require 'timeout'
2
+
1
3
  module Cistern
2
- def self.wait_for(timeout=Cistern.timeout, interval=1, &block)
3
- duration = 0
4
- start = Time.now
5
- until yield || duration > timeout
6
- sleep(interval.to_f)
7
- duration = Time.now - start
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
- if duration > timeout
10
- false
11
- else
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.2.3
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-17 00:00:00.000000000 Z
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: