cistern 0.6.0 → 0.7.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.
- 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
|