cistern 0.12.3 → 1.0.0.pre
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/Gemfile +1 -1
- data/README.md +7 -30
- data/lib/cistern.rb +2 -2
- data/lib/cistern/attributes.rb +14 -37
- data/lib/cistern/collection.rb +15 -11
- data/lib/cistern/formatter.rb +0 -4
- data/lib/cistern/model.rb +17 -11
- data/lib/cistern/request.rb +19 -32
- data/lib/cistern/service.rb +38 -119
- data/lib/cistern/singular.rb +1 -1
- data/lib/cistern/string.rb +19 -11
- data/lib/cistern/version.rb +1 -1
- data/spec/collection_spec.rb +2 -3
- data/spec/dirty_spec.rb +1 -1
- data/spec/formatter_spec.rb +29 -57
- data/spec/mock_data_spec.rb +16 -22
- data/spec/model_spec.rb +6 -6
- data/spec/request_spec.rb +22 -14
- data/spec/service_spec.rb +0 -0
- data/spec/singular_spec.rb +4 -4
- data/spec/spec_helper.rb +3 -0
- data/spec/support/service.rb +1 -0
- data/spec/wait_for_spec.rb +8 -18
- metadata +10 -7
- data/.gemrelease +0 -3
- data/lib/cistern/formatter/default.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d450addd353c421e2096a7236b9df93588d33dd0
|
4
|
+
data.tar.gz: 1eed93cace8518f20b98d0576a672c2e32f825aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 787f6a8105570622d8846676e87f04bdeb53a15d88ffe6e18ba54afa264b7aeb4c893cfdafd1cf924c3ebdbfda6480aab88b32580bb334283cfec801cad12d17
|
7
|
+
data.tar.gz: abd365799a52bc3192712595b7574b8aad97975a66535bc78f073442e32c43c73b4ff8bfb167e71410ad0a567cc775914acc9f9ac1dbf8f979a6734eeb95ccde
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -58,29 +58,6 @@ All declared requests can be listed via `Cistern::Service#requests`.
|
|
58
58
|
Foo::Client.requests # => [:get_bar, :get_bars]
|
59
59
|
```
|
60
60
|
|
61
|
-
##### Forward Compatible
|
62
|
-
|
63
|
-
Ish.
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
# client/requests/get_bar.rb
|
67
|
-
class Foo:Client::GetBar
|
68
|
-
|
69
|
-
service Foo::Client
|
70
|
-
|
71
|
-
def real(bar_id)
|
72
|
-
connection.request("http://example.org/bar/#{bar_id}")
|
73
|
-
end
|
74
|
-
|
75
|
-
def mock
|
76
|
-
# do some mock things
|
77
|
-
end
|
78
|
-
end
|
79
|
-
```
|
80
|
-
|
81
|
-
In this context, `connection` is reference to the `Cistern::Service`.
|
82
|
-
|
83
|
-
|
84
61
|
#### Models and Collections
|
85
62
|
|
86
63
|
Models and collections have declaration semantics similar to requests. Models and collections are enumerated via `model` and `collection` respectively.
|
@@ -231,7 +208,7 @@ Patient::Mock.store_in(:hash)
|
|
231
208
|
|
232
209
|
### Model
|
233
210
|
|
234
|
-
* `
|
211
|
+
* `service` represents the associated `Foo::Client` instance.
|
235
212
|
* `collection` represents the related collection (if applicable)
|
236
213
|
|
237
214
|
Example
|
@@ -248,7 +225,7 @@ class Foo::Client::Bar < Cistern::Model
|
|
248
225
|
params = {
|
249
226
|
"id" => self.identity
|
250
227
|
}
|
251
|
-
self.
|
228
|
+
self.service.destroy_bar(params).body["request"]
|
252
229
|
end
|
253
230
|
|
254
231
|
def save
|
@@ -262,11 +239,11 @@ class Foo::Client::Bar < Cistern::Model
|
|
262
239
|
}
|
263
240
|
|
264
241
|
if new_record?
|
265
|
-
merge_attributes(
|
242
|
+
merge_attributes(service.create_bar(params).body["bar"])
|
266
243
|
else
|
267
244
|
requires :identity
|
268
245
|
|
269
|
-
merge_attributes(
|
246
|
+
merge_attributes(service.update_bar(params).body["bar"])
|
270
247
|
end
|
271
248
|
end
|
272
249
|
end
|
@@ -309,7 +286,7 @@ class Foo::Client::Bars < Cistern::Collection
|
|
309
286
|
model Foo::Client::Bar
|
310
287
|
|
311
288
|
def all(params = {})
|
312
|
-
response =
|
289
|
+
response = service.get_bars(params)
|
313
290
|
|
314
291
|
data = response.body
|
315
292
|
|
@@ -323,11 +300,11 @@ class Foo::Client::Bars < Cistern::Collection
|
|
323
300
|
}
|
324
301
|
params.merge!("location" => options[:location]) if options.key?(:location)
|
325
302
|
|
326
|
-
|
303
|
+
service.requests.new(service.discover_bar(params).body["request"])
|
327
304
|
end
|
328
305
|
|
329
306
|
def get(id)
|
330
|
-
if data =
|
307
|
+
if data = service.get_bar("id" => id).body["bar"]
|
331
308
|
new(data)
|
332
309
|
else
|
333
310
|
nil
|
data/lib/cistern.rb
CHANGED
@@ -9,6 +9,7 @@ module Cistern
|
|
9
9
|
Timeout = Class.new(Error)
|
10
10
|
|
11
11
|
require 'cistern/hash'
|
12
|
+
require 'cistern/string'
|
12
13
|
require 'cistern/mock'
|
13
14
|
require 'cistern/wait_for'
|
14
15
|
require 'cistern/attributes'
|
@@ -16,11 +17,10 @@ module Cistern
|
|
16
17
|
require 'cistern/model'
|
17
18
|
require 'cistern/service'
|
18
19
|
require 'cistern/singular'
|
20
|
+
require 'cistern/request'
|
19
21
|
require 'cistern/data'
|
20
22
|
require 'cistern/data/hash'
|
21
23
|
require 'cistern/data/redis'
|
22
|
-
require 'cistern/request'
|
23
|
-
require 'cistern/string'
|
24
24
|
|
25
25
|
extend WaitFor
|
26
26
|
|
data/lib/cistern/attributes.rb
CHANGED
@@ -121,19 +121,9 @@ module Cistern::Attributes
|
|
121
121
|
def read_attribute(name)
|
122
122
|
options = self.class.attributes[name] || {}
|
123
123
|
# record the attribute was accessed
|
124
|
-
|
125
|
-
self.class.attributes[name.to_s.to_sym][:coverage_hits] += 1
|
126
|
-
rescue
|
127
|
-
nil
|
128
|
-
end
|
129
|
-
|
130
|
-
default = options[:default]
|
131
|
-
|
132
|
-
unless default.nil?
|
133
|
-
default = Marshal.load(Marshal.dump(default))
|
134
|
-
end
|
124
|
+
self.class.attributes[name.to_s.to_sym][:coverage_hits] += 1 rescue nil
|
135
125
|
|
136
|
-
attributes.fetch(name.to_s.to_sym, default)
|
126
|
+
attributes.fetch(name.to_s.to_sym, options[:default])
|
137
127
|
end
|
138
128
|
|
139
129
|
def write_attribute(name, value)
|
@@ -183,39 +173,26 @@ module Cistern::Attributes
|
|
183
173
|
end
|
184
174
|
|
185
175
|
def merge_attributes(new_attributes = {})
|
186
|
-
protected_methods = (Cistern::Model.instance_methods - [:connection, :identity, :collection])
|
187
|
-
ignored_attributes = self.class.ignored_attributes
|
188
|
-
class_attributes = self.class.attributes
|
189
|
-
class_aliases = self.class.aliases
|
190
|
-
|
191
176
|
new_attributes.each do |_key, value|
|
192
|
-
|
193
|
-
symbol_key = case _key
|
194
|
-
when String
|
195
|
-
_key.to_sym
|
196
|
-
when Symbol
|
197
|
-
_key
|
198
|
-
else
|
199
|
-
string_key.to_sym
|
200
|
-
end
|
201
|
-
|
177
|
+
key = _key.to_s.to_sym
|
202
178
|
# find nested paths
|
203
|
-
value.is_a?(::Hash) &&
|
204
|
-
if options[:squash]
|
205
|
-
send("#{name}=", {
|
179
|
+
value.is_a?(::Hash) && self.class.attributes.each do |name, options|
|
180
|
+
if (options[:squash] || []).first == key.to_s
|
181
|
+
send("#{name}=", {key => value})
|
206
182
|
end
|
207
183
|
end
|
208
184
|
|
209
|
-
unless ignored_attributes.include?(
|
210
|
-
if
|
211
|
-
|
185
|
+
unless self.class.ignored_attributes.include?(key)
|
186
|
+
if self.class.aliases.has_key?(key)
|
187
|
+
self.class.aliases[key].each do |aliased_key|
|
212
188
|
send("#{aliased_key}=", value)
|
213
189
|
end
|
214
190
|
end
|
215
191
|
|
216
|
-
|
217
|
-
|
218
|
-
|
192
|
+
protected_methods = Cistern::Model.instance_methods - [:service, :identity, :collection]
|
193
|
+
|
194
|
+
if !protected_methods.include?(key) && self.respond_to?("#{key}=", true)
|
195
|
+
send("#{key}=", value)
|
219
196
|
end
|
220
197
|
end
|
221
198
|
end
|
@@ -259,7 +236,7 @@ module Cistern::Attributes
|
|
259
236
|
protected
|
260
237
|
|
261
238
|
def missing_attributes(args)
|
262
|
-
([:
|
239
|
+
([:service] | args).select{|arg| send("#{arg}").nil?}
|
263
240
|
end
|
264
241
|
|
265
242
|
def changed!(attribute, from, to)
|
data/lib/cistern/collection.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
extend Cistern::Attributes::ClassMethods
|
3
|
-
include Cistern::Attributes::InstanceMethods
|
1
|
+
module Cistern::Collection
|
4
2
|
|
5
3
|
BLACKLISTED_ARRAY_METHODS = [
|
6
4
|
:compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
|
@@ -8,15 +6,21 @@ class Cistern::Collection
|
|
8
6
|
:keep_if, :pop, :shift, :delete_at, :compact
|
9
7
|
].to_set # :nodoc:
|
10
8
|
|
11
|
-
|
9
|
+
def self.service_collection(service, klass)
|
10
|
+
plural_name = Cistern::String.underscore(Cistern::String.demodulize(klass.name))
|
12
11
|
|
13
|
-
|
12
|
+
service.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
|
13
|
+
def #{plural_name}(attributes={})
|
14
|
+
#{klass.name}.new(attributes.merge(service: self))
|
15
|
+
end
|
16
|
+
EOS
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@model
|
19
|
+
attr_accessor :records, :loaded, :service
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def model(new_model=nil)
|
23
|
+
@model ||= new_model
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
@@ -75,7 +79,7 @@ class Cistern::Collection
|
|
75
79
|
model.new(
|
76
80
|
{
|
77
81
|
:collection => self,
|
78
|
-
:
|
82
|
+
:service => service,
|
79
83
|
}.merge(attributes)
|
80
84
|
)
|
81
85
|
end
|
data/lib/cistern/formatter.rb
CHANGED
data/lib/cistern/model.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
-
|
2
|
-
extend Cistern::Attributes::ClassMethods
|
1
|
+
module Cistern::Model
|
3
2
|
include Cistern::Attributes::InstanceMethods
|
4
3
|
|
5
|
-
|
4
|
+
def self.included(klass)
|
5
|
+
klass.send(:extend, Cistern::Attributes::ClassMethods)
|
6
|
+
klass.send(:include, Cistern::Attributes::InstanceMethods)
|
7
|
+
end
|
6
8
|
|
7
|
-
|
9
|
+
attr_accessor :collection, :service
|
8
10
|
|
9
11
|
def inspect
|
10
|
-
Cistern.formatter
|
12
|
+
if Cistern.formatter
|
13
|
+
Cistern.formatter.call(self)
|
14
|
+
else
|
15
|
+
"#<#{self.class} #{self.identity}"
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
def initialize(attributes={})
|
@@ -50,15 +56,15 @@ class Cistern::Model
|
|
50
56
|
end
|
51
57
|
end
|
52
58
|
|
53
|
-
def
|
54
|
-
|
59
|
+
def wait_for(timeout = self.service_class.timeout, interval = self.service_class.poll_interval, &block)
|
60
|
+
service_class.wait_for(timeout, interval) { reload && block.call(self) }
|
55
61
|
end
|
56
62
|
|
57
|
-
def wait_for(timeout = self.
|
58
|
-
|
63
|
+
def wait_for!(timeout = self.service_class.timeout, interval = self.service_class.poll_interval, &block)
|
64
|
+
service_class.wait_for!(timeout, interval) { reload && block.call(self) }
|
59
65
|
end
|
60
66
|
|
61
|
-
def
|
62
|
-
service
|
67
|
+
def service_class
|
68
|
+
self.service ? self.service.class : Cistern
|
63
69
|
end
|
64
70
|
end
|
data/lib/cistern/request.rb
CHANGED
@@ -1,41 +1,28 @@
|
|
1
|
-
|
2
|
-
def self.
|
3
|
-
|
4
|
-
end
|
1
|
+
module Cistern::Request
|
2
|
+
def self.service_request(service, klass)
|
3
|
+
request = klass.request_name || Cistern::String.camelize(klass.name)
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Cistern::Request.register(klass, self, @service_method)
|
12
|
-
end
|
5
|
+
service::Mock.module_eval <<-EOS, __FILE__, __LINE__
|
6
|
+
def #{request}(*args)
|
7
|
+
#{klass}.new(self).mock(*args)
|
8
|
+
end
|
9
|
+
EOS
|
13
10
|
|
14
|
-
|
15
|
-
|
11
|
+
service::Real.module_eval <<-EOS, __FILE__, __LINE__
|
12
|
+
def #{request}(*args)
|
13
|
+
#{klass}.new(self).real(*args)
|
14
|
+
end
|
15
|
+
EOS
|
16
16
|
end
|
17
17
|
|
18
|
-
attr_reader :
|
19
|
-
|
20
|
-
alias service connection
|
21
|
-
|
22
|
-
def initialize(connection)
|
23
|
-
@connection = connection
|
24
|
-
end
|
25
|
-
|
26
|
-
def _real(*args, &block)
|
27
|
-
real(*args, &block)
|
28
|
-
end
|
29
|
-
|
30
|
-
def _mock(*args, &block)
|
31
|
-
mock(*args, &block)
|
32
|
-
end
|
18
|
+
attr_reader :service
|
33
19
|
|
34
|
-
def
|
35
|
-
|
20
|
+
def initialize(service)
|
21
|
+
@service = service
|
36
22
|
end
|
37
23
|
|
38
|
-
|
39
|
-
|
24
|
+
module ClassMethods
|
25
|
+
def request_name
|
26
|
+
end
|
40
27
|
end
|
41
28
|
end
|
data/lib/cistern/service.rb
CHANGED
@@ -42,6 +42,44 @@ class Cistern::Service
|
|
42
42
|
def initialize(options={})
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
class Model
|
47
|
+
include Cistern::Model
|
48
|
+
|
49
|
+
def self.service
|
50
|
+
#{klass.name}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Collection
|
55
|
+
include Cistern::Collection
|
56
|
+
|
57
|
+
def self.inherited(klass)
|
58
|
+
klass.extend(Cistern::Attributes::ClassMethods)
|
59
|
+
klass.extend(Cistern::Collection::ClassMethods)
|
60
|
+
klass.include(Cistern::Attributes::InstanceMethods)
|
61
|
+
|
62
|
+
Cistern::Collection.service_collection(service, klass)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.service
|
66
|
+
#{klass.name}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Request
|
71
|
+
include Cistern::Request
|
72
|
+
|
73
|
+
def self.inherited(klass)
|
74
|
+
klass.extend(Cistern::Request::ClassMethods)
|
75
|
+
|
76
|
+
Cistern::Request.service_request(service, klass)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.service
|
80
|
+
#{klass.name}
|
81
|
+
end
|
82
|
+
end
|
45
83
|
EOS
|
46
84
|
|
47
85
|
klass.send(:const_set, :Timeout, Class.new(Cistern::Error))
|
@@ -57,30 +95,6 @@ class Cistern::Service
|
|
57
95
|
klass::Real.timeout_error = klass::Timeout
|
58
96
|
end
|
59
97
|
|
60
|
-
def collection_path(collection_path = nil)
|
61
|
-
if collection_path
|
62
|
-
@collection_path = collection_path
|
63
|
-
else
|
64
|
-
@collection_path
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def model_path(model_path = nil)
|
69
|
-
if model_path
|
70
|
-
@model_path = model_path
|
71
|
-
else
|
72
|
-
@model_path
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def request_path(request_path = nil)
|
77
|
-
if request_path
|
78
|
-
@request_path = request_path
|
79
|
-
else
|
80
|
-
@request_path
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
98
|
def collections
|
85
99
|
@collections ||= []
|
86
100
|
end
|
@@ -109,22 +123,10 @@ class Cistern::Service
|
|
109
123
|
self.recognized_arguments.concat(args)
|
110
124
|
end
|
111
125
|
|
112
|
-
def model(model_name, options={})
|
113
|
-
models << [model_name, options]
|
114
|
-
end
|
115
|
-
|
116
126
|
def mocked_requests
|
117
127
|
@mocked_requests ||= []
|
118
128
|
end
|
119
129
|
|
120
|
-
def request(request_name, options={})
|
121
|
-
requests << [request_name, options]
|
122
|
-
end
|
123
|
-
|
124
|
-
def collection(collection_name, options={})
|
125
|
-
collections << [collection_name, options]
|
126
|
-
end
|
127
|
-
|
128
130
|
def validate_options(options={})
|
129
131
|
required_options = Cistern::Hash.slice(options, *required_arguments)
|
130
132
|
|
@@ -141,91 +143,8 @@ class Cistern::Service
|
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
144
|
-
def setup_requirements
|
145
|
-
@required ||= false
|
146
|
-
unless @required
|
147
|
-
|
148
|
-
# setup models
|
149
|
-
models.each do |model, options|
|
150
|
-
unless options[:require] == false
|
151
|
-
require(options[:require] || File.join(@model_path, model.to_s))
|
152
|
-
end
|
153
|
-
|
154
|
-
class_name = options[:class] || model.to_s.split("_").map(&:capitalize).join
|
155
|
-
singular_name = options[:model] || model.to_s.gsub("/", "_")
|
156
|
-
|
157
|
-
self.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
|
158
|
-
def #{singular_name}(attributes={})
|
159
|
-
#{service}::#{class_name}.new({connection: self}.merge(attributes))
|
160
|
-
end
|
161
|
-
EOS
|
162
|
-
end
|
163
|
-
|
164
|
-
# setup requests
|
165
|
-
requests.each do |request, options|
|
166
|
-
unless (options[:require] == false) || options[:new] || service::Real.method_defined?(request.to_s)
|
167
|
-
require(options[:require] || File.join(@request_path, request.to_s))
|
168
|
-
end
|
169
|
-
|
170
|
-
if options[:new]
|
171
|
-
klass = options.fetch(:class)
|
172
|
-
method = options.fetch(:method)
|
173
|
-
|
174
|
-
unless klass.instance_methods.include?(:mock)
|
175
|
-
klass.class_eval <<-EOS, __FILE__, __LINE__
|
176
|
-
def #{method}(*args)
|
177
|
-
Cistern::Mock.not_implemented(request)
|
178
|
-
end
|
179
|
-
EOS
|
180
|
-
end
|
181
|
-
|
182
|
-
service::Real.module_eval <<-EOS, __FILE__, __LINE__
|
183
|
-
def #{method}(*args)
|
184
|
-
#{klass}.new(self)._real(*args)
|
185
|
-
end
|
186
|
-
EOS
|
187
|
-
|
188
|
-
service::Mock.module_eval <<-EOS, __FILE__, __LINE__
|
189
|
-
def #{method}(*args)
|
190
|
-
#{klass}.new(self)._mock(*args)
|
191
|
-
end
|
192
|
-
EOS
|
193
|
-
elsif service::Mock.method_defined?(request)
|
194
|
-
mocked_requests << request
|
195
|
-
else
|
196
|
-
service::Mock.module_eval <<-EOS, __FILE__, __LINE__
|
197
|
-
def #{request}(*args)
|
198
|
-
Cistern::Mock.not_implemented(request)
|
199
|
-
end
|
200
|
-
EOS
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
# setup collections
|
205
|
-
collections.each do |collection, options|
|
206
|
-
unless false == options[:require]
|
207
|
-
require(options[:require] || File.join(@collection_path || @model_path, collection.to_s))
|
208
|
-
end
|
209
|
-
|
210
|
-
class_name = collection.to_s.
|
211
|
-
split("/").map(&:capitalize).join("::").split("_").
|
212
|
-
map { |s| "#{s[0].upcase}#{s[1..-1]}" }.join
|
213
|
-
plural_name = options[:collection] || collection.to_s.gsub("/", "_")
|
214
|
-
|
215
|
-
self.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
|
216
|
-
def #{plural_name}(attributes={})
|
217
|
-
#{service}::#{class_name}.new({connection: self}.merge(attributes))
|
218
|
-
end
|
219
|
-
EOS
|
220
|
-
end
|
221
|
-
|
222
|
-
@required = true
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
146
|
def new(options={})
|
227
147
|
validate_options(options)
|
228
|
-
setup_requirements
|
229
148
|
|
230
149
|
self.const_get(self.mocking? ? :Mock : :Real).new(options)
|
231
150
|
end
|
data/lib/cistern/singular.rb
CHANGED
data/lib/cistern/string.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
class Cistern::String
|
2
|
-
|
2
|
+
def self.camelize(string)
|
3
|
+
string.gsub(/[A-Z]+/) { |w| "_#{w.downcase}" }.gsub(/^_/, "")
|
4
|
+
end
|
5
|
+
|
6
|
+
# File activesupport/lib/active_support/inflector/methods.rb, line 90
|
7
|
+
def self.underscore(camel_cased_word)
|
8
|
+
word = camel_cased_word.to_s.gsub('::', '/')
|
9
|
+
#word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
|
10
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
11
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
12
|
+
word.tr!("-", "_")
|
13
|
+
word.downcase!
|
14
|
+
word
|
15
|
+
end
|
16
|
+
|
17
|
+
# File activesupport/lib/active_support/inflector/methods.rb, line 168
|
3
18
|
def self.demodulize(path)
|
4
19
|
path = path.to_s
|
5
20
|
if i = path.rindex('::')
|
@@ -9,15 +24,8 @@ class Cistern::String
|
|
9
24
|
end
|
10
25
|
end
|
11
26
|
|
12
|
-
#
|
13
|
-
def self.
|
14
|
-
|
15
|
-
word = camel_cased_word.to_s.gsub(/::/, '/')
|
16
|
-
#word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'}#{$2.downcase}" }
|
17
|
-
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
18
|
-
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
19
|
-
word.tr!("-", "_")
|
20
|
-
word.downcase!
|
21
|
-
word
|
27
|
+
# @todo omg so bad
|
28
|
+
def self.pluralize(string)
|
29
|
+
"#{string}s"
|
22
30
|
end
|
23
31
|
end
|
data/lib/cistern/version.rb
CHANGED
data/spec/collection_spec.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Cistern::Collection" do
|
4
|
-
|
5
|
-
class SampleCollectionModel < Cistern::Model
|
4
|
+
class SampleCollectionModel < Sample::Model
|
6
5
|
identity :id
|
7
6
|
attribute :name
|
8
7
|
end
|
9
8
|
|
10
|
-
class SampleCollection <
|
9
|
+
class SampleCollection < Sample::Collection
|
11
10
|
model SampleCollectionModel
|
12
11
|
|
13
12
|
def all
|
data/spec/dirty_spec.rb
CHANGED
data/spec/formatter_spec.rb
CHANGED
@@ -1,75 +1,41 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
class Anon < Cistern::Model
|
9
|
-
attribute :chan
|
10
|
-
end
|
11
|
-
|
12
|
-
class Inspectors < Cistern::Collection
|
13
|
-
model Inspector
|
14
|
-
|
15
|
-
def all(options={})
|
16
|
-
merge_attributes(options)
|
17
|
-
self.load([{id: 1, name: "2"},{id: 3, name: "4"}])
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe Cistern::Formatter::Default do
|
22
|
-
before { Cistern.formatter = described_class }
|
23
|
-
|
24
|
-
it "formats a model" do
|
25
|
-
expect(
|
26
|
-
Inspector.new(id: 1, name: "name").inspect
|
27
|
-
).to match(
|
28
|
-
/<Inspector:0x[a-z0-9]+> {:id=>1, :name=>\"name\"}/
|
29
|
-
)
|
30
|
-
|
31
|
-
Anon.inspect
|
32
|
-
end
|
33
|
-
|
34
|
-
it "formats a collection" do
|
35
|
-
expect(
|
36
|
-
Inspectors.new.all.inspect
|
37
|
-
).to match(
|
38
|
-
/<Inspectors:0x[a-z0-9]+> {} records/
|
39
|
-
)
|
3
|
+
describe "#inspect" do
|
4
|
+
class Inspector < Sample::Model
|
5
|
+
identity :id
|
6
|
+
attribute :name
|
40
7
|
end
|
41
|
-
end
|
42
8
|
|
43
|
-
|
44
|
-
|
9
|
+
class Inspectors < Sample::Collection
|
10
|
+
model Inspector
|
45
11
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
/(?x-mi:\#<Inspector:0x[0-9a-f]+>\ {\n\ \ \ \ \ \ :id\x1B\[0;37m\ =>\ \x1B\[0m\x1B\[1;34m1\x1B\[0m,\n\ \ \ \ :name\x1B\[0;37m\ =>\ \x1B\[0m\x1B\[0;33m"name"\x1B\[0m\n})/
|
51
|
-
)
|
12
|
+
def all(options={})
|
13
|
+
merge_attributes(options)
|
14
|
+
self.load([{id: 1, name: "2"},{id: 3, name: "4"}])
|
15
|
+
end
|
52
16
|
end
|
53
17
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
18
|
+
describe "Cistern::Model" do
|
19
|
+
it "should use awesome_print" do
|
20
|
+
Cistern.formatter = Cistern::Formatter::AwesomePrint
|
58
21
|
|
59
|
-
|
60
|
-
|
22
|
+
Inspector.new(id: 1, name: "name").inspect.match /(?x-mi:\#<Inspector:0x[0-9a-f]+>\ {\n\ \ \ \ \ \ :id\x1B\[0;37m\ =>\ \x1B\[0m\x1B\[1;34m1\x1B\[0m,\n\ \ \ \ :name\x1B\[0;37m\ =>\ \x1B\[0m\x1B\[0;33m"name"\x1B\[0m\n})/
|
23
|
+
end
|
61
24
|
|
62
|
-
|
63
|
-
|
25
|
+
it "should use formatador" do
|
26
|
+
Cistern.formatter = Cistern::Formatter::Formatador
|
64
27
|
|
65
|
-
|
28
|
+
expect(Inspector.new(id: 1, name: "name").inspect).to eq(%q{ <Inspector
|
66
29
|
id=1,
|
67
30
|
name="name"
|
68
31
|
>})
|
32
|
+
end
|
69
33
|
end
|
70
34
|
|
71
|
-
|
72
|
-
|
35
|
+
describe "Cistern::Collection" do
|
36
|
+
it "should use formatador" do
|
37
|
+
Cistern.formatter = Cistern::Formatter::Formatador
|
38
|
+
expect(Inspectors.new.all.inspect).to eq(%q{ <Inspectors
|
73
39
|
[
|
74
40
|
<Inspector
|
75
41
|
id=1,
|
@@ -81,5 +47,11 @@ describe Cistern::Formatter::Formatador do
|
|
81
47
|
>
|
82
48
|
]
|
83
49
|
>})
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should use awesome_print" do
|
53
|
+
Cistern.formatter = Cistern::Formatter::AwesomePrint
|
54
|
+
expect(Inspectors.new.all.inspect).to match(/Inspectors\s+{.*}$/m) # close enough
|
55
|
+
end
|
84
56
|
end
|
85
57
|
end
|
data/spec/mock_data_spec.rb
CHANGED
@@ -1,37 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'mock data' do
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
class Real
|
9
|
-
def diagnosis(options={})
|
10
|
-
end
|
4
|
+
class Diagnosis < Sample::Request
|
5
|
+
def real(diagnosis)
|
6
|
+
end
|
11
7
|
|
12
|
-
|
13
|
-
|
8
|
+
def mock(diagnosis)
|
9
|
+
service.data.store(:diagnosis, service.data.fetch(:diagnosis) + [diagnosis])
|
14
10
|
end
|
11
|
+
end
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
self.data.store(:diagnosis, self.data.fetch(:diagnosis) + [diagnosis])
|
20
|
-
end
|
13
|
+
class Treat < Sample::Request
|
14
|
+
def real(treatment)
|
15
|
+
end
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
17
|
+
def mock(treatment)
|
18
|
+
service.data[:treatments] += [treatment]
|
25
19
|
end
|
26
20
|
end
|
27
21
|
|
28
22
|
shared_examples "mock_data#backend" do |backend, options|
|
29
23
|
it "should store mock data" do
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
Sample.mock!
|
25
|
+
Sample::Mock.store_in(backend, options)
|
26
|
+
Sample.reset!
|
33
27
|
|
34
|
-
p =
|
28
|
+
p = Sample.new
|
35
29
|
p.diagnosis("sick")
|
36
30
|
expect(p.data[:diagnosis]).to eq(["sick"])
|
37
31
|
|
@@ -42,7 +36,7 @@ describe 'mock data' do
|
|
42
36
|
p.treat("healthy")
|
43
37
|
expect(p.data[:treatments]).to eq(["healthy"])
|
44
38
|
|
45
|
-
|
39
|
+
Sample.reset!
|
46
40
|
|
47
41
|
expect(p.data[:treatments]).to eq([])
|
48
42
|
end
|
data/spec/model_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Cistern::Model" do
|
4
4
|
describe "#update" do
|
5
|
-
class UpdateSpec <
|
5
|
+
class UpdateSpec < Sample::Model
|
6
6
|
identity :id
|
7
7
|
attribute :name
|
8
8
|
attribute :properties
|
@@ -21,7 +21,7 @@ describe "Cistern::Model" do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should duplicate a model" do
|
24
|
-
class DupSpec <
|
24
|
+
class DupSpec < Sample::Model
|
25
25
|
identity :id
|
26
26
|
attribute :name
|
27
27
|
attribute :properties
|
@@ -38,7 +38,7 @@ describe "Cistern::Model" do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
context "attribute parsing" do
|
41
|
-
class TypeSpec <
|
41
|
+
class TypeSpec < Sample::Model
|
42
42
|
identity :id
|
43
43
|
attribute :name, type: :string
|
44
44
|
attribute :created_at, type: :time
|
@@ -155,17 +155,17 @@ describe "Cistern::Model" do
|
|
155
155
|
|
156
156
|
describe "#requires" do
|
157
157
|
it "should raise if attribute not provided" do
|
158
|
-
expect { TypeSpec.new({"
|
158
|
+
expect { TypeSpec.new({"service" => "fake", "something" => {"id" => "12"}}).save }.to raise_exception(ArgumentError)
|
159
159
|
end
|
160
160
|
|
161
161
|
it "should raise if attribute is provided and is nil" do
|
162
|
-
expect { TypeSpec.new({"
|
162
|
+
expect { TypeSpec.new({"service" => "fake", "custom" => nil}).save }.to raise_exception(ArgumentError)
|
163
163
|
end
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
167
167
|
context "attribute coverage info collecting", :coverage do
|
168
|
-
class CoverageSpec <
|
168
|
+
class CoverageSpec < Sample::Model
|
169
169
|
identity :id
|
170
170
|
|
171
171
|
attribute :used, type: :string
|
data/spec/request_spec.rb
CHANGED
@@ -1,29 +1,37 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Cistern do
|
3
|
+
describe "Cistern::Request" do
|
4
4
|
class SampleService < Cistern::Service
|
5
|
+
recognizes :key
|
6
|
+
|
5
7
|
class Real
|
8
|
+
attr_reader :service_args
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
@service_args = args
|
12
|
+
end
|
6
13
|
end
|
7
14
|
end
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
|
16
|
+
# @todo Sample::Service.request
|
17
|
+
class ListSamples < SampleService::Request
|
18
|
+
# @todo name
|
19
|
+
# name :list_all_samples
|
12
20
|
def real(*args)
|
13
|
-
args
|
21
|
+
service.service_args + args + ["real"]
|
14
22
|
end
|
15
|
-
end
|
16
23
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
def real(*args)
|
21
|
-
{"samples" => args}
|
24
|
+
def mock(*args)
|
25
|
+
args + ["mock"]
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
25
|
-
it "
|
26
|
-
expect(SampleService.new.
|
27
|
-
expect(SampleService.new.list_samples("
|
29
|
+
it "should execute a new-style request" do
|
30
|
+
expect(SampleService.new.list_samples("sample1")).to eq([{}, "sample1", "real"])
|
31
|
+
expect(SampleService::Real.new.list_samples("sample2")).to eq(["sample2", "real"])
|
32
|
+
expect(SampleService::Mock.new.list_samples("sample3")).to eq(["sample3", "mock"])
|
33
|
+
|
34
|
+
# service access
|
35
|
+
expect(SampleService.new(:key => "value").list_samples("stat")).to eq([{:key => "value"}, "stat", "real"])
|
28
36
|
end
|
29
37
|
end
|
File without changes
|
data/spec/singular_spec.rb
CHANGED
@@ -6,8 +6,8 @@ describe "Cistern::Singular" do
|
|
6
6
|
attribute :count, type: :number
|
7
7
|
|
8
8
|
def fetch_attributes
|
9
|
-
#test that initialize waits for
|
10
|
-
raise "missing
|
9
|
+
#test that initialize waits for service to be defined
|
10
|
+
raise "missing service" unless service
|
11
11
|
|
12
12
|
@counter ||= 0
|
13
13
|
@counter += 1
|
@@ -16,11 +16,11 @@ describe "Cistern::Singular" do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should work" do
|
19
|
-
expect(SampleSingular.new(
|
19
|
+
expect(SampleSingular.new(service: :fake).name).to eq("amazing")
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should reload" do
|
23
|
-
singular = SampleSingular.new(
|
23
|
+
singular = SampleSingular.new(service: :fake)
|
24
24
|
old_count = singular.count
|
25
25
|
expect(singular.count).to eq(old_count)
|
26
26
|
expect(singular.reload.count).to be > old_count
|
data/spec/spec_helper.rb
CHANGED
@@ -4,10 +4,13 @@ if ENV["TRAVIS"]
|
|
4
4
|
end
|
5
5
|
|
6
6
|
require File.expand_path('../../lib/cistern', __FILE__)
|
7
|
+
Dir[File.expand_path("../{support,shared,matchers,fixtures}/*.rb", __FILE__)].each{|f| require(f)}
|
7
8
|
|
8
9
|
Bundler.require(:test)
|
9
10
|
|
10
11
|
RSpec.configure do |c|
|
12
|
+
c.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
|
11
14
|
if Kernel.respond_to?(:caller_locations)
|
12
15
|
require File.expand_path('../../lib/cistern/coverage', __FILE__)
|
13
16
|
else
|
@@ -0,0 +1 @@
|
|
1
|
+
class Sample < Cistern::Service; end
|
data/spec/wait_for_spec.rb
CHANGED
@@ -1,23 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
class
|
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
|
3
|
+
class WaitForModel < Sample::Model
|
14
4
|
identity :id
|
15
5
|
|
16
6
|
attribute :name
|
17
7
|
end
|
18
8
|
|
19
|
-
class
|
20
|
-
model
|
9
|
+
class WaitForModels < Sample::Collection
|
10
|
+
model WaitForModel
|
21
11
|
|
22
12
|
def get(identity)
|
23
13
|
self
|
@@ -37,17 +27,17 @@ describe 'Cistern#wait_for!' do
|
|
37
27
|
end
|
38
28
|
|
39
29
|
describe 'Cistern::Model#wait_for!' do
|
40
|
-
let(:service) {
|
30
|
+
let(:service) { Sample.new }
|
41
31
|
let(:model) { service.wait_for_models.new(identity: 1) }
|
42
32
|
|
43
33
|
it "should raise if timeout exceeded" do
|
44
|
-
expect { model.wait_for!(0, 0) { false } }.to raise_exception(
|
34
|
+
expect { model.wait_for!(0, 0) { false } }.to raise_exception(Sample::Timeout)
|
45
35
|
end
|
46
36
|
end
|
47
37
|
|
48
38
|
|
49
39
|
describe "WaitForModel#timeout" do
|
50
|
-
let(:service) {
|
40
|
+
let(:service) { Sample.new }
|
51
41
|
let(:model) { service.wait_for_models.new(identity: 1) }
|
52
42
|
|
53
43
|
it "should use service-specific timeout in #wait_for" do
|
@@ -59,7 +49,7 @@ describe "WaitForModel#timeout" do
|
|
59
49
|
timeout(2) do
|
60
50
|
expect do
|
61
51
|
model.wait_for! { sleep(0.2); elapsed += 0.2; elapsed > 0.2 }
|
62
|
-
end.to raise_exception(
|
52
|
+
end.to raise_exception(Sample::Timeout)
|
63
53
|
end
|
64
54
|
end
|
65
55
|
|
@@ -72,7 +62,7 @@ describe "WaitForModel#timeout" do
|
|
72
62
|
timeout(2) do
|
73
63
|
expect do
|
74
64
|
model.wait_for!(0.1) { sleep(0.2); elapsed += 0.2; elapsed > 0.2 }
|
75
|
-
end.to raise_exception(
|
65
|
+
end.to raise_exception(Sample::Timeout)
|
76
66
|
end
|
77
67
|
end
|
78
68
|
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: 1.0.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Lane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: API client framework extracted from Fog
|
14
14
|
email:
|
@@ -17,7 +17,6 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
-
- ".gemrelease"
|
21
20
|
- ".gitignore"
|
22
21
|
- ".travis.yml"
|
23
22
|
- Gemfile
|
@@ -36,7 +35,6 @@ files:
|
|
36
35
|
- lib/cistern/data/redis.rb
|
37
36
|
- lib/cistern/formatter.rb
|
38
37
|
- lib/cistern/formatter/awesome_print.rb
|
39
|
-
- lib/cistern/formatter/default.rb
|
40
38
|
- lib/cistern/formatter/formatador.rb
|
41
39
|
- lib/cistern/hash.rb
|
42
40
|
- lib/cistern/mock.rb
|
@@ -55,8 +53,10 @@ files:
|
|
55
53
|
- spec/mock_data_spec.rb
|
56
54
|
- spec/model_spec.rb
|
57
55
|
- spec/request_spec.rb
|
56
|
+
- spec/service_spec.rb
|
58
57
|
- spec/singular_spec.rb
|
59
58
|
- spec/spec_helper.rb
|
59
|
+
- spec/support/service.rb
|
60
60
|
- spec/wait_for_spec.rb
|
61
61
|
homepage: http://joshualane.com/cistern
|
62
62
|
licenses:
|
@@ -73,12 +73,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
73
|
version: '0'
|
74
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
75
|
requirements:
|
76
|
-
- - "
|
76
|
+
- - ">"
|
77
77
|
- !ruby/object:Gem::Version
|
78
|
-
version:
|
78
|
+
version: 1.3.1
|
79
79
|
requirements: []
|
80
80
|
rubyforge_project:
|
81
|
-
rubygems_version: 2.
|
81
|
+
rubygems_version: 2.2.2
|
82
82
|
signing_key:
|
83
83
|
specification_version: 4
|
84
84
|
summary: API client framework
|
@@ -90,6 +90,9 @@ test_files:
|
|
90
90
|
- spec/mock_data_spec.rb
|
91
91
|
- spec/model_spec.rb
|
92
92
|
- spec/request_spec.rb
|
93
|
+
- spec/service_spec.rb
|
93
94
|
- spec/singular_spec.rb
|
94
95
|
- spec/spec_helper.rb
|
96
|
+
- spec/support/service.rb
|
95
97
|
- spec/wait_for_spec.rb
|
98
|
+
has_rdoc:
|
data/.gemrelease
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module Cistern::Formatter::Default
|
2
|
-
class << self
|
3
|
-
def call(object)
|
4
|
-
case object
|
5
|
-
when Cistern::Collection
|
6
|
-
format_collection(object)
|
7
|
-
when Cistern::Model
|
8
|
-
format_model(object)
|
9
|
-
else
|
10
|
-
object.to_s
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def format_model(model)
|
15
|
-
"#{model.to_s} #{model.attributes.inspect}"
|
16
|
-
end
|
17
|
-
|
18
|
-
def format_collection(collection)
|
19
|
-
"#{collection.to_s} #{collection.attributes.inspect} records=[#{collection.records.map { |m| format_model(m) }.join(", ")}]"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|