mince_dynamo_db 1.3.1 → 2.0.0.pre.1
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.
- data/.travis.yml +7 -0
- data/README.md +4 -4
- data/lib/dynamo_db.rb +10 -0
- data/lib/dynamo_db/config.rb +56 -0
- data/lib/dynamo_db/connection.rb +21 -0
- data/lib/dynamo_db/data_sanitizer.rb +13 -0
- data/lib/dynamo_db/data_store.rb +44 -0
- data/lib/dynamo_db/interface.rb +232 -0
- data/lib/dynamo_db/version.rb +22 -0
- data/mince_dynamo_db.gemspec +5 -2
- data/spec/integration/mince_interface_spec.rb +18 -0
- data/spec/units/config_spec.rb +19 -0
- data/spec/{lib → units}/connection_spec.rb +4 -4
- data/spec/{lib → units}/data_sanitizer_spec.rb +2 -2
- data/spec/units/data_store_spec.rb +55 -0
- data/spec/{lib/data_store_spec.rb → units/interface_spec.rb} +21 -28
- metadata +76 -20
- data/lib/mince_dynamo_db.rb +0 -3
- data/lib/mince_dynamo_db/connection.rb +0 -24
- data/lib/mince_dynamo_db/data_sanitizer.rb +0 -11
- data/lib/mince_dynamo_db/data_store.rb +0 -269
- data/lib/mince_dynamo_db/version.rb +0 -3
- data/spec/integration/persisting_to_dynamodb_test.rb +0 -18
- data/spec/support/aws_example.yml +0 -0
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Provides a very light weight interface for storing and retreiving information to
|
|
7
7
|
The motivation behind this is so your application is not tightly tied to a specific database. As your application grows you may need to upgrade to a different database or pull specific models to a different persistence strategy.
|
8
8
|
|
9
9
|
[@github](https://github.com/coffeencoke/mince_dynamo_db)
|
10
|
-
[@rubygems
|
10
|
+
[@rubygems](https://rubygems.org/gems/mince_dynamo_db)
|
11
11
|
|
12
12
|
# How to use
|
13
13
|
|
@@ -15,13 +15,13 @@ view the [example mince rails app](https://github.com/coffeencoke/mince_rails_ex
|
|
15
15
|
|
16
16
|
<pre>
|
17
17
|
# Add a book to the books collection
|
18
|
-
MinceDynamoDb::DataStore.add 'books', title: 'The World In Photographs', publisher: 'National Geographic'
|
18
|
+
MinceDynamoDb::DataStore.instance.add 'books', title: 'The World In Photographs', publisher: 'National Geographic'
|
19
19
|
|
20
20
|
# Retrieve all records from the books collection
|
21
|
-
MinceDynamoDb::DataStore.find_all 'books'
|
21
|
+
MinceDynamoDb::DataStore.instance.find_all 'books'
|
22
22
|
|
23
23
|
# Replace a specific book
|
24
|
-
MinceDynamoDb::DataStore.replace 'books', id: 1, title: 'A World In Photographs', publisher: 'National Geographic'
|
24
|
+
MinceDynamoDb::DataStore.instance.replace 'books', id: 1, title: 'A World In Photographs', publisher: 'National Geographic'
|
25
25
|
</pre>
|
26
26
|
|
27
27
|
View the docs for MinceDynamoDb::DataStore for all methods available.
|
data/lib/dynamo_db.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Mince
|
2
|
+
module DynamoDb
|
3
|
+
require_relative 'dynamo_db/config'
|
4
|
+
require_relative 'dynamo_db/connection'
|
5
|
+
require_relative 'dynamo_db/data_sanitizer'
|
6
|
+
require_relative 'dynamo_db/data_store'
|
7
|
+
require_relative 'dynamo_db/interface'
|
8
|
+
require_relative 'dynamo_db/version'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Mince # :nodoc:
|
2
|
+
module DynamoDb # :nodoc:
|
3
|
+
require 'singleton'
|
4
|
+
require 'aws'
|
5
|
+
|
6
|
+
# = DynamoDb Config
|
7
|
+
#
|
8
|
+
# Config specifies the configuration settings
|
9
|
+
#
|
10
|
+
# @author Matt Simpson
|
11
|
+
class Config
|
12
|
+
include Singleton
|
13
|
+
|
14
|
+
# Returns the primary key identifier for records. This is necessary because not all databases use the same
|
15
|
+
# primary key.
|
16
|
+
#
|
17
|
+
# @return [Symbol] the name of the primary key field.
|
18
|
+
def self.primary_key
|
19
|
+
instance.primary_key
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the secret access key used for authenticating with Amazon's DynamoDB service
|
23
|
+
#
|
24
|
+
# Can either be set explicitely, or uses AWS.config by default
|
25
|
+
def self.secret_access_key
|
26
|
+
instance.secret_access_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.access_key_id
|
30
|
+
instance.access_key_id
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :primary_key
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@primary_key = :id
|
37
|
+
end
|
38
|
+
|
39
|
+
def access_key_id
|
40
|
+
AWS.config.access_key_id
|
41
|
+
end
|
42
|
+
|
43
|
+
def access_key_id=(id)
|
44
|
+
AWS.config.access_key_id = id
|
45
|
+
end
|
46
|
+
|
47
|
+
def secret_access_key
|
48
|
+
AWS.config.secret_access_key
|
49
|
+
end
|
50
|
+
|
51
|
+
def secret_access_key=(key)
|
52
|
+
AWS.config.secret_access_key = key
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Mince
|
2
|
+
module DynamoDb
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'config'
|
5
|
+
require 'aws/dynamo_db'
|
6
|
+
|
7
|
+
class Connection
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def self.connection
|
11
|
+
instance.connection
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :connection
|
15
|
+
|
16
|
+
def connection
|
17
|
+
@connection ||= AWS::DynamoDB.new(access_key_id: Config.access_key_id, secret_access_key: Config.secret_access_key)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Mince
|
2
|
+
module DynamoDb
|
3
|
+
module DataSanitizer
|
4
|
+
def self.prepare_field_for_storage(field)
|
5
|
+
field.is_a?(Numeric) ? field : field.to_s
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.prepare_hash_for_storage(hash)
|
9
|
+
hash.each{|key, value| hash[key] = prepare_field_for_storage(value) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Mince
|
2
|
+
module DynamoDb # :nodoc:
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'connection'
|
5
|
+
|
6
|
+
class DataStore
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
# Returns the collection, or table, for a given collection name
|
10
|
+
#
|
11
|
+
# The schema must be loaded before any queries are made against a collection
|
12
|
+
# There are a couple ways to do this, from digging in their documentation about
|
13
|
+
# this topic. One is to have the schema loaded in memory, which is not recommended
|
14
|
+
# for production code, the other way is to call hash_key :) not sure why this works.
|
15
|
+
#
|
16
|
+
# @param [String] collection_name the name of the collection
|
17
|
+
# @return [AWS::DynamoDB::Table] the AWS::DynamoDB::Table for the given collection_name
|
18
|
+
def self.collection(collection_name)
|
19
|
+
collections[collection_name.to_s].tap do |c|
|
20
|
+
c.hash_key unless c.schema_loaded?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the database object which comes from MinceDynamoDb::Connection
|
25
|
+
def self.db
|
26
|
+
instance.db
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.collections
|
30
|
+
db.tables
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.items(collection_name)
|
34
|
+
collection(collection_name).items
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_accessor :db
|
38
|
+
|
39
|
+
def db
|
40
|
+
@db ||= Connection.connection
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Mince
|
2
|
+
module DynamoDb # :nodoc:
|
3
|
+
require 'digest'
|
4
|
+
require 'active_support/hash_with_indifferent_access'
|
5
|
+
require_relative 'data_store'
|
6
|
+
require_relative 'data_sanitizer'
|
7
|
+
require_relative 'config'
|
8
|
+
|
9
|
+
module Interface
|
10
|
+
# Not yet implemented
|
11
|
+
def self.update_field_with_value(*args)
|
12
|
+
raise %(The method `MinceDynamoDb::DataStore.singleton.update_field_with_value` is not implemented, you should implement it for us!)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.delete_field(collection_name, field)
|
16
|
+
raise %(The method `MinceDynamoDb::DataStore.singleton.delete_field` is not implemented, you should implement it for us!)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.delete_collection(collection_name)
|
20
|
+
raise %(The method `MinceDynamoDb::DataStore.singleton.delete_collection` is not implemented, you should implement it for us!)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.increment_field_by_amount(collection_name, id, field_name, amount)
|
24
|
+
raise %(The method `MinceDynamoDb::DataStore.singleton.increment_field_by_amount` is not implemented, you should implement it for us!)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
# Returns the primary key identifier for records. This is necessary because not all databases use the same
|
30
|
+
# primary key.
|
31
|
+
#
|
32
|
+
# @return [String] the name of the primary key field.
|
33
|
+
def self.primary_key_identifier
|
34
|
+
Config.primary_key
|
35
|
+
end
|
36
|
+
|
37
|
+
# Generates a unique ID for a database record
|
38
|
+
#
|
39
|
+
# @note This is necessary because different databases use different types of primary key values. Thus, each mince
|
40
|
+
# implementation must define how to generate a unique id.
|
41
|
+
#
|
42
|
+
# @param [#to_s] salt any object that responds to #to_s
|
43
|
+
# @return [String] A unique id based on the salt and the current time
|
44
|
+
def self.generate_unique_id(salt)
|
45
|
+
Digest::SHA256.hexdigest("#{Time.current.utc}#{salt}")[0..6]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Inserts one record into a collection.
|
49
|
+
#
|
50
|
+
# @param [String] collection_name the name of the collection
|
51
|
+
# @param [Hash] hash a hash of data to be added to the collection
|
52
|
+
def self.add(collection_name, hash)
|
53
|
+
hash.delete_if{|k,v| v.nil? }
|
54
|
+
items(collection_name).create(sanitized_hash(hash))
|
55
|
+
end
|
56
|
+
|
57
|
+
# Replaces a record in the collection based on the primary key's value. The hash must contain a key, defined
|
58
|
+
# by the +primary_key_identifier+ method, with a value. If a record in the data store is found with that key and
|
59
|
+
# value, the entire record will be replaced with the given hash.
|
60
|
+
#
|
61
|
+
# @param [String] collection_name the name of the collection
|
62
|
+
# @param [Hash] hash a hash to replace the record in the collection with
|
63
|
+
def self.replace(collection_name, hash)
|
64
|
+
items(collection_name).put(sanitized_hash(hash))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Gets all records that have the value for a given key.
|
68
|
+
#
|
69
|
+
# @param [String] collection_name the name of the collection
|
70
|
+
# @param [String] key the key, or field, to get a record for
|
71
|
+
# @param [*] value the value to get a record for
|
72
|
+
# @return [Array] an array of records that match the key and value
|
73
|
+
def self.get_all_for_key_with_value(collection_name, key, value)
|
74
|
+
get_by_params(collection_name, key => value)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Gets the first record that has the value for a given key.
|
78
|
+
#
|
79
|
+
# @param [String] collection_name the name of the collection
|
80
|
+
# @param [String] key the key to find a record by
|
81
|
+
# @param [String] value the value to find a record by
|
82
|
+
# @return [Hash] a hash for the record found by the key and value in the collection
|
83
|
+
def self.get_for_key_with_value(collection_name, key, value)
|
84
|
+
get_all_for_key_with_value(collection_name, key.to_s, value).first
|
85
|
+
end
|
86
|
+
|
87
|
+
# Gets all records that have all of the keys and values in the given hash.
|
88
|
+
#
|
89
|
+
# @param [String] collection_name the name of the collection
|
90
|
+
# @param [Hash] hash a hash to get a record for
|
91
|
+
# @return [Array] an array of all records matching the given hash for the collection
|
92
|
+
def self.get_by_params(collection_name, hash)
|
93
|
+
hash = HashWithIndifferentAccess.new(hash)
|
94
|
+
array_to_hash(items(collection_name).where(hash).select)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Gets all records for a collection
|
98
|
+
#
|
99
|
+
# @param [String] collection_name the name of the collection
|
100
|
+
# @return [Array] all records for the given collection name
|
101
|
+
def self.find_all(collection_name)
|
102
|
+
array_to_hash(items(collection_name).select)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Gets a record matching a key and value
|
106
|
+
#
|
107
|
+
# @param [String] collection_name the name of the collection
|
108
|
+
# @param [String] key the key to find a record by
|
109
|
+
# @param [*] value a value the find a record by
|
110
|
+
# @return [Hash] a record that matches the given key and value
|
111
|
+
def self.find(collection_name, key, value)
|
112
|
+
get_for_key_with_value(collection_name, key, value)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Pushes a value to a record's key that is an array
|
116
|
+
#
|
117
|
+
# @param [String] collection_name the name of the collection
|
118
|
+
# @param [String] identifying_key the field used to find the record
|
119
|
+
# @param [*] identifying_value the value used to find the record
|
120
|
+
# @param [String] array_key the field to push an array to
|
121
|
+
# @param [*] value_to_push the value to push to the array
|
122
|
+
def self.push_to_array(collection_name, identifying_key, identifying_value, array_key, value_to_push)
|
123
|
+
item = items(collection_name).where(identifying_key.to_s => identifying_value).first
|
124
|
+
item.attributes.add(array_key => [sanitized_field(value_to_push)])
|
125
|
+
end
|
126
|
+
|
127
|
+
# Removes a value from a record's key that is an array
|
128
|
+
#
|
129
|
+
# @param [String] collection_name the name of the collection
|
130
|
+
# @param [String] identifying_key the field used to find the record
|
131
|
+
# @param [*] identifying_value the value used to find the record
|
132
|
+
# @param [String] array_key the field to push an array from
|
133
|
+
# @param [*] value_to_remove the value to remove from the array
|
134
|
+
def self.remove_from_array(collection_name, identifying_key, identifying_value, array_key, value_to_remove)
|
135
|
+
item = items(collection_name).where(identifying_key.to_s => identifying_value).select.first
|
136
|
+
item.attributes.delete(array_key => [value_to_remove])
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns all records where the given key contains any of the values provided
|
140
|
+
#
|
141
|
+
# @param [String] collection_name the name of the collection
|
142
|
+
# @param [String] key the key to find the record by
|
143
|
+
# @param [Array] values an array of values that the record could contain
|
144
|
+
# @return [Array] all records that contain any of the values given
|
145
|
+
def self.containing_any(collection_name, key, values)
|
146
|
+
array_to_hash items(collection_name).where(key.to_sym).in(values).select
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns all records where the given key contains the given value
|
150
|
+
#
|
151
|
+
# @param [String] collection_name the name of the collection
|
152
|
+
# @param [String] key the key to find records by
|
153
|
+
# @param [*] value the value to find a record by
|
154
|
+
# @return [Array] all records where the key contains the given value
|
155
|
+
def self.array_contains(collection_name, key, value)
|
156
|
+
array_to_hash items(collection_name).where(key.to_sym).contains(value).select
|
157
|
+
end
|
158
|
+
|
159
|
+
# Deletes a record that matches the given criteria from the data store.
|
160
|
+
def self.delete_by_params(collection_name, params)
|
161
|
+
item = items(collection_name).where(params).select.first
|
162
|
+
item.delete
|
163
|
+
end
|
164
|
+
|
165
|
+
# Clears the data store.
|
166
|
+
# Mainly used for rolling back the data store in tests.
|
167
|
+
def self.clear
|
168
|
+
db.tables.each do |t|
|
169
|
+
t.hash_key unless t.schema_loaded? # to load the schema
|
170
|
+
t.items.each do |i|
|
171
|
+
i.delete
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.set_data(data)
|
177
|
+
clear
|
178
|
+
|
179
|
+
data.each do |key, records|
|
180
|
+
records.each do |record|
|
181
|
+
add key, record
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns the collection, or table, for a given collection name
|
187
|
+
#
|
188
|
+
# The schema must be loaded before any queries are made against a collection
|
189
|
+
# There are a couple ways to do this, from digging in their documentation about
|
190
|
+
# this topic. One is to have the schema loaded in memory, which is not recommended
|
191
|
+
# for production code, the other way is to call hash_key :) not sure why this works.
|
192
|
+
#
|
193
|
+
# @param [String] collection_name the name of the collection
|
194
|
+
# @return [AWS::DynamoDB::Table] the AWS::DynamoDB::Table for the given collection_name
|
195
|
+
def self.collection(collection_name)
|
196
|
+
DataStore.collection(collection_name)
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.sanitized_field(field)
|
200
|
+
DataSanitizer.prepare_field_for_storage(field)
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.sanitized_hash(hash)
|
204
|
+
DataSanitizer.prepare_hash_for_storage(hash)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Takes a DynamoDB item and returns the attributes of that item as a hash
|
208
|
+
def self.to_hash(item)
|
209
|
+
item.attributes if item
|
210
|
+
end
|
211
|
+
|
212
|
+
# Takes an array of DynamoDB items and returns the attributes of each item as a hash.
|
213
|
+
# calls
|
214
|
+
def self.array_to_hash(array)
|
215
|
+
array.map{|a| to_hash(a) }
|
216
|
+
end
|
217
|
+
|
218
|
+
# Returns the database object which comes from MinceDynamoDb::Connection
|
219
|
+
def self.db
|
220
|
+
DataStore.db
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.collections
|
224
|
+
DataStore.collections
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.items(collection_name)
|
228
|
+
DataStore.items(collection_name)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Mince
|
2
|
+
module DynamoDb
|
3
|
+
module Version
|
4
|
+
def self.major
|
5
|
+
2
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.minor
|
9
|
+
0
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.patch
|
13
|
+
"0.pre.1"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.version
|
18
|
+
[Version.major, Version.minor, Version.patch].join(".")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
data/mince_dynamo_db.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "
|
3
|
+
require "dynamo_db/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "mince_dynamo_db"
|
7
|
-
s.version =
|
7
|
+
s.version = Mince::DynamoDb.version
|
8
8
|
s.authors = ["Matt Simpson"]
|
9
9
|
s.email = ["matt@railsgrammer.com"]
|
10
10
|
s.homepage = "https://github.com/coffeencoke/mince_dynamo_db"
|
@@ -21,8 +21,11 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency 'aws-sdk', "~> 1.3.7"
|
22
22
|
s.add_dependency 'activesupport', "~> 3.0"
|
23
23
|
|
24
|
+
s.add_development_dependency('rake', '~> 0.9')
|
24
25
|
s.add_development_dependency "rspec", "~> 2.8.0"
|
25
26
|
s.add_development_dependency "guard-rspec", "~> 0.6.0"
|
26
27
|
s.add_development_dependency "yard", "~> 0.7.5"
|
27
28
|
s.add_development_dependency "redcarpet", "~> 2.1.1"
|
29
|
+
s.add_development_dependency "fake_dynamo"
|
30
|
+
s.add_development_dependency "mince", "~> 2.0.0.pre.2"
|
28
31
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative '../../lib/dynamo_db'
|
2
|
+
require 'mince/shared_examples/interface_example'
|
3
|
+
|
4
|
+
describe 'Mince Interface with DynamoDb' do
|
5
|
+
pending do
|
6
|
+
before do
|
7
|
+
AWS.config(
|
8
|
+
:use_ssl => false,
|
9
|
+
:dynamo_db_endpoint => 'localhost',
|
10
|
+
:access_key_id => "xxx",
|
11
|
+
:secret_access_key => "xxx"
|
12
|
+
)
|
13
|
+
Mince::Config.interface = Mince::DynamoDb::Interface
|
14
|
+
end
|
15
|
+
|
16
|
+
it_behaves_like 'a mince interface'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../../lib/dynamo_db/config'
|
2
|
+
|
3
|
+
describe Mince::DynamoDb::Config do
|
4
|
+
let(:secret_access_key) { mock }
|
5
|
+
let(:access_key_id) { mock }
|
6
|
+
|
7
|
+
before do
|
8
|
+
AWS.config(access_key_id: access_key_id, secret_access_key: secret_access_key)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'uses "id" as the primary key for the database' do
|
12
|
+
described_class.primary_key.should == :id
|
13
|
+
end
|
14
|
+
|
15
|
+
it "has the credentials for using Amazon's Dynamo DB service" do
|
16
|
+
described_class.secret_access_key.should == secret_access_key
|
17
|
+
described_class.access_key_id.should == access_key_id
|
18
|
+
end
|
19
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require_relative '../../lib/
|
1
|
+
require_relative '../../lib/dynamo_db/connection'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe Mince::DynamoDb::Connection do
|
4
4
|
subject { described_class.instance }
|
5
5
|
|
6
6
|
let(:connection) { mock 'an amazon dynamo db object' }
|
@@ -9,7 +9,7 @@ describe MinceDynamoDb::Connection do
|
|
9
9
|
let(:aws_config) { mock 'aws config object', secret_access_key: secret_access_key, access_key_id: access_key_id }
|
10
10
|
|
11
11
|
before do
|
12
|
-
|
12
|
+
Mince::DynamoDb::Config.stub(access_key_id: access_key_id, secret_access_key: secret_access_key)
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'has a dynamo db connection' do
|
@@ -17,4 +17,4 @@ describe MinceDynamoDb::Connection do
|
|
17
17
|
|
18
18
|
subject.connection.should == connection
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require_relative '../../lib/
|
1
|
+
require_relative '../../lib/dynamo_db/data_sanitizer'
|
2
2
|
|
3
3
|
require 'date'
|
4
4
|
|
5
|
-
describe
|
5
|
+
describe Mince::DynamoDb::DataSanitizer do
|
6
6
|
it 'converts a Time object to a string' do
|
7
7
|
value = Time.now
|
8
8
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative '../../lib/dynamo_db/data_store'
|
2
|
+
|
3
|
+
describe Mince::DynamoDb::DataStore do
|
4
|
+
let(:db) { mock 'db connection', tables: collections }
|
5
|
+
let(:collections) { { collection_name => collection } }
|
6
|
+
let(:collection) { mock 'some collection', items: items, schema_loaded?: true }
|
7
|
+
let(:collection_name) { 'some_collection_name'}
|
8
|
+
let(:items) { mock 'items' }
|
9
|
+
|
10
|
+
before do
|
11
|
+
described_class.instance.db = db # do this each time because it's a singleton
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'when getting a collection' do
|
15
|
+
context 'when the schema has not been loaded' do
|
16
|
+
before do
|
17
|
+
collection.stub(schema_loaded?: false, hash_key: nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'loads the schema' do
|
21
|
+
collection.should_receive(:hash_key)
|
22
|
+
|
23
|
+
described_class.collection(collection_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns the collection' do
|
27
|
+
described_class.collection(collection_name).should == collection
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when the schema has been loaded' do
|
32
|
+
it 'does not load the schema' do
|
33
|
+
collection.should_not_receive(:hash_key)
|
34
|
+
|
35
|
+
described_class.collection(collection_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns the collection' do
|
39
|
+
described_class.collection(collection_name).should == collection
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'can get the items for a given collection' do
|
45
|
+
described_class.items(collection_name).should == items
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can return the db' do
|
49
|
+
described_class.db.should == db
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'can get all collections' do
|
53
|
+
described_class.collections.should == collections
|
54
|
+
end
|
55
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
require_relative '../../lib/
|
2
|
-
|
3
|
-
describe MinceDynamoDb::DataStore do
|
4
|
-
subject { described_class.instance }
|
1
|
+
require_relative '../../lib/dynamo_db/interface'
|
5
2
|
|
3
|
+
describe Mince::DynamoDb::Interface do
|
6
4
|
let(:db) { mock 'dynamo db connection', tables: { collection_name => collection } }
|
7
5
|
let(:mince_dynamo_db_connection) { mock 'mince dynamo db connection', connection: db }
|
8
6
|
let(:collection) { mock 'some collection', items: items, schema_loaded?: true }
|
7
|
+
let(:collections) { mock }
|
9
8
|
let(:collection_name) { 'some_collection_name'}
|
10
9
|
let(:primary_key) { mock 'primary key'}
|
11
10
|
let(:mock_id) { mock 'id' }
|
@@ -18,16 +17,10 @@ describe MinceDynamoDb::DataStore do
|
|
18
17
|
let(:items) { mock 'items' }
|
19
18
|
|
20
19
|
before do
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
it 'uses the correct collection' do
|
26
|
-
subject.collection(collection_name).should == collection
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'has a primary key identifier' do
|
30
|
-
described_class.primary_key_identifier.should == 'id'
|
20
|
+
Mince::DynamoDb::DataStore.stub(:items).with(collection_name).and_return(items)
|
21
|
+
Mince::DynamoDb::DataStore.stub(:collection).with(collection_name).and_return(collection)
|
22
|
+
Mince::DynamoDb::DataStore.stub(collections: collections, db: db)
|
23
|
+
Mince::DynamoDb::DataSanitizer.stub(:prepare_hash_for_storage).with(data).and_return(sanitized_data)
|
31
24
|
end
|
32
25
|
|
33
26
|
describe "Generating a primary key" do
|
@@ -50,21 +43,21 @@ describe MinceDynamoDb::DataStore do
|
|
50
43
|
it 'can write to the collection' do
|
51
44
|
items.should_receive(:create).with(sanitized_data).and_return(return_data)
|
52
45
|
|
53
|
-
|
46
|
+
described_class.add(collection_name, data).should == return_data
|
54
47
|
end
|
55
48
|
|
56
49
|
it 'can read from the collection' do
|
57
50
|
item_attributes = mock 'attributes for an item'
|
58
51
|
item = mock 'item', attributes: item_attributes
|
59
|
-
|
52
|
+
items.should_receive(:select).and_return([item])
|
60
53
|
|
61
|
-
|
54
|
+
described_class.find_all(collection_name).should == [item_attributes]
|
62
55
|
end
|
63
56
|
|
64
57
|
it 'can replace a record' do
|
65
58
|
items.should_receive(:put).with(sanitized_data)
|
66
59
|
|
67
|
-
|
60
|
+
described_class.replace(collection_name, data)
|
68
61
|
end
|
69
62
|
|
70
63
|
it 'can get one document' do
|
@@ -73,7 +66,7 @@ describe MinceDynamoDb::DataStore do
|
|
73
66
|
|
74
67
|
items.should_receive(:where).with(field => value).and_return([return_data])
|
75
68
|
|
76
|
-
|
69
|
+
described_class.find(collection_name, field, value).should == attributes
|
77
70
|
end
|
78
71
|
|
79
72
|
it 'can delete a record that matches a criteria' do
|
@@ -81,7 +74,7 @@ describe MinceDynamoDb::DataStore do
|
|
81
74
|
items.should_receive(:where).with(params).and_return([return_data])
|
82
75
|
return_data.should_receive(:delete)
|
83
76
|
|
84
|
-
|
77
|
+
described_class.delete_by_params(collection_name, params)
|
85
78
|
end
|
86
79
|
|
87
80
|
it 'can clear the data store' do
|
@@ -96,19 +89,19 @@ describe MinceDynamoDb::DataStore do
|
|
96
89
|
item.should_receive(:delete)
|
97
90
|
item2.should_receive(:delete)
|
98
91
|
|
99
|
-
|
92
|
+
described_class.clear
|
100
93
|
end
|
101
94
|
|
102
95
|
it 'can get all records of a specific key value' do
|
103
96
|
items.should_receive(:where).with("key" => "value").and_return([return_data])
|
104
97
|
|
105
|
-
|
98
|
+
described_class.get_all_for_key_with_value(collection_name, "key", "value").should == [attributes]
|
106
99
|
end
|
107
100
|
|
108
101
|
it 'can get a record of a specific key value' do
|
109
102
|
items.should_receive(:where).with({"key" => "value"}).and_return([return_data])
|
110
103
|
|
111
|
-
|
104
|
+
described_class.get_for_key_with_value(collection_name, "key", "value").should == attributes
|
112
105
|
end
|
113
106
|
|
114
107
|
it 'can get all records where a value includes any of a set of values' do
|
@@ -116,7 +109,7 @@ describe MinceDynamoDb::DataStore do
|
|
116
109
|
items.should_receive(:where).with(:key1).and_return(filter)
|
117
110
|
filter.should_receive(:in).with([1,2,4]).and_return([return_data])
|
118
111
|
|
119
|
-
|
112
|
+
described_class.containing_any(collection_name, "key1", [1,2,4]).should == [attributes]
|
120
113
|
end
|
121
114
|
|
122
115
|
it 'can get all records where the array includes a value' do
|
@@ -124,23 +117,23 @@ describe MinceDynamoDb::DataStore do
|
|
124
117
|
items.should_receive(:where).with(:key).and_return(filter)
|
125
118
|
filter.should_receive(:contains).with('value').and_return([return_data])
|
126
119
|
|
127
|
-
|
120
|
+
described_class.array_contains(collection_name, "key", "value").should == [attributes]
|
128
121
|
end
|
129
122
|
|
130
123
|
it 'can push a value to an array for a specific record' do
|
131
124
|
value_to_push = mock 'value to push to the record'
|
132
125
|
sanitized_value_to_push = mock 'sanitized value to push to the record'
|
133
|
-
|
126
|
+
Mince::DynamoDb::DataSanitizer.stub(:prepare_field_for_storage).with(value_to_push).and_return(sanitized_value_to_push)
|
134
127
|
items.should_receive(:where).with("key" => "value").and_return([return_data])
|
135
128
|
attributes.should_receive(:add).with(:array_key => [sanitized_value_to_push])
|
136
129
|
|
137
|
-
|
130
|
+
described_class.push_to_array(collection_name, :key, "value", :array_key, value_to_push)
|
138
131
|
end
|
139
132
|
|
140
133
|
it 'can remove a value from an array for a specific record' do
|
141
134
|
items.should_receive(:where).with("key" => "value").and_return([return_data])
|
142
135
|
attributes.should_receive(:delete).with(:array_key => ["value_to_remove"])
|
143
136
|
|
144
|
-
|
137
|
+
described_class.remove_from_array(collection_name, :key, "value", :array_key, "value_to_remove")
|
145
138
|
end
|
146
139
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mince_dynamo_db
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0.pre.1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Matt Simpson
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '3.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.9'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
46
62
|
- !ruby/object:Gem::Dependency
|
47
63
|
name: rspec
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -107,6 +123,38 @@ dependencies:
|
|
107
123
|
- - ~>
|
108
124
|
- !ruby/object:Gem::Version
|
109
125
|
version: 2.1.1
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: fake_dynamo
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: mince
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 2.0.0.pre.2
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 2.0.0.pre.2
|
110
158
|
description: Lightweight ORM for Amazon's DynamoDB with Ruby Apps
|
111
159
|
email:
|
112
160
|
- matt@railsgrammer.com
|
@@ -117,21 +165,25 @@ files:
|
|
117
165
|
- .gitignore
|
118
166
|
- .rspec
|
119
167
|
- .rvmrc
|
168
|
+
- .travis.yml
|
120
169
|
- Gemfile
|
121
170
|
- Guardfile
|
122
171
|
- README.md
|
123
172
|
- Rakefile
|
124
|
-
- lib/
|
125
|
-
- lib/
|
126
|
-
- lib/
|
127
|
-
- lib/
|
128
|
-
- lib/
|
173
|
+
- lib/dynamo_db.rb
|
174
|
+
- lib/dynamo_db/config.rb
|
175
|
+
- lib/dynamo_db/connection.rb
|
176
|
+
- lib/dynamo_db/data_sanitizer.rb
|
177
|
+
- lib/dynamo_db/data_store.rb
|
178
|
+
- lib/dynamo_db/interface.rb
|
179
|
+
- lib/dynamo_db/version.rb
|
129
180
|
- mince_dynamo_db.gemspec
|
130
|
-
- spec/integration/
|
131
|
-
- spec/
|
132
|
-
- spec/
|
133
|
-
- spec/
|
134
|
-
- spec/
|
181
|
+
- spec/integration/mince_interface_spec.rb
|
182
|
+
- spec/units/config_spec.rb
|
183
|
+
- spec/units/connection_spec.rb
|
184
|
+
- spec/units/data_sanitizer_spec.rb
|
185
|
+
- spec/units/data_store_spec.rb
|
186
|
+
- spec/units/interface_spec.rb
|
135
187
|
homepage: https://github.com/coffeencoke/mince_dynamo_db
|
136
188
|
licenses: []
|
137
189
|
post_install_message:
|
@@ -144,12 +196,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
196
|
- - ! '>='
|
145
197
|
- !ruby/object:Gem::Version
|
146
198
|
version: '0'
|
199
|
+
segments:
|
200
|
+
- 0
|
201
|
+
hash: -4539691897538722192
|
147
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
203
|
none: false
|
149
204
|
requirements:
|
150
|
-
- - ! '
|
205
|
+
- - ! '>'
|
151
206
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
207
|
+
version: 1.3.1
|
153
208
|
requirements: []
|
154
209
|
rubyforge_project: mince_dynamo_db
|
155
210
|
rubygems_version: 1.8.24
|
@@ -157,9 +212,10 @@ signing_key:
|
|
157
212
|
specification_version: 3
|
158
213
|
summary: Lightweight ORM for Amazon's DynamoDB with Ruby Apps
|
159
214
|
test_files:
|
160
|
-
- spec/integration/
|
161
|
-
- spec/
|
162
|
-
- spec/
|
163
|
-
- spec/
|
164
|
-
- spec/
|
215
|
+
- spec/integration/mince_interface_spec.rb
|
216
|
+
- spec/units/config_spec.rb
|
217
|
+
- spec/units/connection_spec.rb
|
218
|
+
- spec/units/data_sanitizer_spec.rb
|
219
|
+
- spec/units/data_store_spec.rb
|
220
|
+
- spec/units/interface_spec.rb
|
165
221
|
has_rdoc:
|
data/lib/mince_dynamo_db.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'aws'
|
2
|
-
require 'singleton'
|
3
|
-
|
4
|
-
module MinceDynamoDb
|
5
|
-
class Connection
|
6
|
-
include Singleton
|
7
|
-
|
8
|
-
attr_reader :connection
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
@connection = AWS::DynamoDB.new access_key_id: access_key_id, secret_access_key: secret_access_key
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def access_key_id
|
17
|
-
AWS.config.access_key_id
|
18
|
-
end
|
19
|
-
|
20
|
-
def secret_access_key
|
21
|
-
AWS.config.secret_access_key
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module MinceDynamoDb
|
2
|
-
module DataSanitizer
|
3
|
-
def self.prepare_field_for_storage(field)
|
4
|
-
field.is_a?(Numeric) ? field : field.to_s
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.prepare_hash_for_storage(hash)
|
8
|
-
hash.each{|key, value| hash[key] = prepare_field_for_storage(value) }
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
@@ -1,269 +0,0 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
require 'digest'
|
3
|
-
require 'active_support/hash_with_indifferent_access'
|
4
|
-
|
5
|
-
require_relative 'connection'
|
6
|
-
require_relative 'data_sanitizer'
|
7
|
-
|
8
|
-
module MinceDynamoDb # :nodoc:
|
9
|
-
# = Mince DynamoDb Data Store
|
10
|
-
#
|
11
|
-
# Mince DynamoDb Data Store stores and retrieves data from a DynamoDB table. It supports the same methods
|
12
|
-
# as the following libraries:
|
13
|
-
#
|
14
|
-
# Mince::
|
15
|
-
# A lightweight ruby library to store and retrieve data from MongoDB (https://github.com/asynchrony/mince)
|
16
|
-
# HashyDb::
|
17
|
-
# A lightweight ruby library to store and retrieve data from a hash in-memory. (https://github.com/asynchrony/hashy_db)
|
18
|
-
#
|
19
|
-
# Using this library offers more extensibility and growth for your application. If at any point in time
|
20
|
-
# you want to support a different database for all, or even one, of your classes, you only need to implement
|
21
|
-
# the few methods defined in this class.
|
22
|
-
#
|
23
|
-
# You can use the {https://github.com/asynchrony/mince_data_model Mince Data Model} as a helper library to
|
24
|
-
# provide support in writing an application using these data persistance libraries.
|
25
|
-
#
|
26
|
-
# To use the Data Store, define your DynamoDB access_key_id and secret_access_key. If you are using rails
|
27
|
-
# you can do this by creating a file at config/aws.yml with the following contents:
|
28
|
-
#
|
29
|
-
# development:
|
30
|
-
# access_key_id: REPLACE_WITH_ACCESS_KEY_ID
|
31
|
-
# secret_access_key: REPLACE_WITH_SECRET_ACCESS_KEY
|
32
|
-
#
|
33
|
-
# Otherwise, you can set the configurations like so:
|
34
|
-
#
|
35
|
-
# require 'aws'
|
36
|
-
#
|
37
|
-
# AWS.config.access_key_id = REPLACE_WITH_ACCESS_KEY_ID
|
38
|
-
# AWS.config.secret_access_key = REPLACE_WITH_SECRET_ACCESS_KEY
|
39
|
-
#
|
40
|
-
# View the aws documentation for more details at http://docs.amazonwebservices.com/AWSRubySDK/latest/frames.html
|
41
|
-
#
|
42
|
-
# Once you have the settings configured, you can start storing and retrieving data:
|
43
|
-
#
|
44
|
-
# data_store = MinceDynamoDb::DataStore.instance
|
45
|
-
# data_store.add 'fruits', id: '1', name: 'Shnawzberry', color: 'redish', quantity: '20'
|
46
|
-
# data_store.get_for_key_with_value 'fruits', :color, 'redish'
|
47
|
-
#
|
48
|
-
# @author Matt Simpson
|
49
|
-
class DataStore
|
50
|
-
include Singleton
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# Not yet implemented
|
55
|
-
def update_field_with_value(*args)
|
56
|
-
raise %(The method `MinceDynamoDb::DataStore.singleton.update_field_with_value` is not implemented, you should implement it for us!)
|
57
|
-
end
|
58
|
-
|
59
|
-
def delete_field(collection_name, field)
|
60
|
-
raise %(The method `MinceDynamoDb::DataStore.singleton.delete_field` is not implemented, you should implement it for us!)
|
61
|
-
end
|
62
|
-
|
63
|
-
def delete_collection(collection_name)
|
64
|
-
raise %(The method `MinceDynamoDb::DataStore.singleton.delete_collection` is not implemented, you should implement it for us!)
|
65
|
-
end
|
66
|
-
|
67
|
-
def increment_field_by_amount(collection_name, id, field_name, amount)
|
68
|
-
raise %(The method `MinceDynamoDb::DataStore.singleton.increment_field_by_amount` is not implemented, you should implement it for us!)
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
# Returns the primary key identifier for records. This is necessary because not all databases use the same
|
74
|
-
# primary key.
|
75
|
-
#
|
76
|
-
# @return [String] the name of the primary key field.
|
77
|
-
def self.primary_key_identifier
|
78
|
-
'id'
|
79
|
-
end
|
80
|
-
|
81
|
-
# Generates a unique ID for a database record
|
82
|
-
#
|
83
|
-
# @note This is necessary because different databases use different types of primary key values. Thus, each mince
|
84
|
-
# implementation must define how to generate a unique id.
|
85
|
-
#
|
86
|
-
# @param [#to_s] salt any object that responds to #to_s
|
87
|
-
# @return [String] A unique id based on the salt and the current time
|
88
|
-
def self.generate_unique_id(salt)
|
89
|
-
Digest::SHA256.hexdigest("#{Time.current.utc}#{salt}")[0..6]
|
90
|
-
end
|
91
|
-
|
92
|
-
# Inserts one record into a collection.
|
93
|
-
#
|
94
|
-
# @param [String] collection_name the name of the collection
|
95
|
-
# @param [Hash] hash a hash of data to be added to the collection
|
96
|
-
def add(collection_name, hash)
|
97
|
-
hash.delete_if{|k,v| v.nil? }
|
98
|
-
items(collection_name).create(sanitized_hash(hash))
|
99
|
-
end
|
100
|
-
|
101
|
-
# Replaces a record in the collection based on the primary key's value. The hash must contain a key, defined
|
102
|
-
# by the +primary_key_identifier+ method, with a value. If a record in the data store is found with that key and
|
103
|
-
# value, the entire record will be replaced with the given hash.
|
104
|
-
#
|
105
|
-
# @param [String] collection_name the name of the collection
|
106
|
-
# @param [Hash] hash a hash to replace the record in the collection with
|
107
|
-
def replace(collection_name, hash)
|
108
|
-
items(collection_name).put(sanitized_hash(hash))
|
109
|
-
end
|
110
|
-
|
111
|
-
# Gets all records that have the value for a given key.
|
112
|
-
#
|
113
|
-
# @param [String] collection_name the name of the collection
|
114
|
-
# @param [String] key the key, or field, to get a record for
|
115
|
-
# @param [*] value the value to get a record for
|
116
|
-
# @return [Array] an array of records that match the key and value
|
117
|
-
def get_all_for_key_with_value(collection_name, key, value)
|
118
|
-
get_by_params(collection_name, key => value)
|
119
|
-
end
|
120
|
-
|
121
|
-
# Gets the first record that has the value for a given key.
|
122
|
-
#
|
123
|
-
# @param [String] collection_name the name of the collection
|
124
|
-
# @param [String] key the key to find a record by
|
125
|
-
# @param [String] value the value to find a record by
|
126
|
-
# @return [Hash] a hash for the record found by the key and value in the collection
|
127
|
-
def get_for_key_with_value(collection_name, key, value)
|
128
|
-
get_all_for_key_with_value(collection_name, key.to_s, value).first
|
129
|
-
end
|
130
|
-
|
131
|
-
# Gets all records that have all of the keys and values in the given hash.
|
132
|
-
#
|
133
|
-
# @param [String] collection_name the name of the collection
|
134
|
-
# @param [Hash] hash a hash to get a record for
|
135
|
-
# @return [Array] an array of all records matching the given hash for the collection
|
136
|
-
def get_by_params(collection_name, hash)
|
137
|
-
hash = HashWithIndifferentAccess.new(hash)
|
138
|
-
array_to_hash(items(collection_name).where(hash).select)
|
139
|
-
end
|
140
|
-
|
141
|
-
# Gets all records for a collection
|
142
|
-
#
|
143
|
-
# @param [String] collection_name the name of the collection
|
144
|
-
# @return [Array] all records for the given collection name
|
145
|
-
def find_all(collection_name)
|
146
|
-
array_to_hash(items(collection_name).select)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Gets a record matching a key and value
|
150
|
-
#
|
151
|
-
# @param [String] collection_name the name of the collection
|
152
|
-
# @param [String] key the key to find a record by
|
153
|
-
# @param [*] value a value the find a record by
|
154
|
-
# @return [Hash] a record that matches the given key and value
|
155
|
-
def find(collection_name, key, value)
|
156
|
-
get_for_key_with_value(collection_name, key, value)
|
157
|
-
end
|
158
|
-
|
159
|
-
# Pushes a value to a record's key that is an array
|
160
|
-
#
|
161
|
-
# @param [String] collection_name the name of the collection
|
162
|
-
# @param [String] identifying_key the field used to find the record
|
163
|
-
# @param [*] identifying_value the value used to find the record
|
164
|
-
# @param [String] array_key the field to push an array to
|
165
|
-
# @param [*] value_to_push the value to push to the array
|
166
|
-
def push_to_array(collection_name, identifying_key, identifying_value, array_key, value_to_push)
|
167
|
-
item = items(collection_name).where(identifying_key.to_s => identifying_value).first
|
168
|
-
item.attributes.add(array_key => [sanitized_field(value_to_push)])
|
169
|
-
end
|
170
|
-
|
171
|
-
# Removes a value from a record's key that is an array
|
172
|
-
#
|
173
|
-
# @param [String] collection_name the name of the collection
|
174
|
-
# @param [String] identifying_key the field used to find the record
|
175
|
-
# @param [*] identifying_value the value used to find the record
|
176
|
-
# @param [String] array_key the field to push an array from
|
177
|
-
# @param [*] value_to_remove the value to remove from the array
|
178
|
-
def remove_from_array(collection_name, identifying_key, identifying_value, array_key, value_to_remove)
|
179
|
-
item = items(collection_name).where(identifying_key.to_s => identifying_value).select.first
|
180
|
-
item.attributes.delete(array_key => [value_to_remove])
|
181
|
-
end
|
182
|
-
|
183
|
-
# Returns all records where the given key contains any of the values provided
|
184
|
-
#
|
185
|
-
# @param [String] collection_name the name of the collection
|
186
|
-
# @param [String] key the key to find the record by
|
187
|
-
# @param [Array] values an array of values that the record could contain
|
188
|
-
# @return [Array] all records that contain any of the values given
|
189
|
-
def containing_any(collection_name, key, values)
|
190
|
-
array_to_hash items(collection_name).where(key.to_sym).in(values).select
|
191
|
-
end
|
192
|
-
|
193
|
-
# Returns all records where the given key contains the given value
|
194
|
-
#
|
195
|
-
# @param [String] collection_name the name of the collection
|
196
|
-
# @param [String] key the key to find records by
|
197
|
-
# @param [*] value the value to find a record by
|
198
|
-
# @return [Array] all records where the key contains the given value
|
199
|
-
def array_contains(collection_name, key, value)
|
200
|
-
array_to_hash items(collection_name).where(key.to_sym).contains(value).select
|
201
|
-
end
|
202
|
-
|
203
|
-
# Deletes a record that matches the given criteria from the data store.
|
204
|
-
def delete_by_params(collection_name, params)
|
205
|
-
item = items(collection_name).where(params).select.first
|
206
|
-
item.delete
|
207
|
-
end
|
208
|
-
|
209
|
-
# Clears the data store.
|
210
|
-
# Mainly used for rolling back the data store in tests.
|
211
|
-
def clear
|
212
|
-
db.tables.each do |t|
|
213
|
-
t.hash_key unless t.schema_loaded? # to load the schema
|
214
|
-
t.items.each do |i|
|
215
|
-
i.delete
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
# Returns the collection, or table, for a given collection name
|
221
|
-
#
|
222
|
-
# The schema must be loaded before any queries are made against a collection
|
223
|
-
# There are a couple ways to do this, from digging in their documentation about
|
224
|
-
# this topic. One is to have the schema loaded in memory, which is not recommended
|
225
|
-
# for production code, the other way is to call hash_key :) not sure why this works.
|
226
|
-
#
|
227
|
-
# @param [String] collection_name the name of the collection
|
228
|
-
# @return [AWS::DynamoDB::Table] the AWS::DynamoDB::Table for the given collection_name
|
229
|
-
def collection(collection_name)
|
230
|
-
db.tables[collection_name.to_s].tap do |c|
|
231
|
-
c.hash_key unless c.schema_loaded?
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
private
|
236
|
-
|
237
|
-
def sanitized_field(field)
|
238
|
-
DataSanitizer.prepare_field_for_storage(field)
|
239
|
-
end
|
240
|
-
|
241
|
-
def sanitized_hash(hash)
|
242
|
-
DataSanitizer.prepare_hash_for_storage(hash)
|
243
|
-
end
|
244
|
-
|
245
|
-
# Takes a DynamoDB item and returns the attributes of that item as a hash
|
246
|
-
def to_hash(item)
|
247
|
-
item.attributes if item
|
248
|
-
end
|
249
|
-
|
250
|
-
# Takes an array of DynamoDB items and returns the attributes of each item as a hash.
|
251
|
-
# calls
|
252
|
-
def array_to_hash(array)
|
253
|
-
array.map{|a| to_hash(a) }
|
254
|
-
end
|
255
|
-
|
256
|
-
# Returns the database object which comes from MinceDynamoDb::Connection
|
257
|
-
def db
|
258
|
-
MinceDynamoDb::Connection.instance.connection
|
259
|
-
end
|
260
|
-
|
261
|
-
def collections
|
262
|
-
db.tables
|
263
|
-
end
|
264
|
-
|
265
|
-
def items(collection_name)
|
266
|
-
collection(collection_name).items
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
require_relative '../../lib/mince_dynamo_db'
|
2
|
-
require 'aws'
|
3
|
-
|
4
|
-
describe 'Testing mince_dynamo_db/data_store while actually hitting dynamo db:' do
|
5
|
-
AWS.config.access_key_id = 'enter_access_key_id_here'
|
6
|
-
AWS.config.secret_access_key = 'enter_secret_access_key_here'
|
7
|
-
|
8
|
-
# Ensure clear db before tests
|
9
|
-
before(:all) do
|
10
|
-
MinceDynamoeDb::DataStore.instance.clear
|
11
|
-
end
|
12
|
-
|
13
|
-
# Ensure clear db after tests
|
14
|
-
after(:all) do
|
15
|
-
MinceDynamoeDb::DataStore.instance.clear
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
File without changes
|