aws-sdk-rails 3.6.1 → 3.13.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 +4 -4
- data/VERSION +1 -1
- data/bin/aws_sqs_active_job +1 -0
- data/lib/action_dispatch/session/dynamodb_store.rb +9 -3
- data/lib/active_job/queue_adapters/amazon_sqs_adapter/params.rb +78 -0
- data/lib/active_job/queue_adapters/amazon_sqs_adapter.rb +33 -37
- data/lib/active_job/queue_adapters/amazon_sqs_async_adapter.rb +12 -11
- data/lib/aws/rails/middleware/ebs_sqs_active_job_middleware.rb +31 -5
- data/lib/aws/rails/notifications.rb +1 -4
- data/lib/aws/rails/railtie.rb +9 -4
- data/lib/aws/rails/{mailer.rb → ses_mailer.rb} +12 -10
- data/lib/aws/rails/sesv2_mailer.rb +60 -0
- data/lib/aws/rails/sqs_active_job/configuration.rb +61 -24
- data/lib/aws/rails/sqs_active_job/deduplication.rb +21 -0
- data/lib/aws/rails/sqs_active_job/executor.rb +47 -28
- data/lib/aws/rails/sqs_active_job/job_runner.rb +5 -1
- data/lib/aws/rails/sqs_active_job/lambda_handler.rb +3 -6
- data/lib/aws/rails/sqs_active_job/poller.rb +56 -32
- data/lib/aws-sdk-rails.rb +4 -1
- data/lib/generators/aws_record/base.rb +164 -168
- data/lib/generators/aws_record/generated_attribute.rb +50 -41
- data/lib/generators/aws_record/model/model_generator.rb +8 -4
- data/lib/generators/aws_record/secondary_index.rb +31 -25
- data/lib/generators/dynamo_db/session_store_migration/session_store_migration_generator.rb +3 -1
- data/lib/tasks/aws_record/migrate.rake +2 -0
- data/lib/tasks/dynamo_db/session_store.rake +2 -0
- metadata +52 -18
- /data/lib/generators/aws_record/model/templates/{model.rb → model.erb} +0 -0
- /data/lib/generators/aws_record/model/templates/{table_config.rb → table_config.erb} +0 -0
- /data/lib/generators/dynamo_db/session_store_migration/templates/{session_store_migration.rb → session_store_migration.erb} +0 -0
@@ -1,217 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails/generators'
|
2
4
|
require_relative 'generated_attribute'
|
3
5
|
require_relative 'secondary_index'
|
4
6
|
|
5
7
|
module AwsRecord
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
8
|
+
module Generators
|
9
|
+
class Base < Rails::Generators::NamedBase
|
10
|
+
argument :attributes, type: :array, default: [], banner: 'field[:type][:opts]...',
|
11
|
+
desc: 'Describes the fields in the model'
|
12
|
+
check_class_collision
|
13
|
+
|
14
|
+
class_option :disable_mutation_tracking, type: :boolean, desc: 'Disables dirty tracking'
|
15
|
+
class_option :timestamps, type: :boolean, desc: 'Adds created, updated timestamps to the model'
|
16
|
+
class_option :table_config, type: :hash, default: {}, banner: 'primary:R-W [SecondaryIndex1:R-W]...',
|
17
|
+
desc: 'Declares the r/w units for the model as well as any secondary indexes', required: true
|
18
|
+
class_option :gsi, type: :array, default: [],
|
19
|
+
banner: 'name:hkey{field_name}[,rkey{field_name},proj_type{ALL|KEYS_ONLY|INCLUDE}]...', desc: 'Allows for the declaration of secondary indexes'
|
20
|
+
class_option :table_name, type: :string, banner: 'model_table_name'
|
21
|
+
class_option :password_digest, type: :boolean, desc: 'Whether to add a password_digest field to the model'
|
22
|
+
|
23
|
+
class_option :required, type: :string, banner: 'field1...',
|
24
|
+
desc: 'A list of attributes that are required for an instance of the model'
|
25
|
+
class_option :length_validations, type: :hash, default: {}, banner: 'field1:MIN-MAX...',
|
26
|
+
desc: 'Validations on the length of attributes in a model'
|
27
|
+
|
28
|
+
attr_accessor :primary_read_units, :primary_write_units, :gsi_rw_units, :gsis, :required_attrs,
|
29
|
+
:length_validations
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def initialize(args, *options)
|
34
|
+
options[0] << '--skip-table-config' if options[1][:behavior] == :revoke
|
35
|
+
@parse_errors = []
|
36
|
+
|
37
|
+
super
|
38
|
+
ensure_unique_fields
|
39
|
+
ensure_hkey
|
40
|
+
parse_gsis!
|
41
|
+
parse_table_config!
|
42
|
+
parse_validations!
|
43
|
+
|
44
|
+
return if @parse_errors.empty?
|
45
|
+
|
46
|
+
warn 'The following errors were encountered while trying to parse the given attributes'
|
47
|
+
$stderr.puts
|
48
|
+
warn @parse_errors
|
49
|
+
$stderr.puts
|
50
|
+
|
51
|
+
abort('Please fix the errors before proceeding.')
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_attributes!
|
55
|
+
self.attributes = (attributes || []).map do |attr|
|
56
|
+
GeneratedAttribute.parse(attr)
|
57
|
+
rescue ArgumentError => e
|
58
|
+
@parse_errors << e
|
59
|
+
next
|
44
60
|
end
|
61
|
+
self.attributes = attributes.compact
|
45
62
|
|
46
|
-
|
63
|
+
if options['password_digest']
|
64
|
+
attributes << GeneratedAttribute.new('password_digest', :string_attr, digest: true)
|
65
|
+
end
|
47
66
|
|
48
|
-
|
49
|
-
begin
|
50
|
-
GeneratedAttribute.parse(attr)
|
51
|
-
rescue ArgumentError => e
|
52
|
-
@parse_errors << e
|
53
|
-
next
|
54
|
-
end
|
55
|
-
end
|
56
|
-
self.attributes = self.attributes.compact
|
67
|
+
return unless options['timestamps']
|
57
68
|
|
58
|
-
|
59
|
-
|
60
|
-
|
69
|
+
attributes << GeneratedAttribute.parse('created:datetime:default_value{Time.now}')
|
70
|
+
attributes << GeneratedAttribute.parse('updated:datetime:default_value{Time.now}')
|
71
|
+
end
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
73
|
+
def ensure_unique_fields
|
74
|
+
used_names = Set.new
|
75
|
+
duplicate_fields = []
|
67
76
|
|
68
|
-
|
69
|
-
|
70
|
-
|
77
|
+
attributes.each do |attr|
|
78
|
+
duplicate_fields << [:attribute, attr.name] if used_names.include? attr.name
|
79
|
+
used_names.add attr.name
|
71
80
|
|
72
|
-
|
81
|
+
next unless attr.options.key? :database_attribute_name
|
73
82
|
|
74
|
-
|
75
|
-
duplicate_fields << [:attribute, attr.name]
|
76
|
-
end
|
77
|
-
used_names.add attr.name
|
83
|
+
raw_db_attr_name = attr.options[:database_attribute_name].delete('"') # db attribute names are wrapped with " to make template generation easier
|
78
84
|
|
79
|
-
|
80
|
-
raw_db_attr_name = attr.options[:database_attribute_name].delete('"') # db attribute names are wrapped with " to make template generation easier
|
85
|
+
duplicate_fields << [:database_attribute_name, raw_db_attr_name] if used_names.include? raw_db_attr_name
|
81
86
|
|
82
|
-
|
83
|
-
|
84
|
-
end
|
87
|
+
used_names.add raw_db_attr_name
|
88
|
+
end
|
85
89
|
|
86
|
-
|
87
|
-
end
|
88
|
-
end
|
90
|
+
return if duplicate_fields.empty?
|
89
91
|
|
90
|
-
|
91
|
-
|
92
|
-
@parse_errors << ArgumentError.new("Found duplicated field name: #{invalid_attr[1]}, in attribute#{invalid_attr[0]}")
|
93
|
-
end
|
94
|
-
end
|
92
|
+
duplicate_fields.each do |invalid_attr|
|
93
|
+
@parse_errors << ArgumentError.new("Found duplicated field name: #{invalid_attr[1]}, in attribute#{invalid_attr[0]}")
|
95
94
|
end
|
95
|
+
end
|
96
96
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
self.attributes.each do |attr|
|
103
|
-
if attr.options.key? :hash_key
|
104
|
-
if hkey_member
|
105
|
-
@parse_errors << ArgumentError.new("Redefinition of hash_key attr: #{attr.name}, original declaration of hash_key on: #{hkey_member.name}")
|
106
|
-
next
|
107
|
-
end
|
108
|
-
|
109
|
-
hkey_member = attr
|
110
|
-
elsif attr.options.key? :range_key
|
111
|
-
if rkey_member
|
112
|
-
@parse_errors << ArgumentError.new("Redefinition of range_key attr: #{attr.name}, original declaration of range_key on: #{hkey_member.name}")
|
113
|
-
next
|
114
|
-
end
|
115
|
-
|
116
|
-
rkey_member = attr
|
117
|
-
end
|
97
|
+
def ensure_hkey
|
98
|
+
uuid_member = nil
|
99
|
+
hkey_member = nil
|
100
|
+
rkey_member = nil
|
118
101
|
|
119
|
-
|
120
|
-
|
102
|
+
attributes.each do |attr|
|
103
|
+
if attr.options.key? :hash_key
|
104
|
+
if hkey_member
|
105
|
+
@parse_errors << ArgumentError.new("Redefinition of hash_key attr: #{attr.name}, original declaration of hash_key on: #{hkey_member.name}")
|
106
|
+
next
|
121
107
|
end
|
122
|
-
end
|
123
108
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
109
|
+
hkey_member = attr
|
110
|
+
elsif attr.options.key? :range_key
|
111
|
+
if rkey_member
|
112
|
+
@parse_errors << ArgumentError.new("Redefinition of range_key attr: #{attr.name}, original declaration of range_key on: #{hkey_member.name}")
|
113
|
+
next
|
129
114
|
end
|
115
|
+
|
116
|
+
rkey_member = attr
|
130
117
|
end
|
131
|
-
end
|
132
118
|
|
133
|
-
|
134
|
-
options['disable_mutation_tracking']
|
119
|
+
uuid_member = attr if attr.name.include? 'uuid'
|
135
120
|
end
|
136
121
|
|
137
|
-
|
138
|
-
|
122
|
+
return if hkey_member
|
123
|
+
|
124
|
+
if uuid_member
|
125
|
+
uuid_member.options[:hash_key] = true
|
126
|
+
else
|
127
|
+
attributes.unshift GeneratedAttribute.parse('uuid:hkey')
|
139
128
|
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def mutation_tracking_disabled?
|
132
|
+
options['disable_mutation_tracking']
|
133
|
+
end
|
140
134
|
|
141
|
-
|
142
|
-
|
135
|
+
def has_validations?
|
136
|
+
!@required_attrs.empty? || !@length_validations.empty?
|
137
|
+
end
|
143
138
|
|
144
|
-
|
139
|
+
def parse_table_config!
|
140
|
+
return unless options['table_config']
|
145
141
|
|
146
|
-
|
147
|
-
[idx.name, parse_rw_units(idx.name)]
|
148
|
-
}.to_h
|
142
|
+
@primary_read_units, @primary_write_units = parse_rw_units('primary')
|
149
143
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
else
|
154
|
-
gsi = @gsis.select { |idx| idx.name == config}
|
144
|
+
@gsi_rw_units = @gsis.map do |idx|
|
145
|
+
[idx.name, parse_rw_units(idx.name)]
|
146
|
+
end.to_h
|
155
147
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
148
|
+
options['table_config'].each_key do |config|
|
149
|
+
next if config == 'primary'
|
150
|
+
|
151
|
+
gsi = @gsis.select { |idx| idx.name == config }
|
152
|
+
|
153
|
+
@parse_errors << ArgumentError.new("Could not find a gsi declaration for #{config}") if gsi.empty?
|
161
154
|
end
|
155
|
+
end
|
162
156
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
157
|
+
def parse_rw_units(name)
|
158
|
+
if options['table_config'].key? name
|
159
|
+
rw_units = options['table_config'][name]
|
160
|
+
rw_units.gsub(/[,.-]/, ':').split(':').reject(&:empty?)
|
161
|
+
else
|
162
|
+
@parse_errors << ArgumentError.new("Please provide a table_config definition for #{name}")
|
170
163
|
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def parse_gsis!
|
167
|
+
@gsis = (options['gsi'] || []).map do |raw_idx|
|
168
|
+
idx = SecondaryIndex.parse(raw_idx)
|
169
|
+
|
170
|
+
attributes = self.attributes.select { |attr| attr.name == idx.hash_key }
|
171
|
+
if attributes.empty?
|
172
|
+
@parse_errors << ArgumentError.new("Could not find attribute #{idx.hash_key} for gsi #{idx.name} hkey")
|
173
|
+
next
|
174
|
+
end
|
171
175
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
idx
|
176
|
-
|
177
|
-
attributes = self.attributes.select { |attr| attr.name == idx.hash_key}
|
178
|
-
if attributes.empty?
|
179
|
-
@parse_errors << ArgumentError.new("Could not find attribute #{idx.hash_key} for gsi #{idx.name} hkey")
|
180
|
-
next
|
181
|
-
end
|
182
|
-
|
183
|
-
if idx.range_key
|
184
|
-
attributes = self.attributes.select { |attr| attr.name == idx.range_key}
|
185
|
-
if attributes.empty?
|
186
|
-
@parse_errors << ArgumentError.new("Could not find attribute #{idx.range_key} for gsi #{idx.name} rkey")
|
187
|
-
next
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
idx
|
192
|
-
rescue ArgumentError => e
|
193
|
-
@parse_errors << e
|
176
|
+
if idx.range_key
|
177
|
+
attributes = self.attributes.select { |attr| attr.name == idx.range_key }
|
178
|
+
if attributes.empty?
|
179
|
+
@parse_errors << ArgumentError.new("Could not find attribute #{idx.range_key} for gsi #{idx.name} rkey")
|
194
180
|
next
|
195
181
|
end
|
196
182
|
end
|
197
183
|
|
198
|
-
|
184
|
+
idx
|
185
|
+
rescue ArgumentError => e
|
186
|
+
@parse_errors << e
|
187
|
+
next
|
199
188
|
end
|
200
189
|
|
201
|
-
|
202
|
-
|
203
|
-
@required_attrs.each do |val_attr|
|
204
|
-
@parse_errors << ArgumentError.new("No such field #{val_attr} in required validations") if !self.attributes.any? { |attr| attr.name == val_attr }
|
205
|
-
end
|
190
|
+
@gsis = @gsis.compact
|
191
|
+
end
|
206
192
|
|
207
|
-
|
208
|
-
|
193
|
+
def parse_validations!
|
194
|
+
@required_attrs = options['required'] ? options['required'].split(',') : []
|
195
|
+
@required_attrs.each do |val_attr|
|
196
|
+
@parse_errors << ArgumentError.new("No such field #{val_attr} in required validations") if attributes.none? do |attr|
|
197
|
+
attr.name == val_attr
|
198
|
+
end
|
199
|
+
end
|
209
200
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
201
|
+
@length_validations = options['length_validations'].map do |val_attr, bounds|
|
202
|
+
@parse_errors << ArgumentError.new("No such field #{val_attr} in required validations") if attributes.none? do |attr|
|
203
|
+
attr.name == val_attr
|
204
|
+
end
|
205
|
+
|
206
|
+
bounds = bounds.gsub(/[,.-]/, ':').split(':').reject(&:empty?)
|
207
|
+
[val_attr, "#{bounds[0]}..#{bounds[1]}"]
|
214
208
|
end
|
209
|
+
@length_validations = @length_validations.to_h
|
215
210
|
end
|
216
211
|
end
|
217
212
|
end
|
213
|
+
end
|
@@ -1,28 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AwsRecord
|
2
4
|
module Generators
|
3
5
|
class GeneratedAttribute
|
4
|
-
|
5
|
-
|
6
|
-
INVALID_HKEY_TYPES = %i(map_attr list_attr numeric_set_attr string_set_attr)
|
6
|
+
OPTS = %w[hkey rkey persist_nil db_attr_name ddb_type default_value].freeze
|
7
|
+
INVALID_HKEY_TYPES = %i[map_attr list_attr numeric_set_attr string_set_attr].freeze
|
7
8
|
attr_reader :name, :type
|
8
9
|
attr_accessor :options
|
9
10
|
|
10
11
|
def field_type
|
11
12
|
case @type
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
when :integer_attr then :number_field
|
14
|
+
when :date_attr then :date_select
|
15
|
+
when :datetime_attr then :datetime_select
|
16
|
+
when :boolean_attr then :check_box
|
17
|
+
else :text_field
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
class << self
|
21
|
-
|
22
22
|
def parse(field_definition)
|
23
23
|
name, type, opts = field_definition.split(':')
|
24
|
-
type
|
25
|
-
|
24
|
+
type ||= 'string'
|
25
|
+
if OPTS.any? { |opt| type.include? opt }
|
26
|
+
opts = type
|
27
|
+
type = 'string'
|
28
|
+
end
|
26
29
|
|
27
30
|
opts = opts.split(',') if opts
|
28
31
|
type, opts = parse_type_and_options(name, type, opts)
|
@@ -34,65 +37,71 @@ module AwsRecord
|
|
34
37
|
private
|
35
38
|
|
36
39
|
def validate_opt_combs(name, type, opts)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
+
return unless opts
|
41
|
+
|
42
|
+
is_hkey = opts.key?(:hash_key)
|
43
|
+
is_rkey = opts.key?(:range_key)
|
40
44
|
|
41
|
-
|
42
|
-
|
45
|
+
if is_hkey && is_rkey
|
46
|
+
raise ArgumentError,
|
47
|
+
"Field #{name} cannot be a range key and hash key simultaneously"
|
43
48
|
end
|
49
|
+
return unless is_hkey && INVALID_HKEY_TYPES.include?(type)
|
50
|
+
|
51
|
+
raise ArgumentError,
|
52
|
+
"Field #{name} cannot be a hash key and be of type #{type}"
|
44
53
|
end
|
45
54
|
|
46
55
|
def parse_type_and_options(name, type, opts)
|
47
|
-
opts
|
48
|
-
|
56
|
+
opts ||= []
|
57
|
+
[parse_type(name, type), opts.map { |opt| parse_option(name, opt) }.to_h]
|
49
58
|
end
|
50
59
|
|
51
60
|
def parse_option(name, opt)
|
52
61
|
case opt
|
53
62
|
|
54
|
-
when
|
55
|
-
|
56
|
-
when
|
57
|
-
|
58
|
-
when
|
59
|
-
|
63
|
+
when 'hkey'
|
64
|
+
[:hash_key, true]
|
65
|
+
when 'rkey'
|
66
|
+
[:range_key, true]
|
67
|
+
when 'persist_nil'
|
68
|
+
[:persist_nil, true]
|
60
69
|
when /db_attr_name\{(\w+)\}/
|
61
|
-
|
70
|
+
[:database_attribute_name, "\"#{::Regexp.last_match(1)}\""]
|
62
71
|
when /ddb_type\{(S|N|B|BOOL|SS|NS|BS|M|L)\}/i
|
63
|
-
|
72
|
+
[:dynamodb_type, "\"#{::Regexp.last_match(1).upcase}\""]
|
64
73
|
when /default_value\{(.+)\}/
|
65
|
-
|
74
|
+
[:default_value, ::Regexp.last_match(1)]
|
66
75
|
else
|
67
|
-
raise ArgumentError
|
76
|
+
raise ArgumentError, "You provided an invalid option for #{name}: #{opt}"
|
68
77
|
end
|
69
78
|
end
|
70
79
|
|
71
80
|
def parse_type(name, type)
|
72
81
|
case type.downcase
|
73
82
|
|
74
|
-
when
|
83
|
+
when 'bool', 'boolean'
|
75
84
|
:boolean_attr
|
76
|
-
when
|
85
|
+
when 'date'
|
77
86
|
:date_attr
|
78
|
-
when
|
87
|
+
when 'datetime'
|
79
88
|
:datetime_attr
|
80
|
-
when
|
89
|
+
when 'float'
|
81
90
|
:float_attr
|
82
|
-
when
|
91
|
+
when 'int', 'integer'
|
83
92
|
:integer_attr
|
84
|
-
when
|
93
|
+
when 'list'
|
85
94
|
:list_attr
|
86
|
-
when
|
95
|
+
when 'map'
|
87
96
|
:map_attr
|
88
|
-
when
|
97
|
+
when 'num_set', 'numeric_set', 'nset'
|
89
98
|
:numeric_set_attr
|
90
|
-
when
|
99
|
+
when 'string_set', 's_set', 'sset'
|
91
100
|
:string_set_attr
|
92
|
-
when
|
101
|
+
when 'string'
|
93
102
|
:string_attr
|
94
103
|
else
|
95
|
-
raise ArgumentError
|
104
|
+
raise ArgumentError, "Invalid type for #{name}: #{type}"
|
96
105
|
end
|
97
106
|
end
|
98
107
|
end
|
@@ -114,8 +123,8 @@ module AwsRecord
|
|
114
123
|
end
|
115
124
|
|
116
125
|
def column_name
|
117
|
-
if @name ==
|
118
|
-
|
126
|
+
if @name == 'password_digest'
|
127
|
+
'password'
|
119
128
|
else
|
120
129
|
@name
|
121
130
|
end
|
@@ -1,21 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../base'
|
2
4
|
|
3
5
|
module AwsRecord
|
4
6
|
module Generators
|
5
7
|
class ModelGenerator < Base
|
6
8
|
def initialize(args, *options)
|
7
|
-
self.class.source_root File.expand_path('
|
9
|
+
self.class.source_root File.expand_path('templates', __dir__)
|
8
10
|
super
|
9
11
|
end
|
10
12
|
|
11
13
|
def create_model
|
12
|
-
template
|
14
|
+
template 'model.erb', File.join('app/models', class_path, "#{file_name}.rb")
|
13
15
|
end
|
14
16
|
|
15
17
|
def create_table_config
|
16
|
-
|
17
|
-
end
|
18
|
+
return unless options['table_config']
|
18
19
|
|
20
|
+
template 'table_config.erb',
|
21
|
+
File.join('db/table_config', class_path, "#{file_name}_config.rb")
|
22
|
+
end
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AwsRecord
|
2
4
|
module Generators
|
3
5
|
class SecondaryIndex
|
4
|
-
|
5
|
-
PROJ_TYPES = %w(ALL KEYS_ONLY INCLUDE)
|
6
|
+
PROJ_TYPES = %w[ALL KEYS_ONLY INCLUDE].freeze
|
6
7
|
attr_reader :name, :hash_key, :range_key, :projection_type
|
7
8
|
|
8
9
|
class << self
|
@@ -15,45 +16,50 @@ module AwsRecord
|
|
15
16
|
end
|
16
17
|
|
17
18
|
private
|
18
|
-
def parse_raw_options(raw_opts)
|
19
|
-
raw_opts = [] if not raw_opts
|
20
|
-
raw_opts.map { |opt| get_option_value(opt) }.to_h
|
21
|
-
end
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
20
|
+
def parse_raw_options(raw_opts)
|
21
|
+
raw_opts ||= []
|
22
|
+
raw_opts.map { |opt| get_option_value(opt) }.to_h
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_option_value(raw_option)
|
26
|
+
case raw_option
|
27
|
+
|
28
|
+
when /hkey\{(\w+)\}/
|
29
|
+
[:hash_key, ::Regexp.last_match(1)]
|
30
|
+
when /rkey\{(\w+)\}/
|
31
|
+
[:range_key, ::Regexp.last_match(1)]
|
32
|
+
when /proj_type\{(\w+)\}/
|
33
|
+
[:projection_type, ::Regexp.last_match(1)]
|
34
|
+
else
|
35
|
+
raise ArgumentError, "Invalid option for secondary index #{raw_option}"
|
35
36
|
end
|
37
|
+
end
|
36
38
|
end
|
37
39
|
|
38
40
|
def initialize(name, opts)
|
39
|
-
raise ArgumentError
|
40
|
-
raise ArgumentError
|
41
|
+
raise ArgumentError, 'You must provide a name' unless name
|
42
|
+
raise ArgumentError, 'You must provide a hash key' unless opts[:hash_key]
|
41
43
|
|
42
44
|
if opts.key? :projection_type
|
43
|
-
|
44
|
-
|
45
|
+
unless PROJ_TYPES.include? opts[:projection_type]
|
46
|
+
raise ArgumentError, "Invalid projection type #{opts[:projection_type]}"
|
47
|
+
end
|
48
|
+
if opts[:projection_type] != 'ALL'
|
49
|
+
raise NotImplementedError, 'ALL is the only projection type currently supported'
|
50
|
+
end
|
45
51
|
else
|
46
|
-
opts[:projection_type] =
|
52
|
+
opts[:projection_type] = 'ALL'
|
47
53
|
end
|
48
54
|
|
49
55
|
if opts[:hash_key] == opts[:range_key]
|
50
|
-
raise ArgumentError
|
56
|
+
raise ArgumentError, "#{opts[:hash_key]} cannot be both the rkey and hkey for gsi #{name}"
|
51
57
|
end
|
52
58
|
|
53
59
|
@name = name
|
54
60
|
@hash_key = opts[:hash_key]
|
55
61
|
@range_key = opts[:range_key]
|
56
|
-
@projection_type =
|
62
|
+
@projection_type = "\"#{opts[:projection_type]}\""
|
57
63
|
end
|
58
64
|
end
|
59
65
|
end
|