couchpillow 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="