dynamo-record 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.rubocop.yml +81 -0
- data/.travis.yml +5 -0
- data/Dockerfile.test +23 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +163 -0
- data/README.md +110 -0
- data/build.sh +28 -0
- data/docker-compose.override.yml +6 -0
- data/docker-compose.yml +17 -0
- data/dynamo-record.gemspec +35 -0
- data/lib/dynamo-record.rb +4 -0
- data/lib/dynamo-record/marshalers.rb +44 -0
- data/lib/dynamo-record/model.rb +122 -0
- data/lib/dynamo-record/record.rb +7 -0
- data/lib/dynamo-record/record/railtie.rb +13 -0
- data/lib/dynamo-record/record/version.rb +5 -0
- data/lib/dynamo-record/table_migration.rb +36 -0
- data/lib/dynamo-record/task_helpers/cleanup.rb +16 -0
- data/lib/dynamo-record/task_helpers/drop_all_tables.rb +17 -0
- data/lib/dynamo-record/task_helpers/drop_table.rb +11 -0
- data/lib/dynamo-record/task_helpers/list_tables.rb +13 -0
- data/lib/dynamo-record/task_helpers/migration_runner.rb +53 -0
- data/lib/dynamo-record/task_helpers/scale.rb +86 -0
- data/lib/model_existence_validator.rb +7 -0
- data/lib/tasks/dynamo.rake +38 -0
- metadata +207 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77cdbe44e4be22d2294565970b108d66c48ffb87
|
4
|
+
data.tar.gz: 70653d3d5cddc536eeefb74d47ea8522665f4b3b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b7fe3b6bd23079f734531545ee1ae42079d5053b99b058d9cc1c96a0ba78804de27b8f3fca6ed7609d299d7c20bf0bfd21c38b34823099b8da6cf254197c2537
|
7
|
+
data.tar.gz: ec96773a915b361673ee1af591ea13a84b8cb5724cc5fc76168412d75cb43bd590d7b721462c2a682a677237459bc0449ee0c5e8f660806aa33097e3157c3731
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
Rails:
|
2
|
+
Enabled: true
|
3
|
+
|
4
|
+
Rails/HttpPositionalArguments:
|
5
|
+
# Renable once we are on Rails v5
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
AllCops:
|
9
|
+
TargetRubyVersion: 2.3
|
10
|
+
|
11
|
+
Metrics/ClassLength:
|
12
|
+
Max: 200 # Default: 100
|
13
|
+
|
14
|
+
Metrics/LineLength:
|
15
|
+
Max: 120 # Default: 80
|
16
|
+
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Max: 20 # Default: 10
|
19
|
+
|
20
|
+
Metrics/BlockLength:
|
21
|
+
Max: 30
|
22
|
+
Exclude:
|
23
|
+
- 'spec/**/*.rb'
|
24
|
+
|
25
|
+
Style/AlignParameters:
|
26
|
+
# Alignment of parameters in multi-line method calls.
|
27
|
+
#
|
28
|
+
# The `with_fixed_indentation` style aligns the following lines with one
|
29
|
+
# level of indentation relative to the start of the line with the method call.
|
30
|
+
#
|
31
|
+
# method_call(a,
|
32
|
+
# b)
|
33
|
+
EnforcedStyle: with_fixed_indentation
|
34
|
+
|
35
|
+
Style/ClassAndModuleChildren:
|
36
|
+
# Checks the style of children definitions at classes and modules.
|
37
|
+
#
|
38
|
+
# Basically there are two different styles:
|
39
|
+
#
|
40
|
+
# `nested` - have each child on a separate line
|
41
|
+
# class Foo
|
42
|
+
# class Bar
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# `compact` - combine definitions as much as possible
|
47
|
+
# class Foo::Bar
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# The compact style is only forced, for classes / modules with one child.
|
51
|
+
EnforcedStyle: nested
|
52
|
+
Enabled: false
|
53
|
+
|
54
|
+
Style/Documentation:
|
55
|
+
# This cop checks for missing top-level documentation of classes and modules.
|
56
|
+
# Classes with no body and namespace modules are exempt from the check.
|
57
|
+
# Namespace modules are modules that have nothing in their bodies except
|
58
|
+
# classes or other modules.
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Lint/EndAlignment:
|
62
|
+
AlignWith: variable
|
63
|
+
|
64
|
+
Style/CaseIndentation:
|
65
|
+
IndentWhenRelativeTo: end
|
66
|
+
|
67
|
+
Style/FrozenStringLiteralComment:
|
68
|
+
# `when_needed` will add the frozen string literal comment to files
|
69
|
+
# only when the `TargetRubyVersion` is set to 2.3+.
|
70
|
+
# `always` will always add the frozen string literal comment to a file
|
71
|
+
# regardless of the Ruby version or if `freeze` or `<<` are called on a
|
72
|
+
# string literal. If you run code against multiple versions of Ruby, it is
|
73
|
+
# possible that this will create errors in Ruby 2.3.0+.
|
74
|
+
#
|
75
|
+
# See: https://wyeworks.com/blog/2015/12/1/immutable-strings-in-ruby-2-dot-3
|
76
|
+
EnforcedStyle: when_needed
|
77
|
+
Enabled: false
|
78
|
+
|
79
|
+
Style/NumericPredicate:
|
80
|
+
Exclude:
|
81
|
+
- 'spec/**/*'
|
data/.travis.yml
ADDED
data/Dockerfile.test
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
FROM instructure/ruby:2.3
|
2
|
+
|
3
|
+
ENV APP_HOME "/usr/src/app/"
|
4
|
+
|
5
|
+
USER root
|
6
|
+
|
7
|
+
COPY dynamo-record.gemspec Gemfile Gemfile.lock $APP_HOME
|
8
|
+
RUN mkdir -p $APP_HOME/lib/dynamo-record/record
|
9
|
+
COPY lib/dynamo-record/record/version.rb $APP_HOME/lib/dynamo-record/record
|
10
|
+
RUN chown -R docker:docker $APP_HOME
|
11
|
+
|
12
|
+
USER docker
|
13
|
+
RUN gem install bundler
|
14
|
+
RUN bundle install --quiet --jobs 8
|
15
|
+
USER root
|
16
|
+
|
17
|
+
COPY . $APP_HOME
|
18
|
+
RUN mkdir -p $APP_HOME/coverage && \
|
19
|
+
mkdir -p $APP_HOME/spec/internal/log && \
|
20
|
+
chown -R docker:docker $APP_HOME
|
21
|
+
USER docker
|
22
|
+
|
23
|
+
CMD ["bundle", "exec", "rspec"]
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
dynamo-record (0.1.0)
|
5
|
+
aws-record (~> 1.1)
|
6
|
+
rails (~> 4.2)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionmailer (4.2.8)
|
12
|
+
actionpack (= 4.2.8)
|
13
|
+
actionview (= 4.2.8)
|
14
|
+
activejob (= 4.2.8)
|
15
|
+
mail (~> 2.5, >= 2.5.4)
|
16
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
17
|
+
actionpack (4.2.8)
|
18
|
+
actionview (= 4.2.8)
|
19
|
+
activesupport (= 4.2.8)
|
20
|
+
rack (~> 1.6)
|
21
|
+
rack-test (~> 0.6.2)
|
22
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
23
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
24
|
+
actionview (4.2.8)
|
25
|
+
activesupport (= 4.2.8)
|
26
|
+
builder (~> 3.1)
|
27
|
+
erubis (~> 2.7.0)
|
28
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
29
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
30
|
+
activejob (4.2.8)
|
31
|
+
activesupport (= 4.2.8)
|
32
|
+
globalid (>= 0.3.0)
|
33
|
+
activemodel (4.2.8)
|
34
|
+
activesupport (= 4.2.8)
|
35
|
+
builder (~> 3.1)
|
36
|
+
activerecord (4.2.8)
|
37
|
+
activemodel (= 4.2.8)
|
38
|
+
activesupport (= 4.2.8)
|
39
|
+
arel (~> 6.0)
|
40
|
+
activesupport (4.2.8)
|
41
|
+
i18n (~> 0.7)
|
42
|
+
minitest (~> 5.1)
|
43
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
44
|
+
tzinfo (~> 1.1)
|
45
|
+
addressable (2.5.1)
|
46
|
+
public_suffix (~> 2.0, >= 2.0.2)
|
47
|
+
arel (6.0.4)
|
48
|
+
aws-record (1.1.0)
|
49
|
+
aws-sdk-resources (~> 2.0)
|
50
|
+
aws-sdk-core (2.9.11)
|
51
|
+
aws-sigv4 (~> 1.0)
|
52
|
+
jmespath (~> 1.0)
|
53
|
+
aws-sdk-resources (2.9.11)
|
54
|
+
aws-sdk-core (= 2.9.11)
|
55
|
+
aws-sigv4 (1.0.0)
|
56
|
+
builder (3.2.3)
|
57
|
+
byebug (9.0.6)
|
58
|
+
combustion (0.6.0)
|
59
|
+
activesupport (>= 3.0.0)
|
60
|
+
railties (>= 3.0.0)
|
61
|
+
thor (>= 0.14.6)
|
62
|
+
concurrent-ruby (1.0.5)
|
63
|
+
crack (0.4.3)
|
64
|
+
safe_yaml (~> 1.0.0)
|
65
|
+
diff-lcs (1.3)
|
66
|
+
docile (1.1.5)
|
67
|
+
erubis (2.7.0)
|
68
|
+
globalid (0.4.0)
|
69
|
+
activesupport (>= 4.2.0)
|
70
|
+
hashdiff (0.3.2)
|
71
|
+
i18n (0.8.1)
|
72
|
+
jmespath (1.3.1)
|
73
|
+
json (2.1.0)
|
74
|
+
loofah (2.0.3)
|
75
|
+
nokogiri (>= 1.5.9)
|
76
|
+
mail (2.6.4)
|
77
|
+
mime-types (>= 1.16, < 4)
|
78
|
+
mime-types (3.1)
|
79
|
+
mime-types-data (~> 3.2015)
|
80
|
+
mime-types-data (3.2016.0521)
|
81
|
+
mini_portile2 (2.1.0)
|
82
|
+
minitest (5.10.1)
|
83
|
+
nokogiri (1.7.1)
|
84
|
+
mini_portile2 (~> 2.1.0)
|
85
|
+
public_suffix (2.0.5)
|
86
|
+
rack (1.6.5)
|
87
|
+
rack-test (0.6.3)
|
88
|
+
rack (>= 1.0)
|
89
|
+
rails (4.2.8)
|
90
|
+
actionmailer (= 4.2.8)
|
91
|
+
actionpack (= 4.2.8)
|
92
|
+
actionview (= 4.2.8)
|
93
|
+
activejob (= 4.2.8)
|
94
|
+
activemodel (= 4.2.8)
|
95
|
+
activerecord (= 4.2.8)
|
96
|
+
activesupport (= 4.2.8)
|
97
|
+
bundler (>= 1.3.0, < 2.0)
|
98
|
+
railties (= 4.2.8)
|
99
|
+
sprockets-rails
|
100
|
+
rails-deprecated_sanitizer (1.0.3)
|
101
|
+
activesupport (>= 4.2.0.alpha)
|
102
|
+
rails-dom-testing (1.0.8)
|
103
|
+
activesupport (>= 4.2.0.beta, < 5.0)
|
104
|
+
nokogiri (~> 1.6)
|
105
|
+
rails-deprecated_sanitizer (>= 1.0.1)
|
106
|
+
rails-html-sanitizer (1.0.3)
|
107
|
+
loofah (~> 2.0)
|
108
|
+
railties (4.2.8)
|
109
|
+
actionpack (= 4.2.8)
|
110
|
+
activesupport (= 4.2.8)
|
111
|
+
rake (>= 0.8.7)
|
112
|
+
thor (>= 0.18.1, < 2.0)
|
113
|
+
rake (10.5.0)
|
114
|
+
rspec (3.5.0)
|
115
|
+
rspec-core (~> 3.5.0)
|
116
|
+
rspec-expectations (~> 3.5.0)
|
117
|
+
rspec-mocks (~> 3.5.0)
|
118
|
+
rspec-core (3.5.4)
|
119
|
+
rspec-support (~> 3.5.0)
|
120
|
+
rspec-expectations (3.5.0)
|
121
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
122
|
+
rspec-support (~> 3.5.0)
|
123
|
+
rspec-mocks (3.5.0)
|
124
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
125
|
+
rspec-support (~> 3.5.0)
|
126
|
+
rspec-support (3.5.0)
|
127
|
+
safe_yaml (1.0.4)
|
128
|
+
simplecov (0.14.1)
|
129
|
+
docile (~> 1.1.0)
|
130
|
+
json (>= 1.8, < 3)
|
131
|
+
simplecov-html (~> 0.10.0)
|
132
|
+
simplecov-html (0.10.0)
|
133
|
+
sprockets (3.7.1)
|
134
|
+
concurrent-ruby (~> 1.0)
|
135
|
+
rack (> 1, < 3)
|
136
|
+
sprockets-rails (3.2.0)
|
137
|
+
actionpack (>= 4.0)
|
138
|
+
activesupport (>= 4.0)
|
139
|
+
sprockets (>= 3.0.0)
|
140
|
+
thor (0.19.4)
|
141
|
+
thread_safe (0.3.6)
|
142
|
+
tzinfo (1.2.3)
|
143
|
+
thread_safe (~> 0.1)
|
144
|
+
webmock (2.3.2)
|
145
|
+
addressable (>= 2.3.6)
|
146
|
+
crack (>= 0.3.2)
|
147
|
+
hashdiff
|
148
|
+
|
149
|
+
PLATFORMS
|
150
|
+
ruby
|
151
|
+
|
152
|
+
DEPENDENCIES
|
153
|
+
bundler (~> 1.14)
|
154
|
+
byebug (~> 9.0)
|
155
|
+
combustion (~> 0.6.0)
|
156
|
+
dynamo-record!
|
157
|
+
rake (~> 10.0)
|
158
|
+
rspec (~> 3.0)
|
159
|
+
simplecov (~> 0.12)
|
160
|
+
webmock (~> 2.1)
|
161
|
+
|
162
|
+
BUNDLED WITH
|
163
|
+
1.14.6
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# DynamoRecord
|
2
|
+
|
3
|
+
Provides helpful rake tasks and model extensions on top of [aws-record](https://github.com/aws/aws-sdk-ruby-record).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'dynamo-record'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install dynamo-record
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Models
|
24
|
+
|
25
|
+
In `app/models`, create a class that includes `DynamoRecord::Model` and contains one or more of the following attribute declarations:
|
26
|
+
|
27
|
+
* `integer_attr`
|
28
|
+
* `float_attr`
|
29
|
+
* `map_attr`
|
30
|
+
* `composite_string_attr`
|
31
|
+
* `composite_integer_attr`
|
32
|
+
|
33
|
+
An example file:
|
34
|
+
|
35
|
+
```
|
36
|
+
class Model1
|
37
|
+
include DynamoRecord::Model
|
38
|
+
composite_string_attr(
|
39
|
+
:model_key,
|
40
|
+
hash_key: true,
|
41
|
+
parts: [:model_id, :account_id]
|
42
|
+
)
|
43
|
+
integer_attr :position, range_key: true
|
44
|
+
string_attr :user_id
|
45
|
+
float_attr :score
|
46
|
+
map_attr :map_value
|
47
|
+
composite_string_attr :quiz_key, parts: [:quiz_id]
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
The partition key is labeled with `hash_key: true`. The sort key is labeled with `range_key: true`.
|
52
|
+
|
53
|
+
A secondary index can be defined as follows:
|
54
|
+
|
55
|
+
```
|
56
|
+
global_secondary_index(
|
57
|
+
:secondary_idx,
|
58
|
+
hash_key: :user_id,
|
59
|
+
range_key: :score,
|
60
|
+
projection: {
|
61
|
+
projection_type: 'INCLUDE',
|
62
|
+
non_key_attributes: [
|
63
|
+
:map_value
|
64
|
+
]
|
65
|
+
}
|
66
|
+
)
|
67
|
+
```
|
68
|
+
|
69
|
+
The full documentation for `projection_type` and `non_key_attributes` can be found [here](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-projectionobject.html).
|
70
|
+
|
71
|
+
### Migrations
|
72
|
+
|
73
|
+
Dynamo migration files are stored in `db/dynamo_migrate`. The name of the file follows the style of standard Rails migration files like `YYYYMMDDHHMMSS_create_model_1.rb`. A migration file that creates a Dynamo table looks like:
|
74
|
+
|
75
|
+
```
|
76
|
+
module DynamoMigrate
|
77
|
+
class CreateModel1 < DynamoRecord::TableMigration
|
78
|
+
def self.up
|
79
|
+
migrate(Model1) do |migration|
|
80
|
+
migration.create!(
|
81
|
+
provisioned_throughput: {
|
82
|
+
read_capacity_units: 1,
|
83
|
+
write_capacity_units: 1
|
84
|
+
},
|
85
|
+
global_secondary_index_throughput: {
|
86
|
+
secondary_idx: {
|
87
|
+
read_capacity_units: 1,
|
88
|
+
write_capacity_units: 1
|
89
|
+
}
|
90
|
+
}
|
91
|
+
)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Note that the throughput of the table can be configured separately from the throughput of the secondary index.
|
99
|
+
|
100
|
+
A migration file that creates a Dynamo stream looks like:
|
101
|
+
|
102
|
+
```
|
103
|
+
module DynamoMigrate
|
104
|
+
class AddModel1Stream < DynamoRecord::TableMigration
|
105
|
+
def self.update
|
106
|
+
add_stream(Model1)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
```
|
data/build.sh
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
export COMPOSE_PROJECT_NAME=dynamorecord
|
4
|
+
export COMPOSE_FILE=docker-compose.yml
|
5
|
+
|
6
|
+
function cleanup() {
|
7
|
+
exit_code=$?
|
8
|
+
set +e
|
9
|
+
docker-compose stop test
|
10
|
+
docker-compose rm -fa test
|
11
|
+
docker rmi -f $(docker images -qf "dangling=true") &>/dev/null
|
12
|
+
exit $exit_code
|
13
|
+
}
|
14
|
+
trap cleanup INT TERM EXIT
|
15
|
+
|
16
|
+
set -e
|
17
|
+
|
18
|
+
docker-compose build --pull
|
19
|
+
docker-compose run --name test test
|
20
|
+
docker cp test:/usr/src/app/coverage .
|
21
|
+
|
22
|
+
#echo "Reporting test coverage"
|
23
|
+
#if [[ $? -eq 0 && -a 'coverage/.last_run.json' ]]; then
|
24
|
+
# ruby_coverage=$(cat coverage/api/.last_run.json | grep 'covered_percent' | egrep -o '[0-9]+(\.[0-9]+)?')
|
25
|
+
# echo "Sending to gergich"
|
26
|
+
# gergich message ":ruby: <http://jenkins.instructure.com/job/dynamo-record/${BUILD_ID}/|${ruby_coverage}%>"
|
27
|
+
# gergich publish
|
28
|
+
#fi
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '2'
|
2
|
+
|
3
|
+
services:
|
4
|
+
test:
|
5
|
+
build:
|
6
|
+
context: .
|
7
|
+
dockerfile: Dockerfile.test
|
8
|
+
environment:
|
9
|
+
AWS_ACCESS_KEY_ID: x
|
10
|
+
AWS_SECRET_ACCESS_KEY: x
|
11
|
+
AWS_REGION: us-west-2
|
12
|
+
DYNAMO_ENDPOINT: http://dynamo:8000
|
13
|
+
links:
|
14
|
+
- dynamo
|
15
|
+
|
16
|
+
dynamo:
|
17
|
+
image: instructure/dynamodb
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dynamo-record/record/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'dynamo-record'
|
8
|
+
spec.version = DynamoRecord::Record::VERSION
|
9
|
+
spec.license = 'MIT'
|
10
|
+
spec.homepage = 'https://instructure.com'
|
11
|
+
spec.authors = ['Davis McClellan', 'Ryan Taylor', 'Bryan Petty', 'Michael Brewer-Davis', 'Marc Phillips', 'Augusto Callejas']
|
12
|
+
spec.email = ['dmcclellan@instructure.com', 'rtaylor@instructure.com', 'bpetty@instructure.com',
|
13
|
+
'mbd@instructure.com', 'mphillips@instructure.com', 'acallejas@instructure.com']
|
14
|
+
|
15
|
+
spec.summary = 'Extensions for working with dynamo via aws-record'
|
16
|
+
spec.description = 'A set of extensions simplifying database operations in aws-record'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_dependency 'aws-record', '~> 1.1'
|
26
|
+
spec.add_dependency 'rails', '~> 4.2'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
29
|
+
spec.add_development_dependency 'byebug', '~> 9.0'
|
30
|
+
spec.add_development_dependency 'combustion', '~> 0.6.0'
|
31
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
33
|
+
spec.add_development_dependency 'simplecov', '~> 0.12'
|
34
|
+
spec.add_development_dependency 'webmock', '~> 2.1'
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
module Marshalers
|
3
|
+
COMPOSITE_DELIMETER = '|'.freeze
|
4
|
+
|
5
|
+
def self.included(sub_class)
|
6
|
+
sub_class.extend(ClassMethods)
|
7
|
+
super(sub_class)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def composite_integer_attr(name, opts = {})
|
12
|
+
composite_attr(name, opts)
|
13
|
+
define_readers(name, opts[:parts], :to_i) if opts.key? :parts
|
14
|
+
end
|
15
|
+
|
16
|
+
def composite_string_attr(name, opts = {})
|
17
|
+
composite_attr(name, opts)
|
18
|
+
define_readers(name, opts[:parts], :to_s) if opts.key? :parts
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def composite_attr(name, opts = {})
|
24
|
+
opts[:dynamodb_type] = 'S'
|
25
|
+
|
26
|
+
# It is very unfortunate that Aws::Record used `attr`
|
27
|
+
# rubocop:disable Style/Attr
|
28
|
+
attr(name, Aws::Record::Marshalers::StringMarshaler.new(opts), opts)
|
29
|
+
# rubocop:enable Style/Attr
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_readers(name, parts, cast_function, allowed_repeats = [:account_uuid])
|
33
|
+
parts.each_with_index do |part, i|
|
34
|
+
raise "#{part} already defined" unless parts.find_index(part) == i
|
35
|
+
next if method_defined?(part)
|
36
|
+
define_method(part) do
|
37
|
+
# @data is used internally by Aws::Record to store all of the attributes
|
38
|
+
@data.get_attribute(name).split(COMPOSITE_DELIMETER)[i].send(cast_function)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'aws-record'
|
2
|
+
|
3
|
+
module DynamoRecord::Model
|
4
|
+
COMPOSITE_DELIMITER = '|'.freeze
|
5
|
+
|
6
|
+
def self.included(klass)
|
7
|
+
klass.include(Aws::Record)
|
8
|
+
klass.include(DynamoRecord::Marshalers)
|
9
|
+
|
10
|
+
klass.extend ClassMethods
|
11
|
+
klass.send :prepend, InstanceMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def table_name
|
16
|
+
[Rails.configuration.dynamo['prefix'], name.tableize].join('-').tr('/', '.')
|
17
|
+
end
|
18
|
+
|
19
|
+
def scan
|
20
|
+
raise 'no scanning in production' if Rails.env.production?
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(opts)
|
25
|
+
super(opts).tap do |record|
|
26
|
+
unless record
|
27
|
+
name = self.name.demodulize
|
28
|
+
conditions = opts.map { |k, v| "#{k}=#{v}" }.join(', ')
|
29
|
+
error = "Couldn't find #{name} with #{conditions}"
|
30
|
+
raise Aws::Record::Errors::NotFound, error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_all_by_hash_key(hash_key_value, opts = {})
|
36
|
+
find_all_by_index_and_hash_key(hash_key, hash_key_value, opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_all_by_gsi_hash_key(gsi_name, hash_key_value, opts = {})
|
40
|
+
hash_key_name = global_secondary_indexes[gsi_name.to_sym][:hash_key]
|
41
|
+
find_all_by_index_and_hash_key(hash_key_name, hash_key_value, opts, gsi_name.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_all_by_gsi_hash_and_range_keys(gsi_name, hash_key_value, range_key_value)
|
45
|
+
gsi_config = global_secondary_indexes[gsi_name.to_sym]
|
46
|
+
find_all_by_index_hash_and_range_keys(
|
47
|
+
hash_config: { name: gsi_config[:hash_key], value: hash_key_value },
|
48
|
+
range_config: { name: gsi_config[:range_key], value: range_key_value },
|
49
|
+
index_name: gsi_name.to_s
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_all_by_index_and_hash_key(hash_key_name, hash_key_value, opts = {}, index_name = nil)
|
54
|
+
query_options = {
|
55
|
+
select: 'ALL_ATTRIBUTES',
|
56
|
+
key_condition_expression: "#{hash_key_name} = :hash_key_value",
|
57
|
+
expression_attribute_values: {
|
58
|
+
':hash_key_value': hash_key_value
|
59
|
+
},
|
60
|
+
scan_index_forward: true
|
61
|
+
}
|
62
|
+
query_options[:index_name] = index_name if index_name
|
63
|
+
query_options.merge!(opts)
|
64
|
+
query(query_options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_all_by_index_hash_and_range_keys(hash_config:, range_config:, index_name: nil,
|
68
|
+
scan_index_forward: true, limit: nil)
|
69
|
+
query_options = {
|
70
|
+
select: 'ALL_ATTRIBUTES',
|
71
|
+
key_condition_expression: "#{hash_config[:name]} = :hkv AND #{range_config[:name]} = :rkv",
|
72
|
+
expression_attribute_values: {
|
73
|
+
':hkv': hash_config[:value],
|
74
|
+
':rkv': range_config[:value]
|
75
|
+
},
|
76
|
+
scan_index_forward: scan_index_forward,
|
77
|
+
limit: limit
|
78
|
+
}
|
79
|
+
query_options[:index_name] = index_name if index_name
|
80
|
+
query(query_options)
|
81
|
+
end
|
82
|
+
|
83
|
+
def composite_key(*args)
|
84
|
+
args.join(COMPOSITE_DELIMITER)
|
85
|
+
end
|
86
|
+
|
87
|
+
def split_composite(string)
|
88
|
+
string.split(COMPOSITE_DELIMITER)
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_all_by_model(model, opts = {})
|
92
|
+
find_all_by_hash_key(model.dynamo_key, opts)
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_all_by_model_paginated(model, opts = {})
|
96
|
+
find_all_by_model(model, pagination_opts(opts))
|
97
|
+
end
|
98
|
+
|
99
|
+
def pagination_opts(opts = {})
|
100
|
+
{ limit: opts[:limit], exclusive_start_key: opts[:exclusive_start_key] }
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: Create a batch save method.
|
104
|
+
end
|
105
|
+
|
106
|
+
module InstanceMethods
|
107
|
+
def read_attribute_for_serialization(attribute)
|
108
|
+
send(attribute)
|
109
|
+
end
|
110
|
+
|
111
|
+
def attribute_hash
|
112
|
+
attrs = self.class.attributes.attributes
|
113
|
+
attr_keys = attrs.keys
|
114
|
+
hash = {}
|
115
|
+
|
116
|
+
attr_keys.each do |key|
|
117
|
+
hash[key] = attrs[key].type_cast(send(key))
|
118
|
+
end
|
119
|
+
hash
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
class TableMigration
|
3
|
+
def self.migrate(model)
|
4
|
+
migration = Aws::Record::TableMigration.new(model)
|
5
|
+
begin
|
6
|
+
migration.client.describe_table(table_name: model.table_name)
|
7
|
+
return :exists
|
8
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
9
|
+
yield migration
|
10
|
+
migration.wait_until_available
|
11
|
+
return :migrated
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.migrate_updates(model)
|
16
|
+
migration = Aws::Record::TableMigration.new(model)
|
17
|
+
yield migration
|
18
|
+
migration.wait_until_available
|
19
|
+
:updated
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.add_stream(model)
|
23
|
+
migrate_updates(model) do |migration|
|
24
|
+
migration.update!(
|
25
|
+
stream_specification: {
|
26
|
+
stream_enabled: true,
|
27
|
+
stream_view_type: 'NEW_IMAGE'
|
28
|
+
}
|
29
|
+
)
|
30
|
+
end
|
31
|
+
rescue Aws::DynamoDB::Errors::ValidationException => e
|
32
|
+
return e.message if e.message == 'Table already has an enabled stream'
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
module TaskHelpers
|
3
|
+
class Cleanup
|
4
|
+
def self.run
|
5
|
+
raise 'Task not available on production' if Rails.env.production?
|
6
|
+
Dir[Rails.root.join('app/models/*.rb').to_s].each do |filename|
|
7
|
+
klass = File.basename(filename, '.rb').camelize.constantize
|
8
|
+
if klass.included_modules.include? DynamoRecord::Model
|
9
|
+
puts "Deleting all items in table: #{klass}"
|
10
|
+
klass.scan.each(&:delete!)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
module TaskHelpers
|
3
|
+
class DropAllTables
|
4
|
+
def self.run(override = false)
|
5
|
+
raise 'Task not available on production' if Rails.env.production?
|
6
|
+
env = Rails.env
|
7
|
+
dynamodb = Aws::DynamoDB::Client.new
|
8
|
+
tables = dynamodb.list_tables
|
9
|
+
tables.table_names.map do |t|
|
10
|
+
next unless t.include?(env) || override
|
11
|
+
dt = dynamodb.delete_table(table_name: t)
|
12
|
+
dt ? "Deleted: #{t}" : "Delete failed: #{t}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
module TaskHelpers
|
3
|
+
class ListTables
|
4
|
+
def self.run
|
5
|
+
dynamodb = Aws::DynamoDB::Client.new
|
6
|
+
tables = dynamodb.list_tables
|
7
|
+
tables.table_names.select do |tn|
|
8
|
+
tn.starts_with?(Rails.configuration.dynamo['prefix'])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
module TaskHelpers
|
3
|
+
class MigrationRunner
|
4
|
+
def self.run(path='db/dynamo_migrate')
|
5
|
+
constants = []
|
6
|
+
filename_regexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/
|
7
|
+
|
8
|
+
# Sorts the files located in `db/dynamo_migrate` to ensure order is preserved
|
9
|
+
Dir[Rails.root.join("#{path}/*.rb")].sort.each do |f|
|
10
|
+
raise "Non-numeric prefix: #{f}" if File.basename(f).scan(filename_regexp).first.nil?
|
11
|
+
require f
|
12
|
+
|
13
|
+
# finds the constant that was added on the require statement above
|
14
|
+
migration_sym = (DynamoMigrate.constants - constants).first
|
15
|
+
migration = DynamoMigrate.const_get(migration_sym)
|
16
|
+
constants.push migration_sym
|
17
|
+
|
18
|
+
# starts the migration
|
19
|
+
yield "Migrating: #{migration}"
|
20
|
+
|
21
|
+
begin
|
22
|
+
status = up(migration)
|
23
|
+
yield status if status
|
24
|
+
|
25
|
+
status = update(migration)
|
26
|
+
yield status if status
|
27
|
+
rescue => e
|
28
|
+
yield "Migration failed: #{e}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.up(migration)
|
34
|
+
return unless migration.respond_to? :up
|
35
|
+
case migration.up
|
36
|
+
when :exists
|
37
|
+
'Table already exists'
|
38
|
+
when :migrated
|
39
|
+
'Migration successful'
|
40
|
+
else
|
41
|
+
raise 'Migration failed'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.update(migration)
|
46
|
+
return unless migration.respond_to? :update
|
47
|
+
status = migration.update
|
48
|
+
return 'Migration successful' if status == :updated
|
49
|
+
status
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module DynamoRecord
|
2
|
+
module TaskHelpers
|
3
|
+
class Scale
|
4
|
+
attr_reader :model, :attribute_selector, :new_throughput
|
5
|
+
attr_reader :migration, :existing_throughput, :model_name
|
6
|
+
|
7
|
+
def initialize(model_name, attribute_selector, new_throughput)
|
8
|
+
@model_name = model_name
|
9
|
+
@attribute_selector = attribute_selector
|
10
|
+
@new_throughput = new_throughput
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
return description if [model_name, attribute_selector, new_throughput].any?(&:nil?)
|
15
|
+
|
16
|
+
@model = model_name.constantize
|
17
|
+
@migration = Aws::Record::TableMigration.new(model)
|
18
|
+
@existing_throughput = model.provisioned_throughput
|
19
|
+
|
20
|
+
update_throughput
|
21
|
+
success_message
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def success_message
|
27
|
+
"Successfully updated #{model.table_name} throughput to #{update_instructions[:provisioned_throughput]}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_throughput
|
31
|
+
raise_attribute_error if !read? && !write?
|
32
|
+
|
33
|
+
migration.update!(update_instructions)
|
34
|
+
end
|
35
|
+
|
36
|
+
def update_instructions
|
37
|
+
{
|
38
|
+
provisioned_throughput: {
|
39
|
+
write_capacity_units: (write? && new_throughput) || existing_write,
|
40
|
+
read_capacity_units: (read? && new_throughput) || existing_read
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def existing_write
|
46
|
+
existing_throughput[:write_capacity_units]
|
47
|
+
end
|
48
|
+
|
49
|
+
def existing_read
|
50
|
+
existing_throughput[:read_capacity_units]
|
51
|
+
end
|
52
|
+
|
53
|
+
def both?
|
54
|
+
attribute_selector.to_sym == :both
|
55
|
+
end
|
56
|
+
|
57
|
+
def read?
|
58
|
+
both? || attribute_selector.to_sym == :read
|
59
|
+
end
|
60
|
+
|
61
|
+
def write?
|
62
|
+
both? || attribute_selector.to_sym == :write
|
63
|
+
end
|
64
|
+
|
65
|
+
def raise_attribute_error
|
66
|
+
raise ArgumentError, 'You didn\'t provide an appropriate attribute selection. We accept [:both, :read, :write]'
|
67
|
+
end
|
68
|
+
|
69
|
+
def description
|
70
|
+
<<~DESCRIPTION
|
71
|
+
--------------------------------------------------------------------------------
|
72
|
+
Here's some usage information:
|
73
|
+
Scale a dynamo table. Requires three inputs.
|
74
|
+
- ModelName
|
75
|
+
ruby class of the model
|
76
|
+
- attribute
|
77
|
+
valid values include "both", "read" and "write"
|
78
|
+
- new_throughput
|
79
|
+
numerical value for the new read/write capacity units
|
80
|
+
Example: `rake dynamo:scale[MySuperDynamoModel,both,50]`
|
81
|
+
--------------------------------------------------------------------------------
|
82
|
+
DESCRIPTION
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
class ModelExistenceValidator < ActiveModel::EachValidator
|
3
|
+
def validate_each(record, attribute, value)
|
4
|
+
return if options[:model].exists? value
|
5
|
+
record.errors[attribute] << (options[:message] || "#{attribute}:#{value} is not a valid #{options[:model]}")
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
namespace :dynamo do
|
2
|
+
desc 'Run dynamo migrations'
|
3
|
+
task migrate: :environment do
|
4
|
+
DynamoRecord::TaskHelpers::MigrationRunner.run { |msg| puts msg }
|
5
|
+
end
|
6
|
+
|
7
|
+
desc 'Drops all dynamo tables and re-runs migrations'
|
8
|
+
task :reset do
|
9
|
+
Rake::Task['dynamo:drop_all'].invoke
|
10
|
+
Rake::Task['dynamo:migrate'].invoke
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Drop all dynamo tables'
|
14
|
+
task :drop_all, [:override] => :environment do |_t, args|
|
15
|
+
puts DynamoRecord::TaskHelpers::DropAllTables.run args[:override]
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Drop a specified dynamo table'
|
19
|
+
task :drop, [:table_name] => :environment do |_t, args|
|
20
|
+
puts DynamoRecord::TaskHelpers::DropTable.run args[:table_name]
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'List all tables with dynamo prefix'
|
24
|
+
task list_tables: :environment do
|
25
|
+
puts DynamoRecord::TaskHelpers::ListTables.run
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Delete all records in all DyanmoDB tables.'
|
29
|
+
task cleanup: :environment do
|
30
|
+
DynamoRecord::TaskHelpers::Cleanup.run
|
31
|
+
puts 'Finished deleting all records in all DynamoDB tables.'
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Scale a dynamo table read/write capacity to the new capacity value provided'
|
35
|
+
task :scale, [:model_name, :attribute, :new_throughput] => :environment do |_t, args|
|
36
|
+
puts DynamoRecord::TaskHelpers::Scale.new(args[:model_name], args[:attribute], args[:new_throughput].to_i).run
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dynamo-record
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Davis McClellan
|
8
|
+
- Ryan Taylor
|
9
|
+
- Bryan Petty
|
10
|
+
- Michael Brewer-Davis
|
11
|
+
- Marc Phillips
|
12
|
+
- Augusto Callejas
|
13
|
+
autorequire:
|
14
|
+
bindir: exe
|
15
|
+
cert_chain: []
|
16
|
+
date: 2017-05-02 00:00:00.000000000 Z
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: aws-record
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - "~>"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '1.1'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - "~>"
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '1.1'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: rails
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - "~>"
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '4.2'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - "~>"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '4.2'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '1.14'
|
53
|
+
type: :development
|
54
|
+
prerelease: false
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.14'
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: byebug
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '9.0'
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '9.0'
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: combustion
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 0.6.0
|
81
|
+
type: :development
|
82
|
+
prerelease: false
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 0.6.0
|
88
|
+
- !ruby/object:Gem::Dependency
|
89
|
+
name: rake
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '10.0'
|
95
|
+
type: :development
|
96
|
+
prerelease: false
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '10.0'
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: rspec
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '3.0'
|
109
|
+
type: :development
|
110
|
+
prerelease: false
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '3.0'
|
116
|
+
- !ruby/object:Gem::Dependency
|
117
|
+
name: simplecov
|
118
|
+
requirement: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0.12'
|
123
|
+
type: :development
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0.12'
|
130
|
+
- !ruby/object:Gem::Dependency
|
131
|
+
name: webmock
|
132
|
+
requirement: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '2.1'
|
137
|
+
type: :development
|
138
|
+
prerelease: false
|
139
|
+
version_requirements: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - "~>"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '2.1'
|
144
|
+
description: A set of extensions simplifying database operations in aws-record
|
145
|
+
email:
|
146
|
+
- dmcclellan@instructure.com
|
147
|
+
- rtaylor@instructure.com
|
148
|
+
- bpetty@instructure.com
|
149
|
+
- mbd@instructure.com
|
150
|
+
- mphillips@instructure.com
|
151
|
+
- acallejas@instructure.com
|
152
|
+
executables: []
|
153
|
+
extensions: []
|
154
|
+
extra_rdoc_files: []
|
155
|
+
files:
|
156
|
+
- ".gitignore"
|
157
|
+
- ".rspec"
|
158
|
+
- ".rubocop.yml"
|
159
|
+
- ".travis.yml"
|
160
|
+
- Dockerfile.test
|
161
|
+
- Gemfile
|
162
|
+
- Gemfile.lock
|
163
|
+
- README.md
|
164
|
+
- build.sh
|
165
|
+
- docker-compose.override.yml
|
166
|
+
- docker-compose.yml
|
167
|
+
- dynamo-record.gemspec
|
168
|
+
- lib/dynamo-record.rb
|
169
|
+
- lib/dynamo-record/marshalers.rb
|
170
|
+
- lib/dynamo-record/model.rb
|
171
|
+
- lib/dynamo-record/record.rb
|
172
|
+
- lib/dynamo-record/record/railtie.rb
|
173
|
+
- lib/dynamo-record/record/version.rb
|
174
|
+
- lib/dynamo-record/table_migration.rb
|
175
|
+
- lib/dynamo-record/task_helpers/cleanup.rb
|
176
|
+
- lib/dynamo-record/task_helpers/drop_all_tables.rb
|
177
|
+
- lib/dynamo-record/task_helpers/drop_table.rb
|
178
|
+
- lib/dynamo-record/task_helpers/list_tables.rb
|
179
|
+
- lib/dynamo-record/task_helpers/migration_runner.rb
|
180
|
+
- lib/dynamo-record/task_helpers/scale.rb
|
181
|
+
- lib/model_existence_validator.rb
|
182
|
+
- lib/tasks/dynamo.rake
|
183
|
+
homepage: https://instructure.com
|
184
|
+
licenses:
|
185
|
+
- MIT
|
186
|
+
metadata: {}
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options: []
|
189
|
+
require_paths:
|
190
|
+
- lib
|
191
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
requirements: []
|
202
|
+
rubyforge_project:
|
203
|
+
rubygems_version: 2.5.1
|
204
|
+
signing_key:
|
205
|
+
specification_version: 4
|
206
|
+
summary: Extensions for working with dynamo via aws-record
|
207
|
+
test_files: []
|