cistern 0.2.3 → 0.3.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.
@@ -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: