dynamo-record 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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