dynamoid 1.0.0 → 1.1.0

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: 9a8130b39add7870e0217be2f31fc55808f02be5
4
- data.tar.gz: 73f7caa9ae29e070fcae4deb87461f7a004d0470
3
+ metadata.gz: d03723c3178362472031077f90ef4d732c9f6a59
4
+ data.tar.gz: d5255b0e842355e872056f10c2619f9c9609e96a
5
5
  SHA512:
6
- metadata.gz: 1e73c399b586b8c264f1b19dcb99e121705a82aaea2efcf68b4b250d1b35d30065ddd3c2fa213c6226260063dd8afd3beb6cd78a6ff7c1130fe92486544392ec
7
- data.tar.gz: 89d9baede7e7b40c1e15ffa358b84199c8b5cdc9ea8fb855083da2b63651b1f8304b4afe845c0f7b9b1771a9957e0e0a57881663ad40d67542a0aaa9175605d7
6
+ metadata.gz: de28cab30c3bd935e588f481986fddc5ad25e46a40a0e5a9675524e71e112f67333f2a663b8951253742b33e655ec556381834acf122c9cb484829c68beb48dc
7
+ data.tar.gz: 2514d74830f6f8fa4f427eb0ddbed13eec808aaa437a7cac7dc1772a8eb4592dbb1d35551b426cad71ed0e693e6c9d2d532f7d2d1949f72c59c6712136582615
@@ -0,0 +1,21 @@
1
+ # 1.1.0
2
+
3
+ * Added support for optimistic locking on delete (PR #29, sumocoder)
4
+ * upgrade concurrent-ruby requirement to 1.0 (PR #31, keithmgould)
5
+
6
+ # 1.0.0
7
+
8
+ * Add support for AWS SDK v2.
9
+ * Add support for custom class type for fields.
10
+ * Remove partitioning support.
11
+ * Remove support for Dynamoid's (pseudo)indexes, now that DynamoDB offers
12
+ local and global indexes.
13
+ * Rename :float field type to :number.
14
+ * Rename Chain#limit to Chain#eval_limit.
15
+
16
+ Housekeeping:
17
+
18
+ * Switch from `fake_dynamo` for unit tests to DynamoDBLocal. This is the new authoritative
19
+ implementation of DynamoDB for testing, and it supports AWS SDK v2.
20
+ * Use Travis CI to auto-run unit tests on multiple Rubies.
21
+ * Randomize spec order.
@@ -364,6 +364,7 @@ Also, without contributors the project wouldn't be nearly as awesome. So many th
364
364
  Running the tests is fairly simple. In one window, run `bin/start_dynamodblocal`, and in the other, use `rake`.
365
365
 
366
366
  [![Build Status](https://travis-ci.org/Dynamoid/Dynamoid.svg)](https://travis-ci.org/Dynamoid/Dynamoid)
367
+ [![Coverage Status](https://coveralls.io/repos/Dynamoid/Dynamoid/badge.svg?branch=master&service=github)](https://coveralls.io/github/Dynamoid/Dynamoid?branch=master)
367
368
 
368
369
  ## Copyright
369
370
 
@@ -2,20 +2,20 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "dynamoid"
5
- s.version = "1.0.0"
5
+ s.version = "1.1.0"
6
6
 
7
7
  # Keep in sync with README
8
- s.authors = %w(
9
- Josh Symonds
10
- Logan Bowers
11
- Craig Heneveld
12
- Anatha Kumaran
13
- Jason Dew
14
- Luis Arias
15
- Stefan Neculai
16
- Philip White
17
- Peeyush Kumar
18
- )
8
+ s.authors = [
9
+ 'Josh Symonds',
10
+ 'Logan Bowers',
11
+ 'Craig Heneveld',
12
+ 'Anatha Kumaran',
13
+ 'Jason Dew',
14
+ 'Luis Arias',
15
+ 'Stefan Neculai',
16
+ 'Philip White',
17
+ 'Peeyush Kumar',
18
+ ]
19
19
  s.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement."
20
20
  s.extra_rdoc_files = [
21
21
  "LICENSE.txt",
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
23
23
  ]
24
24
  # file list is generated with `git ls-files | grep -v -E -e '^spec/' -e '^\.' -e 'bin/'`
25
25
  s.files = %w(
26
+ CHANGELOG.md
26
27
  Gemfile
27
28
  LICENSE.txt
28
29
  README.markdown
@@ -62,12 +63,13 @@ Gem::Specification.new do |s|
62
63
 
63
64
  s.add_runtime_dependency(%q<activemodel>, ["~> 4"])
64
65
  s.add_runtime_dependency(%q<aws-sdk-resources>, ["~> 2"])
65
- s.add_runtime_dependency(%q<concurrent-ruby>, ["~> 0.9"])
66
+ s.add_runtime_dependency(%q<concurrent-ruby>, [">= 1.0"])
66
67
  s.add_development_dependency(%q<rake>, [">= 0"])
67
68
  s.add_development_dependency(%q<rspec>, ["~> 3"])
68
69
  s.add_development_dependency(%q<bundler>, [">= 0"])
69
70
  s.add_development_dependency(%q<yard>, [">= 0"])
70
71
  s.add_development_dependency(%q<github-markup>, [">= 0"])
71
72
  s.add_development_dependency(%q<pry>, [">= 0"])
73
+ s.add_development_dependency(%q<coveralls>, [">= 0"])
72
74
  end
73
75
 
@@ -65,7 +65,7 @@ module Dynamoid
65
65
  results = client.batch_get_item(
66
66
  request_items: request_items
67
67
  )
68
-
68
+
69
69
  ret = Hash.new([].freeze) # Default for tables where no rows are returned
70
70
  results.data[:responses].each do |table, rows|
71
71
  ret[table] = rows.collect { |r| result_item_to_hash(r) }
@@ -107,14 +107,14 @@ module Dynamoid
107
107
  read_capacity = options[:read_capacity] || Dynamoid::Config.read_capacity
108
108
  write_capacity = options[:write_capacity] || Dynamoid::Config.write_capacity
109
109
  range_key = options[:range_key]
110
-
110
+
111
111
  key_schema = [
112
112
  { attribute_name: key.to_s, key_type: HASH_KEY }
113
113
  ]
114
- key_schema << {
114
+ key_schema << {
115
115
  attribute_name: range_key.keys.first.to_s, key_type: RANGE_KEY
116
116
  } if(range_key)
117
-
117
+
118
118
  #TODO: Provide support for number and binary hash key
119
119
  attribute_definitions = [
120
120
  { attribute_name: key.to_s, attribute_type: 'S' }
@@ -122,10 +122,10 @@ module Dynamoid
122
122
  attribute_definitions << {
123
123
  attribute_name: range_key.keys.first.to_s, attribute_type: api_type(range_key.values.first)
124
124
  } if(range_key)
125
-
125
+
126
126
  client.create_table(table_name: table_name,
127
127
  provisioned_throughput: {
128
- read_capacity_units: read_capacity,
128
+ read_capacity_units: read_capacity,
129
129
  write_capacity_units: write_capacity
130
130
  },
131
131
  key_schema: key_schema,
@@ -144,9 +144,17 @@ module Dynamoid
144
144
  # @since 1.0.0
145
145
  #
146
146
  # @todo: Provide support for various options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#delete_item-instance_method
147
- def delete_item(table_name, key, options = nil)
147
+ def delete_item(table_name, key, options = {})
148
+ range_key = options[:range_key]
149
+ conditions = options[:conditions]
148
150
  table = describe_table(table_name)
149
- client.delete_item(table_name: table_name, key: key_stanza(table, key, options && options[:range_key]))
151
+ client.delete_item(
152
+ table_name: table_name,
153
+ key: key_stanza(table, key, range_key),
154
+ expected: expected_stanza(conditions)
155
+ )
156
+ rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
157
+ raise Dynamoid::Errors::ConditionalCheckFailedException, e
150
158
  end
151
159
 
152
160
  # Deletes an entire table from DynamoDB.
@@ -231,7 +239,7 @@ module Dynamoid
231
239
  # @todo: Provide support for various options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method
232
240
  def put_item(table_name, object, options = nil)
233
241
  item = {}
234
-
242
+
235
243
  object.each do |k, v|
236
244
  next if v.nil? || (v.respond_to?(:empty?) && v.empty?)
237
245
  item[k.to_s] = v
@@ -342,10 +350,10 @@ module Dynamoid
342
350
  def scan(table_name, scan_hash, select_opts = {})
343
351
  limit = select_opts.delete(:limit)
344
352
  batch = select_opts.delete(:batch_size)
345
-
353
+
346
354
  request = { table_name: table_name }
347
355
  request[:limit] = batch || limit if batch || limit
348
- request[:scan_filter] = scan_hash.reduce({}) do |memo, kvp|
356
+ request[:scan_filter] = scan_hash.reduce({}) do |memo, kvp|
349
357
  memo[kvp[0].to_s] = {
350
358
  attribute_value_list: [kvp[1]],
351
359
  # TODO: Provide support for all comparison operators
@@ -369,7 +377,7 @@ module Dynamoid
369
377
  end
370
378
  end
371
379
  end
372
-
380
+
373
381
 
374
382
  #
375
383
  # Truncates all records in the given table
@@ -381,7 +389,7 @@ module Dynamoid
381
389
  table = describe_table(table_name)
382
390
  hk = table.hash_key
383
391
  rk = table.range_key
384
-
392
+
385
393
  scan(table_name, {}, {}).each do |attributes|
386
394
  opts = {range_key: attributes[rk.to_sym] } if rk
387
395
  delete_item(table_name, attributes[hk], opts)
@@ -393,7 +401,7 @@ module Dynamoid
393
401
  end
394
402
 
395
403
  protected
396
-
404
+
397
405
  STRING_TYPE = "S".freeze
398
406
  NUM_TYPE = "N".freeze
399
407
  BOOLEAN_TYPE = "B".freeze
@@ -418,7 +426,7 @@ module Dynamoid
418
426
  key[table.range_key.to_s] = range_key if range_key
419
427
  key
420
428
  end
421
-
429
+
422
430
  #
423
431
  # @param [Hash] conditions Conditions to enforce on operation (e.g. { :if => { :count => 5 }, :unless_exists => ['id']})
424
432
  # @return an Expected stanza for the given conditions hash
@@ -426,20 +434,20 @@ module Dynamoid
426
434
  def expected_stanza(conditions = nil)
427
435
  expected = Hash.new { |h,k| h[k] = {} }
428
436
  return expected unless conditions
429
-
437
+
430
438
  conditions[:unless_exists].try(:each) do |col|
431
439
  expected[col.to_s][:exists] = false
432
440
  end
433
441
  conditions[:if].try(:each) do |col,val|
434
442
  expected[col.to_s][:value] = val
435
443
  end
436
-
444
+
437
445
  expected
438
446
  end
439
-
447
+
440
448
  HASH_KEY = "HASH".freeze
441
449
  RANGE_KEY = "RANGE".freeze
442
-
450
+
443
451
  #
444
452
  # New, semi-arbitrary API to get data on the table
445
453
  #
@@ -448,7 +456,7 @@ module Dynamoid
448
456
  table_cache[table_name] = Table.new(client.describe_table(table_name: table_name).data)
449
457
  end
450
458
  end
451
-
459
+
452
460
  #
453
461
  # Converts a hash returned by get_item, scan, etc. into a key-value hash
454
462
  #
@@ -457,35 +465,35 @@ module Dynamoid
457
465
  item.each { |k,v| r[k.to_sym] = v }
458
466
  end
459
467
  end
460
-
468
+
461
469
  #
462
470
  # Represents a table. Exposes data from the "DescribeTable" API call, and also
463
471
  # provides methods for coercing values to the proper types based on the table's schema data
464
472
  #
465
473
  class Table
466
474
  attr_reader :schema
467
-
475
+
468
476
  #
469
477
  # @param [Hash] schema Data returns from a "DescribeTable" call
470
478
  #
471
479
  def initialize(schema)
472
480
  @schema = schema[:table]
473
481
  end
474
-
482
+
475
483
  def range_key
476
484
  @range_key ||= schema[:key_schema].find { |d| d[:key_type] == RANGE_KEY }.try(:attribute_name)
477
485
  end
478
-
486
+
479
487
  def range_type
480
- range_type ||= schema[:attribute_definitions].find { |d|
488
+ range_type ||= schema[:attribute_definitions].find { |d|
481
489
  d[:attribute_name] == range_key
482
490
  }.try(:fetch,:attribute_type, nil)
483
491
  end
484
-
492
+
485
493
  def hash_key
486
494
  @hash_key ||= schema[:key_schema].find { |d| d[:key_type] == HASH_KEY }.try(:attribute_name).to_sym
487
495
  end
488
-
496
+
489
497
  #
490
498
  # Returns the API type (e.g. "N", "S") for the given column, if the schema defines it,
491
499
  # nil otherwise
@@ -500,31 +508,31 @@ module Dynamoid
500
508
  schema[:item_count]
501
509
  end
502
510
  end
503
-
511
+
504
512
  #
505
- # Mimics behavior of the yielded object on DynamoDB's update_item API (high level).
513
+ # Mimics behavior of the yielded object on DynamoDB's update_item API (high level).
506
514
  #
507
515
  class ItemUpdater
508
516
  attr_reader :table, :key, :range_key
509
-
517
+
510
518
  def initialize(table, key, range_key = nil)
511
519
  @table = table; @key = key, @range_key = range_key
512
520
  @additions = {}
513
521
  @deletions = {}
514
522
  @updates = {}
515
523
  end
516
-
524
+
517
525
  #
518
- # Adds the given values to the values already stored in the corresponding columns.
519
- # The column must contain a Set or a number.
526
+ # Adds the given values to the values already stored in the corresponding columns.
527
+ # The column must contain a Set or a number.
520
528
  #
521
- # @param [Hash] vals keys of the hash are the columns to update, vals are the values to
529
+ # @param [Hash] vals keys of the hash are the columns to update, vals are the values to
522
530
  # add. values must be a Set, Array, or Numeric
523
531
  #
524
532
  def add(values)
525
533
  @additions.merge!(values)
526
534
  end
527
-
535
+
528
536
  #
529
537
  # Removes values from the sets of the given columns
530
538
  #
@@ -538,19 +546,19 @@ module Dynamoid
538
546
  #
539
547
  # Replaces the values of one or more attributes
540
548
  #
541
- def set(values)
549
+ def set(values)
542
550
  @updates.merge!(values)
543
551
  end
544
-
552
+
545
553
  #
546
554
  # Returns an AttributeUpdates hash suitable for passing to the V2 Client API
547
555
  #
548
556
  def to_h
549
557
  ret = {}
550
-
558
+
551
559
  @additions.each do |k,v|
552
- ret[k.to_s] = {
553
- action: ADD,
560
+ ret[k.to_s] = {
561
+ action: ADD,
554
562
  value: v
555
563
  }
556
564
  end
@@ -569,7 +577,7 @@ module Dynamoid
569
577
 
570
578
  ret
571
579
  end
572
-
580
+
573
581
  ADD = "ADD".freeze
574
582
  DELETE = "DELETE".freeze
575
583
  PUT = "PUT".freeze
@@ -171,8 +171,8 @@ module Dynamoid
171
171
  end
172
172
 
173
173
  #
174
- # update!() will increment the lock_version if the table has the column, but will not check it. Thus, a concurrent save will
175
- # never cause an update! to fail, but an update! may cause a concurrent save to fail.
174
+ # update!() will increment the lock_version if the table has the column, but will not check it. Thus, a concurrent save will
175
+ # never cause an update! to fail, but an update! may cause a concurrent save to fail.
176
176
  #
177
177
  #
178
178
  def update!(conditions = {}, &block)
@@ -216,7 +216,21 @@ module Dynamoid
216
216
  # @since 0.2.0
217
217
  def delete
218
218
  options = range_key ? {:range_key => dump_field(self.read_attribute(range_key), self.class.attributes[range_key])} : {}
219
+
220
+ # Add an optimistic locking check if the lock_version column exists
221
+ if(self.class.attributes[:lock_version])
222
+ conditions = {:if => {}}
223
+ conditions[:if][:lock_version] =
224
+ if changes[:lock_version].nil?
225
+ self.lock_version
226
+ else
227
+ changes[:lock_version][0]
228
+ end
229
+ options[:conditions] = conditions
230
+ end
219
231
  Dynamoid.adapter.delete(self.class.table_name, self.hash_key, options)
232
+ rescue Dynamoid::Errors::ConditionalCheckFailedException
233
+ raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
220
234
  end
221
235
 
222
236
  # Dump this object's attributes into hash form, fit to be persisted into the datastore.
@@ -268,14 +282,14 @@ module Dynamoid
268
282
  end
269
283
  end
270
284
  end
271
-
285
+
272
286
  # Persist the object into the datastore. Assign it an id first if it doesn't have one.
273
287
  #
274
288
  # @since 0.2.0
275
289
  def persist(conditions = nil)
276
290
  run_callbacks(:save) do
277
291
  self.hash_key = SecureRandom.uuid if self.hash_key.nil? || self.hash_key.blank?
278
-
292
+
279
293
  # Add an exists check to prevent overwriting existing records with new ones
280
294
  if(new_record?)
281
295
  conditions ||= {}
metadata CHANGED
@@ -1,31 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Josh
8
- - Symonds
9
- - Logan
10
- - Bowers
11
- - Craig
12
- - Heneveld
13
- - Anatha
14
- - Kumaran
15
- - Jason
16
- - Dew
17
- - Luis
18
- - Arias
19
- - Stefan
20
- - Neculai
21
- - Philip
22
- - White
23
- - Peeyush
24
- - Kumar
7
+ - Josh Symonds
8
+ - Logan Bowers
9
+ - Craig Heneveld
10
+ - Anatha Kumaran
11
+ - Jason Dew
12
+ - Luis Arias
13
+ - Stefan Neculai
14
+ - Philip White
15
+ - Peeyush Kumar
25
16
  autorequire:
26
17
  bindir: bin
27
18
  cert_chain: []
28
- date: 2015-11-25 00:00:00.000000000 Z
19
+ date: 2015-12-30 00:00:00.000000000 Z
29
20
  dependencies:
30
21
  - !ruby/object:Gem::Dependency
31
22
  name: activemodel
@@ -59,16 +50,16 @@ dependencies:
59
50
  name: concurrent-ruby
60
51
  requirement: !ruby/object:Gem::Requirement
61
52
  requirements:
62
- - - "~>"
53
+ - - ">="
63
54
  - !ruby/object:Gem::Version
64
- version: '0.9'
55
+ version: '1.0'
65
56
  type: :runtime
66
57
  prerelease: false
67
58
  version_requirements: !ruby/object:Gem::Requirement
68
59
  requirements:
69
- - - "~>"
60
+ - - ">="
70
61
  - !ruby/object:Gem::Version
71
- version: '0.9'
62
+ version: '1.0'
72
63
  - !ruby/object:Gem::Dependency
73
64
  name: rake
74
65
  requirement: !ruby/object:Gem::Requirement
@@ -153,6 +144,20 @@ dependencies:
153
144
  - - ">="
154
145
  - !ruby/object:Gem::Version
155
146
  version: '0'
147
+ - !ruby/object:Gem::Dependency
148
+ name: coveralls
149
+ requirement: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ type: :development
155
+ prerelease: false
156
+ version_requirements: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
156
161
  description: Dynamoid is an ORM for Amazon's DynamoDB that supports offline development,
157
162
  associations, querying, and everything else you'd expect from an ActiveRecord-style
158
163
  replacement.
@@ -163,6 +168,7 @@ extra_rdoc_files:
163
168
  - LICENSE.txt
164
169
  - README.markdown
165
170
  files:
171
+ - CHANGELOG.md
166
172
  - Gemfile
167
173
  - LICENSE.txt
168
174
  - README.markdown
@@ -213,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
213
219
  version: '0'
214
220
  requirements: []
215
221
  rubyforge_project:
216
- rubygems_version: 2.4.5.1
222
+ rubygems_version: 2.5.1
217
223
  signing_key:
218
224
  specification_version: 4
219
225
  summary: Dynamoid is an ORM for Amazon's DynamoDB