cistern 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -5
- data/Gemfile +4 -1
- data/Guardfile +7 -1
- data/README.md +96 -8
- data/lib/cistern/data/hash.rb +38 -0
- data/lib/cistern/data/redis.rb +54 -0
- data/lib/cistern/data.rb +45 -0
- data/lib/cistern/service.rb +13 -2
- data/lib/cistern/version.rb +1 -1
- data/lib/cistern.rb +4 -1
- data/spec/cistern_spec.rb +5 -5
- data/spec/collection_spec.rb +9 -9
- data/spec/mock_data_spec.rb +75 -0
- data/spec/model_spec.rb +47 -47
- data/spec/singular_spec.rb +3 -3
- data/spec/spec_helper.rb +5 -0
- data/spec/wait_for_spec.rb +7 -7
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21564d955c7fdeaa4da5659b23af34e3ef33a502
|
4
|
+
data.tar.gz: 16c0a32c23c662d1711ac3a1b0dd47bf78707fc5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a624addb8fd71e993b43550c1c3b7b6359a84ebd2f4918f46f6483836c177933a17ae2ff13267d8ab109386b5ccf6be91dccdffc85ba4aced97766b92ff5b3c8
|
7
|
+
data.tar.gz: 762a3e88c051ca923ed0a6a07a40ad09a24031df8059490715658e26f26b4277f9f92af65906cc5d303bb2b8ace09d4b5a7b31ff6f4dd9c5d00915c82e6754fd
|
data/.travis.yml
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
- 2.1.1
|
4
|
+
- 1.9.3
|
5
|
+
- jruby-19mode
|
6
6
|
bundler_args: "--without development"
|
7
7
|
before_install:
|
8
|
-
|
9
|
-
script:
|
8
|
+
- gem install bundler -v 1.5.2
|
9
|
+
script: bundle exec rake --trace
|
10
10
|
notifications:
|
11
11
|
email:
|
12
12
|
on_success: never
|
13
13
|
on_failure: change
|
14
|
+
services:
|
15
|
+
- redis-server
|
16
|
+
env:
|
17
|
+
matrix:
|
18
|
+
secure: eiDhDgp8jBYKZVOqe891g4StnFsqRXcQwlDSnXthRSuWNMd6oFyFOo/s9aXd9lNFkaTBnSAFrUvywH1t2+H3j+cPMn/91W2s2Ldc+SxVxjrY3mWyr6NIudro/rdK7nIfIcWJFtm0teSXg/1nRPZt0qlXc4bZmvwvN3T8MvdgI2I=
|
data/Gemfile
CHANGED
@@ -4,10 +4,13 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
group :test do
|
7
|
-
gem "guard-rspec"
|
7
|
+
gem "guard-rspec", "~> 4.2", require: false
|
8
|
+
gem "guard-bundler", "~> 2.0", require: false
|
8
9
|
gem "pry-nav"
|
9
10
|
gem "rake"
|
10
11
|
gem "rspec", "~> 2.0"
|
12
|
+
gem "redis-namespace"
|
13
|
+
gem "codeclimate-test-reporter", require: false
|
11
14
|
end
|
12
15
|
|
13
16
|
group :formatters do
|
data/Guardfile
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
guard
|
1
|
+
guard :bundler do
|
2
|
+
watch('Gemfile')
|
3
|
+
# Uncomment next line if your Gemfile contains the `gemspec' command.
|
4
|
+
watch(/^.+\.gemspec/)
|
5
|
+
end
|
6
|
+
|
7
|
+
guard 'rspec', all_on_start: true, all_after_pass: true, cmd: 'bundle exec rspec' do
|
2
8
|
watch(%r{^spec/.+_spec\.rb$})
|
3
9
|
watch(%r{^lib/(.+)\.rb$}) { "spec" }
|
4
10
|
watch('spec/spec_helper.rb') { "spec" }
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ This represents the remote service that you are wrapping. If the service name is
|
|
13
13
|
|
14
14
|
#### Requests
|
15
15
|
|
16
|
-
Requests are enumerated using the
|
16
|
+
Requests are enumerated using the `request` method and required immediately via the relative path specified via `request_path`.
|
17
17
|
|
18
18
|
class Foo::Client < Cistern::Service
|
19
19
|
request_path "my-foo/requests"
|
@@ -48,13 +48,13 @@ A request is method defined within the context of service and mode (Real or Mock
|
|
48
48
|
end # Mock
|
49
49
|
end # Foo::client
|
50
50
|
|
51
|
-
All declared requests can be listed via
|
51
|
+
All declared requests can be listed via `Cistern::Service#requests`.
|
52
52
|
|
53
53
|
Foo::Client.requests # => [:get_bar, :get_bars]
|
54
54
|
|
55
55
|
#### Models and Collections
|
56
56
|
|
57
|
-
Models and collections have declaration semantics similar to requests. Models and collections are enumerated via
|
57
|
+
Models and collections have declaration semantics similar to requests. Models and collections are enumerated via `model` and `collection` respectively.
|
58
58
|
|
59
59
|
class Foo::Client < Cistern::Service
|
60
60
|
model_path "my-foo/models"
|
@@ -65,7 +65,7 @@ Models and collections have declaration semantics similar to requests. Models a
|
|
65
65
|
|
66
66
|
#### Initialization
|
67
67
|
|
68
|
-
Service initialization parameters are enumerated by
|
68
|
+
Service initialization parameters are enumerated by `requires` and `recognizes`. `recognizes` parameters are optional.
|
69
69
|
|
70
70
|
class Foo::Client < Cistern::Service
|
71
71
|
requires :hmac_id, :hmac_secret
|
@@ -81,9 +81,9 @@ Service initialization parameters are enumerated by ```requires``` and ```recogn
|
|
81
81
|
Foo::Client.new(hmac_id: "1")
|
82
82
|
|
83
83
|
|
84
|
-
|
84
|
+
### Mocking
|
85
85
|
|
86
|
-
Cistern strongly encourages you to generate mock support for service. Mocking can be enabled using
|
86
|
+
Cistern strongly encourages you to generate mock support for service. Mocking can be enabled using `mock!`.
|
87
87
|
|
88
88
|
Foo::Client.mocking? # falsey
|
89
89
|
real = Foo::Client.new # Foo::Client::Real
|
@@ -95,10 +95,98 @@ Cistern strongly encourages you to generate mock support for service. Mocking ca
|
|
95
95
|
real.is_a?(Foo::Client::Real) # true
|
96
96
|
fake.is_a?(Foo::Client::Mock) # true
|
97
97
|
|
98
|
+
#### Data
|
99
|
+
|
100
|
+
A uniform interface for mock data is mixed into the `Mock` class by default.
|
101
|
+
|
102
|
+
Foo::Client.mock!
|
103
|
+
client = Foo::Client.new # Foo::Client::Mock
|
104
|
+
client.data # Cistern::Data::Hash
|
105
|
+
client.data["bars"] += ["x"] # ["x"]
|
106
|
+
|
107
|
+
Mock data is class-level by default
|
108
|
+
|
109
|
+
Foo::Client::Mock.data["bars"] # ["x"]
|
110
|
+
|
111
|
+
`reset!` dimisses the `data` object.
|
112
|
+
|
113
|
+
client.data.object_id # 70199868585600
|
114
|
+
client.reset!
|
115
|
+
client.data["bars"] # []
|
116
|
+
client.data.object_id # 70199868566840
|
117
|
+
|
118
|
+
`clear` removes existing keys and values but keeps the same object.
|
119
|
+
|
120
|
+
client.data["bars"] += ["y"] # ["y"]
|
121
|
+
client.data.object_id # 70199868378300
|
122
|
+
client.clear
|
123
|
+
client.data["bars"] # []
|
124
|
+
|
125
|
+
client.data.object_id # 70199868566840
|
126
|
+
|
127
|
+
* `store` and `[]=` write
|
128
|
+
* `fetch` and `[]` read
|
129
|
+
|
130
|
+
You can make the service bypass Cistern's mock data structures by simply creating a `self.data` function in your service `Mock` declaration.
|
131
|
+
|
132
|
+
class Foo::Client < Cistern::Service
|
133
|
+
class Mock
|
134
|
+
def self.data
|
135
|
+
@data ||= {}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
#### Requests
|
142
|
+
|
143
|
+
Mock requests should be defined within the contextual `Mock` module and interact with the `data` object directly.
|
144
|
+
|
145
|
+
# lib/foo/requests/create_bar.rb
|
146
|
+
class Foo::Client
|
147
|
+
class Mock
|
148
|
+
def create_bar(options={})
|
149
|
+
id = Foo.random_hex(6)
|
150
|
+
|
151
|
+
bar = {
|
152
|
+
"id" => id
|
153
|
+
}.merge(options)
|
154
|
+
|
155
|
+
self.data[:bars][id] = bar
|
156
|
+
|
157
|
+
response(
|
158
|
+
:body => {"bar" => bar},
|
159
|
+
:status => 201,
|
160
|
+
:path => '/bar',
|
161
|
+
)
|
162
|
+
end
|
163
|
+
end # Mock
|
164
|
+
end # Foo::Client
|
165
|
+
|
166
|
+
|
167
|
+
#### Storage
|
168
|
+
|
169
|
+
Currently supported storage backends are:
|
170
|
+
|
171
|
+
* `:hash` : `Cistern::Data::Hash` (default)
|
172
|
+
* `:redis` : `Cistern::Data::Redis`
|
173
|
+
|
174
|
+
|
175
|
+
Backends can be switched by using `store_in`.
|
176
|
+
|
177
|
+
# use redis with defaults
|
178
|
+
Patient::Mock.store_in(:redis)
|
179
|
+
# use redis with a specific client
|
180
|
+
Patient::Mock.store_in(:redis, client: Redis::Namespace.new("cistern", redis: Redis.new(host: "10.1.0.1"))
|
181
|
+
# use a hash
|
182
|
+
Patient::Mock.store_in(:hash)
|
98
183
|
|
99
184
|
### Model
|
100
185
|
|
101
|
-
|
186
|
+
* `connection` represents the associated `Foo::Client` instance.
|
187
|
+
* `collection` represents the related collection (if applicable)
|
188
|
+
|
189
|
+
Example
|
102
190
|
|
103
191
|
class Foo::Client::Bar < Cistern::Model
|
104
192
|
identity :id
|
@@ -136,7 +224,7 @@ Cistern strongly encourages you to generate mock support for service. Mocking ca
|
|
136
224
|
|
137
225
|
### Collection
|
138
226
|
|
139
|
-
|
227
|
+
`model` tells Cistern which class is contained within the collection. `Cistern::Collection` inherits from `Array` and lazy loads where applicable.
|
140
228
|
|
141
229
|
class Foo::Client::Bars < Cistern::Collection
|
142
230
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Cistern::Data::Hash
|
2
|
+
Cistern::Data.backends[:hash] = self
|
3
|
+
|
4
|
+
def initialize(options={}, &default)
|
5
|
+
@hash = Hash.new
|
6
|
+
@default = default
|
7
|
+
end
|
8
|
+
|
9
|
+
def clear
|
10
|
+
hash.clear
|
11
|
+
end
|
12
|
+
|
13
|
+
def store(key, *args)
|
14
|
+
assign_default(key)
|
15
|
+
|
16
|
+
hash.store(key, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
alias []= store
|
20
|
+
|
21
|
+
def fetch(key, *args)
|
22
|
+
assign_default(key)
|
23
|
+
|
24
|
+
hash.fetch(key, *args)
|
25
|
+
end
|
26
|
+
|
27
|
+
alias [] fetch
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
attr_reader :hash, :default
|
32
|
+
|
33
|
+
def assign_default(key)
|
34
|
+
if !hash.key?(key) && default
|
35
|
+
default.call(hash, key)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Cistern::Data::Redis
|
2
|
+
Cistern::Data.backends[:redis] = self
|
3
|
+
|
4
|
+
def self.marshal
|
5
|
+
@marshal ||= begin
|
6
|
+
require 'multi_json'
|
7
|
+
MultiJson
|
8
|
+
rescue LoadError
|
9
|
+
require 'json'
|
10
|
+
::JSON
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.marshal=(marshal)
|
15
|
+
@marshal = marshal
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(options={}, &block)
|
19
|
+
@client = options[:client] || ::Redis.new
|
20
|
+
@default = block
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear
|
24
|
+
unless (keys = client.keys("*")).empty?
|
25
|
+
client.del(*keys)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def store(key, value, *args)
|
30
|
+
assign_default(key)
|
31
|
+
|
32
|
+
client.set(key, Cistern::Data::Redis.marshal.dump(value), *args)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias []= store
|
36
|
+
|
37
|
+
def fetch(key, *args)
|
38
|
+
assign_default(key)
|
39
|
+
|
40
|
+
Cistern::Data::Redis.marshal.load(client.get(key, *args))
|
41
|
+
end
|
42
|
+
|
43
|
+
alias [] fetch
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
attr_reader :client, :default
|
48
|
+
|
49
|
+
def assign_default(key)
|
50
|
+
if client.keys(key).empty? && default
|
51
|
+
default.call(client, key)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/cistern/data.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Cistern::Data
|
2
|
+
def self.extended(klass)
|
3
|
+
klass.send(:extend, ClassMethods)
|
4
|
+
klass.send(:include, InstanceMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.backends
|
8
|
+
@backends ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def data
|
13
|
+
@data ||= Cistern::Data.backends[storage].new(@options || {}) { |d,k| d[k] = [] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset!
|
17
|
+
clear!
|
18
|
+
@data = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear!
|
22
|
+
self.data.clear
|
23
|
+
end
|
24
|
+
|
25
|
+
def store_in(storage, options)
|
26
|
+
@storage = storage
|
27
|
+
@options = options
|
28
|
+
@data = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def storage
|
32
|
+
@storage ||= :hash
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module InstanceMethods
|
37
|
+
def data
|
38
|
+
self.class.data
|
39
|
+
end
|
40
|
+
|
41
|
+
def reset!
|
42
|
+
self.class.reset!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/cistern/service.rb
CHANGED
@@ -34,9 +34,13 @@ class Cistern::Service
|
|
34
34
|
end
|
35
35
|
|
36
36
|
class Real
|
37
|
+
def initialize(options={})
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
class Mock
|
42
|
+
def initialize(options={})
|
43
|
+
end
|
40
44
|
end
|
41
45
|
EOS
|
42
46
|
|
@@ -44,6 +48,7 @@ class Cistern::Service
|
|
44
48
|
|
45
49
|
klass::Mock.send(:include, klass::Collections)
|
46
50
|
klass::Mock.send(:extend, Cistern::WaitFor)
|
51
|
+
klass::Mock.send(:extend, Cistern::Data)
|
47
52
|
klass::Mock.timeout_error = klass::Timeout
|
48
53
|
klass::Real.send(:include, klass::Collections)
|
49
54
|
klass::Real.send(:extend, Cistern::WaitFor)
|
@@ -108,12 +113,15 @@ class Cistern::Service
|
|
108
113
|
|
109
114
|
def validate_options(options={})
|
110
115
|
required_options = Cistern::Hash.slice(options, *required_arguments)
|
116
|
+
|
111
117
|
missing_required_options = required_arguments - required_options.keys
|
118
|
+
|
112
119
|
unless missing_required_options.empty?
|
113
120
|
raise "Missing required options: #{missing_required_options.inspect}"
|
114
121
|
end
|
115
|
-
|
122
|
+
|
116
123
|
unrecognized_options = options.keys - (required_arguments + recognized_arguments)
|
124
|
+
|
117
125
|
unless unrecognized_options.empty?
|
118
126
|
raise "Unrecognized options: #{unrecognized_options.inspect}"
|
119
127
|
end
|
@@ -132,7 +140,10 @@ class Cistern::Service
|
|
132
140
|
EOS
|
133
141
|
end
|
134
142
|
requests.each do |request|
|
135
|
-
|
143
|
+
unless service::Real.method_defined?(request.to_s)
|
144
|
+
require File.join(@request_path, request.to_s)
|
145
|
+
end
|
146
|
+
|
136
147
|
if service::Mock.method_defined?(request)
|
137
148
|
mocked_requests << request
|
138
149
|
else
|
data/lib/cistern/version.rb
CHANGED
data/lib/cistern.rb
CHANGED
@@ -16,10 +16,13 @@ module Cistern
|
|
16
16
|
require 'cistern/model'
|
17
17
|
require 'cistern/service'
|
18
18
|
require 'cistern/singular'
|
19
|
+
require 'cistern/data'
|
20
|
+
require 'cistern/data/hash'
|
21
|
+
require 'cistern/data/redis'
|
19
22
|
|
20
23
|
extend WaitFor
|
21
24
|
|
22
|
-
|
25
|
+
require 'cistern/formatter'
|
23
26
|
|
24
27
|
def self.formatter=(formatter); @formatter = formatter; end
|
25
28
|
|
data/spec/cistern_spec.rb
CHANGED
@@ -26,17 +26,17 @@ describe "#inspect" do
|
|
26
26
|
it "should use formatador" do
|
27
27
|
Cistern.formatter = Cistern::Formatter::Formatador
|
28
28
|
|
29
|
-
Inspector.new(id: 1, name: "name").inspect.
|
29
|
+
expect(Inspector.new(id: 1, name: "name").inspect).to eq(%q{ <Inspector
|
30
30
|
id=1,
|
31
31
|
name="name"
|
32
|
-
>}
|
32
|
+
>})
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
describe "Cistern::Collection" do
|
37
37
|
it "should use formatador" do
|
38
38
|
Cistern.formatter = Cistern::Formatter::Formatador
|
39
|
-
Inspectors.new.all.inspect.
|
39
|
+
expect(Inspectors.new.all.inspect).to eq(%q{ <Inspectors
|
40
40
|
[
|
41
41
|
<Inspector
|
42
42
|
id=1,
|
@@ -47,12 +47,12 @@ describe "#inspect" do
|
|
47
47
|
name="4"
|
48
48
|
>
|
49
49
|
]
|
50
|
-
>}
|
50
|
+
>})
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should use awesome_print" do
|
54
54
|
Cistern.formatter = Cistern::Formatter::AwesomePrint
|
55
|
-
Inspectors.new.all.inspect.
|
55
|
+
expect(Inspectors.new.all.inspect).to match(/Inspectors\s+{.*}$/m) # close enough
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
data/spec/collection_spec.rb
CHANGED
@@ -15,33 +15,33 @@ describe "Cistern::Collection" do
|
|
15
15
|
|
16
16
|
it "should give to_s" do
|
17
17
|
collection = SampleCollection.new
|
18
|
-
collection.to_s.
|
19
|
-
collection.to_s.gsub(/:[^>]*/,'').
|
18
|
+
expect(collection.to_s).not_to eq "[]"
|
19
|
+
expect(collection.to_s.gsub(/:[^>]*/,'')).to eq(collection.all.to_s.gsub(/:[^>]*/,''))
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should give size and count" do
|
23
|
-
SampleCollection.new.size.
|
24
|
-
SampleCollection.new.count.
|
23
|
+
expect(SampleCollection.new.size).to eq(3)
|
24
|
+
expect(SampleCollection.new.count).to eq(3)
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should give first" do
|
28
|
-
SampleCollection.new.first.
|
28
|
+
expect(SampleCollection.new.first).to eq(SampleCollectionModel.new(id: 1))
|
29
29
|
end
|
30
30
|
|
31
31
|
it "should give last" do
|
32
|
-
SampleCollection.new.last.
|
32
|
+
expect(SampleCollection.new.last).to eq(SampleCollectionModel.new(id: 2))
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should reject" do
|
36
|
-
SampleCollection.new.reject{|m| m.id == 2}.
|
36
|
+
expect(SampleCollection.new.reject{|m| m.id == 2}).to eq([SampleCollectionModel.new(id: 1), SampleCollectionModel.new(id: 3)])
|
37
37
|
end
|
38
38
|
|
39
39
|
it "should select" do
|
40
|
-
SampleCollection.new.select{|m| m.id == 2}.
|
40
|
+
expect(SampleCollection.new.select{|m| m.id == 2}).to eq([SampleCollectionModel.new(id: 2)])
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should slice" do
|
44
|
-
SampleCollection.new.slice(0,2).
|
44
|
+
expect(SampleCollection.new.slice(0,2)).to eq([SampleCollectionModel.new(id: 1), SampleCollectionModel.new(id: 3, name: "tom")])
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should ==" do
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'mock data' do
|
4
|
+
class Patient < Cistern::Service
|
5
|
+
request :diagnosis
|
6
|
+
request :treat
|
7
|
+
|
8
|
+
class Real
|
9
|
+
def diagnosis(options={})
|
10
|
+
end
|
11
|
+
|
12
|
+
def treat(options={})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Mock
|
17
|
+
def diagnosis(diagnosis)
|
18
|
+
#self.data[:diagnosis] << rand(2) == 0 ? "sick" : "healthy"
|
19
|
+
self.data.store(:diagnosis, self.data.fetch(:diagnosis) + [diagnosis])
|
20
|
+
end
|
21
|
+
|
22
|
+
def treat(treatment)
|
23
|
+
self.data[:treatments] += [treatment]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
shared_examples "mock_data#backend" do |backend, options|
|
29
|
+
it "should store mock data" do
|
30
|
+
Patient.mock!
|
31
|
+
Patient::Mock.store_in(backend, options)
|
32
|
+
Patient.reset!
|
33
|
+
|
34
|
+
p = Patient.new
|
35
|
+
p.diagnosis("sick")
|
36
|
+
expect(p.data[:diagnosis]).to eq(["sick"])
|
37
|
+
|
38
|
+
p.reset!
|
39
|
+
|
40
|
+
expect(p.data[:diagnosis]).to eq([])
|
41
|
+
|
42
|
+
p.treat("healthy")
|
43
|
+
expect(p.data[:treatments]).to eq(["healthy"])
|
44
|
+
|
45
|
+
Patient.reset!
|
46
|
+
|
47
|
+
expect(p.data[:treatments]).to eq([])
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with a storage backend" do
|
53
|
+
describe "Cistern::Data::Hash" do
|
54
|
+
include_examples "mock_data#backend", :hash
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "Cistern::Data::Redis" do
|
58
|
+
include_examples "mock_data#backend", :redis
|
59
|
+
|
60
|
+
context "with an explicit client" do
|
61
|
+
before(:each) {
|
62
|
+
@other = Redis::Namespace.new("other_cistern", Redis.new)
|
63
|
+
@other.set("x", "y")
|
64
|
+
}
|
65
|
+
|
66
|
+
include_examples "mock_data#backend", :redis, client: Redis::Namespace.new("cistern", Redis.new)
|
67
|
+
|
68
|
+
after(:each) {
|
69
|
+
expect(@other.get("x")).to eq("y")
|
70
|
+
@other.del("x")
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/model_spec.rb
CHANGED
@@ -12,11 +12,11 @@ describe "Cistern::Model" do
|
|
12
12
|
model = DupSpec.new(id: 1, name: "string", properties: {value: "something", else: "what"})
|
13
13
|
duplicate = model.dup
|
14
14
|
|
15
|
-
duplicate.
|
16
|
-
duplicate.
|
15
|
+
expect(duplicate).to eq(model)
|
16
|
+
expect(duplicate).not_to eql model
|
17
17
|
|
18
18
|
model.name= "anotherstring"
|
19
|
-
duplicate.name.
|
19
|
+
expect(duplicate.name).to eq("string")
|
20
20
|
end
|
21
21
|
|
22
22
|
context "attribute parsing" do
|
@@ -48,95 +48,95 @@ describe "Cistern::Model" do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should parse string" do
|
51
|
-
TypeSpec.new(name: 1).name.
|
51
|
+
expect(TypeSpec.new(name: 1).name).to eq("1")
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should parse time" do
|
55
55
|
time = Time.now
|
56
56
|
created_at = TypeSpec.new(created_at: time.to_s).created_at
|
57
|
-
created_at.
|
58
|
-
created_at.to_i.
|
57
|
+
expect(created_at).to be_a(Time)
|
58
|
+
expect(created_at.to_i).to eq(time.to_i)
|
59
59
|
end
|
60
60
|
|
61
61
|
it "should parse boolean" do
|
62
|
-
TypeSpec.new(flag: "false").flag.
|
63
|
-
TypeSpec.new(flag: "true").flag.
|
64
|
-
TypeSpec.new(flag: false).flag.
|
65
|
-
TypeSpec.new(flag: true).flag.
|
66
|
-
TypeSpec.new(flag: "0").flag.
|
67
|
-
TypeSpec.new(flag: "1").flag.
|
68
|
-
TypeSpec.new(flag: 0).flag.
|
69
|
-
TypeSpec.new(flag: 1).flag.
|
70
|
-
TypeSpec.new(flag: false).
|
71
|
-
TypeSpec.new(flag: true).
|
62
|
+
expect(TypeSpec.new(flag: "false").flag).to be_false
|
63
|
+
expect(TypeSpec.new(flag: "true").flag).to be_true
|
64
|
+
expect(TypeSpec.new(flag: false).flag).to be_false
|
65
|
+
expect(TypeSpec.new(flag: true).flag).to be_true
|
66
|
+
expect(TypeSpec.new(flag: "0").flag).to be_false
|
67
|
+
expect(TypeSpec.new(flag: "1").flag).to be_true
|
68
|
+
expect(TypeSpec.new(flag: 0).flag).to be_false
|
69
|
+
expect(TypeSpec.new(flag: 1).flag).to be_true
|
70
|
+
expect(TypeSpec.new(flag: false)).not_to be_flag
|
71
|
+
expect(TypeSpec.new(flag: true)).to be_flag
|
72
72
|
end
|
73
73
|
|
74
74
|
it "should parse an array" do
|
75
|
-
TypeSpec.new(list: []).list.
|
76
|
-
TypeSpec.new(list: "item").list.
|
75
|
+
expect(TypeSpec.new(list: []).list).to eq([])
|
76
|
+
expect(TypeSpec.new(list: "item").list).to eq(["item"])
|
77
77
|
end
|
78
78
|
|
79
79
|
it "should parse a float" do
|
80
|
-
TypeSpec.new(floater: "0.01").floater.
|
81
|
-
TypeSpec.new(floater: 0.01).floater.
|
80
|
+
expect(TypeSpec.new(floater: "0.01").floater).to eq(0.01)
|
81
|
+
expect(TypeSpec.new(floater: 0.01).floater).to eq(0.01)
|
82
82
|
end
|
83
83
|
|
84
84
|
it "should use custom parser" do
|
85
|
-
TypeSpec.new(custom: "15").custom.
|
85
|
+
expect(TypeSpec.new(custom: "15").custom).to eq("X!15")
|
86
86
|
end
|
87
87
|
|
88
88
|
it "should squash, cast, alias an attribute and keep a vanilla reference" do
|
89
89
|
# vanilla squash
|
90
|
-
TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).butternut_type.
|
91
|
-
TypeSpec.new({"squash" => {"id" => "12", "type" => nil}}).butternut_type.
|
92
|
-
TypeSpec.new({"squash" => nil}).butternut_type.
|
90
|
+
expect(TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).butternut_type).to eq("fred")
|
91
|
+
expect(TypeSpec.new({"squash" => {"id" => "12", "type" => nil}}).butternut_type).to be_nil
|
92
|
+
expect(TypeSpec.new({"squash" => nil}).butternut_type).to be_nil
|
93
93
|
|
94
94
|
# composite processors: squash and cast
|
95
|
-
TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).butternut_id.
|
96
|
-
TypeSpec.new({"squash" => {"id" => nil, "type" => "fred"}}).butternut_id.
|
97
|
-
TypeSpec.new({"squash" => {"type" => "fred"}}).butternut_id.
|
95
|
+
expect(TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).butternut_id).to eq(12)
|
96
|
+
expect(TypeSpec.new({"squash" => {"id" => nil, "type" => "fred"}}).butternut_id).to be_nil
|
97
|
+
expect(TypeSpec.new({"squash" => {"type" => "fred"}}).butternut_id).to be_nil
|
98
98
|
|
99
99
|
# override intermediate processing
|
100
|
-
TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).squash.
|
100
|
+
expect(TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).squash).to eq({"id" => "12", "type" => "fred"})
|
101
101
|
|
102
102
|
# alias of override
|
103
|
-
TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).vegetable.
|
103
|
+
expect(TypeSpec.new({"squash" => {"id" => "12", "type" => "fred"}}).vegetable).to eq({"id" => "12", "type" => "fred"})
|
104
104
|
end
|
105
105
|
|
106
106
|
it "should set a default value" do
|
107
|
-
TypeSpec.new.default.
|
107
|
+
expect(TypeSpec.new.default).to eq("im a squash")
|
108
108
|
end
|
109
109
|
|
110
110
|
it "should override a default value" do
|
111
|
-
TypeSpec.new(default: "now im a different squash").default.
|
111
|
+
expect(TypeSpec.new(default: "now im a different squash").default).to eq("now im a different squash")
|
112
112
|
end
|
113
113
|
|
114
114
|
context "allowing the same alias for multiple attributes" do
|
115
115
|
it "should do so when not squashing" do
|
116
116
|
type_spec = TypeSpec.new({"nested" => "bamboo"})
|
117
|
-
type_spec.same_alias_1.
|
118
|
-
type_spec.same_alias_2.
|
117
|
+
expect(type_spec.same_alias_1).to eq("bamboo")
|
118
|
+
expect(type_spec.same_alias_2).to eq("bamboo")
|
119
119
|
end
|
120
120
|
|
121
121
|
it "should do so when squashing" do
|
122
122
|
type_spec = TypeSpec.new({"nested" => {"attr_1" => "bamboo", "attr_2" => "panda"}})
|
123
|
-
type_spec.same_alias_squashed_1.
|
124
|
-
type_spec.same_alias_squashed_2.
|
125
|
-
type_spec.same_alias_squashed_3.
|
123
|
+
expect(type_spec.same_alias_squashed_1).to eq("bamboo")
|
124
|
+
expect(type_spec.same_alias_squashed_2).to eq("panda")
|
125
|
+
expect(type_spec.same_alias_squashed_3).to eq("panda")
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
129
|
it "should slice out unaccounted for attributes" do
|
130
|
-
TypeSpec.new({"something" => {"id" => "12"}}).attributes.keys.
|
130
|
+
expect(TypeSpec.new({"something" => {"id" => "12"}}).attributes.keys).not_to include("something")
|
131
131
|
end
|
132
132
|
|
133
133
|
describe "#requires" do
|
134
134
|
it "should raise if attribute not provided" do
|
135
|
-
|
135
|
+
expect { TypeSpec.new({"connection" => "fake", "something" => {"id" => "12"}}).save }.to raise_exception(ArgumentError)
|
136
136
|
end
|
137
137
|
|
138
138
|
it "should raise if attribute is provided and is nil" do
|
139
|
-
|
139
|
+
expect { TypeSpec.new({"connection" => "fake", "custom" => nil}).save }.to raise_exception(ArgumentError)
|
140
140
|
end
|
141
141
|
end
|
142
142
|
end
|
@@ -153,25 +153,25 @@ describe "Cistern::Model" do
|
|
153
153
|
|
154
154
|
before(:each) do
|
155
155
|
CoverageSpec.attributes[:used][:coverage_hits] = 0
|
156
|
-
obj.used.
|
157
|
-
obj.used.
|
156
|
+
expect(obj.used).to eq("foo") # once
|
157
|
+
expect(obj.used).to eq("foo") # twice
|
158
158
|
end
|
159
159
|
|
160
160
|
it "should store the file path where the attribute was defined" do
|
161
|
-
CoverageSpec.attributes[:used][:coverage_file].
|
162
|
-
CoverageSpec.attributes[:unused][:coverage_file].
|
161
|
+
expect(CoverageSpec.attributes[:used][:coverage_file]).to eq(__FILE__)
|
162
|
+
expect(CoverageSpec.attributes[:unused][:coverage_file]).to eq(__FILE__)
|
163
163
|
end
|
164
164
|
|
165
165
|
it "should store the line number where the attribute was defined" do
|
166
166
|
src_lines = File.read(__FILE__).lines
|
167
167
|
|
168
|
-
src_lines[CoverageSpec.attributes[:used][:coverage_line] - 1].
|
169
|
-
src_lines[CoverageSpec.attributes[:unused][:coverage_line] - 1].
|
168
|
+
expect(src_lines[CoverageSpec.attributes[:used][:coverage_line] - 1]).to match(/attribute :used/)
|
169
|
+
expect(src_lines[CoverageSpec.attributes[:unused][:coverage_line] - 1]).to match(/attribute :unused/)
|
170
170
|
end
|
171
171
|
|
172
172
|
it "should store how many times an attribute's reader is called" do
|
173
|
-
CoverageSpec.attributes[:used][:coverage_hits].
|
174
|
-
CoverageSpec.attributes[:unused][:coverage_hits].
|
173
|
+
expect(CoverageSpec.attributes[:used][:coverage_hits]).to eq(2)
|
174
|
+
expect(CoverageSpec.attributes[:unused][:coverage_hits]).to eq(0)
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
data/spec/singular_spec.rb
CHANGED
@@ -16,13 +16,13 @@ describe "Cistern::Singular" do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should work" do
|
19
|
-
SampleSingular.new(connection: :fake).name.
|
19
|
+
expect(SampleSingular.new(connection: :fake).name).to eq("amazing")
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should reload" do
|
23
23
|
singular = SampleSingular.new(connection: :fake)
|
24
24
|
old_count = singular.count
|
25
|
-
singular.count.
|
26
|
-
singular.reload.count.
|
25
|
+
expect(singular.count).to eq(old_count)
|
26
|
+
expect(singular.reload.count).to be > old_count
|
27
27
|
end
|
28
28
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/wait_for_spec.rb
CHANGED
@@ -26,13 +26,13 @@ end
|
|
26
26
|
|
27
27
|
describe 'Cistern#wait_for' do
|
28
28
|
it "should return false if timeout exceeded" do
|
29
|
-
Cistern.wait_for(0, 0) { false }.
|
29
|
+
expect(Cistern.wait_for(0, 0) { false }).to be_false
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
describe 'Cistern#wait_for!' do
|
34
34
|
it "should raise if timeout exceeded" do
|
35
|
-
|
35
|
+
expect { Cistern.wait_for!(0, 0) { false } }.to raise_exception(Cistern::Timeout)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -41,7 +41,7 @@ describe 'Cistern::Model#wait_for!' do
|
|
41
41
|
let(:model) { service.wait_for_models.new(identity: 1) }
|
42
42
|
|
43
43
|
it "should raise if timeout exceeded" do
|
44
|
-
|
44
|
+
expect { model.wait_for!(0, 0) { false } }.to raise_exception(WaitForService::Timeout)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -57,9 +57,9 @@ describe "WaitForModel#timeout" do
|
|
57
57
|
elapsed = 0
|
58
58
|
|
59
59
|
timeout(2) do
|
60
|
-
|
60
|
+
expect do
|
61
61
|
model.wait_for! { sleep(0.2); elapsed += 0.2; elapsed > 0.2 }
|
62
|
-
end.
|
62
|
+
end.to raise_exception(WaitForService::Timeout)
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -70,9 +70,9 @@ describe "WaitForModel#timeout" do
|
|
70
70
|
elapsed = 0
|
71
71
|
|
72
72
|
timeout(2) do
|
73
|
-
|
73
|
+
expect do
|
74
74
|
model.wait_for!(0.1) { sleep(0.2); elapsed += 0.2; elapsed > 0.2 }
|
75
|
-
end.
|
75
|
+
end.to raise_exception(WaitForService::Timeout)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cistern
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Lane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: API client framework extracted from Fog
|
14
14
|
email:
|
@@ -30,6 +30,9 @@ files:
|
|
30
30
|
- lib/cistern/attributes.rb
|
31
31
|
- lib/cistern/collection.rb
|
32
32
|
- lib/cistern/coverage.rb
|
33
|
+
- lib/cistern/data.rb
|
34
|
+
- lib/cistern/data/hash.rb
|
35
|
+
- lib/cistern/data/redis.rb
|
33
36
|
- lib/cistern/formatter.rb
|
34
37
|
- lib/cistern/formatter/awesome_print.rb
|
35
38
|
- lib/cistern/formatter/formatador.rb
|
@@ -43,6 +46,7 @@ files:
|
|
43
46
|
- lib/cistern/wait_for.rb
|
44
47
|
- spec/cistern_spec.rb
|
45
48
|
- spec/collection_spec.rb
|
49
|
+
- spec/mock_data_spec.rb
|
46
50
|
- spec/model_spec.rb
|
47
51
|
- spec/singular_spec.rb
|
48
52
|
- spec/spec_helper.rb
|
@@ -74,6 +78,7 @@ summary: API client framework
|
|
74
78
|
test_files:
|
75
79
|
- spec/cistern_spec.rb
|
76
80
|
- spec/collection_spec.rb
|
81
|
+
- spec/mock_data_spec.rb
|
77
82
|
- spec/model_spec.rb
|
78
83
|
- spec/singular_spec.rb
|
79
84
|
- spec/spec_helper.rb
|