dynamini 2.9.2 → 2.9.3

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: 4da5852711d8ecb8a226a9ae7c987f2ea0324927
4
- data.tar.gz: eec0aaae89719060294b9ae055f8effabf79dd9b
3
+ metadata.gz: 1dfc513f038cc378fa09c49d4371fb55de060207
4
+ data.tar.gz: 278157bffc21bbae3fbaa3a7e2a1652571afc4e3
5
5
  SHA512:
6
- metadata.gz: f6eaf764f422c931ae8e5d669f2b26349cfaffedfcabb6c13654f06ac40b758ea371b0328149d1f3c0914de67972fa7971d7a01d89bef94b6ab825b3bd1527c3
7
- data.tar.gz: d97dab7690b1c371052bff3c1c1a7b8d39107281b2ed71c90e6eb05b3dc18998d9f51c8ad81307ebb249de1d4924f56245ef8f4e85100d42d4fd71b870f5081c
6
+ metadata.gz: bb4bc3eeaf5c7f7b1321fd0aa29da4bcc5d5c8b968f3e2ab5619cc60fe8dde8ac53ca2135b3c73de70dcbaf4797183cec0a82ceabac6678c097c1a2d6522ff2b
7
+ data.tar.gz: 51d96dc40837859551f3fffd6a1dcbdf5d5137613a4e610bb5f980c744c67d78d55c001f986e9be9518e70b4e29fc3af983cecdc65dfcb376ecebe7e17f624b0
@@ -2,10 +2,10 @@ language: ruby
2
2
  script: bundle exec rspec
3
3
  rvm:
4
4
  - 2.0.0
5
- - 2.2.1
5
+ - 2.3.4
6
6
  notifications:
7
7
  email:
8
8
  recipients:
9
9
  - dev@retailcommon.com
10
10
  on_success: change
11
- on_failure: always
11
+ on_failure: always
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamini (2.9.2)
4
+ dynamini (2.9.3)
5
5
  activemodel (>= 3, < 5.0)
6
6
  aws-sdk (~> 2)
7
7
 
data/README.md CHANGED
@@ -5,8 +5,21 @@ Dynamini is a lightweight DynamoDB interface designed as a drop-in replacement f
5
5
  [![Code Climate](https://codeclimate.com/github/47colborne/dynamini/badges/gpa.svg)](https://codeclimate.com/github/47colborne/dynamini)
6
6
  [![Gem Version](https://badge.fury.io/rb/dynamini.svg)](https://badge.fury.io/rb/dynamini)
7
7
 
8
+ ##### Table of Contents
9
+ - [The Basics](#the-basics)
10
+ - [Configuration](#configuration)
11
+ - [Datatype Handling](#datatype-handling)
12
+ - [Enumerable Attributes](#enumerable-attributes)
13
+ - [Querying](#querying)
14
+ - [Scanning](#scanning)
15
+ - [Secondary Indices](#secondary-indices)
16
+ - [Batch Saving](#batch-saving)
17
+ - [Testing](#testing)
18
+ - [Things to Remember](#things-to-remember)
19
+ - [Contributing](#contributing)
20
+
8
21
  ## The Basics
9
- This gem provides an opinionated interface, set up to let you use Amazon's DynamoDB at its most efficient. That means traditional relational DB functions like WHERE, GROUP BY, and HAVING aren't provided, since these trigger table scans that defeat the performance gains realized by switching to Dynamo in the first place. Use this gem when you have an relational table with too much concurrent activity, resulting in constant table locking. After you've moved your data to Dynamo, and installed and configured Dynamini, the following ActiveRecord functions will be preserved:
22
+ This gem is designed to provide an ActiveRecord-like interface for Amazon's DynamoDB, making it easy for you to make the switch from a traditional relational DB. Once you've set up your table in the DynamoDB console, and installed and configured Dynamini, the behavior of the following ActiveRecord methods will be preserved:
10
23
 
11
24
  Class methods:
12
25
  * create(attributes)
@@ -38,7 +51,8 @@ Instance methods:
38
51
  * created_at
39
52
 
40
53
  We've included ActiveModel::Validations, so any validators will still work and be triggered by the save/create methods.
41
- There are also some new functions specific to DynamoDB's API:
54
+
55
+ There are also some new methods specific to DynamoDB's API that don't have a counterpart in ActiveRecord:
42
56
 
43
57
  * find_or_nil(hash_key, range_key) - since ActiveRecord's find_by isn't applicable to noSQL, use this method if you want a .find that doesn't raise exceptions when the item doesn't exist
44
58
  * batch_find([keys]) - to retrieve multiple objects at once.
@@ -48,7 +62,7 @@ There are also some new functions specific to DynamoDB's API:
48
62
  * delete_attribute!(attrubute) - same as above but with an included save!
49
63
 
50
64
  ## Configuration
51
- In application.rb, or in initializers/dynamini.rb, include your AWS settings like so:
65
+ In application.rb, or in initializers/dynamini.rb, include your AWS settings like this:
52
66
 
53
67
  ```ruby
54
68
  Dynamini.configure do |config|
@@ -107,7 +121,7 @@ The following datatypes are supported by handle:
107
121
  Booleans and strings don't actually need to be translated, but you can set up defaults for those fields this way.
108
122
  The magic fields updated_at and created_at are handled as :time by default.
109
123
 
110
- ## Enumerable Support
124
+ ## Enumerable Attributes
111
125
  You can save arrays and sets to your Dynamini model. Optionally, you can have Dynamini perform type conversion on each element of your enumerable. Here's how it works:
112
126
 
113
127
  ```ruby
@@ -146,9 +160,11 @@ Please note that changing enumerables in place using mutator methods like << or
146
160
 
147
161
  If you want to make changes like this, either clone it then use the assignment operator (e.g. model.array = model.array.dup << 'foo') or call model.mark(:attribute) after mutation and before saving to force Dynamini to write the change.
148
162
 
149
- ## Querying With Range Keys
163
+ ## Querying
164
+
165
+ Dynamini includes a query function that's much more narrow than ActiveRecord's where function, since DynamoDB is not automatically optimized for highly flexible read operations. It's designed to retrieve a selection of records that belong to a given hash key but have various range key values.
150
166
 
151
- Dynamini includes a query function that's much more narrow than ActiveRecord's where function. It's designed to retrieve a selection of records that belong to a given hash key but have various range key values. To use .query, your table needs to be configured with a range key, and you need to :handle that range field as a fundamentally numeric type - integer, float, date, or time. If your range key field isn't numeric, you won't be able to .query, but you'll still be able to .find your records normally.
167
+ To use .query, your table needs to be configured with a range key, and you need to :handle that range field as a fundamentally numeric type - integer, float, date, or time. If your range key field isn't numeric, you won't be able to .query, but you'll still be able to .find your records normally.
152
168
 
153
169
  Query takes the following arguments:
154
170
  * :hash_key (required)
@@ -199,7 +215,7 @@ DailyWeather.query(hash_key: "Toronto", scan_index_forward: false)
199
215
  > [C, B, A]
200
216
  ```
201
217
 
202
- ## Table Scans
218
+ ## Scanning
203
219
  Table scanning is a very expensive operation, and should not be undertaken without a good understanding of the read/write costs. As such, Dynamini doesn't implement the traditional ActiveRecord collection methods like .all or .where. Instead, you can .scan, which has an interface much closer to DynamoDB's native SDK method.
204
220
 
205
221
  The following options are supported:
@@ -215,7 +231,6 @@ Note that start_key can be either a hash { "AttributeName" => "Value" } or a val
215
231
 
216
232
  For more information about using segment and total_segments for parallelization, see: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html
217
233
 
218
-
219
234
  ```ruby
220
235
  products_page_one = Product.scan(limit: 100, start_key: 'abcd')
221
236
 
@@ -229,7 +244,7 @@ page_two = Product.scan(start_key: products_page_one.last_evaluated_key)
229
244
  ```
230
245
 
231
246
  ## Secondary Indices
232
- To define a secondary index (so that you can .scan it or .query it), you can list them at the top of your Dynamini subclass. The index names have to match the names you've set up through the DynamoDB console. If your secondary index uses a range key, specify it here as well.
247
+ To define a secondary index (so that you can .scan or .query it), you can set them at the top of your Dynamini subclass. The index names have to match the names you've set up through the DynamoDB console. If your secondary index uses a range key, specify it here as well.
233
248
 
234
249
  ```ruby
235
250
  class Comment < Dynamini::Base
@@ -240,28 +255,6 @@ class Comment < Dynamini::Base
240
255
  end
241
256
  ```
242
257
  For more information on how and why to use secondary indices, see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html
243
- ## Testing
244
- We've included an optional in-memory test client, so you don't necessarily have to connect to a real Dynamo instance when running tests. You could also use this in your development environment if you don't have a real Dynamo instance yet, but the data saved to it won't persist through a server restart.
245
-
246
- To activate this feature, just require the testing module:
247
- ```ruby
248
- require 'dynamini/testing'
249
- ```
250
- This module replaces all API calls Dynamini makes to AWS DynamoDB with calls to Dynamini::TestClient.
251
-
252
- The test client will not reset its database unless you tell it to, like so:
253
- ```ruby
254
- Vehicle.client.reset
255
- ```
256
-
257
- So, for instance, to get Rspec working with your test suite the way your ActiveRecord model behaved, add these lines to your spec_helper.rb:
258
- ```ruby
259
- require 'dynamini/testing'
260
-
261
- config.after(:each) {
262
- Vehicle.client.reset # Large test suites will be very slow and unpredictable otherwise!
263
- }
264
- ```
265
258
 
266
259
  ## Batch Saving
267
260
  Dynamini implements DynamoDB's batch write operation, mapped to the .import method you might be used to from ActiveRecord. http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
@@ -294,6 +287,29 @@ Product.find('qwerty').updated_at
294
287
 
295
288
  ````
296
289
 
290
+ ## Testing
291
+ We've included an optional in-memory test client, so you don't necessarily have to connect to a real Dynamo instance when running tests. You could also use this in your development environment if you don't have a real Dynamo instance yet, but the data saved to it won't persist through a server restart.
292
+
293
+ To activate this feature, just require the testing module:
294
+ ```ruby
295
+ require 'dynamini/testing'
296
+ ```
297
+ This module replaces all API calls Dynamini makes to AWS DynamoDB with calls to Dynamini::TestClient.
298
+
299
+ The test client will not reset its database unless you tell it to, like so:
300
+ ```ruby
301
+ Vehicle.client.reset
302
+ ```
303
+
304
+ So, for instance, to get Rspec working with your test suite the way your ActiveRecord model behaved, add these lines to your spec_helper.rb:
305
+ ```ruby
306
+ require 'dynamini/testing'
307
+
308
+ config.after(:each) {
309
+ Vehicle.client.reset # Large test suites will be very slow and unpredictable otherwise!
310
+ }
311
+ ```
312
+
297
313
  ## Things to remember
298
314
  * Since DynamoDB is schemaless, your model will respond to any method that looks like a reader, meaning model.foo will return nil.
299
315
  * You can also write any arbitrary attribute to your model.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'dynamini'
3
- s.version = '2.9.2'
3
+ s.version = '2.9.3'
4
4
  s.summary = 'DynamoDB interface'
5
5
  s.description = 'Lightweight DynamoDB interface gem designed as
6
6
  a drop-in replacement for ActiveRecord.
@@ -94,7 +94,7 @@ module Dynamini
94
94
 
95
95
  def write_attribute(attribute, new_value, change: true, **options)
96
96
  old_value = read_attribute(attribute)
97
- if (handle = self.class.handles[attribute.to_sym]) && !new_value.nil?
97
+ if (handle = self.class.handles[attribute.to_sym])
98
98
  new_value = self.class.attribute_callback(TypeHandler::SETTER_PROCS, handle, new_value, change)
99
99
  end
100
100
  @attributes[attribute] = new_value
@@ -4,33 +4,35 @@ module Dynamini
4
4
  module TypeHandler
5
5
 
6
6
  GETTER_PROCS = {
7
- integer: proc { |v| v.to_i },
7
+ integer: proc { |v| v&.to_i },
8
8
  date: proc do |v|
9
9
  if v.is_a?(Date)
10
10
  v
11
- else
11
+ elsif v
12
12
  Time.methods.include?(:zone) ? Time.zone.at(v).to_date : Time.at(v).to_date
13
13
  end
14
14
  end,
15
15
  time: proc do |v|
16
- Time.methods.include?(:zone) ? Time.zone.at(v.to_f) : Time.at(v.to_f)
16
+ if v
17
+ Time.methods.include?(:zone) ? Time.zone.at(v.to_f) : Time.at(v.to_f)
18
+ end
17
19
  end,
18
- float: proc { |v| v.to_f },
19
- symbol: proc { |v| v.to_sym },
20
- string: proc { |v| v.to_s },
20
+ float: proc { |v| v&.to_f },
21
+ symbol: proc { |v| v&.to_sym },
22
+ string: proc { |v| v&.to_s },
21
23
  boolean: proc { |v| v },
22
24
  array: proc { |v| v.is_a?(Enumerable) ? v.to_a : [v] },
23
25
  set: proc { |v| v.is_a?(Enumerable) ? Set.new(v) : Set.new([v]) }
24
26
  }.freeze
25
27
 
26
28
  SETTER_PROCS = {
27
- integer: proc { |v| v.to_i },
29
+ integer: proc { |v| v&.to_i },
28
30
  time: proc { |v| (v.is_a?(Date) ? v.to_time : v).to_f },
29
- float: proc { |v| v.to_f },
30
- symbol: proc { |v| v.to_s },
31
- string: proc { |v| v.to_s },
31
+ float: proc { |v| v&.to_f },
32
+ symbol: proc { |v| v&.to_s },
33
+ string: proc { |v| v&.to_s },
32
34
  boolean: proc { |v| v },
33
- date: proc { |v| v.to_time.to_f },
35
+ date: proc { |v| v&.to_time.to_f },
34
36
  array: proc { |v| v.is_a?(Enumerable) ? v.to_a : [v] },
35
37
  set: proc { |v| v.is_a?(Enumerable) ? Set.new(v) : Set.new([v]) }
36
38
  }.freeze
@@ -91,6 +93,7 @@ module Dynamini
91
93
  end
92
94
 
93
95
  def attribute_callback(procs, handle, value, validate)
96
+ value = handle[:options][:default] if value.nil?
94
97
  callback = procs[handle[:format]]
95
98
  if should_convert_elements?(handle, value)
96
99
  result = convert_elements(value, procs[handle[:options][:of]])
@@ -129,6 +129,16 @@ describe Dynamini::Attributes do
129
129
  expect(model.foo).to eq([])
130
130
  end
131
131
  end
132
+
133
+ context 'when setting a handled attribute to its current value' do
134
+ it 'should not detect a change' do
135
+ Dynamini::Base.handle(:my_set, :set, of: :string)
136
+ Dynamini::Base.create!(id: '123', my_set: nil)
137
+ model = Dynamini::Base.find('123')
138
+ model.my_set = nil
139
+ expect(model.changes).to be_empty
140
+ end
141
+ end
132
142
  end
133
143
 
134
144
  describe '#delete_attribute' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamini
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.2
4
+ version: 2.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Ward
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2017-09-12 00:00:00.000000000 Z
18
+ date: 2017-09-13 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activemodel