mince_dynamo_db 1.3.1 → 2.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|