dynamo-record 0.1.1 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d18ec7a057cdce73f1f6e3f6aafa7b0175580be9
4
- data.tar.gz: b12e97171dc6df596a9c787750c463741295c626
3
+ metadata.gz: 7f61aad11c87426b8d0330bf728967ebfe7e0811
4
+ data.tar.gz: 04d634aaa02c527fd052585b6b90cfde1c461127
5
5
  SHA512:
6
- metadata.gz: 509bdcf316a75601110e40ffaf80238d152c077d648f7433f4fef6566f458c9baad55e3cb66f75246166d54f2c9ab6e6f08b31128f92c0be6dac20dce47ff3ec
7
- data.tar.gz: baf21793319609920a4526a027ebf2446a2af125e014717cf0493bcaa88ee97a0aad0af687fb1e4d5f892a0c342ed67afed9486006d0bab77c5bde3934010362
6
+ metadata.gz: bdd7d9f796e1b86a259cc4f59def7e6e022866889ccfd238150684c9dee04f4cac2b9d3f90b6f2c76fe889971e00cb66c031897880cb5eab416050d6b833cf20
7
+ data.tar.gz: '03964856097c204b9fd484e4ef20a8d86eecdf800374a5a424122544fbb679da079f01213902d4739a8107469d174c0dce93858f05e0408ab67def2e7c7011e5'
data/.gitignore CHANGED
@@ -2,7 +2,6 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /coverage/
5
- /doc/
6
5
  /pkg/
7
6
  /spec/reports/
8
7
  /tmp/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamo-record (0.1.0)
4
+ dynamo-record (0.1.3)
5
5
  aws-record (~> 1.1)
6
6
  rails (~> 4.2)
7
7
 
@@ -45,13 +45,14 @@ GEM
45
45
  addressable (2.5.1)
46
46
  public_suffix (~> 2.0, >= 2.0.2)
47
47
  arel (6.0.4)
48
+ ast (2.3.0)
48
49
  aws-record (1.1.0)
49
50
  aws-sdk-resources (~> 2.0)
50
- aws-sdk-core (2.9.11)
51
+ aws-sdk-core (2.9.14)
51
52
  aws-sigv4 (~> 1.0)
52
53
  jmespath (~> 1.0)
53
- aws-sdk-resources (2.9.11)
54
- aws-sdk-core (= 2.9.11)
54
+ aws-sdk-resources (2.9.14)
55
+ aws-sdk-core (= 2.9.14)
55
56
  aws-sigv4 (1.0.0)
56
57
  builder (3.2.3)
57
58
  byebug (9.0.6)
@@ -73,7 +74,7 @@ GEM
73
74
  json (2.1.0)
74
75
  loofah (2.0.3)
75
76
  nokogiri (>= 1.5.9)
76
- mail (2.6.4)
77
+ mail (2.6.5)
77
78
  mime-types (>= 1.16, < 4)
78
79
  mime-types (3.1)
79
80
  mime-types-data (~> 3.2015)
@@ -82,6 +83,9 @@ GEM
82
83
  minitest (5.10.1)
83
84
  nokogiri (1.7.1)
84
85
  mini_portile2 (~> 2.1.0)
86
+ parser (2.4.0.0)
87
+ ast (~> 2.2)
88
+ powerpack (0.1.1)
85
89
  public_suffix (2.0.5)
86
90
  rack (1.6.5)
87
91
  rack-test (0.6.3)
@@ -110,6 +114,8 @@ GEM
110
114
  activesupport (= 4.2.8)
111
115
  rake (>= 0.8.7)
112
116
  thor (>= 0.18.1, < 2.0)
117
+ rainbow (2.2.2)
118
+ rake
113
119
  rake (10.5.0)
114
120
  rspec (3.5.0)
115
121
  rspec-core (~> 3.5.0)
@@ -124,6 +130,13 @@ GEM
124
130
  diff-lcs (>= 1.2.0, < 2.0)
125
131
  rspec-support (~> 3.5.0)
126
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)
127
140
  safe_yaml (1.0.4)
128
141
  simplecov (0.14.1)
129
142
  docile (~> 1.1.0)
@@ -141,6 +154,7 @@ GEM
141
154
  thread_safe (0.3.6)
142
155
  tzinfo (1.2.3)
143
156
  thread_safe (~> 0.1)
157
+ unicode-display_width (1.2.1)
144
158
  webmock (2.3.2)
145
159
  addressable (>= 2.3.6)
146
160
  crack (>= 0.3.2)
@@ -156,6 +170,7 @@ DEPENDENCIES
156
170
  dynamo-record!
157
171
  rake (~> 10.0)
158
172
  rspec (~> 3.0)
173
+ rubocop (~> 0.44.1)
159
174
  simplecov (~> 0.12)
160
175
  webmock (~> 2.1)
161
176
 
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # DynamoRecord
2
2
 
3
- Provides helpful rake tasks and model extensions on top of [aws-record](https://github.com/aws/aws-sdk-ruby-record).
3
+ Provides helpful rake tasks and model extensions on top of
4
+ [aws-record](https://github.com/aws/aws-sdk-ruby-record).
4
5
 
5
6
  ## Installation
6
7
 
@@ -22,18 +23,19 @@ Or install it yourself as:
22
23
 
23
24
  ### Models
24
25
 
25
- In `app/models`, create a class that includes `DynamoRecord::Model` and contains one or more of the following attribute declarations:
26
+ In `app/models`, create a class that includes `DynamoRecord::Model` and contains
27
+ one or more of the
28
+ [standard set](http://docs.aws.amazon.com/awssdkrubyrecord/api/Aws/Record.html)
29
+ of attribute declarations, along with the following new composite attribute
30
+ types:
26
31
 
27
- * `integer_attr`
28
- * `float_attr`
29
- * `map_attr`
30
32
  * `composite_string_attr`
31
33
  * `composite_integer_attr`
32
34
 
33
35
  An example file:
34
36
 
35
37
  ```
36
- class Model1
38
+ class MyModel
37
39
  include DynamoRecord::Model
38
40
  composite_string_attr(
39
41
  :model_key,
@@ -44,17 +46,20 @@ class Model1
44
46
  string_attr :user_id
45
47
  float_attr :score
46
48
  map_attr :map_value
47
- composite_string_attr :quiz_key, parts: [:quiz_id]
48
49
  end
49
50
  ```
50
51
 
51
- The partition key is labeled with `hash_key: true`. The sort key is labeled with `range_key: true`.
52
+ The partition key is labeled with `hash_key: true`. The sort key is labeled with
53
+ `range_key: true`.
52
54
 
53
- A secondary index can be defined as follows:
55
+ Declaring secondary indexes are included in `aws-record` and are defined
56
+ [here](http://docs.aws.amazon.com/awssdkrubyrecord/api/Aws/Record/SecondaryIndexes/SecondaryIndexesClassMethods.html).
57
+
58
+ As an example, a global secondary index can be defined as follows:
54
59
 
55
60
  ```
56
61
  global_secondary_index(
57
- :secondary_idx,
62
+ :user_idx,
58
63
  hash_key: :user_id,
59
64
  range_key: :score,
60
65
  projection: {
@@ -66,24 +71,110 @@ A secondary index can be defined as follows:
66
71
  )
67
72
  ```
68
73
 
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).
74
+ As an example, a local secondary index can be defined as follows:
75
+
76
+ ```
77
+ local_secondary_index(
78
+ :score_idx,
79
+ range_key: :score,
80
+ projection: {
81
+ projection_type: 'INCLUDE',
82
+ non_key_attributes: [
83
+ :map_value
84
+ ]
85
+ }
86
+ )
87
+ ```
88
+
89
+ The full documentation for `projection_type` and `non_key_attributes` can be
90
+ found [here](http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Types/Projection.html).
91
+
92
+ ### Query Helpers
93
+
94
+ Query helpers are included as class methods.
95
+
96
+ * `composite_key(*args)`
97
+
98
+ For models that have composite attributes, create a composite key value from the
99
+ individual attribute parts:
100
+
101
+ ```
102
+ hash_key = MyModel.composite_key('model_1', 'account_1')
103
+ ```
104
+
105
+ * `split_composite(string)`
106
+
107
+ Split a composite attribute value into its individual attribute parts:
108
+
109
+ ```
110
+ model_id, acccount_id = MyModel.split_composite(hash_key)
111
+ ```
112
+
113
+ * `find_all_by_hash_key(hash_key_value, opts = {})`
114
+
115
+ Find all item instances that match a given hash key value. `opts` are options
116
+ passed to the underlying query.
117
+
118
+ ```
119
+ MyModel.find_all_by_hash_key(hash_key)
120
+ ```
121
+
122
+ * `find_all_by_gsi_hash_key(gsi_name, hash_key_value, opts = {})`
123
+
124
+ Find all item instances using a global secondary instance using a hash key
125
+ value. `opts` are options passed to the underlying query.
126
+
127
+ ```
128
+ MyModel.find_all_by_gsi_hash_key('user_idx', 'user_1')
129
+ ```
130
+
131
+ * `find_all_by_gsi_hash_and_range_keys(gsi_name, hash_key_value, range_key_value)`
132
+
133
+ Find all item instances using a global secondary instance using a hash key value
134
+ and range key value.
135
+
136
+ ```
137
+ MyModel.find_all_by_gsi_hash_key('user_idx', 'user_1', 0.0)
138
+ ```
139
+
140
+ * `find_all_by_lsi_hash_key(lsi_name, hash_key_value, opts = {})`
141
+
142
+ Find all item instances using a local secondary instance using a hash key value.
143
+ `opts` are options passed to the underlying query.
144
+
145
+ ```
146
+ MyModel.find_all_by_lsi_hash_key('score_idx', 'user_1')
147
+ ```
148
+
149
+ * `find_all_by_lsi_hash_and_range_keys(lsi_name, hash_key_value, range_key_value)`
150
+
151
+ Find all item instances using a local secondary instance using a hash key value
152
+ and range key value.
153
+
154
+ ```
155
+ MyModel.find_all_by_lsi_hash_key('score_idx', 'user_1', 0.0)
156
+ ```
157
+
70
158
 
71
159
  ### Migrations
72
160
 
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:
161
+ Dynamo migration files are stored in `db/dynamo_migrate`. The name of the file
162
+ follows the style of standard Rails migration files like
163
+ `YYYYMMDDHHMMSS_create_model_1.rb`. A migration file that creates a Dynamo table
164
+ looks like:
74
165
 
75
166
  ```
76
167
  module DynamoMigrate
77
- class CreateModel1 < DynamoRecord::TableMigration
168
+ class CreateMyModel < DynamoRecord::TableMigration
78
169
  def self.up
79
- migrate(Model1) do |migration|
170
+ migrate(MyModel) do |migration|
80
171
  migration.create!(
81
172
  provisioned_throughput: {
82
173
  read_capacity_units: 1,
83
174
  write_capacity_units: 1
84
175
  },
85
176
  global_secondary_index_throughput: {
86
- secondary_idx: {
177
+ user_idx: {
87
178
  read_capacity_units: 1,
88
179
  write_capacity_units: 1
89
180
  }
@@ -95,15 +186,16 @@ module DynamoMigrate
95
186
  end
96
187
  ```
97
188
 
98
- Note that the throughput of the table can be configured separately from the throughput of the secondary index.
189
+ Note that the throughput of the table can be configured separately from the
190
+ throughput of the secondary index.
99
191
 
100
192
  A migration file that creates a Dynamo stream looks like:
101
193
 
102
194
  ```
103
195
  module DynamoMigrate
104
- class AddModel1Stream < DynamoRecord::TableMigration
196
+ class AddMyModelStream < DynamoRecord::TableMigration
105
197
  def self.update
106
- add_stream(Model1)
198
+ add_stream(MyModel)
107
199
  end
108
200
  end
109
201
  end
@@ -0,0 +1,11 @@
1
+ # Testing
2
+
3
+ Run rspec in the `test` container:
4
+
5
+ `docker-compose run --rm test bundle exec rspec`
6
+
7
+ ## Static Code Analysis
8
+
9
+ Run RuboCop static code analysis in the `test` container:
10
+
11
+ `docker-compose run --rm test bundle exec rubocop`
@@ -8,9 +8,11 @@ Gem::Specification.new do |spec|
8
8
  spec.version = DynamoRecord::Record::VERSION
9
9
  spec.license = 'MIT'
10
10
  spec.homepage = 'https://instructure.com'
11
- spec.authors = ['Davis McClellan', 'Ryan Taylor', 'Bryan Petty', 'Michael Brewer-Davis', 'Marc Phillips', 'Augusto Callejas']
11
+ spec.authors = ['Davis McClellan', 'Ryan Taylor', 'Bryan Petty', 'Michael Brewer-Davis', 'Marc Phillips',
12
+ 'Augusto Callejas', 'Frank Murphy']
12
13
  spec.email = ['dmcclellan@instructure.com', 'rtaylor@instructure.com', 'bpetty@instructure.com',
13
- 'mbd@instructure.com', 'mphillips@instructure.com', 'acallejas@instructure.com']
14
+ 'mbd@instructure.com', 'mphillips@instructure.com', 'acallejas@instructure.com',
15
+ 'fmurphy@instructure.com']
14
16
 
15
17
  spec.summary = 'Extensions for working with dynamo via aws-record'
16
18
  spec.description = 'A set of extensions simplifying database operations in aws-record'
@@ -30,6 +32,7 @@ Gem::Specification.new do |spec|
30
32
  spec.add_development_dependency 'combustion', '~> 0.6.0'
31
33
  spec.add_development_dependency 'rake', '~> 10.0'
32
34
  spec.add_development_dependency 'rspec', '~> 3.0'
35
+ spec.add_development_dependency 'rubocop', '~> 0.44.1'
33
36
  spec.add_development_dependency 'simplecov', '~> 0.12'
34
37
  spec.add_development_dependency 'webmock', '~> 2.1'
35
38
  end
@@ -1,4 +1,4 @@
1
1
  Gem.find_files("#{File.dirname(__FILE__)}/dynamo-record/**/*.rb").each do |path|
2
2
  require path.gsub(/\.rb$/, '')
3
3
  end
4
- require 'model_existence_validator'
4
+ require 'model_existence_validator'
@@ -29,7 +29,7 @@ module DynamoRecord
29
29
  # rubocop:enable Style/Attr
30
30
  end
31
31
 
32
- def define_readers(name, parts, cast_function, allowed_repeats = [:account_uuid])
32
+ def define_readers(name, parts, cast_function)
33
33
  parts.each_with_index do |part, i|
34
34
  raise "#{part} already defined" unless parts.find_index(part) == i
35
35
  next if method_defined?(part)
@@ -51,6 +51,20 @@ module DynamoRecord
51
51
  )
52
52
  end
53
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
+
54
68
  def find_all_by_index_and_hash_key(hash_key_name, hash_key_value, opts = {}, index_name = nil)
55
69
  query_options = {
56
70
  select: 'ALL_ATTRIBUTES',
@@ -67,9 +81,10 @@ module DynamoRecord
67
81
 
68
82
  def find_all_by_index_hash_and_range_keys(hash_config:, range_config:, index_name: nil,
69
83
  scan_index_forward: true, limit: nil)
84
+ range_expression = range_config[:expression] || "#{range_config[:name]} = :rkv"
70
85
  query_options = {
71
86
  select: 'ALL_ATTRIBUTES',
72
- key_condition_expression: "#{hash_config[:name]} = :hkv AND #{range_config[:name]} = :rkv",
87
+ key_condition_expression: "#{hash_config[:name]} = :hkv AND #{range_expression}",
73
88
  expression_attribute_values: {
74
89
  ':hkv': hash_config[:value],
75
90
  ':rkv': range_config[:value]
@@ -89,18 +104,6 @@ module DynamoRecord
89
104
  string.split(COMPOSITE_DELIMITER)
90
105
  end
91
106
 
92
- def find_all_by_model(model, opts = {})
93
- find_all_by_hash_key(model.dynamo_key, opts)
94
- end
95
-
96
- def find_all_by_model_paginated(model, opts = {})
97
- find_all_by_model(model, pagination_opts(opts))
98
- end
99
-
100
- def pagination_opts(opts = {})
101
- { limit: opts[:limit], exclusive_start_key: opts[:exclusive_start_key] }
102
- end
103
-
104
107
  # TODO: Create a batch save method.
105
108
  end
106
109
 
@@ -6,7 +6,7 @@ module DynamoRecord
6
6
  railtie_name :dynamo_record
7
7
 
8
8
  rake_tasks do
9
- load "tasks/dynamo.rake"
9
+ load 'tasks/dynamo.rake'
10
10
  end
11
11
  end
12
12
  end
@@ -1,5 +1,5 @@
1
1
  module DynamoRecord
2
2
  module Record
3
- VERSION = '0.1.1'.freeze
3
+ VERSION = '0.1.3'.freeze
4
4
  end
5
5
  end
@@ -4,13 +4,16 @@ module DynamoRecord
4
4
  def self.run
5
5
  raise 'Task not available on production' if Rails.env.production?
6
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
7
+ delete_by_class(filename)
12
8
  end
13
9
  end
10
+
11
+ def self.delete_by_class(filename)
12
+ klass = File.basename(filename, '.rb').camelize.constantize
13
+ return unless klass.included_modules.include? DynamoRecord::Model
14
+ Rails.logger.info "Deleting all items in table: #{klass}"
15
+ klass.scan.each(&:delete!)
16
+ end
14
17
  end
15
18
  end
16
19
  end
@@ -1,19 +1,13 @@
1
1
  module DynamoRecord
2
2
  module TaskHelpers
3
3
  class MigrationRunner
4
- def self.run(path='db/dynamo_migrate')
4
+ def self.run(path = 'db/dynamo_migrate')
5
5
  constants = []
6
6
  filename_regexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/
7
7
 
8
8
  # Sorts the files located in `db/dynamo_migrate` to ensure order is preserved
9
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
10
+ migration = migration(f, filename_regexp, constants)
17
11
 
18
12
  # starts the migration
19
13
  yield "Migrating: #{migration}"
@@ -30,6 +24,17 @@ module DynamoRecord
30
24
  end
31
25
  end
32
26
 
27
+ def self.migration(f, filename_regexp, constants)
28
+ raise "Non-numeric prefix: #{f}" if File.basename(f).scan(filename_regexp).first.nil?
29
+ require f
30
+
31
+ # finds the constant that was added on the require statement above
32
+ migration_sym = (DynamoMigrate.constants - constants).first
33
+ migration = DynamoMigrate.const_get(migration_sym)
34
+ constants.push migration_sym
35
+ migration
36
+ end
37
+
33
38
  def self.up(migration)
34
39
  return unless migration.respond_to? :up
35
40
  case migration.up
@@ -63,7 +63,7 @@ module DynamoRecord
63
63
  end
64
64
 
65
65
  def raise_attribute_error
66
- raise ArgumentError, 'You didn\'t provide an appropriate attribute selection. We accept [:both, :read, :write]'
66
+ raise ArgumentError, 'You didn\'t provide an appropriate attribute selection. We accept [:both, :read, :write]'
67
67
  end
68
68
 
69
69
  def description
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: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davis McClellan
@@ -10,10 +10,11 @@ authors:
10
10
  - Michael Brewer-Davis
11
11
  - Marc Phillips
12
12
  - Augusto Callejas
13
+ - Frank Murphy
13
14
  autorequire:
14
15
  bindir: exe
15
16
  cert_chain: []
16
- date: 2017-05-04 00:00:00.000000000 Z
17
+ date: 2017-05-08 00:00:00.000000000 Z
17
18
  dependencies:
18
19
  - !ruby/object:Gem::Dependency
19
20
  name: aws-record
@@ -113,6 +114,20 @@ dependencies:
113
114
  - - "~>"
114
115
  - !ruby/object:Gem::Version
115
116
  version: '3.0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubocop
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.44.1
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.44.1
116
131
  - !ruby/object:Gem::Dependency
117
132
  name: simplecov
118
133
  requirement: !ruby/object:Gem::Requirement
@@ -149,6 +164,7 @@ email:
149
164
  - mbd@instructure.com
150
165
  - mphillips@instructure.com
151
166
  - acallejas@instructure.com
167
+ - fmurphy@instructure.com
152
168
  executables: []
153
169
  extensions: []
154
170
  extra_rdoc_files: []
@@ -162,6 +178,7 @@ files:
162
178
  - Gemfile.lock
163
179
  - README.md
164
180
  - build.sh
181
+ - doc/testing.md
165
182
  - docker-compose.override.yml
166
183
  - docker-compose.yml
167
184
  - dynamo-record.gemspec
@@ -200,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
217
  version: '0'
201
218
  requirements: []
202
219
  rubyforge_project:
203
- rubygems_version: 2.5.1
220
+ rubygems_version: 2.6.8
204
221
  signing_key:
205
222
  specification_version: 4
206
223
  summary: Extensions for working with dynamo via aws-record