dynamo-record 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +15 -0
- data/.gitignore +9 -5
- data/.rspec +1 -0
- data/.rubocop.yml +9 -30
- data/.travis.yml +18 -2
- data/Dockerfile +22 -0
- data/Gemfile +0 -1
- data/LICENSE.txt +21 -0
- data/README.md +75 -17
- data/build.sh +10 -20
- data/docker-compose.override.example.yml +19 -0
- data/docker-compose.yml +7 -2
- data/dynamo-record.gemspec +40 -28
- data/lib/dynamo/record.rb +17 -0
- data/lib/dynamo/record/marshalers.rb +46 -0
- data/lib/dynamo/record/model.rb +127 -0
- data/lib/dynamo/record/model_existence_validator.rb +10 -0
- data/lib/{dynamo-record → dynamo}/record/railtie.rb +1 -3
- data/lib/dynamo/record/table_migration.rb +59 -0
- data/lib/dynamo/record/task_helpers/cleanup.rb +21 -0
- data/lib/dynamo/record/task_helpers/drop_all_tables.rb +19 -0
- data/lib/dynamo/record/task_helpers/drop_table.rb +13 -0
- data/lib/dynamo/record/task_helpers/list_tables.rb +15 -0
- data/lib/dynamo/record/task_helpers/migration_runner.rb +72 -0
- data/lib/dynamo/record/task_helpers/scale.rb +90 -0
- data/lib/dynamo/record/version.rb +5 -0
- data/lib/tasks/dynamo.rake +7 -7
- metadata +96 -37
- data/Dockerfile.test +0 -23
- data/Gemfile.lock +0 -178
- data/doc/testing.md +0 -11
- data/docker-compose.dev.override.yml +0 -6
- data/lib/dynamo-record.rb +0 -4
- data/lib/dynamo-record/marshalers.rb +0 -44
- data/lib/dynamo-record/model.rb +0 -127
- data/lib/dynamo-record/record.rb +0 -7
- data/lib/dynamo-record/record/version.rb +0 -5
- data/lib/dynamo-record/table_migration.rb +0 -58
- data/lib/dynamo-record/task_helpers/cleanup.rb +0 -19
- data/lib/dynamo-record/task_helpers/drop_all_tables.rb +0 -17
- data/lib/dynamo-record/task_helpers/drop_table.rb +0 -11
- data/lib/dynamo-record/task_helpers/list_tables.rb +0 -13
- data/lib/dynamo-record/task_helpers/migration_runner.rb +0 -70
- data/lib/dynamo-record/task_helpers/scale.rb +0 -86
- data/lib/model_existence_validator.rb +0 -7
data/Dockerfile.test
DELETED
@@ -1,23 +0,0 @@
|
|
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.lock
DELETED
@@ -1,178 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
dynamo-record (0.1.3)
|
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
|
-
ast (2.3.0)
|
49
|
-
aws-record (1.1.0)
|
50
|
-
aws-sdk-resources (~> 2.0)
|
51
|
-
aws-sdk-core (2.9.14)
|
52
|
-
aws-sigv4 (~> 1.0)
|
53
|
-
jmespath (~> 1.0)
|
54
|
-
aws-sdk-resources (2.9.14)
|
55
|
-
aws-sdk-core (= 2.9.14)
|
56
|
-
aws-sigv4 (1.0.0)
|
57
|
-
builder (3.2.3)
|
58
|
-
byebug (9.0.6)
|
59
|
-
combustion (0.6.0)
|
60
|
-
activesupport (>= 3.0.0)
|
61
|
-
railties (>= 3.0.0)
|
62
|
-
thor (>= 0.14.6)
|
63
|
-
concurrent-ruby (1.0.5)
|
64
|
-
crack (0.4.3)
|
65
|
-
safe_yaml (~> 1.0.0)
|
66
|
-
diff-lcs (1.3)
|
67
|
-
docile (1.1.5)
|
68
|
-
erubis (2.7.0)
|
69
|
-
globalid (0.4.0)
|
70
|
-
activesupport (>= 4.2.0)
|
71
|
-
hashdiff (0.3.2)
|
72
|
-
i18n (0.8.1)
|
73
|
-
jmespath (1.3.1)
|
74
|
-
json (2.1.0)
|
75
|
-
loofah (2.0.3)
|
76
|
-
nokogiri (>= 1.5.9)
|
77
|
-
mail (2.6.5)
|
78
|
-
mime-types (>= 1.16, < 4)
|
79
|
-
mime-types (3.1)
|
80
|
-
mime-types-data (~> 3.2015)
|
81
|
-
mime-types-data (3.2016.0521)
|
82
|
-
mini_portile2 (2.1.0)
|
83
|
-
minitest (5.10.1)
|
84
|
-
nokogiri (1.7.1)
|
85
|
-
mini_portile2 (~> 2.1.0)
|
86
|
-
parser (2.4.0.0)
|
87
|
-
ast (~> 2.2)
|
88
|
-
powerpack (0.1.1)
|
89
|
-
public_suffix (2.0.5)
|
90
|
-
rack (1.6.5)
|
91
|
-
rack-test (0.6.3)
|
92
|
-
rack (>= 1.0)
|
93
|
-
rails (4.2.8)
|
94
|
-
actionmailer (= 4.2.8)
|
95
|
-
actionpack (= 4.2.8)
|
96
|
-
actionview (= 4.2.8)
|
97
|
-
activejob (= 4.2.8)
|
98
|
-
activemodel (= 4.2.8)
|
99
|
-
activerecord (= 4.2.8)
|
100
|
-
activesupport (= 4.2.8)
|
101
|
-
bundler (>= 1.3.0, < 2.0)
|
102
|
-
railties (= 4.2.8)
|
103
|
-
sprockets-rails
|
104
|
-
rails-deprecated_sanitizer (1.0.3)
|
105
|
-
activesupport (>= 4.2.0.alpha)
|
106
|
-
rails-dom-testing (1.0.8)
|
107
|
-
activesupport (>= 4.2.0.beta, < 5.0)
|
108
|
-
nokogiri (~> 1.6)
|
109
|
-
rails-deprecated_sanitizer (>= 1.0.1)
|
110
|
-
rails-html-sanitizer (1.0.3)
|
111
|
-
loofah (~> 2.0)
|
112
|
-
railties (4.2.8)
|
113
|
-
actionpack (= 4.2.8)
|
114
|
-
activesupport (= 4.2.8)
|
115
|
-
rake (>= 0.8.7)
|
116
|
-
thor (>= 0.18.1, < 2.0)
|
117
|
-
rainbow (2.2.2)
|
118
|
-
rake
|
119
|
-
rake (10.5.0)
|
120
|
-
rspec (3.5.0)
|
121
|
-
rspec-core (~> 3.5.0)
|
122
|
-
rspec-expectations (~> 3.5.0)
|
123
|
-
rspec-mocks (~> 3.5.0)
|
124
|
-
rspec-core (3.5.4)
|
125
|
-
rspec-support (~> 3.5.0)
|
126
|
-
rspec-expectations (3.5.0)
|
127
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
128
|
-
rspec-support (~> 3.5.0)
|
129
|
-
rspec-mocks (3.5.0)
|
130
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
131
|
-
rspec-support (~> 3.5.0)
|
132
|
-
rspec-support (3.5.0)
|
133
|
-
rubocop (0.44.1)
|
134
|
-
parser (>= 2.3.1.1, < 3.0)
|
135
|
-
powerpack (~> 0.1)
|
136
|
-
rainbow (>= 1.99.1, < 3.0)
|
137
|
-
ruby-progressbar (~> 1.7)
|
138
|
-
unicode-display_width (~> 1.0, >= 1.0.1)
|
139
|
-
ruby-progressbar (1.8.1)
|
140
|
-
safe_yaml (1.0.4)
|
141
|
-
simplecov (0.14.1)
|
142
|
-
docile (~> 1.1.0)
|
143
|
-
json (>= 1.8, < 3)
|
144
|
-
simplecov-html (~> 0.10.0)
|
145
|
-
simplecov-html (0.10.0)
|
146
|
-
sprockets (3.7.1)
|
147
|
-
concurrent-ruby (~> 1.0)
|
148
|
-
rack (> 1, < 3)
|
149
|
-
sprockets-rails (3.2.0)
|
150
|
-
actionpack (>= 4.0)
|
151
|
-
activesupport (>= 4.0)
|
152
|
-
sprockets (>= 3.0.0)
|
153
|
-
thor (0.19.4)
|
154
|
-
thread_safe (0.3.6)
|
155
|
-
tzinfo (1.2.3)
|
156
|
-
thread_safe (~> 0.1)
|
157
|
-
unicode-display_width (1.2.1)
|
158
|
-
webmock (2.3.2)
|
159
|
-
addressable (>= 2.3.6)
|
160
|
-
crack (>= 0.3.2)
|
161
|
-
hashdiff
|
162
|
-
|
163
|
-
PLATFORMS
|
164
|
-
ruby
|
165
|
-
|
166
|
-
DEPENDENCIES
|
167
|
-
bundler (~> 1.14)
|
168
|
-
byebug (~> 9.0)
|
169
|
-
combustion (~> 0.6.0)
|
170
|
-
dynamo-record!
|
171
|
-
rake (~> 10.0)
|
172
|
-
rspec (~> 3.0)
|
173
|
-
rubocop (~> 0.44.1)
|
174
|
-
simplecov (~> 0.12)
|
175
|
-
webmock (~> 2.1)
|
176
|
-
|
177
|
-
BUNDLED WITH
|
178
|
-
1.14.6
|
data/doc/testing.md
DELETED
data/lib/dynamo-record.rb
DELETED
@@ -1,44 +0,0 @@
|
|
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)
|
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
|
data/lib/dynamo-record/model.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
require 'aws-record'
|
2
|
-
|
3
|
-
module DynamoRecord
|
4
|
-
module Model
|
5
|
-
COMPOSITE_DELIMITER = '|'.freeze
|
6
|
-
|
7
|
-
def self.included(klass)
|
8
|
-
klass.include(Aws::Record)
|
9
|
-
klass.include(DynamoRecord::Marshalers)
|
10
|
-
|
11
|
-
klass.extend ClassMethods
|
12
|
-
klass.send :prepend, InstanceMethods
|
13
|
-
end
|
14
|
-
|
15
|
-
module ClassMethods
|
16
|
-
def table_name
|
17
|
-
[Rails.configuration.dynamo['prefix'], name.tableize].join('-').tr('/', '.')
|
18
|
-
end
|
19
|
-
|
20
|
-
def scan
|
21
|
-
raise 'no scanning in production' if Rails.env.production?
|
22
|
-
super
|
23
|
-
end
|
24
|
-
|
25
|
-
def find(opts)
|
26
|
-
super(opts).tap do |record|
|
27
|
-
unless record
|
28
|
-
name = self.name.demodulize
|
29
|
-
conditions = opts.map { |k, v| "#{k}=#{v}" }.join(', ')
|
30
|
-
error = "Couldn't find #{name} with #{conditions}"
|
31
|
-
raise Aws::Record::Errors::NotFound, error
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def find_all_by_hash_key(hash_key_value, opts = {})
|
37
|
-
find_all_by_index_and_hash_key(hash_key, hash_key_value, opts)
|
38
|
-
end
|
39
|
-
|
40
|
-
def find_all_by_gsi_hash_key(gsi_name, hash_key_value, opts = {})
|
41
|
-
hash_key_name = global_secondary_indexes[gsi_name.to_sym][:hash_key]
|
42
|
-
find_all_by_index_and_hash_key(hash_key_name, hash_key_value, opts, gsi_name.to_s)
|
43
|
-
end
|
44
|
-
|
45
|
-
def find_all_by_gsi_hash_and_range_keys(gsi_name, hash_key_value, range_key_value)
|
46
|
-
gsi_config = global_secondary_indexes[gsi_name.to_sym]
|
47
|
-
find_all_by_index_hash_and_range_keys(
|
48
|
-
hash_config: { name: gsi_config[:hash_key], value: hash_key_value },
|
49
|
-
range_config: { name: gsi_config[:range_key], value: range_key_value },
|
50
|
-
index_name: gsi_name.to_s
|
51
|
-
)
|
52
|
-
end
|
53
|
-
|
54
|
-
def find_all_by_lsi_hash_key(lsi_name, hash_key_value, opts = {})
|
55
|
-
hash_key_name = local_secondary_indexes[lsi_name.to_sym][:hash_key]
|
56
|
-
find_all_by_index_and_hash_key(hash_key_name, hash_key_value, opts, lsi_name.to_s)
|
57
|
-
end
|
58
|
-
|
59
|
-
def find_all_by_lsi_hash_and_range_keys(lsi_name, hash_key_value, range_key_value)
|
60
|
-
lsi_config = local_secondary_indexes[lsi_name.to_sym]
|
61
|
-
find_all_by_index_hash_and_range_keys(
|
62
|
-
hash_config: { name: lsi_config[:hash_key], value: hash_key_value },
|
63
|
-
range_config: { name: lsi_config[:range_key], value: range_key_value },
|
64
|
-
index_name: lsi_name.to_s
|
65
|
-
)
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_all_by_index_and_hash_key(hash_key_name, hash_key_value, opts = {}, index_name = nil)
|
69
|
-
query_options = {
|
70
|
-
select: 'ALL_ATTRIBUTES',
|
71
|
-
key_condition_expression: "#{hash_key_name} = :hash_key_value",
|
72
|
-
expression_attribute_values: {
|
73
|
-
':hash_key_value': hash_key_value
|
74
|
-
},
|
75
|
-
scan_index_forward: true
|
76
|
-
}
|
77
|
-
query_options[:index_name] = index_name if index_name
|
78
|
-
query_options.merge!(opts)
|
79
|
-
query(query_options)
|
80
|
-
end
|
81
|
-
|
82
|
-
def find_all_by_index_hash_and_range_keys(hash_config:, range_config:, index_name: nil,
|
83
|
-
scan_index_forward: true, limit: nil)
|
84
|
-
range_expression = range_config[:expression] || "#{range_config[:name]} = :rkv"
|
85
|
-
query_options = {
|
86
|
-
select: 'ALL_ATTRIBUTES',
|
87
|
-
key_condition_expression: "#{hash_config[:name]} = :hkv AND #{range_expression}",
|
88
|
-
expression_attribute_values: {
|
89
|
-
':hkv': hash_config[:value],
|
90
|
-
':rkv': range_config[:value]
|
91
|
-
},
|
92
|
-
scan_index_forward: scan_index_forward,
|
93
|
-
limit: limit
|
94
|
-
}
|
95
|
-
query_options[:index_name] = index_name if index_name
|
96
|
-
query(query_options)
|
97
|
-
end
|
98
|
-
|
99
|
-
def composite_key(*args)
|
100
|
-
args.join(COMPOSITE_DELIMITER)
|
101
|
-
end
|
102
|
-
|
103
|
-
def split_composite(string)
|
104
|
-
string.split(COMPOSITE_DELIMITER)
|
105
|
-
end
|
106
|
-
|
107
|
-
# TODO: Create a batch save method.
|
108
|
-
end
|
109
|
-
|
110
|
-
module InstanceMethods
|
111
|
-
def read_attribute_for_serialization(attribute)
|
112
|
-
send(attribute)
|
113
|
-
end
|
114
|
-
|
115
|
-
def attribute_hash
|
116
|
-
attrs = self.class.attributes.attributes
|
117
|
-
attr_keys = attrs.keys
|
118
|
-
hash = {}
|
119
|
-
|
120
|
-
attr_keys.each do |key|
|
121
|
-
hash[key] = attrs[key].type_cast(send(key))
|
122
|
-
end
|
123
|
-
hash
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
data/lib/dynamo-record/record.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
module DynamoRecord
|
2
|
-
class TableMigration
|
3
|
-
def self.table_config_check
|
4
|
-
if migrate_table?
|
5
|
-
table_config.migrate!
|
6
|
-
return :migrated
|
7
|
-
end
|
8
|
-
:exists
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.migrate(model)
|
12
|
-
migration = Aws::Record::TableMigration.new(model)
|
13
|
-
begin
|
14
|
-
migration.client.describe_table(table_name: model.table_name)
|
15
|
-
return :exists
|
16
|
-
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
17
|
-
yield migration
|
18
|
-
migration.wait_until_available
|
19
|
-
return :migrated
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.migrate_updates(model)
|
24
|
-
migration = Aws::Record::TableMigration.new(model)
|
25
|
-
yield migration
|
26
|
-
migration.wait_until_available
|
27
|
-
:updated
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.add_stream(model)
|
31
|
-
migrate_updates(model) do |migration|
|
32
|
-
migration.update!(
|
33
|
-
stream_specification: {
|
34
|
-
stream_enabled: true,
|
35
|
-
stream_view_type: 'NEW_IMAGE'
|
36
|
-
}
|
37
|
-
)
|
38
|
-
end
|
39
|
-
rescue Aws::DynamoDB::Errors::ValidationException => e
|
40
|
-
return e.message if e.message == 'Table already has an enabled stream'
|
41
|
-
raise e
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.migrate_table?(update_provisioned_throughput = false)
|
45
|
-
unless update_provisioned_throughput
|
46
|
-
client = table_config.client
|
47
|
-
table_name = table_config.instance_values['model_class'].table_name
|
48
|
-
described_table = client.describe_table table_name: table_name
|
49
|
-
provisioned_throughput = described_table.table.provisioned_throughput
|
50
|
-
table_config.read_capacity_units provisioned_throughput.read_capacity_units
|
51
|
-
table_config.write_capacity_units provisioned_throughput.write_capacity_units
|
52
|
-
end
|
53
|
-
!table_config.exact_match?
|
54
|
-
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
55
|
-
true
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|