couchpillow 0.4.3 → 0.4.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8766ff55f2a06b3e6a73581eb02a5add86407868
4
- data.tar.gz: e4b22c670eefa9f68deb5f4bf5de5db8dfa03613
3
+ metadata.gz: ccd762815a2494ec8a42b99e4d8feebf29d77fac
4
+ data.tar.gz: 9b0f6be497aa1a4cc2f0647ff0179953938a4be9
5
5
  SHA512:
6
- metadata.gz: d8fd8cec7c43b888807d46a1faac51ed83087b1d7a2af8a85ebff8ec517b8937a8b26bf8cb76bc06213eec4c711d4f51de41190b7c4abde78c7c21acce6cd166
7
- data.tar.gz: c793792ad45b53674963350e3b740d0b3310e1a10a5b8379a387cee46692c9d1e21d79625e1c44ef13054c9071c9bf485fb218d050a641bc46928d4393bd344d
6
+ metadata.gz: 8771a637242a813b851b683c96fd12b8064c923ec0aafa86a491e40bf3e4d383760e4f428e756ad7518840a2990f07f5b3ff55dc4b605d7475c3a1aa3ac2e9a8
7
+ data.tar.gz: b4dd50c6540f695ba3f04ecf73299ce0f517563ea9843b237dd4132c49ee8944c60c81ee305afac6d3a9c2351a65b4c40ebbabaf56b3e7179281902625c59996
data/README.markdown CHANGED
@@ -1,14 +1,13 @@
1
1
  # CouchPillow
2
2
 
3
- Light and comfortable Document integrity tool for Couchbase Server.
4
-
5
3
  CouchPillow is a document integrity tool for Couchbase Documents to make sure
6
4
  that all current and existing documents can work nicely with the current code.
7
5
 
8
6
  CouchPillow separates itself from the database drivers, making it light and
9
7
  independent from the implementation. Although it is initially designed to work
10
- with Couchbase Server, one can easily extend this to other NoSQL drivers as
11
- long as they `respond_to?` the `set`, `delete`, `replace`, and `get` methods.
8
+ with Couchbase Server, it can be easily extended to other NoSQL databases, by
9
+ creating a driver that `respond_to?` the `set`, `delete`, `replace`, and `get`
10
+ methods.
12
11
 
13
12
 
14
13
  ## Features
@@ -29,7 +28,7 @@ long as they `respond_to?` the `set`, `delete`, `replace`, and `get` methods.
29
28
 
30
29
  class MyDocument < CouchPillow::Document
31
30
  type :my_document
32
- attribute(:stuff)
31
+ attribute :stuff
33
32
  end
34
33
 
35
34
  CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
@@ -39,9 +38,9 @@ long as they `respond_to?` the `set`, `delete`, `replace`, and `get` methods.
39
38
  # {
40
39
  # '_id': 'my_document::fb579b265cc005c47ff420a5c2a15d2b',
41
40
  # '_type': 'my_document',
42
- # 'stuff': 'hello',
43
41
  # '_created_at': '2014-07-04 00:00:00 UTC'
44
42
  # '_updated_at': '2014-07-04 00:00:00 UTC'
43
+ # 'stuff': 'hello',
45
44
  # }
46
45
 
47
46
 
@@ -50,11 +49,11 @@ Retrieving Documents:
50
49
  doc = MyDocument.get('my_document::fb579b265cc005c47ff420a5c2a15d2b')
51
50
  doc.stuff # 'hello'
52
51
 
53
- Specifying custom id if the auto-generated id is too confusing:
52
+ Specifying custom id:
54
53
 
55
54
  class User < CouchPillow::Document
56
55
  type :user
57
- attribute(:email)
56
+ attribute :email
58
57
  end
59
58
 
60
59
  CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
@@ -65,9 +64,9 @@ Specifying custom id if the auto-generated id is too confusing:
65
64
  # {
66
65
  # '_id': '123',
67
66
  # '_type': 'user',
67
+ # '_created_at': '2014-07-04 00:00:00 UTC'
68
+ # '_updated_at': '2014-07-04 00:00:00 UTC'
68
69
  # 'email': 'john@email.com',
69
- # 'created_at': '2014-07-04 00:00:00 UTC'
70
- # 'updated_at': '2014-07-04 00:00:00 UTC'
71
70
  # }
72
71
 
73
72
  ### Attributes
@@ -75,12 +74,15 @@ Specifying custom id if the auto-generated id is too confusing:
75
74
  Using Attribute Directives:
76
75
 
77
76
  class User < CouchPillow::Document
78
- type :user
79
- attribute(:email)
80
- .required
77
+ type :user
81
78
 
82
- attribute(:first_name)
83
- .type(String)
79
+ attribute :email do
80
+ required
81
+ end
82
+
83
+ attribute :first_name do
84
+ type String
85
+ end
84
86
  end
85
87
 
86
88
  CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
@@ -102,19 +104,87 @@ List of Attribute Directives:
102
104
 
103
105
  * `auto_convert`
104
106
 
105
- Enables auto-conversion to the specified type. This gets ignored if `type`
106
- directive is not specified.
107
+ Enables auto-conversion to the specified type. This gets ignored if `type`
108
+ directive is not specified.
107
109
 
108
110
  * `default(&block)`
109
111
 
110
- Runs the block to set the default value for this attribute, if it's missing
111
- or nil.
112
+ Runs the block to set the default value for this attribute, if it's missing
113
+ or nil. This is triggered on document creation and save.
112
114
 
113
115
  * `content(&block)`
114
116
 
115
- Custom validation method to check the value of the attribute. This is useful
116
- in cases where you only want certain values to be stored (e.g a number
117
- between 1-10 only)
117
+ Custom validation method to check the value of the attribute. This is useful
118
+ in cases where you only want certain values to be stored (e.g a number
119
+ between 1-10 only)
120
+
121
+
122
+ ### TTL Support
123
+
124
+ TTL is supported by passing options when saving the document. Using the above
125
+ example:
126
+
127
+ doc = User.new( { :email => 'john@email.com' } )
128
+ doc.save! ttl: 3600
129
+
130
+
131
+ ### Multiple DB Connections Support
132
+
133
+ If you have a model that's accessing a different Couchbase bucket, or a
134
+ different Couchbase DB cluster entirely, you can specify the connections
135
+ via the `db` directive. Example:
136
+
137
+ CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
138
+ memcached = Couchbase.connect( bucket: 'mymemcache_bucket', host: '128.128.128.128' )
139
+
140
+ class User < CouchPillow::Document
141
+ type :user
142
+
143
+ attribute :first_name do
144
+ type String
145
+ end
146
+ end
147
+
148
+ class Token < CouchPillow::Document
149
+ type :token
150
+
151
+ db memcached
152
+
153
+ attribute :token do
154
+ type String
155
+ end
156
+ end
157
+
158
+ doc = User.new( { :first_name => 'John' } )
159
+ doc.save! # This gets saved to the localhost/bucket
160
+
161
+ token = Token.new( token: SecureRandom.uuid )
162
+ doc.save! # This gets saved to the 128.128.128.128/mymemcache_bucket
163
+
164
+
165
+ You can also specify multiple `db` directives. The first time the `db`
166
+ directive is called, it sets that connection as the primary connection, as the
167
+ above example shows. Any subsequent calls will insert that DB connection as
168
+ secondary connections, which will only be trigger on write (`save!`, `update!`,
169
+ and `delete!`).
170
+
171
+
172
+ class Token < CouchPillow::Document
173
+ type :token
174
+
175
+ db primary_connection
176
+ db migration
177
+ db backup
178
+
179
+ attribute :token do
180
+ type String
181
+ end
182
+ end
183
+
184
+
185
+ This can be useful as part of a migration process where you want to save
186
+ incoming data to another cluster while keeping the old one active.
187
+
118
188
 
119
189
 
120
190
  ### Migration
@@ -124,7 +194,7 @@ after a migration.
124
194
 
125
195
  class User < CouchPillow::Document
126
196
  rename :username, :nickname
127
- attribute(:nickname)
197
+ attribute :nickname
128
198
  end
129
199
  u = User.new( { :username => 'jdoe' } )
130
200
  u.nickname # 'jdoe'
@@ -138,11 +208,11 @@ as it reads them.
138
208
 
139
209
  ## Design Docs and Views
140
210
 
141
- What about Design Docs and Views? They are outside the scope of CouchPillow.
211
+ Design Docs and Views are outside the scope of CouchPillow.
142
212
  However, given a design doc named `my_design_doc` and a View named `by_email`,
143
213
  that returns documents as values, you can easily use it like this:
144
214
 
145
215
  CouchPillow.db.design_docs['my_design_doc'].
146
- by_email(:body => { :key => 'john@email.com' }).map do |v|
147
- new User(v.doc, v.id)
216
+ by_email(:key => 'john@email.com').map do |v|
217
+ User.new(v.doc, v.id)
148
218
  end
data/couchpillow.gemspec CHANGED
@@ -14,6 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.test_files = `git ls-files -- {test}/*`.split("\n")
15
15
  s.require_paths = ["lib"]
16
16
 
17
+ s.required_ruby_version = '~> 2.0'
18
+
19
+ s.add_runtime_dependency 'celluloid', '~> 0.16'
20
+
17
21
  s.add_development_dependency 'minitest', '~> 5.3'
18
22
  s.add_development_dependency 'mocha', '~> 1.1'
19
23
  s.add_development_dependency 'rake', '~> 10.3'
@@ -8,21 +8,25 @@ module CouchPillow
8
8
 
9
9
  RESERVED_KEYS = %i[_id _type _created_at _updated_at]
10
10
 
11
- DEFAULT_TYPE = "default".freeze
11
+ DEFAULT_TYPE = "couchpillow".freeze
12
12
 
13
13
 
14
- attribute(:_created_at)
15
- .required
16
- .type(Time).auto_convert
17
- .default { Time.now.utc }
14
+ attribute :_created_at do
15
+ required
16
+ type Time
17
+ auto_convert
18
+ default { Time.now.utc }
19
+ end
18
20
 
19
- attribute(:_updated_at)
20
- .required
21
- .type(Time).auto_convert
22
- .default { Time.now.utc }
21
+ attribute :_updated_at do
22
+ required
23
+ type Time
24
+ auto_convert
25
+ default { Time.now.utc }
26
+ end
23
27
 
24
28
 
25
- def initialize hash = {}, id = "#{self.class._type}::#{SecureRandom.hex}"
29
+ def initialize hash = {}, id = "#{self.class.doc_type}::#{SecureRandom.hex}"
26
30
  @data = self.class.symbolize(hash)
27
31
 
28
32
  @id = id
@@ -30,6 +34,8 @@ module CouchPillow
30
34
  @data[:_created_at] ||= time
31
35
  @data[:_updated_at] = time
32
36
 
37
+ @futures = []
38
+
33
39
  rename!
34
40
  whitelist!
35
41
  assign_defaults!
@@ -47,13 +53,6 @@ module CouchPillow
47
53
  end
48
54
 
49
55
 
50
- # @private
51
- #
52
- def timestamp!
53
- @data[:_updated_at] = Time.now.utc
54
- end
55
-
56
-
57
56
  # Save this document to the server
58
57
  #
59
58
  def save! opts = {}
@@ -62,16 +61,38 @@ module CouchPillow
62
61
  timestamp!
63
62
  validate!
64
63
  to_save = @data.merge({
65
- :_type => self.class._type
64
+ :_type => self.class.doc_type
66
65
  })
67
- CouchPillow.db.set(@id, to_save, opts)
66
+
67
+ # write to all connections
68
+ result = self.class.default_db.set(@id, to_save, opts)
69
+
70
+ unless self.class.secondary_dbs.empty?
71
+ @futures << Celluloid::Future.new do
72
+ self.class.secondary_dbs.each do |db|
73
+ db.set(@id, to_save, opts)
74
+ end
75
+ end
76
+ end
77
+
78
+ result
68
79
  end
69
80
 
70
81
 
71
82
  # Delete this document from the server.
72
83
  #
73
84
  def delete!
74
- CouchPillow.db.delete @id
85
+ result = self.class.default_db.delete @id
86
+
87
+ unless self.class.secondary_dbs.empty?
88
+ @futures << Celluloid::Future.new do
89
+ self.class.secondary_dbs.each do |db|
90
+ db.delete @id
91
+ end
92
+ end
93
+ end
94
+
95
+ result
75
96
  end
76
97
 
77
98
 
@@ -84,9 +105,20 @@ module CouchPillow
84
105
  timestamp!
85
106
  validate!
86
107
  to_save = @data.merge({
87
- :_type => self.class._type
108
+ :_type => self.class.doc_type
88
109
  })
89
- CouchPillow.db.replace @id, to_save
110
+
111
+ result = self.class.default_db.replace @id, to_save
112
+
113
+ unless self.class.secondary_dbs.empty?
114
+ @futures << Celluloid::Future.new do
115
+ self.class.secondary_dbs.each do |db|
116
+ db.replace @id, to_save
117
+ end
118
+ end
119
+ end
120
+
121
+ result
90
122
  end
91
123
 
92
124
 
@@ -100,21 +132,50 @@ module CouchPillow
100
132
  end
101
133
  rename!
102
134
  whitelist!
135
+ auto_convert!
103
136
  end
104
137
 
105
138
 
139
+ # Check if this Document has the key
140
+ #
106
141
  def has? key
107
142
  @data.has_key?(key)
108
143
  end
109
144
 
110
145
 
146
+ # Convert this Document to a JSON string
147
+ #
111
148
  def to_json *a
112
149
  to_hash.to_json(*a)
113
150
  end
114
151
 
115
152
 
153
+ # Convert this Document to a Hash
154
+ #
116
155
  def to_hash
117
- { :_id => @id, :_type => self.class._type }.merge!(@data)
156
+ { :_id => @id, :_type => self.class.doc_type }.merge!(@data)
157
+ end
158
+
159
+
160
+ # Helper to get the type of this Document.
161
+ # Can't really name this `type`. Need to avoid name conflict with Ruby's own `type` method.
162
+ #
163
+ def doc_type
164
+ self.class.doc_type
165
+ end
166
+
167
+
168
+ # Blocks until all pending tasks has completed.
169
+ # Returns the result of those tasks in an array.
170
+ #
171
+ def wait
172
+ result = []
173
+ until @futures.empty?
174
+ f = @futures.shift
175
+ result << f.value
176
+ end
177
+
178
+ result
118
179
  end
119
180
 
120
181
 
@@ -138,7 +199,7 @@ module CouchPillow
138
199
  end
139
200
 
140
201
 
141
- # Assign default value
202
+ # Assign default values.
142
203
  #
143
204
  def assign_defaults!
144
205
  self.class.attributes.each do |k, attr|
@@ -157,13 +218,12 @@ module CouchPillow
157
218
 
158
219
 
159
220
  # Go through each attribute, and validate the values.
160
- # Validation also perform auto-conversion if auto-conversion is enabled
161
- # for that attribute.
221
+ # Validation also perform auto-conversion if auto-conversion is enabled for that attribute.
162
222
  #
163
223
  def validate!
164
224
  self.class.attributes.each do |k, attr|
165
225
  if has?(k)
166
- @data[k] = attr.validate(@data[k]) if has?(k)
226
+ @data[k] = attr.validate(@data[k])
167
227
  else
168
228
  @data[k] = attr.trigger_default_directive if attr.has_default?
169
229
  raise ValidationError, "Attribute '#{k}' is required" if attr.required? && !has?(k)
@@ -179,24 +239,14 @@ module CouchPillow
179
239
  end
180
240
 
181
241
 
182
- def _type
183
- self.class._type
184
- end
185
-
186
-
187
- def _id
188
- @id
189
- end
190
-
191
-
192
242
  # Get a Document given an id.
193
243
  #
194
244
  # @return nil if not found or Document is of a different type.
195
245
  #
196
246
  def self.get id
197
- result = CouchPillow.db.get(id) and
247
+ result = default_db.get(id) and
198
248
  type = result[:_type] || result["_type"] and
199
- type == _type and
249
+ type == doc_type and
200
250
  new(result, id) or
201
251
  nil
202
252
  end
@@ -220,10 +270,37 @@ module CouchPillow
220
270
  end
221
271
 
222
272
 
273
+ # Set a DB connection. Overrides the default CouchPillow.db connection
274
+ # for the first time this method gets called. Subsequent calls will set
275
+ # secondary connections, which will only be used for write only.
276
+ #
277
+ # Example:
278
+ # db primary_db # use for both read and write
279
+ # db backup_db1 # write only
280
+ # db backup_db2 # write only
281
+ #
282
+ def self.db conn
283
+ # set the primary db connection
284
+ @primary_db ||= conn
285
+
286
+ # insert as backup db connections
287
+ if conn && @primary_db && conn != @primary_db
288
+ secondary_dbs << conn
289
+ end
290
+ end
291
+
292
+
223
293
  private
224
294
 
225
295
 
226
- def self._type
296
+ # Timestamp this document
297
+ #
298
+ def timestamp!
299
+ @data[:_updated_at] = Time.now.utc
300
+ end
301
+
302
+
303
+ def self.doc_type
227
304
  @type ||= DEFAULT_TYPE
228
305
  end
229
306
 
@@ -233,6 +310,16 @@ module CouchPillow
233
310
  end
234
311
 
235
312
 
313
+ def self.default_db
314
+ @default_db ||= (@primary_db || CouchPillow.db)
315
+ end
316
+
317
+
318
+ def self.secondary_dbs
319
+ @secondary_dbs ||= []
320
+ end
321
+
322
+
236
323
  def self.symbolize hash
237
324
  hash.inject({}) do |memo,(k,v)|
238
325
  memo[k.to_sym] = v
@@ -1,5 +1,5 @@
1
1
  module CouchPillow
2
2
  GEM_NAME = "couchpillow"
3
3
  NAME = "CouchPillow"
4
- VERSION = "0.4.3"
4
+ VERSION = "0.4.4"
5
5
  end
data/lib/couchpillow.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  require 'securerandom'
2
2
  require 'json'
3
3
  require 'time'
4
+ require 'celluloid/autostart'
4
5
 
5
6
  module CouchPillow
6
7
 
8
+ Celluloid.logger = nil
9
+
7
10
  def self.db= driver
8
11
  @db = driver
9
12
  end
@@ -33,7 +33,7 @@ class TestDocument < Minitest::Test
33
33
 
34
34
  def test_default_type
35
35
  d = Document.new({}, "1")
36
- assert_equal "default", d._type
36
+ assert_equal "couchpillow", d.doc_type
37
37
  end
38
38
 
39
39
 
@@ -41,7 +41,7 @@ class TestDocument < Minitest::Test
41
41
  d = Class.new(Document) do
42
42
  type 'test'
43
43
  end.new
44
- assert_equal "test", d._type
44
+ assert_equal "test", d.doc_type
45
45
  end
46
46
 
47
47
 
@@ -124,14 +124,14 @@ class TestDocument < Minitest::Test
124
124
  def test_to_json
125
125
  mock_time
126
126
  d = Document.new({}, "1")
127
- assert_equal "{\"_id\":\"1\",\"_type\":\"default\",\"_created_at\":\"#{mock_time.to_s}\",\"_updated_at\":\"#{mock_time.to_s}\"}", d.to_json
127
+ assert_equal "{\"_id\":\"1\",\"_type\":\"couchpillow\",\"_created_at\":\"#{mock_time.to_s}\",\"_updated_at\":\"#{mock_time.to_s}\"}", d.to_json
128
128
  end
129
129
 
130
130
 
131
131
  def test_to_hash
132
132
  mock_time
133
133
  d = Document.new({}, "1")
134
- assert_equal({ :_id => "1", :_created_at => mock_time, :_type => "default", :_updated_at => mock_time }, d.to_hash)
134
+ assert_equal({ :_id => "1", :_created_at => mock_time, :_type => "couchpillow", :_updated_at => mock_time }, d.to_hash)
135
135
  end
136
136
 
137
137
 
@@ -201,7 +201,7 @@ class TestDocument < Minitest::Test
201
201
  k = klass.new
202
202
  k.foo = 100
203
203
  k.save!
204
- k_id = k._id
204
+ k_id = k.id
205
205
 
206
206
  d = klass.get(k_id)
207
207
  assert_equal 100, d.foo
@@ -210,6 +210,7 @@ class TestDocument < Minitest::Test
210
210
 
211
211
  def test_get_returns_nil
212
212
  CouchPillow.db.expects(:get).with('123').returns(nil)
213
+ Document.expects(:default_db).returns(CouchPillow.db)
213
214
  d = Document.get('123')
214
215
  assert_equal nil, d
215
216
  end
@@ -338,6 +339,25 @@ class TestDocument < Minitest::Test
338
339
  end
339
340
 
340
341
 
342
+ def test_auto_convert_triggers_on_update
343
+ d = Class.new(Document) do
344
+ type 'test'
345
+
346
+ attribute :foo do
347
+ type(String)
348
+ auto_convert
349
+ end
350
+
351
+ attribute :not_auto do
352
+ type Integer
353
+ end
354
+ end.new( foo: 1, not_auto: "100" )
355
+
356
+ d.update( foo: 1776 )
357
+ assert_equal "1776", d.foo
358
+ end
359
+
360
+
341
361
  def test_dsl_style
342
362
  d = Class.new(Document) do
343
363
  type 'test'
@@ -393,4 +413,77 @@ class TestDocument < Minitest::Test
393
413
  d.save! ttl: 100
394
414
  end
395
415
 
416
+
417
+ def test_custom_db_connection
418
+ conn1 = FakeCouchbaseServer.new
419
+
420
+ d = Class.new(Document) do
421
+ type 'test'
422
+
423
+ db conn1
424
+
425
+ attribute :foo do
426
+ type String
427
+ auto_convert
428
+ required
429
+ default { "tester" }
430
+ end
431
+
432
+
433
+ def initialize doc = {}
434
+ super(doc, "1")
435
+ end
436
+
437
+ end.new
438
+ d.foo = "hello"
439
+ d.save!
440
+
441
+ fromdefdb = CouchPillow.db.get("1")
442
+ assert_nil fromdefdb
443
+
444
+ fromprimdb = conn1.get("1")
445
+ assert_equal "hello", fromprimdb[:foo]
446
+ end
447
+
448
+
449
+ def test_multiple_db_connections
450
+ db1 = FakeCouchbaseServer.new
451
+ db2 = FakeCouchbaseServer.new
452
+ db3 = FakeCouchbaseServer.new
453
+
454
+ d = Class.new(Document) do
455
+ type 'test'
456
+
457
+ db db1
458
+ db db2
459
+ db db3
460
+
461
+ attribute :foo do
462
+ type String
463
+ auto_convert
464
+ required
465
+ default { "tester" }
466
+ end
467
+
468
+
469
+ def initialize doc = {}
470
+ super(doc, "1")
471
+ end
472
+
473
+ end.new
474
+ d.foo = "hello"
475
+ d.save!
476
+
477
+ res = d.wait
478
+
479
+ data = db1.get("1")
480
+ assert_equal "hello", data[:foo]
481
+
482
+ data = db2.get("1")
483
+ assert_equal "hello", data[:foo]
484
+
485
+ data = db3.get("1")
486
+ assert_equal "hello", data[:foo]
487
+ end
488
+
396
489
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchpillow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Albert Tedja
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-11 00:00:00.000000000 Z
11
+ date: 2015-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: celluloid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.16'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.16'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: minitest
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -83,9 +97,9 @@ require_paths:
83
97
  - lib
84
98
  required_ruby_version: !ruby/object:Gem::Requirement
85
99
  requirements:
86
- - - ">="
100
+ - - "~>"
87
101
  - !ruby/object:Gem::Version
88
- version: '0'
102
+ version: '2.0'
89
103
  required_rubygems_version: !ruby/object:Gem::Requirement
90
104
  requirements:
91
105
  - - ">="