dynamo-record 0.4.0 → 1.4.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 +5 -5
- data/.rubocop.yml +25 -24
- data/.travis.yml +13 -6
- data/Dockerfile +14 -15
- data/Gemfile +2 -0
- data/Jenkinsfile +45 -0
- data/README.md +5 -3
- data/dynamo-record.gemspec +17 -12
- data/lib/dynamo/record.rb +15 -0
- data/lib/dynamo/record/batch_get.rb +84 -0
- data/lib/dynamo/record/batch_request.rb +8 -2
- data/lib/dynamo/record/batch_write.rb +6 -6
- data/lib/dynamo/record/marshalers.rb +4 -3
- data/lib/dynamo/record/model.rb +9 -2
- data/lib/dynamo/record/model_existence_validator.rb +3 -0
- data/lib/dynamo/record/railtie.rb +6 -0
- data/lib/dynamo/record/table_migration.rb +6 -3
- data/lib/dynamo/record/task_helpers/cleanup.rb +5 -1
- data/lib/dynamo/record/task_helpers/drop_all_tables.rb +5 -1
- data/lib/dynamo/record/task_helpers/drop_table.rb +2 -0
- data/lib/dynamo/record/task_helpers/list_tables.rb +2 -0
- data/lib/dynamo/record/task_helpers/migration_runner.rb +12 -5
- data/lib/dynamo/record/task_helpers/scale.rb +3 -2
- data/lib/dynamo/record/version.rb +3 -1
- data/lib/tasks/dynamo.rake +3 -1
- metadata +58 -45
- data/build.sh +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9a8e9652f6a8164e4c7f9d1ca2ae3c0509c396ebc11e6e9d0596fd1c132184c1
|
4
|
+
data.tar.gz: f0b31912eba1e7a0b53068e530e2c9b6573a7b4edeb3ff94315d3599dfd6789f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8664fc94e0a0a72f4ca4f44682d6fbff512c3808a0ab89a8626c691e93b20b894852f240174ac8dc6c4c44498274c6a436ac48c0184ea10e839720625df18b3
|
7
|
+
data.tar.gz: 69b6aaacf990a51b25363791b14c5d500d227fe9a53e404bf55d7668b335d6f2795a743ec2d16aaa4c694d90aee5d8034c7aaae3fe1bd3bd5a72d34ee0dd2a59
|
data/.rubocop.yml
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require:
|
2
|
+
- rubocop-rails
|
3
3
|
|
4
4
|
AllCops:
|
5
|
+
NewCops: disable
|
6
|
+
SuggestExtensions: false
|
5
7
|
TargetRailsVersion: 4.2
|
6
|
-
TargetRubyVersion: 2.
|
8
|
+
TargetRubyVersion: 2.6
|
7
9
|
|
8
10
|
Metrics/AbcSize:
|
9
11
|
Max: 20 # Default: 15
|
@@ -11,19 +13,22 @@ Metrics/AbcSize:
|
|
11
13
|
Metrics/ClassLength:
|
12
14
|
Max: 200 # Default: 100
|
13
15
|
|
14
|
-
Metrics/LineLength:
|
15
|
-
Max: 120 # Default: 80
|
16
|
-
|
17
16
|
Metrics/MethodLength:
|
18
17
|
Max: 20 # Default: 10
|
19
18
|
|
20
19
|
Metrics/BlockLength:
|
21
20
|
Max: 30
|
22
21
|
Exclude:
|
23
|
-
-
|
24
|
-
-
|
22
|
+
- dynamo-record.gemspec
|
23
|
+
- spec/**/*.rb
|
24
|
+
|
25
|
+
Layout/EndAlignment:
|
26
|
+
EnforcedStyleAlignWith: variable
|
25
27
|
|
26
|
-
Layout/
|
28
|
+
Layout/LineLength:
|
29
|
+
Max: 120 # Default: 80
|
30
|
+
|
31
|
+
Layout/ParameterAlignment:
|
27
32
|
# Alignment of parameters in multi-line method calls.
|
28
33
|
#
|
29
34
|
# The `with_fixed_indentation` style aligns the following lines with one
|
@@ -33,8 +38,16 @@ Layout/AlignParameters:
|
|
33
38
|
# b)
|
34
39
|
EnforcedStyle: with_fixed_indentation
|
35
40
|
|
36
|
-
Lint/
|
37
|
-
|
41
|
+
Lint/ConstantDefinitionInBlock:
|
42
|
+
Exclude:
|
43
|
+
- spec/**/*.rb
|
44
|
+
|
45
|
+
Naming/FileName:
|
46
|
+
Exclude:
|
47
|
+
- spec/gemfiles/*
|
48
|
+
|
49
|
+
Naming/VariableNumber:
|
50
|
+
EnforcedStyle: snake_case
|
38
51
|
|
39
52
|
Style/Documentation:
|
40
53
|
# This cop checks for missing top-level documentation of classes and modules.
|
@@ -43,18 +56,6 @@ Style/Documentation:
|
|
43
56
|
# classes or other modules.
|
44
57
|
Enabled: false
|
45
58
|
|
46
|
-
Style/FrozenStringLiteralComment:
|
47
|
-
# `when_needed` will add the frozen string literal comment to files
|
48
|
-
# only when the `TargetRubyVersion` is set to 2.3+.
|
49
|
-
# `always` will always add the frozen string literal comment to a file
|
50
|
-
# regardless of the Ruby version or if `freeze` or `<<` are called on a
|
51
|
-
# string literal. If you run code against multiple versions of Ruby, it is
|
52
|
-
# possible that this will create errors in Ruby 2.3.0+.
|
53
|
-
#
|
54
|
-
# See: https://wyeworks.com/blog/2015/12/1/immutable-strings-in-ruby-2-dot-3
|
55
|
-
EnforcedStyle: when_needed
|
56
|
-
Enabled: false
|
57
|
-
|
58
59
|
Style/NumericPredicate:
|
59
60
|
Exclude:
|
60
|
-
-
|
61
|
+
- spec/**/*.rb
|
data/.travis.yml
CHANGED
@@ -4,18 +4,25 @@ language: ruby
|
|
4
4
|
cache: bundler
|
5
5
|
|
6
6
|
rvm:
|
7
|
-
- 2.
|
8
|
-
- 2.
|
7
|
+
- 2.6
|
8
|
+
- 2.7
|
9
|
+
- 3.0
|
9
10
|
|
10
11
|
gemfile:
|
11
|
-
- spec/gemfiles/rails-
|
12
|
-
- spec/gemfiles/rails-
|
13
|
-
- spec/gemfiles/rails-
|
12
|
+
- spec/gemfiles/rails-5.2.gemfile
|
13
|
+
- spec/gemfiles/rails-6.0.gemfile
|
14
|
+
- spec/gemfiles/rails-6.1.gemfile
|
15
|
+
|
16
|
+
# Rails 5.2 doesn't support Ruby 3.0, so don't try
|
17
|
+
matrix:
|
18
|
+
exclude:
|
19
|
+
- rvm: 3.0
|
20
|
+
gemfile: spec/gemfiles/rails-5.2.gemfile
|
14
21
|
|
15
22
|
before_install: gem update bundler
|
16
23
|
bundler_args: --jobs 3
|
17
24
|
install: bundle install --jobs 3
|
18
25
|
|
19
26
|
script:
|
20
|
-
- bash -c "if [ '$TRAVIS_RUBY_VERSION' = '
|
27
|
+
- bash -c "if [ '$TRAVIS_RUBY_VERSION' = '3.0' ] && [[ '$BUNDLE_GEMFILE' == *'rails-6.1'* ]]; then bundle exec rubocop --fail-level autocorrect; fi"
|
21
28
|
- bundle exec rspec
|
data/Dockerfile
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
FROM instructure/rvm
|
2
2
|
|
3
3
|
WORKDIR /app
|
4
|
-
|
5
|
-
COPY dynamo-record.gemspec Gemfile /app/
|
6
|
-
COPY lib/dynamo/record/version.rb /app/lib/dynamo/record/version.rb
|
7
|
-
|
8
|
-
USER root
|
9
|
-
RUN mkdir -p /app/coverage \
|
10
|
-
/app/spec/gemfiles/.bundle \
|
11
|
-
/app/spec/internal/log \
|
12
|
-
&& chown -R docker:docker /app
|
13
|
-
|
14
|
-
USER docker
|
15
|
-
RUN /bin/bash -l -c "cd /app && rvm-exec 2.4 bundle install --jobs 5"
|
16
|
-
COPY . /app
|
17
|
-
|
18
4
|
USER root
|
19
5
|
RUN chown -R docker:docker /app
|
20
6
|
USER docker
|
21
7
|
|
22
|
-
|
8
|
+
COPY --chown=docker:docker dynamo-record.gemspec Gemfile /app/
|
9
|
+
COPY --chown=docker:docker lib/dynamo/record/version.rb /app/lib/dynamo/record/version.rb
|
10
|
+
|
11
|
+
RUN mkdir -p coverage \
|
12
|
+
spec/gemfiles/.bundle \
|
13
|
+
spec/internal/log
|
14
|
+
|
15
|
+
RUN bash -lc "rvm 2.6,2.7,3.0 do gem install --no-document bundler -v '~> 2.2'"
|
16
|
+
RUN bash -lc "cd /app && rvm-exec 2.6 bundle install --jobs 5"
|
17
|
+
RUN bash -lc "cd /app && rvm-exec 2.7 bundle install --jobs 5"
|
18
|
+
RUN bash -lc "cd /app && rvm-exec 3.0 bundle install --jobs 5"
|
19
|
+
COPY --chown=docker:docker . /app
|
20
|
+
|
21
|
+
CMD /bin/bash -lc "rvm-exec 3.0 bundle exec wwtd"
|
data/Gemfile
CHANGED
data/Jenkinsfile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env groovy
|
2
|
+
|
3
|
+
pipeline {
|
4
|
+
agent {
|
5
|
+
label 'docker'
|
6
|
+
}
|
7
|
+
options {
|
8
|
+
ansiColor("xterm")
|
9
|
+
buildDiscarder(logRotator(numToKeepStr: '50'))
|
10
|
+
timeout(time: 20, unit: 'MINUTES')
|
11
|
+
}
|
12
|
+
stages {
|
13
|
+
stage('Build') {
|
14
|
+
steps {
|
15
|
+
sh 'docker-compose pull dynamo'
|
16
|
+
sh 'docker-compose up -d dynamo'
|
17
|
+
sh 'docker-compose build --pull app'
|
18
|
+
}
|
19
|
+
}
|
20
|
+
stage('Test') {
|
21
|
+
steps {
|
22
|
+
sh '''
|
23
|
+
docker-compose run --rm app /bin/bash -l -c \
|
24
|
+
"rvm-exec 3.0 bundle exec rubocop --fail-level autocorrect"
|
25
|
+
docker-compose run --name coverage app
|
26
|
+
'''
|
27
|
+
}
|
28
|
+
post {
|
29
|
+
always {
|
30
|
+
sh 'docker cp coverage:/app/coverage .'
|
31
|
+
sh 'docker-compose down --rmi=all --volumes --remove-orphans'
|
32
|
+
|
33
|
+
publishHTML target: [
|
34
|
+
allowMissing: false,
|
35
|
+
alwaysLinkToLastBuild: false,
|
36
|
+
keepAll: true,
|
37
|
+
reportDir: "coverage",
|
38
|
+
reportFiles: 'index.html',
|
39
|
+
reportName: 'Coverage Report'
|
40
|
+
]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
data/README.md
CHANGED
@@ -211,11 +211,13 @@ gem with multiple versions of Ruby. This requires docker and docker-compose to
|
|
211
211
|
be installed. To get started, run the following:
|
212
212
|
|
213
213
|
```bash
|
214
|
-
|
214
|
+
docker-compose build --pull
|
215
|
+
docker-compose up -d dynamo
|
216
|
+
docker-compose run --rm app
|
215
217
|
```
|
216
218
|
|
217
219
|
This will install the gem in a docker image with all versions of Ruby installed,
|
218
|
-
and install all gem dependencies in the Ruby 2.
|
220
|
+
and install all gem dependencies in the Ruby 2.6 set of gems. It will also
|
219
221
|
download and spin up a DynamoDB Local container for use with specs. Finally, it
|
220
222
|
will run [wwtd](https://github.com/grosser/wwtd), which runs all specs across
|
221
223
|
all supported version of Ruby and Rails, bundling gems for each combination
|
@@ -228,7 +230,7 @@ Individual spec runs can be started like so:
|
|
228
230
|
|
229
231
|
```bash
|
230
232
|
docker-compose run --rm app /bin/bash -l -c \
|
231
|
-
"BUNDLE_GEMFILE=spec/gemfiles/rails-
|
233
|
+
"BUNDLE_GEMFILE=spec/gemfiles/rails-6.1.gemfile rvm-exec 3.0 bundle exec rspec"
|
232
234
|
```
|
233
235
|
|
234
236
|
If you'd like to mount your git checkout within the docker container running
|
data/dynamo-record.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require 'dynamo/record/version'
|
4
6
|
|
@@ -33,18 +35,21 @@ Gem::Specification.new do |s|
|
|
33
35
|
end
|
34
36
|
s.require_paths = ['lib']
|
35
37
|
|
36
|
-
s.
|
37
|
-
|
38
|
-
s.add_dependency '
|
38
|
+
s.required_ruby_version = '>= 2.6'
|
39
|
+
|
40
|
+
s.add_dependency 'activemodel', '>= 5.2', '< 6.2'
|
41
|
+
s.add_dependency 'aws-record', '~> 2.0'
|
42
|
+
s.add_dependency 'railties', '>= 5.2', '< 6.2'
|
39
43
|
|
40
|
-
s.add_development_dependency 'activesupport', '>=
|
41
|
-
s.add_development_dependency 'bundler', '~>
|
42
|
-
s.add_development_dependency 'byebug', '~>
|
43
|
-
s.add_development_dependency 'combustion', '~>
|
44
|
-
s.add_development_dependency 'rake', '~>
|
44
|
+
s.add_development_dependency 'activesupport', '>= 5.2', '< 6.2'
|
45
|
+
s.add_development_dependency 'bundler', '~> 2.2'
|
46
|
+
s.add_development_dependency 'byebug', '~> 11.0'
|
47
|
+
s.add_development_dependency 'combustion', '~> 1.3'
|
48
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
45
49
|
s.add_development_dependency 'rspec', '~> 3.6'
|
46
|
-
s.add_development_dependency 'rubocop', '~>
|
47
|
-
s.add_development_dependency '
|
48
|
-
s.add_development_dependency '
|
50
|
+
s.add_development_dependency 'rubocop', '~> 1.8.1'
|
51
|
+
s.add_development_dependency 'rubocop-rails', '~> 2.9.1'
|
52
|
+
s.add_development_dependency 'simplecov', '~> 0.16'
|
53
|
+
s.add_development_dependency 'webmock', '~> 3.3'
|
49
54
|
s.add_development_dependency 'wwtd', '~> 1.3'
|
50
55
|
end
|
data/lib/dynamo/record.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_model'
|
2
4
|
require 'aws-record'
|
3
5
|
require 'rails/railtie'
|
4
6
|
|
5
7
|
require 'dynamo/record/marshalers'
|
8
|
+
require 'dynamo/record/batch_get'
|
6
9
|
require 'dynamo/record/batch_write'
|
7
10
|
require 'dynamo/record/batch_request'
|
8
11
|
require 'dynamo/record/model'
|
@@ -17,3 +20,15 @@ require 'dynamo/record/task_helpers/drop_table'
|
|
17
20
|
require 'dynamo/record/task_helpers/list_tables'
|
18
21
|
require 'dynamo/record/task_helpers/migration_runner'
|
19
22
|
require 'dynamo/record/task_helpers/scale'
|
23
|
+
|
24
|
+
module Dynamo
|
25
|
+
module Record
|
26
|
+
class << self
|
27
|
+
attr_writer :logger
|
28
|
+
|
29
|
+
def logger
|
30
|
+
@logger ||= Logger.new($stdout).tap { |log| log.progname = name }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Dynamo
|
6
|
+
module Record
|
7
|
+
module BatchGet
|
8
|
+
BATCH_SIZE = 100
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def batch_get(keys)
|
12
|
+
process_batch_get_requests(keys)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def process_batch_get_requests(keys) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
18
|
+
attempt = 1
|
19
|
+
results = []
|
20
|
+
unprocessed_keys = keys.dup.map(&:stringify_keys)
|
21
|
+
while !unprocessed_keys.empty? && attempt <= max_retries
|
22
|
+
batch = unprocessed_keys.shift(BATCH_SIZE)
|
23
|
+
Dynamo::Record.logger.debug "#{name} batch_get_item #{batch.count} request_items"
|
24
|
+
processed, unprocessed = get_batch_from_dynamo(batch)
|
25
|
+
results.concat(processed)
|
26
|
+
|
27
|
+
next if unprocessed.empty?
|
28
|
+
|
29
|
+
Dynamo::Record.logger.debug "#{name} batch_get_item #{unprocessed.count} unprocessed_keys"
|
30
|
+
unprocessed_keys.unshift(*unprocessed)
|
31
|
+
sleep(rand(1 << attempt) + 1)
|
32
|
+
attempt += 1
|
33
|
+
end
|
34
|
+
|
35
|
+
if !unprocessed_keys.empty? && attempt > max_retries
|
36
|
+
raise NumberOfRetriesExceeded.new(keys), 'Number of retries exceeded'
|
37
|
+
end
|
38
|
+
|
39
|
+
results.map { |r| build_item_from_hash(r) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# BatchGetItem will return a partial result if provisioned throughput is
|
43
|
+
# exceeded or an internal error occurs. If all items cannot be processed
|
44
|
+
# ProvisionedThroughputExceededException is raised.
|
45
|
+
# https://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#batch_get_item-instance_method
|
46
|
+
def get_batch_from_dynamo(batch)
|
47
|
+
response = dynamodb_client.batch_get_item(
|
48
|
+
request_items: {
|
49
|
+
table_name => { keys: batch }
|
50
|
+
}
|
51
|
+
)
|
52
|
+
[
|
53
|
+
response.responses[table_name],
|
54
|
+
response.unprocessed_keys[table_name]&.keys || []
|
55
|
+
]
|
56
|
+
rescue Aws::DynamoDB::Errors::ProvisionedThroughputExceededException
|
57
|
+
[[], batch]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Convert response hashes to Record instances
|
61
|
+
# Adapted from `build_item_from_resp` used by `find` but that method is closely tied to get_item response format
|
62
|
+
# https://github.com/aws/aws-sdk-ruby-record/blob/v2.3.0/lib/aws-record/record/item_operations.rb#L593
|
63
|
+
def build_item_from_hash(resp)
|
64
|
+
record = new
|
65
|
+
data = record.instance_variable_get('@data')
|
66
|
+
attributes.attributes.each do |name, attr|
|
67
|
+
data.set_attribute(name, attr.extract(resp))
|
68
|
+
data.new_record = false
|
69
|
+
end
|
70
|
+
record
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class NumberOfRetriesExceeded < RuntimeError
|
75
|
+
attr_reader :keys
|
76
|
+
|
77
|
+
def initialize(keys)
|
78
|
+
super
|
79
|
+
@keys = keys
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
class BatchRequest
|
@@ -13,7 +15,7 @@ module Dynamo
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def request
|
16
|
-
@
|
18
|
+
@request ||= send("#{type}_request")
|
17
19
|
end
|
18
20
|
|
19
21
|
def save_request
|
@@ -33,25 +35,29 @@ module Dynamo
|
|
33
35
|
end
|
34
36
|
|
35
37
|
def request_size
|
36
|
-
@
|
38
|
+
@request_size ||= request.to_json.length
|
37
39
|
end
|
38
40
|
|
39
41
|
def validate_request_size
|
40
42
|
return unless request_size > MAX_RECORD_SIZE
|
43
|
+
|
41
44
|
raise RecordTooLargeError.new(record), 'Record is too large'
|
42
45
|
end
|
43
46
|
|
44
47
|
def validate_type
|
45
48
|
return if %i[save delete].include? type
|
49
|
+
|
46
50
|
raise UnsupportedRequestTypeError
|
47
51
|
end
|
48
52
|
end
|
49
53
|
|
50
54
|
class UnsupportedRequestTypeError < RuntimeError; end
|
55
|
+
|
51
56
|
class RecordTooLargeError < RuntimeError
|
52
57
|
attr_reader :record
|
53
58
|
|
54
59
|
def initialize(record)
|
60
|
+
super
|
55
61
|
@record = record
|
56
62
|
end
|
57
63
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module BatchWrite
|
4
6
|
BATCH_SIZE = 25
|
5
|
-
DEFAULT_MAX_RETRIES = 5
|
6
7
|
MAX_PAYLOAD_SIZE = 16_000_000 # 16 MB
|
7
8
|
|
8
9
|
module ClassMethods
|
@@ -62,11 +63,14 @@ module Dynamo
|
|
62
63
|
if retry_count > max_retries
|
63
64
|
raise NumberOfRetriesExceeded.new(unprocessed_items.map(&:record)), 'Number of retries exceeded'
|
64
65
|
end
|
66
|
+
|
65
67
|
sleep(rand(1 << retry_count) + 1)
|
66
68
|
process_batch_write_requests(unprocessed_items, retry_count + 1)
|
67
69
|
end
|
68
70
|
|
69
71
|
def pre_batch(requests)
|
72
|
+
return [] if requests.empty?
|
73
|
+
|
70
74
|
current_batch_size = 0
|
71
75
|
requests.each_with_object([[]]) do |request, batches|
|
72
76
|
if batches.last.length >= BATCH_SIZE || current_batch_size + request.request_size > MAX_PAYLOAD_SIZE
|
@@ -77,17 +81,13 @@ module Dynamo
|
|
77
81
|
current_batch_size += request.request_size
|
78
82
|
end
|
79
83
|
end
|
80
|
-
|
81
|
-
# Override in model if you want it to be different.
|
82
|
-
def max_retries
|
83
|
-
DEFAULT_MAX_RETRIES
|
84
|
-
end
|
85
84
|
end
|
86
85
|
|
87
86
|
class NumberOfRetriesExceeded < RuntimeError
|
88
87
|
attr_reader :unprocessed_items
|
89
88
|
|
90
89
|
def initialize(unprocessed_items)
|
90
|
+
super
|
91
91
|
@unprocessed_items = unprocessed_items
|
92
92
|
end
|
93
93
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module Marshalers
|
4
|
-
COMPOSITE_DELIMETER = '|'
|
6
|
+
COMPOSITE_DELIMETER = '|'
|
5
7
|
|
6
8
|
def self.included(sub_class)
|
7
9
|
sub_class.extend(ClassMethods)
|
@@ -25,15 +27,14 @@ module Dynamo
|
|
25
27
|
opts[:dynamodb_type] = 'S'
|
26
28
|
|
27
29
|
# It is very unfortunate that Aws::Record used `attr`
|
28
|
-
# rubocop:disable Style/Attr
|
29
30
|
attr(name, Aws::Record::Marshalers::StringMarshaler.new(opts), opts)
|
30
|
-
# rubocop:enable Style/Attr
|
31
31
|
end
|
32
32
|
|
33
33
|
def define_readers(name, parts, cast_function)
|
34
34
|
parts.each_with_index do |part, i|
|
35
35
|
raise "#{part} already defined" unless parts.find_index(part) == i
|
36
36
|
next if method_defined?(part)
|
37
|
+
|
37
38
|
define_method(part) do
|
38
39
|
# @data is used internally by Aws::Record to store all of the attributes
|
39
40
|
@data.get_attribute(name).split(COMPOSITE_DELIMETER)[i].send(cast_function)
|
data/lib/dynamo/record/model.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module Model
|
4
|
-
COMPOSITE_DELIMITER = '|'
|
6
|
+
COMPOSITE_DELIMITER = '|'
|
7
|
+
DEFAULT_MAX_RETRIES = 5
|
5
8
|
|
6
9
|
def self.included(klass)
|
7
10
|
klass.include(Aws::Record)
|
@@ -9,6 +12,7 @@ module Dynamo
|
|
9
12
|
|
10
13
|
klass.extend ClassMethods
|
11
14
|
klass.extend Dynamo::Record::BatchWrite::ClassMethods
|
15
|
+
klass.extend Dynamo::Record::BatchGet::ClassMethods
|
12
16
|
klass.send :prepend, InstanceMethods
|
13
17
|
end
|
14
18
|
|
@@ -19,6 +23,7 @@ module Dynamo
|
|
19
23
|
|
20
24
|
def scan
|
21
25
|
raise 'no scanning in production' if Rails.env.production?
|
26
|
+
|
22
27
|
super
|
23
28
|
end
|
24
29
|
|
@@ -104,7 +109,9 @@ module Dynamo
|
|
104
109
|
string.split(COMPOSITE_DELIMITER)
|
105
110
|
end
|
106
111
|
|
107
|
-
|
112
|
+
def max_retries
|
113
|
+
DEFAULT_MAX_RETRIES
|
114
|
+
end
|
108
115
|
end
|
109
116
|
|
110
117
|
module InstanceMethods
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
class ModelExistenceValidator < ActiveModel::EachValidator
|
4
6
|
def validate_each(record, attribute, value)
|
5
7
|
return if options[:model].exists? value
|
8
|
+
|
6
9
|
record.errors[attribute] << (options[:message] || "#{attribute}:#{value} is not a valid #{options[:model]}")
|
7
10
|
end
|
8
11
|
end
|
@@ -1,8 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
class Railtie < Rails::Railtie
|
4
6
|
railtie_name :dynamo_record
|
5
7
|
|
8
|
+
initializer 'logger' do
|
9
|
+
Dynamo::Record.logger = Rails.logger
|
10
|
+
end
|
11
|
+
|
6
12
|
rake_tasks do
|
7
13
|
load 'tasks/dynamo.rake'
|
8
14
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
class TableMigration
|
@@ -13,11 +15,11 @@ module Dynamo
|
|
13
15
|
migration = Aws::Record::TableMigration.new(model)
|
14
16
|
begin
|
15
17
|
migration.client.describe_table(table_name: model.table_name)
|
16
|
-
|
18
|
+
:exists
|
17
19
|
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
18
20
|
yield migration
|
19
21
|
migration.wait_until_available
|
20
|
-
|
22
|
+
:migrated
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
@@ -39,10 +41,11 @@ module Dynamo
|
|
39
41
|
end
|
40
42
|
rescue Aws::DynamoDB::Errors::ValidationException => e
|
41
43
|
return e.message if e.message == 'Table already has an enabled stream'
|
44
|
+
|
42
45
|
raise e
|
43
46
|
end
|
44
47
|
|
45
|
-
def self.migrate_table?(update_provisioned_throughput
|
48
|
+
def self.migrate_table?(update_provisioned_throughput: false)
|
46
49
|
unless update_provisioned_throughput
|
47
50
|
table_name = table_config.instance_values['model_class'].table_name
|
48
51
|
described_table = table_config.client.describe_table table_name: table_name
|
@@ -1,10 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module TaskHelpers
|
4
6
|
class Cleanup
|
5
7
|
def self.run
|
6
8
|
raise 'Task not available on production' if Rails.env.production?
|
7
|
-
|
9
|
+
|
10
|
+
Dir[Rails.root.join('app/models/*.rb').to_s].each do |filename|
|
8
11
|
delete_by_class(filename)
|
9
12
|
end
|
10
13
|
end
|
@@ -12,6 +15,7 @@ module Dynamo
|
|
12
15
|
def self.delete_by_class(filename)
|
13
16
|
klass = File.basename(filename, '.rb').camelize.constantize
|
14
17
|
return unless klass.included_modules.include? Dynamo::Record::Model
|
18
|
+
|
15
19
|
Rails.logger.info "Deleting all items in table: #{klass}"
|
16
20
|
klass.scan.each(&:delete!)
|
17
21
|
end
|
@@ -1,14 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module TaskHelpers
|
4
6
|
class DropAllTables
|
5
|
-
def self.run(override
|
7
|
+
def self.run(override: false)
|
6
8
|
raise 'Task not available on production' if Rails.env.production?
|
9
|
+
|
7
10
|
env = Rails.env
|
8
11
|
dynamodb = Aws::DynamoDB::Client.new
|
9
12
|
tables = dynamodb.list_tables
|
10
13
|
tables.table_names.map do |t|
|
11
14
|
next unless t.include?(env) || override
|
15
|
+
|
12
16
|
dt = dynamodb.delete_table(table_name: t)
|
13
17
|
dt ? "Deleted: #{t}" : "Delete failed: #{t}"
|
14
18
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module TaskHelpers
|
@@ -7,8 +9,8 @@ module Dynamo
|
|
7
9
|
filename_regexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/
|
8
10
|
|
9
11
|
# Sorts the files located in `db/dynamo_migrate` to ensure order is preserved
|
10
|
-
Dir[Rails.root.join("#{path}/*.rb")].sort.each do |
|
11
|
-
migration = migration(
|
12
|
+
Dir[Rails.root.join("#{path}/*.rb")].sort.each do |file|
|
13
|
+
migration = migration(file, filename_regexp, constants)
|
12
14
|
|
13
15
|
# starts the migration
|
14
16
|
yield "Migrating: #{migration}"
|
@@ -28,9 +30,10 @@ module Dynamo
|
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
def self.migration(
|
32
|
-
raise "Non-numeric prefix: #{
|
33
|
-
|
33
|
+
def self.migration(file, filename_regexp, constants)
|
34
|
+
raise "Non-numeric prefix: #{file}" if File.basename(file).scan(filename_regexp).first.nil?
|
35
|
+
|
36
|
+
require file
|
34
37
|
|
35
38
|
# finds the constant that was added on the require statement above
|
36
39
|
migration_sym = (DynamoMigrate.constants - constants).first
|
@@ -52,18 +55,22 @@ module Dynamo
|
|
52
55
|
|
53
56
|
def self.up(migration)
|
54
57
|
return unless migration.respond_to? :up
|
58
|
+
|
55
59
|
status_message migration.up
|
56
60
|
end
|
57
61
|
|
58
62
|
def self.table_config_check(migration)
|
59
63
|
return unless migration.respond_to? :table_config
|
64
|
+
|
60
65
|
status_message migration.table_config_check
|
61
66
|
end
|
62
67
|
|
63
68
|
def self.update(migration)
|
64
69
|
return unless migration.respond_to? :update
|
70
|
+
|
65
71
|
status = migration.update
|
66
72
|
return 'Migration successful' if status == :updated
|
73
|
+
|
67
74
|
status
|
68
75
|
end
|
69
76
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dynamo
|
2
4
|
module Record
|
3
5
|
module TaskHelpers
|
4
6
|
class Scale
|
5
|
-
attr_reader :model, :attribute_selector, :new_throughput
|
6
|
-
attr_reader :migration, :existing_throughput, :model_name
|
7
|
+
attr_reader :model, :attribute_selector, :new_throughput, :migration, :existing_throughput, :model_name
|
7
8
|
|
8
9
|
def initialize(model_name, attribute_selector, new_throughput)
|
9
10
|
@model_name = model_name
|
data/lib/tasks/dynamo.rake
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
namespace :dynamo do
|
2
4
|
desc 'Run dynamo migrations'
|
3
5
|
task migrate: :environment do
|
@@ -5,7 +7,7 @@ namespace :dynamo do
|
|
5
7
|
end
|
6
8
|
|
7
9
|
desc 'Drops all dynamo tables and re-runs migrations'
|
8
|
-
task :
|
10
|
+
task reset: :environment do
|
9
11
|
Rake::Task['dynamo:drop_all'].invoke
|
10
12
|
Rake::Task['dynamo:migrate'].invoke
|
11
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamo-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Davis McClellan
|
@@ -14,138 +14,138 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date:
|
17
|
+
date: 2021-01-26 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
|
-
name:
|
20
|
+
name: activemodel
|
21
21
|
requirement: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- - "
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '5.2'
|
26
|
+
- - "<"
|
24
27
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
28
|
+
version: '6.2'
|
26
29
|
type: :runtime
|
27
30
|
prerelease: false
|
28
31
|
version_requirements: !ruby/object:Gem::Requirement
|
29
32
|
requirements:
|
30
|
-
- - "
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '5.2'
|
36
|
+
- - "<"
|
31
37
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
38
|
+
version: '6.2'
|
33
39
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
40
|
+
name: aws-record
|
35
41
|
requirement: !ruby/object:Gem::Requirement
|
36
42
|
requirements:
|
37
|
-
- - "
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '4.2'
|
40
|
-
- - "<"
|
43
|
+
- - "~>"
|
41
44
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
45
|
+
version: '2.0'
|
43
46
|
type: :runtime
|
44
47
|
prerelease: false
|
45
48
|
version_requirements: !ruby/object:Gem::Requirement
|
46
49
|
requirements:
|
47
|
-
- - "
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '4.2'
|
50
|
-
- - "<"
|
50
|
+
- - "~>"
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: '
|
52
|
+
version: '2.0'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: railties
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '5.2'
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
62
|
+
version: '6.2'
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
69
|
+
version: '5.2'
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: '
|
72
|
+
version: '6.2'
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: activesupport
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
76
76
|
requirements:
|
77
77
|
- - ">="
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: '
|
79
|
+
version: '5.2'
|
80
80
|
- - "<"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '6.2'
|
83
83
|
type: :development
|
84
84
|
prerelease: false
|
85
85
|
version_requirements: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '5.2'
|
90
90
|
- - "<"
|
91
91
|
- !ruby/object:Gem::Version
|
92
|
-
version: '
|
92
|
+
version: '6.2'
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: bundler
|
95
95
|
requirement: !ruby/object:Gem::Requirement
|
96
96
|
requirements:
|
97
97
|
- - "~>"
|
98
98
|
- !ruby/object:Gem::Version
|
99
|
-
version: '
|
99
|
+
version: '2.2'
|
100
100
|
type: :development
|
101
101
|
prerelease: false
|
102
102
|
version_requirements: !ruby/object:Gem::Requirement
|
103
103
|
requirements:
|
104
104
|
- - "~>"
|
105
105
|
- !ruby/object:Gem::Version
|
106
|
-
version: '
|
106
|
+
version: '2.2'
|
107
107
|
- !ruby/object:Gem::Dependency
|
108
108
|
name: byebug
|
109
109
|
requirement: !ruby/object:Gem::Requirement
|
110
110
|
requirements:
|
111
111
|
- - "~>"
|
112
112
|
- !ruby/object:Gem::Version
|
113
|
-
version: '
|
113
|
+
version: '11.0'
|
114
114
|
type: :development
|
115
115
|
prerelease: false
|
116
116
|
version_requirements: !ruby/object:Gem::Requirement
|
117
117
|
requirements:
|
118
118
|
- - "~>"
|
119
119
|
- !ruby/object:Gem::Version
|
120
|
-
version: '
|
120
|
+
version: '11.0'
|
121
121
|
- !ruby/object:Gem::Dependency
|
122
122
|
name: combustion
|
123
123
|
requirement: !ruby/object:Gem::Requirement
|
124
124
|
requirements:
|
125
125
|
- - "~>"
|
126
126
|
- !ruby/object:Gem::Version
|
127
|
-
version:
|
127
|
+
version: '1.3'
|
128
128
|
type: :development
|
129
129
|
prerelease: false
|
130
130
|
version_requirements: !ruby/object:Gem::Requirement
|
131
131
|
requirements:
|
132
132
|
- - "~>"
|
133
133
|
- !ruby/object:Gem::Version
|
134
|
-
version:
|
134
|
+
version: '1.3'
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: rake
|
137
137
|
requirement: !ruby/object:Gem::Requirement
|
138
138
|
requirements:
|
139
139
|
- - "~>"
|
140
140
|
- !ruby/object:Gem::Version
|
141
|
-
version: '
|
141
|
+
version: '13.0'
|
142
142
|
type: :development
|
143
143
|
prerelease: false
|
144
144
|
version_requirements: !ruby/object:Gem::Requirement
|
145
145
|
requirements:
|
146
146
|
- - "~>"
|
147
147
|
- !ruby/object:Gem::Version
|
148
|
-
version: '
|
148
|
+
version: '13.0'
|
149
149
|
- !ruby/object:Gem::Dependency
|
150
150
|
name: rspec
|
151
151
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,42 +166,56 @@ dependencies:
|
|
166
166
|
requirements:
|
167
167
|
- - "~>"
|
168
168
|
- !ruby/object:Gem::Version
|
169
|
-
version:
|
169
|
+
version: 1.8.1
|
170
170
|
type: :development
|
171
171
|
prerelease: false
|
172
172
|
version_requirements: !ruby/object:Gem::Requirement
|
173
173
|
requirements:
|
174
174
|
- - "~>"
|
175
175
|
- !ruby/object:Gem::Version
|
176
|
-
version:
|
176
|
+
version: 1.8.1
|
177
|
+
- !ruby/object:Gem::Dependency
|
178
|
+
name: rubocop-rails
|
179
|
+
requirement: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - "~>"
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: 2.9.1
|
184
|
+
type: :development
|
185
|
+
prerelease: false
|
186
|
+
version_requirements: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - "~>"
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: 2.9.1
|
177
191
|
- !ruby/object:Gem::Dependency
|
178
192
|
name: simplecov
|
179
193
|
requirement: !ruby/object:Gem::Requirement
|
180
194
|
requirements:
|
181
195
|
- - "~>"
|
182
196
|
- !ruby/object:Gem::Version
|
183
|
-
version: '0.
|
197
|
+
version: '0.16'
|
184
198
|
type: :development
|
185
199
|
prerelease: false
|
186
200
|
version_requirements: !ruby/object:Gem::Requirement
|
187
201
|
requirements:
|
188
202
|
- - "~>"
|
189
203
|
- !ruby/object:Gem::Version
|
190
|
-
version: '0.
|
204
|
+
version: '0.16'
|
191
205
|
- !ruby/object:Gem::Dependency
|
192
206
|
name: webmock
|
193
207
|
requirement: !ruby/object:Gem::Requirement
|
194
208
|
requirements:
|
195
209
|
- - "~>"
|
196
210
|
- !ruby/object:Gem::Version
|
197
|
-
version: '
|
211
|
+
version: '3.3'
|
198
212
|
type: :development
|
199
213
|
prerelease: false
|
200
214
|
version_requirements: !ruby/object:Gem::Requirement
|
201
215
|
requirements:
|
202
216
|
- - "~>"
|
203
217
|
- !ruby/object:Gem::Version
|
204
|
-
version: '
|
218
|
+
version: '3.3'
|
205
219
|
- !ruby/object:Gem::Dependency
|
206
220
|
name: wwtd
|
207
221
|
requirement: !ruby/object:Gem::Requirement
|
@@ -236,13 +250,14 @@ files:
|
|
236
250
|
- ".travis.yml"
|
237
251
|
- Dockerfile
|
238
252
|
- Gemfile
|
253
|
+
- Jenkinsfile
|
239
254
|
- LICENSE.txt
|
240
255
|
- README.md
|
241
|
-
- build.sh
|
242
256
|
- docker-compose.override.example.yml
|
243
257
|
- docker-compose.yml
|
244
258
|
- dynamo-record.gemspec
|
245
259
|
- lib/dynamo/record.rb
|
260
|
+
- lib/dynamo/record/batch_get.rb
|
246
261
|
- lib/dynamo/record/batch_request.rb
|
247
262
|
- lib/dynamo/record/batch_write.rb
|
248
263
|
- lib/dynamo/record/marshalers.rb
|
@@ -270,17 +285,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
270
285
|
requirements:
|
271
286
|
- - ">="
|
272
287
|
- !ruby/object:Gem::Version
|
273
|
-
version: '
|
288
|
+
version: '2.6'
|
274
289
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
275
290
|
requirements:
|
276
291
|
- - ">="
|
277
292
|
- !ruby/object:Gem::Version
|
278
293
|
version: '0'
|
279
294
|
requirements: []
|
280
|
-
|
281
|
-
rubygems_version: 2.6.13
|
295
|
+
rubygems_version: 3.0.3
|
282
296
|
signing_key:
|
283
297
|
specification_version: 4
|
284
298
|
summary: Extensions to Aws::Record for working with DynamoDB.
|
285
299
|
test_files: []
|
286
|
-
has_rdoc:
|
data/build.sh
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/bin/bash -ex
|
2
|
-
|
3
|
-
COMPOSE_FILE="docker-compose.yml"
|
4
|
-
|
5
|
-
function cleanup() {
|
6
|
-
exit_code=$?
|
7
|
-
set +e
|
8
|
-
docker cp coverage:/app/coverage .
|
9
|
-
docker-compose kill
|
10
|
-
docker-compose rm -f
|
11
|
-
exit $exit_code
|
12
|
-
}
|
13
|
-
trap cleanup INT TERM EXIT
|
14
|
-
|
15
|
-
docker-compose pull dynamo
|
16
|
-
docker-compose up -d dynamo
|
17
|
-
docker-compose build --pull app
|
18
|
-
docker-compose run --rm app /bin/bash -l -c \
|
19
|
-
"rvm-exec 2.4 bundle exec rubocop --fail-level autocorrect"
|
20
|
-
docker-compose run --name coverage app $@
|