mince_dynamo_db 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/README.md +59 -0
- data/Rakefile +12 -0
- data/lib/mince_dynamo_db/connection.rb +24 -0
- data/lib/mince_dynamo_db/data_store.rb +233 -0
- data/lib/mince_dynamo_db/version.rb +3 -0
- data/lib/mince_dynamo_db.rb +3 -0
- data/mince_dynamo_db.gemspec +28 -0
- data/spec/integration/persisting_to_dynamodb_test.rb +18 -0
- data/spec/lib/connection_spec.rb +20 -0
- data/spec/lib/data_store_spec.rb +133 -0
- data/spec/support/aws_example.yml +0 -0
- metadata +132 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@mince_dynamo_db --create
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
#
|
4
|
+
|
5
|
+
guard 'rspec', :version => 2 do
|
6
|
+
watch(%r{^spec/lib/.+_spec\.rb$})
|
7
|
+
watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
8
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
9
|
+
watch(%r{^spec/support/shared_examples/(.+)\.rb$}) { "spec" }
|
10
|
+
end
|
11
|
+
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# What is mince dynamo db?
|
2
|
+
|
3
|
+
Light weight ORM to persist data to an Amazon DynamoDB database.
|
4
|
+
|
5
|
+
Provides a very light weight interface for storing and retreiving information to DynamoDB.
|
6
|
+
|
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
|
+
|
9
|
+
[@github](https://github.com/coffeencoke/mince_dynamo_db)
|
10
|
+
[@rubygems (not yet published)](#)
|
11
|
+
|
12
|
+
# How to use
|
13
|
+
|
14
|
+
view the [example mince rails app](https://github.com/coffeencoke/mince_rails_example) to see how to use this.
|
15
|
+
|
16
|
+
<pre>
|
17
|
+
# Add a book to the books collection
|
18
|
+
MinceDynamoDb::DataStore.add 'books', title: 'The World In Photographs', publisher: 'National Geographic'
|
19
|
+
|
20
|
+
# Retrieve all records from the books collection
|
21
|
+
MinceDynamoDb::DataStore.find_all 'books'
|
22
|
+
|
23
|
+
# Replace a specific book
|
24
|
+
MinceDynamoDb::DataStore.replace 'books', id: 1, title: 'A World In Photographs', publisher: 'National Geographic'
|
25
|
+
</pre>
|
26
|
+
|
27
|
+
View the docs for MinceDynamoDb::DataStore for all methods available.
|
28
|
+
|
29
|
+
Use with [mince data model](https://github.com/asynchrony/mince_data_model) to make it easy to change from one data storage to another, like [Hashy Db](https://github.com/asynchrony/hashy_db), a Hash data persistence implementation, or [Mince](https://github.com/asynchrony/mince), a MongoDB implementation.
|
30
|
+
|
31
|
+
# Why would you want this?
|
32
|
+
|
33
|
+
- To defer choosing your database until you know most about your application.
|
34
|
+
- Provides assitance in designing a database agnostic architecture.
|
35
|
+
- When used along with [Hashy Db](https://github.com/asynchrony/hashy_db) it offers very little technical dependencies. Use Hashy Db in development mode so that you can clone the repo and develop, and run tests, cucumbers without databases, migrations, etc. Then in production mode, switch to Mince Dynamo DB.
|
36
|
+
|
37
|
+
If you are able to switch between Hashy Db and Mince Dynamo Db, your application will be more open to new and improved database in the future, or as your application evolves you aren't tied to a database.
|
38
|
+
|
39
|
+
|
40
|
+
# Todo
|
41
|
+
|
42
|
+
- Write integration specs
|
43
|
+
- Do not use singleton for data store
|
44
|
+
- Refactor data store
|
45
|
+
- Remove dependency on Active Support
|
46
|
+
|
47
|
+
# Contribute
|
48
|
+
|
49
|
+
- fork into a topic branch, write specs, make a pull request.
|
50
|
+
|
51
|
+
# Owners
|
52
|
+
|
53
|
+
Matt Simpson - [@railsgrammer](https://twitter.com/railsgrammer)
|
54
|
+
|
55
|
+
# Contributors
|
56
|
+
|
57
|
+
- Your name here!
|
58
|
+
|
59
|
+
![Mince Some App](https://github.com/coffeencoke/gist-files/raw/master/images/mince%20garlic.png)
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
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
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'digest'
|
3
|
+
require 'active_support/hash_with_indifferent_access'
|
4
|
+
|
5
|
+
require_relative 'connection'
|
6
|
+
|
7
|
+
module MinceDynamoDb # :nodoc:
|
8
|
+
# = Mince DynamoDb Data Store
|
9
|
+
#
|
10
|
+
# Mince DynamoDb Data Store stores and retrieves data from a DynamoDB table. It supports the same methods
|
11
|
+
# as the following libraries:
|
12
|
+
#
|
13
|
+
# Mince::
|
14
|
+
# A lightweight ruby library to store and retrieve data from MongoDB (https://github.com/asynchrony/mince)
|
15
|
+
# HashyDb::
|
16
|
+
# A lightweight ruby library to store and retrieve data from a hash in-memory. (https://github.com/asynchrony/hashy_db)
|
17
|
+
#
|
18
|
+
# Using this library offers more extensibility and growth for your application. If at any point in time
|
19
|
+
# you want to support a different database for all, or even one, of your classes, you only need to implement
|
20
|
+
# the few methods defined in this class.
|
21
|
+
#
|
22
|
+
# You can use the {https://github.com/asynchrony/mince_data_model Mince Data Model} as a helper library to
|
23
|
+
# provide support in writing an application using these data persistance libraries.
|
24
|
+
#
|
25
|
+
# To use the Data Store, define your DynamoDB access_key_id and secret_access_key. If you are using rails
|
26
|
+
# you can do this by creating a file at config/aws.yml with the following contents:
|
27
|
+
#
|
28
|
+
# development:
|
29
|
+
# access_key_id: REPLACE_WITH_ACCESS_KEY_ID
|
30
|
+
# secret_access_key: REPLACE_WITH_SECRET_ACCESS_KEY
|
31
|
+
#
|
32
|
+
# Otherwise, you can set the configurations like so:
|
33
|
+
#
|
34
|
+
# require 'aws'
|
35
|
+
#
|
36
|
+
# AWS.config.access_key_id = REPLACE_WITH_ACCESS_KEY_ID
|
37
|
+
# AWS.config.secret_access_key = REPLACE_WITH_SECRET_ACCESS_KEY
|
38
|
+
#
|
39
|
+
# View the aws documentation for more details at http://docs.amazonwebservices.com/AWSRubySDK/latest/frames.html
|
40
|
+
#
|
41
|
+
# Once you have the settings configured, you can start storing and retrieving data:
|
42
|
+
#
|
43
|
+
# data_store = MinceDynamoDb::DataStore.instance
|
44
|
+
# data_store.add 'fruits', id: '1', name: 'Shnawzberry', color: 'redish', quantity: '20'
|
45
|
+
# data_store.get_for_key_with_value 'fruits', :color, 'redish'
|
46
|
+
#
|
47
|
+
# @author Matt Simpson
|
48
|
+
class DataStore
|
49
|
+
include Singleton
|
50
|
+
|
51
|
+
# Returns the primary key identifier for records. This is necessary because not all databases use the same
|
52
|
+
# primary key.
|
53
|
+
#
|
54
|
+
# @return [String] the name of the primary key field.
|
55
|
+
def self.primary_key_identifier
|
56
|
+
'id'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generates a unique ID for a database record
|
60
|
+
#
|
61
|
+
# @note This is necessary because different databases use different types of primary key values. Thus, each mince
|
62
|
+
# implementation must define how to generate a unique id.
|
63
|
+
#
|
64
|
+
# @param [#to_s] salt any object that responds to #to_s
|
65
|
+
# @return [String] A unique id based on the salt and the current time
|
66
|
+
def self.generate_unique_id(salt)
|
67
|
+
Digest::SHA256.hexdigest("#{Time.current.utc}#{salt}")[0..6]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Inserts one record into a collection.
|
71
|
+
#
|
72
|
+
# @param [String] collection_name the name of the collection
|
73
|
+
# @param [Hash] hash a hash of data to be added to the collection
|
74
|
+
def add(collection_name, hash)
|
75
|
+
hash.delete_if{|k,v| v.nil? }
|
76
|
+
items(collection_name).create(hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Replaces a record in the collection based on the primary key's value. The hash must contain a key, defined
|
80
|
+
# by the +primary_key_identifier+ method, with a value. If a record in the data store is found with that key and
|
81
|
+
# value, the entire record will be replaced with the given hash.
|
82
|
+
#
|
83
|
+
# @param [String] collection_name the name of the collection
|
84
|
+
# @param [Hash] hash a hash to replace the record in the collection with
|
85
|
+
def replace(collection_name, hash)
|
86
|
+
items(collection_name).put(hash)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Gets all records that have the value for a given key.
|
90
|
+
#
|
91
|
+
# @param [String] collection_name the name of the collection
|
92
|
+
# @param [String] key the key, or field, to get a record for
|
93
|
+
# @param [*] value the value to get a record for
|
94
|
+
# @return [Array] an array of records that match the key and value
|
95
|
+
def get_all_for_key_with_value(collection_name, key, value)
|
96
|
+
get_by_params(collection_name, key => value)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Gets the first record that has the value for a given key.
|
100
|
+
#
|
101
|
+
# @param [String] collection_name the name of the collection
|
102
|
+
# @param [String] key the key to find a record by
|
103
|
+
# @param [String] value the value to find a record by
|
104
|
+
# @return [Hash] a hash for the record found by the key and value in the collection
|
105
|
+
def get_for_key_with_value(collection_name, key, value)
|
106
|
+
get_all_for_key_with_value(collection_name, key.to_s, value).first
|
107
|
+
end
|
108
|
+
|
109
|
+
# Gets all records that have all of the keys and values in the given hash.
|
110
|
+
#
|
111
|
+
# @param [String] collection_name the name of the collection
|
112
|
+
# @param [Hash] hash a hash to get a record for
|
113
|
+
# @return [Array] an array of all records matching the given hash for the collection
|
114
|
+
def get_by_params(collection_name, hash)
|
115
|
+
hash = HashWithIndifferentAccess.new(hash)
|
116
|
+
array_to_hash(items(collection_name).where(hash))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Gets all records for a collection
|
120
|
+
#
|
121
|
+
# @param [String] collection_name the name of the collection
|
122
|
+
# @return [Array] all records for the given collection name
|
123
|
+
def find_all(collection_name)
|
124
|
+
array_to_hash(items(collection_name))
|
125
|
+
end
|
126
|
+
|
127
|
+
# Gets a record matching a key and value
|
128
|
+
#
|
129
|
+
# @param [String] collection_name the name of the collection
|
130
|
+
# @param [String] key the key to find a record by
|
131
|
+
# @param [*] value a value the find a record by
|
132
|
+
# @return [Hash] a record that matches the given key and value
|
133
|
+
def find(collection_name, key, value)
|
134
|
+
get_for_key_with_value(collection_name, key, value)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Pushes a value to a record's key that is an array
|
138
|
+
#
|
139
|
+
# @param [String] collection_name the name of the collection
|
140
|
+
# @param [String] identifying_key the field used to find the record
|
141
|
+
# @param [*] identifying_value the value used to find the record
|
142
|
+
# @param [String] array_key the field to push an array to
|
143
|
+
# @param [*] value_to_push the value to push to the array
|
144
|
+
def push_to_array(collection_name, identifying_key, identifying_value, array_key, value_to_push)
|
145
|
+
item = items(collection_name).where(identifying_key.to_s => identifying_value).first
|
146
|
+
item.attributes.add(array_key => [value_to_push])
|
147
|
+
end
|
148
|
+
|
149
|
+
# Removes a value from a record's key that is an array
|
150
|
+
#
|
151
|
+
# @param [String] collection_name the name of the collection
|
152
|
+
# @param [String] identifying_key the field used to find the record
|
153
|
+
# @param [*] identifying_value the value used to find the record
|
154
|
+
# @param [String] array_key the field to push an array from
|
155
|
+
# @param [*] value_to_remove the value to remove from the array
|
156
|
+
def remove_from_array(collection_name, identifying_key, identifying_value, array_key, value_to_remove)
|
157
|
+
item = items(collection_name).where(identifying_key.to_s => identifying_value).first
|
158
|
+
item.attributes.delete(array_key => [value_to_remove])
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns all records where the given key contains any of the values provided
|
162
|
+
#
|
163
|
+
# @param [String] collection_name the name of the collection
|
164
|
+
# @param [String] key the key to find the record by
|
165
|
+
# @param [Array] values an array of values that the record could contain
|
166
|
+
# @return [Array] all records that contain any of the values given
|
167
|
+
def containing_any(collection_name, key, values)
|
168
|
+
array_to_hash items(collection_name).where(key.to_sym).in(values)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns all records where the given key contains the given value
|
172
|
+
#
|
173
|
+
# @param [String] collection_name the name of the collection
|
174
|
+
# @param [String] key the key to find records by
|
175
|
+
# @param [*] value the value to find a record by
|
176
|
+
# @return [Array] all records where the key contains the given value
|
177
|
+
def array_contains(collection_name, key, value)
|
178
|
+
array_to_hash items(collection_name).where(key.to_sym).contains(value)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Clears the data store.
|
182
|
+
# Mainly used for rolling back the data store in tests.
|
183
|
+
def clear
|
184
|
+
db.tables.each do |t|
|
185
|
+
t.hash_key unless t.schema_loaded? # to load the schema
|
186
|
+
t.items.each do |i|
|
187
|
+
i.delete
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns the collection, or table, for a given collection name
|
193
|
+
#
|
194
|
+
# The schema must be loaded before any queries are made against a collection
|
195
|
+
# There are a couple ways to do this, from digging in their documentation about
|
196
|
+
# this topic. One is to have the schema loaded in memory, which is not recommended
|
197
|
+
# for production code, the other way is to call hash_key :) not sure why this works.
|
198
|
+
#
|
199
|
+
# @param [String] collection_name the name of the collection
|
200
|
+
# @return [AWS::DynamoDB::Table] the AWS::DynamoDB::Table for the given collection_name
|
201
|
+
def collection(collection_name)
|
202
|
+
db.tables[collection_name.to_s].tap do |c|
|
203
|
+
c.hash_key unless c.schema_loaded?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
# Takes a DynamoDB item and returns the attributes of that item as a hash
|
210
|
+
def to_hash(item)
|
211
|
+
item.attributes.to_h if item
|
212
|
+
end
|
213
|
+
|
214
|
+
# Takes an array of DynamoDB items and returns the attributes of each item as a hash.
|
215
|
+
# calls
|
216
|
+
def array_to_hash(array)
|
217
|
+
array.map{|a| to_hash(a) }
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns the database object which comes from MinceDynamoDb::Connection
|
221
|
+
def db
|
222
|
+
MinceDynamoDb::Connection.instance.connection
|
223
|
+
end
|
224
|
+
|
225
|
+
def collections
|
226
|
+
db.tables
|
227
|
+
end
|
228
|
+
|
229
|
+
def items(collection_name)
|
230
|
+
collection(collection_name).items
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mince_dynamo_db/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mince_dynamo_db"
|
7
|
+
s.version = MinceDynamoDb::VERSION
|
8
|
+
s.authors = ["Matt Simpson"]
|
9
|
+
s.email = ["matt@railsgrammer.com"]
|
10
|
+
s.homepage = "https://github.com/coffeencoke/mince_dynamo_db"
|
11
|
+
s.summary = %q{Lightweight ORM for Amazon's DynamoDB with Ruby Apps}
|
12
|
+
s.description = %q{Lightweight ORM for Amazon's DynamoDB with Ruby Apps}
|
13
|
+
|
14
|
+
s.rubyforge_project = "mince_dynamo_db"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency 'aws-sdk', "~> 1.3.7"
|
22
|
+
s.add_dependency 'activesupport', "~> 3.0"
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec", "~> 2.8.0"
|
25
|
+
s.add_development_dependency "guard-rspec", "~> 0.6.0"
|
26
|
+
s.add_development_dependency "yard", "~> 0.7.5"
|
27
|
+
s.add_development_dependency "redcarpet", "~> 2.1.1"
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative '../../lib/mince_dynamo_db/connection'
|
2
|
+
|
3
|
+
describe MinceDynamoDb::Connection do
|
4
|
+
subject { described_class.instance }
|
5
|
+
|
6
|
+
let(:connection) { mock 'an amazon dynamo db object' }
|
7
|
+
let(:access_key_id) { mock 'access key id provided by amazon'}
|
8
|
+
let(:secret_access_key) { mock 'secret access key provided by amazon'}
|
9
|
+
let(:aws_config) { mock 'aws config object', secret_access_key: secret_access_key, access_key_id: access_key_id }
|
10
|
+
|
11
|
+
before do
|
12
|
+
AWS.stub(config: aws_config)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has a dynamo db connection' do
|
16
|
+
AWS::DynamoDB.should_receive(:new).with(access_key_id: access_key_id, secret_access_key: secret_access_key).and_return(connection)
|
17
|
+
|
18
|
+
subject.connection.should == connection
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require_relative '../../lib/mince_dynamo_db/data_store'
|
2
|
+
|
3
|
+
describe MinceDynamoDb::DataStore do
|
4
|
+
subject { described_class.instance }
|
5
|
+
|
6
|
+
let(:db) { mock 'dynamo db connection', tables: { collection_name => collection } }
|
7
|
+
let(:mince_dynamo_db_connection) { mock 'mince dynamo db connection', connection: db }
|
8
|
+
let(:collection) { mock 'some collection', items: items, schema_loaded?: true }
|
9
|
+
let(:collection_name) { 'some_collection_name'}
|
10
|
+
let(:primary_key) { mock 'primary key'}
|
11
|
+
let(:mock_id) { mock 'id' }
|
12
|
+
let(:data) { { :_id => mock_id}}
|
13
|
+
let(:return_data) { mock 'return data', attributes: attributes }
|
14
|
+
let(:attributes) { mock 'attributes', to_h: hash }
|
15
|
+
let(:hash) { mock 'hash' }
|
16
|
+
let(:items) { mock 'items' }
|
17
|
+
|
18
|
+
before do
|
19
|
+
MinceDynamoDb::Connection.stub(:instance => mince_dynamo_db_connection)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'uses the correct collection' do
|
23
|
+
subject.collection(collection_name).should == collection
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has a primary key identifier' do
|
27
|
+
described_class.primary_key_identifier.should == 'id'
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "Generating a primary key" do
|
31
|
+
let(:unique_id) { '123456789012345' }
|
32
|
+
let(:time) { mock 'time' }
|
33
|
+
let(:salt) { mock 'salt' }
|
34
|
+
|
35
|
+
before do
|
36
|
+
Time.stub_chain('current.utc' => time)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should create a reasonably unique id' do
|
40
|
+
Digest::SHA256.should_receive(:hexdigest).with("#{time}#{salt}").and_return(unique_id)
|
41
|
+
shorter_id = unique_id.to_s[0..6]
|
42
|
+
|
43
|
+
described_class.generate_unique_id(salt).should == shorter_id
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can write to the collection' do
|
48
|
+
items.should_receive(:create).with(data).and_return(return_data)
|
49
|
+
|
50
|
+
subject.add(collection_name, data).should == return_data
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'can read from the collection' do
|
54
|
+
item_hash = mock 'hash of attributes for an item'
|
55
|
+
item_attributes = mock 'attributes for an item', to_h: item_hash
|
56
|
+
item = mock 'item', attributes: item_attributes
|
57
|
+
collection.stub(items: [item])
|
58
|
+
|
59
|
+
subject.find_all(collection_name).should == [item_hash]
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can replace a record' do
|
63
|
+
items.should_receive(:put).with(data)
|
64
|
+
|
65
|
+
subject.replace(collection_name, data)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'can get one document' do
|
69
|
+
field = "stuff"
|
70
|
+
value = "more stuff"
|
71
|
+
|
72
|
+
items.should_receive(:where).with(field => value).and_return([return_data])
|
73
|
+
|
74
|
+
subject.find(collection_name, field, value).should == hash
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'can clear the data store' do
|
78
|
+
item = mock 'item', delete: nil
|
79
|
+
item2 = mock 'item 2', delete: nil
|
80
|
+
collection_1 = mock 'collection 1', name: 'collection 1', schema_loaded?: true, items: [item]
|
81
|
+
collection_2 = mock 'collection 2', name: 'collection 2', schema_loaded?: true, items: [item2]
|
82
|
+
collections = [collection_1, collection_2]
|
83
|
+
|
84
|
+
db.stub(:tables => collections)
|
85
|
+
|
86
|
+
item.should_receive(:delete)
|
87
|
+
item2.should_receive(:delete)
|
88
|
+
|
89
|
+
subject.clear
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'can get all records of a specific key value' do
|
93
|
+
items.should_receive(:where).with("key" => "value").and_return([return_data])
|
94
|
+
|
95
|
+
subject.get_all_for_key_with_value(collection_name, "key", "value").should == [hash]
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'can get a record of a specific key value' do
|
99
|
+
items.should_receive(:where).with({"key" => "value"}).and_return([return_data])
|
100
|
+
|
101
|
+
subject.get_for_key_with_value(collection_name, "key", "value").should == hash
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'can get all records where a value includes any of a set of values' do
|
105
|
+
filter = mock 'filter', in: return_data
|
106
|
+
items.should_receive(:where).with(:key1).and_return(filter)
|
107
|
+
filter.should_receive(:in).with([1,2,4]).and_return([return_data])
|
108
|
+
|
109
|
+
subject.containing_any(collection_name, "key1", [1,2,4]).should == [hash]
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'can get all records where the array includes a value' do
|
113
|
+
filter = mock 'filter', contains: return_data
|
114
|
+
items.should_receive(:where).with(:key).and_return(filter)
|
115
|
+
filter.should_receive(:contains).with('value').and_return([return_data])
|
116
|
+
|
117
|
+
subject.array_contains(collection_name, "key", "value").should == [hash]
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'can push a value to an array for a specific record' do
|
121
|
+
items.should_receive(:where).with("key" => "value").and_return([return_data])
|
122
|
+
attributes.should_receive(:add).with(:array_key => ["value_to_push"])
|
123
|
+
|
124
|
+
subject.push_to_array(collection_name, :key, "value", :array_key, "value_to_push")
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'can remove a value from an array for a specific record' do
|
128
|
+
items.should_receive(:where).with("key" => "value").and_return([return_data])
|
129
|
+
attributes.should_receive(:delete).with(:array_key => ["value_to_remove"])
|
130
|
+
|
131
|
+
subject.remove_from_array(collection_name, :key, "value", :array_key, "value_to_remove")
|
132
|
+
end
|
133
|
+
end
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mince_dynamo_db
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Simpson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk
|
16
|
+
requirement: &70289094931040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.3.7
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70289094931040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
requirement: &70289094958080 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70289094958080
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70289094957560 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.8.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70289094957560
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: guard-rspec
|
49
|
+
requirement: &70289094956900 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.6.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70289094956900
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: yard
|
60
|
+
requirement: &70289094956260 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 0.7.5
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70289094956260
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: redcarpet
|
71
|
+
requirement: &70289094955340 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 2.1.1
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70289094955340
|
80
|
+
description: Lightweight ORM for Amazon's DynamoDB with Ruby Apps
|
81
|
+
email:
|
82
|
+
- matt@railsgrammer.com
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .rspec
|
89
|
+
- .rvmrc
|
90
|
+
- Gemfile
|
91
|
+
- Guardfile
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- lib/mince_dynamo_db.rb
|
95
|
+
- lib/mince_dynamo_db/connection.rb
|
96
|
+
- lib/mince_dynamo_db/data_store.rb
|
97
|
+
- lib/mince_dynamo_db/version.rb
|
98
|
+
- mince_dynamo_db.gemspec
|
99
|
+
- spec/integration/persisting_to_dynamodb_test.rb
|
100
|
+
- spec/lib/connection_spec.rb
|
101
|
+
- spec/lib/data_store_spec.rb
|
102
|
+
- spec/support/aws_example.yml
|
103
|
+
homepage: https://github.com/coffeencoke/mince_dynamo_db
|
104
|
+
licenses: []
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project: mince_dynamo_db
|
123
|
+
rubygems_version: 1.8.10
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: Lightweight ORM for Amazon's DynamoDB with Ruby Apps
|
127
|
+
test_files:
|
128
|
+
- spec/integration/persisting_to_dynamodb_test.rb
|
129
|
+
- spec/lib/connection_spec.rb
|
130
|
+
- spec/lib/data_store_spec.rb
|
131
|
+
- spec/support/aws_example.yml
|
132
|
+
has_rdoc:
|