aws-record-generator 1.0.0.pre.1
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 +7 -0
- data/lib/aws-record-generator.rb +28 -0
- data/lib/generators/aws_record/model/USAGE +21 -0
- data/lib/generators/aws_record/model/model_generator.rb +230 -0
- data/lib/generators/aws_record/model/templates/model.rb.tt +33 -0
- data/lib/generators/aws_record/model/templates/table_config.rb.tt +18 -0
- data/lib/generators/generated_attribute.rb +110 -0
- data/lib/generators/secondary_index.rb +73 -0
- data/lib/generators/test_helper.rb +79 -0
- data/lib/tasks/table_config_migrate_task.rake +26 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 88db52d888ba8ab309db4f229686bc1cb036397d
|
4
|
+
data.tar.gz: 7ee3a11e989280135d8854d3eadf6acf04a7ac7d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 47607097dc142faee512968f5845a2ef511c53fa11ece5f352804faac451f4895f2d1e54f168b0b538b5b1416cf9dbbd925f58d82563c308b98a56707e6580c7
|
7
|
+
data.tar.gz: cb09166ed00e62c040732380b38ffde75a37065cb8755c2589f473ec12092d6b38513612c67c16681f4d73dd31f60c0638fcf3bf0632326c267b3531865dd2ca
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
require_relative 'generators/generated_attribute'
|
15
|
+
require_relative 'generators/secondary_index'
|
16
|
+
require_relative 'generators/test_helper'
|
17
|
+
require_relative 'generators/aws_record/model/model_generator'
|
18
|
+
|
19
|
+
require 'aws-record'
|
20
|
+
require 'rails/railtie'
|
21
|
+
|
22
|
+
module AwsRecord
|
23
|
+
class Railtie < Rails::Railtie
|
24
|
+
rake_tasks do
|
25
|
+
load 'tasks/table_config_migrate_task.rake'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Description:
|
2
|
+
rails generator for aws-record models
|
3
|
+
|
4
|
+
Pass the name of the model (preferably in singular form), and an optional list of attributes
|
5
|
+
|
6
|
+
Attributes are declarations of the fields that you wish to store within a model. You can pass
|
7
|
+
a type and list of options for each attribtue in the form: `name:type:options` if you do not provide
|
8
|
+
a type, it is assumed that the attribute is of type `string_attr`
|
9
|
+
|
10
|
+
Each model should have an hkey, if one is not present a `uuid:hkey` will be created for you.
|
11
|
+
|
12
|
+
Timestamps are not added by default but you can add them using the `--timestamps` flag
|
13
|
+
More information can be found at: https://github.com/awslabs/aws-record-generator/blob/master/README.md
|
14
|
+
|
15
|
+
Example:
|
16
|
+
rails generate aws_record:model Forum forum_uuid:hkey post_id:rkey post_title post_body tags:sset:default_value{Set.new} created_at:datetime:d_attr_name{PostCreatedAtTime} moderation:boolean:default_value{false}
|
17
|
+
|
18
|
+
This will create:
|
19
|
+
app/models/forum.rb
|
20
|
+
db/table_config/forum_config.rb
|
21
|
+
lib/tasks/table_config_migrate_task.rake # This is created once the first time the generator is run
|
@@ -0,0 +1,230 @@
|
|
1
|
+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
require 'rails/generators'
|
15
|
+
require 'aws-record-generator'
|
16
|
+
|
17
|
+
module AwsRecord
|
18
|
+
module Generators
|
19
|
+
class ModelGenerator < Rails::Generators::NamedBase
|
20
|
+
|
21
|
+
source_root File.expand_path('../templates', __FILE__)
|
22
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:opts]...", desc: "Describes the fields in the model"
|
23
|
+
check_class_collision
|
24
|
+
|
25
|
+
class_option :disable_mutation_tracking, type: :boolean, desc: "Disables dirty tracking"
|
26
|
+
class_option :timestamps, type: :boolean, desc: "Adds created, updated timestamps to the model"
|
27
|
+
class_option :table_config, type: :hash, default: {}, banner: "primary:R-W [SecondaryIndex1:R-W]...", desc: "Declares the r/w units for the model as well as any secondary indexes", :required => true
|
28
|
+
class_option :gsi, type: :array, default: [], banner: "name:hkey{field_name}[,rkey{field_name},proj_type{ALL|KEYS_ONLY|INCLUDE}]...", desc: "Allows for the declaration of secondary indexes"
|
29
|
+
|
30
|
+
class_option :required, type: :array, default: [], banner: "field1...", desc: "A list of attributes that are required for an instance of the model"
|
31
|
+
class_option :length_validations, type: :hash, default: {}, banner: "field1:MIN-MAX...", desc: "Validations on the length of attributes in a model"
|
32
|
+
|
33
|
+
attr_accessor :primary_read_units, :primary_write_units, :gsi_rw_units, :gsis, :required_attrs, :length_validations
|
34
|
+
|
35
|
+
def create_model
|
36
|
+
template "model.rb", File.join("app/models", class_path, "#{file_name}.rb")
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_table_config
|
40
|
+
template "table_config.rb", File.join("db/table_config", class_path, "#{file_name}_config.rb")
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def initialize(args, *options)
|
46
|
+
@parse_errors = []
|
47
|
+
|
48
|
+
super
|
49
|
+
ensure_unique_fields
|
50
|
+
ensure_hkey
|
51
|
+
parse_gsis!
|
52
|
+
parse_table_config!
|
53
|
+
parse_validations!
|
54
|
+
|
55
|
+
if !@parse_errors.empty?
|
56
|
+
STDERR.puts "The following errors were encountered while trying to parse the given attributes"
|
57
|
+
STDERR.puts
|
58
|
+
STDERR.puts @parse_errors
|
59
|
+
STDERR.puts
|
60
|
+
|
61
|
+
abort("Please fix the errors before proceeding.")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_attributes!
|
66
|
+
|
67
|
+
self.attributes = (attributes || []).map do |attr|
|
68
|
+
begin
|
69
|
+
GeneratedAttribute.parse(attr)
|
70
|
+
rescue ArgumentError => e
|
71
|
+
@parse_errors << e
|
72
|
+
next
|
73
|
+
end
|
74
|
+
end
|
75
|
+
self.attributes = self.attributes.compact
|
76
|
+
|
77
|
+
if options['timestamps']
|
78
|
+
self.attributes << GeneratedAttribute.parse("created:datetime:default_value{Time.now}")
|
79
|
+
self.attributes << GeneratedAttribute.parse("updated:datetime:default_value{Time.now}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def ensure_unique_fields
|
84
|
+
used_names = Set.new
|
85
|
+
duplicate_fields = []
|
86
|
+
|
87
|
+
self.attributes.each do |attr|
|
88
|
+
|
89
|
+
if used_names.include? attr.name
|
90
|
+
duplicate_fields << [:attribute, attr.name]
|
91
|
+
end
|
92
|
+
used_names.add attr.name
|
93
|
+
|
94
|
+
if attr.options.key? :database_attribute_name
|
95
|
+
raw_db_attr_name = attr.options[:database_attribute_name].delete('"') # db attribute names are wrapped with " to make template generation easier
|
96
|
+
|
97
|
+
if used_names.include? raw_db_attr_name
|
98
|
+
duplicate_fields << [:database_attribute_name, raw_db_attr_name]
|
99
|
+
end
|
100
|
+
|
101
|
+
used_names.add raw_db_attr_name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if !duplicate_fields.empty?
|
106
|
+
duplicate_fields.each do |invalid_attr|
|
107
|
+
@parse_errors << ArgumentError.new("Found duplicated field name: #{invalid_attr[1]}, in attribute#{invalid_attr[0]}")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def ensure_hkey
|
113
|
+
uuid_member = nil
|
114
|
+
hkey_member = nil
|
115
|
+
rkey_member = nil
|
116
|
+
|
117
|
+
self.attributes.each do |attr|
|
118
|
+
if attr.options.key? :hash_key
|
119
|
+
if hkey_member
|
120
|
+
@parse_errors << ArgumentError.new("Redefinition of hash_key attr: #{attr.name}, original declaration of hash_key on: #{hkey_member.name}")
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
hkey_member = attr
|
125
|
+
elsif attr.options.key? :range_key
|
126
|
+
if rkey_member
|
127
|
+
@parse_errors << ArgumentError.new("Redefinition of range_key attr: #{attr.name}, original declaration of range_key on: #{hkey_member.name}")
|
128
|
+
next
|
129
|
+
end
|
130
|
+
|
131
|
+
rkey_member = attr
|
132
|
+
end
|
133
|
+
|
134
|
+
if attr.name.include? "uuid"
|
135
|
+
uuid_member = attr
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
if !hkey_member
|
140
|
+
if uuid_member
|
141
|
+
uuid_member.options[:hash_key] = true
|
142
|
+
else
|
143
|
+
self.attributes.unshift GeneratedAttribute.parse("uuid:hkey")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def mutation_tracking_disabled?
|
149
|
+
options['disable_mutation_tracking']
|
150
|
+
end
|
151
|
+
|
152
|
+
def has_validations?
|
153
|
+
!@required_attrs.empty? || !@length_validations.empty?
|
154
|
+
end
|
155
|
+
|
156
|
+
def parse_table_config!
|
157
|
+
@primary_read_units, @primary_write_units = parse_rw_units("primary")
|
158
|
+
|
159
|
+
@gsi_rw_units = @gsis.map { |idx|
|
160
|
+
[idx.name, parse_rw_units(idx.name)]
|
161
|
+
}.to_h
|
162
|
+
|
163
|
+
options['table_config'].each do |config, rw_units|
|
164
|
+
if config == "primary"
|
165
|
+
next
|
166
|
+
else
|
167
|
+
gsi = @gsis.select { |idx| idx.name == config}
|
168
|
+
|
169
|
+
if gsi.empty?
|
170
|
+
@parse_errors << ArgumentError.new("Could not find a gsi declaration for #{config}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def parse_rw_units(name)
|
177
|
+
if !options['table_config'].key? name
|
178
|
+
@parse_errors << ArgumentError.new("Please provide a table_config definition for #{name}")
|
179
|
+
else
|
180
|
+
rw_units = options['table_config'][name]
|
181
|
+
return rw_units.gsub(/[,.-]/, ':').split(':').reject { |s| s.empty? }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def parse_gsis!
|
186
|
+
@gsis = (options['gsi'] || []).map do |raw_idx|
|
187
|
+
begin
|
188
|
+
idx = SecondaryIndex.parse(raw_idx)
|
189
|
+
|
190
|
+
attributes = self.attributes.select { |attr| attr.name == idx.hash_key}
|
191
|
+
if attributes.empty?
|
192
|
+
@parse_errors << ArgumentError.new("Could not find attribute #{idx.hash_key} for gsi #{idx.name} hkey")
|
193
|
+
next
|
194
|
+
end
|
195
|
+
|
196
|
+
if idx.range_key
|
197
|
+
attributes = self.attributes.select { |attr| attr.name == idx.range_key}
|
198
|
+
if attributes.empty?
|
199
|
+
@parse_errors << ArgumentError.new("Could not find attribute #{idx.range_key} for gsi #{idx.name} rkey")
|
200
|
+
next
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
idx
|
205
|
+
rescue ArgumentError => e
|
206
|
+
@parse_errors << e
|
207
|
+
next
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
@gsis = @gsis.compact
|
212
|
+
end
|
213
|
+
|
214
|
+
def parse_validations!
|
215
|
+
@required_attrs = options['required']
|
216
|
+
@required_attrs.each do |val_attr|
|
217
|
+
@parse_errors << ArgumentError.new("No such field #{val_attr} in required validations") if !self.attributes.any? { |attr| attr.name == val_attr }
|
218
|
+
end
|
219
|
+
|
220
|
+
@length_validations = options['length_validations'].map do |val_attr, bounds|
|
221
|
+
@parse_errors << ArgumentError.new("No such field #{val_attr} in required validations") if !self.attributes.any? { |attr| attr.name == val_attr }
|
222
|
+
|
223
|
+
bounds = bounds.gsub(/[,.-]/, ':').split(':').reject { |s| s.empty? }
|
224
|
+
[val_attr, "#{bounds[0]}..#{bounds[1]}"]
|
225
|
+
end
|
226
|
+
@length_validations = @length_validations.to_h
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'aws-record'
|
2
|
+
<%= "require 'active_model'
|
3
|
+
" if has_validations? -%>
|
4
|
+
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= class_name %>
|
7
|
+
include Aws::Record
|
8
|
+
<%= ' include ActiveModel::Validations
|
9
|
+
' if has_validations? -%>
|
10
|
+
<%= ' disable_mutation_tracking
|
11
|
+
' if mutation_tracking_disabled? -%>
|
12
|
+
|
13
|
+
<% attributes.each do |attribute| -%>
|
14
|
+
<%= attribute.type %> :<%= attribute.name %><% *opts, last_opt = attribute.options.to_a %><%= ', ' if last_opt %><% opts.each do |opt| %><%= opt[0] %>: <%= opt[1] %>, <% end %><% if last_opt %><%= last_opt[0] %>: <%= last_opt[1] %><% end %>
|
15
|
+
<% end -%>
|
16
|
+
<% gsis.each do |index| %>
|
17
|
+
global_secondary_index(
|
18
|
+
:<%= index.name %>,
|
19
|
+
hash_key: :<%= index.hash_key -%>,<%- if index.range_key %>
|
20
|
+
range_key: :<%= index.range_key -%>,<%- end %>
|
21
|
+
projection: {
|
22
|
+
projection_type: <%= index.projection_type %>
|
23
|
+
}
|
24
|
+
)
|
25
|
+
<% end -%>
|
26
|
+
<% if !required_attrs.empty? -%>
|
27
|
+
validates_presence_of <% *req, last_req = required_attrs -%><% req.each do |required_validation|-%>:<%= required_validation %>, <% end -%>:<%= last_req %>
|
28
|
+
<% end -%>
|
29
|
+
<% length_validations.each do |attribute, validation| -%>
|
30
|
+
validates_length_of :<%= attribute %>, within: <%= validation %>
|
31
|
+
<% end -%>
|
32
|
+
end
|
33
|
+
<% end -%>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'aws-record'
|
2
|
+
|
3
|
+
module ModelTableConfig
|
4
|
+
def self.config
|
5
|
+
Aws::Record::TableConfig.define do |t|
|
6
|
+
t.model_class <%= class_name %>
|
7
|
+
|
8
|
+
t.read_capacity_units <%= primary_read_units %>
|
9
|
+
t.write_capacity_units <%= primary_write_units %>
|
10
|
+
<%- gsis.each do |index| %>
|
11
|
+
t.global_secondary_index(:<%= index.name %>) do |i|
|
12
|
+
i.read_capacity_units <%= gsi_rw_units[index.name][0] %>
|
13
|
+
i.write_capacity_units <%= gsi_rw_units[index.name][1] %>
|
14
|
+
end
|
15
|
+
<%- end -%>
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
module AwsRecord
|
15
|
+
module Generators
|
16
|
+
class GeneratedAttribute
|
17
|
+
|
18
|
+
OPTS = %w(hkey rkey persist_nil db_attr_name ddb_type default_value)
|
19
|
+
INVALID_HKEY_TYPES = %i(map_attr list_attr numeric_set_attr string_set_attr)
|
20
|
+
attr_reader :name, :type
|
21
|
+
attr_accessor :options
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
def parse(field_definition)
|
26
|
+
name, type, opts = field_definition.split(':')
|
27
|
+
type = "string" if not type
|
28
|
+
type, opts = "string", type if OPTS.any? { |opt| type.include? opt }
|
29
|
+
|
30
|
+
opts = opts.split(',') if opts
|
31
|
+
type, opts = parse_type_and_options(name, type, opts)
|
32
|
+
validate_opt_combs(name, type, opts)
|
33
|
+
|
34
|
+
new(name, type, opts)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def validate_opt_combs(name, type, opts)
|
40
|
+
if opts
|
41
|
+
is_hkey = opts.key?(:hash_key)
|
42
|
+
is_rkey = opts.key?(:range_key)
|
43
|
+
|
44
|
+
raise ArgumentError.new("Field #{name} cannot be a range key and hash key simultaneously") if is_hkey && is_rkey
|
45
|
+
raise ArgumentError.new("Field #{name} cannot be a hash key and be of type #{type}") if is_hkey and INVALID_HKEY_TYPES.include? type
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_type_and_options(name, type, opts)
|
50
|
+
opts = [] if not opts
|
51
|
+
return parse_type(name, type), opts.map { |opt| parse_option(name, opt) }.to_h
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_option(name, opt)
|
55
|
+
case opt
|
56
|
+
|
57
|
+
when "hkey"
|
58
|
+
return :hash_key, true
|
59
|
+
when "rkey"
|
60
|
+
return :range_key, true
|
61
|
+
when "persist_nil"
|
62
|
+
return :persist_nil, true
|
63
|
+
when /db_attr_name\{(\w+)\}/
|
64
|
+
return :database_attribute_name, '"' + $1 + '"'
|
65
|
+
when /ddb_type\{(S|N|B|BOOL|SS|NS|BS|M|L)\}/i
|
66
|
+
return :dynamodb_type, '"' + $1.upcase + '"'
|
67
|
+
when /default_value\{(.+)\}/
|
68
|
+
return :default_value, $1
|
69
|
+
else
|
70
|
+
raise ArgumentError.new("You provided an invalid option for #{name}: #{opt}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_type(name, type)
|
75
|
+
case type.downcase
|
76
|
+
|
77
|
+
when "bool", "boolean"
|
78
|
+
:boolean_attr
|
79
|
+
when "date"
|
80
|
+
:date_attr
|
81
|
+
when "datetime"
|
82
|
+
:datetime_attr
|
83
|
+
when "float"
|
84
|
+
:float_attr
|
85
|
+
when "int", "integer"
|
86
|
+
:integer_attr
|
87
|
+
when "list"
|
88
|
+
:list_attr
|
89
|
+
when "map"
|
90
|
+
:map_attr
|
91
|
+
when "num_set", "numeric_set", "nset"
|
92
|
+
:numeric_set_attr
|
93
|
+
when "string_set", "s_set", "sset"
|
94
|
+
:string_set_attr
|
95
|
+
when "string"
|
96
|
+
:string_attr
|
97
|
+
else
|
98
|
+
raise ArgumentError.new("Invalid type for #{name}: #{type}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize(name, type = :string_attr, options = {})
|
104
|
+
@name = name
|
105
|
+
@type = type
|
106
|
+
@options = options
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
module AwsRecord
|
15
|
+
module Generators
|
16
|
+
class SecondaryIndex
|
17
|
+
|
18
|
+
PROJ_TYPES = %w(ALL KEYS_ONLY INCLUDE)
|
19
|
+
attr_reader :name, :hash_key, :range_key, :projection_type
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def parse(key_definition)
|
23
|
+
name, index_options = key_definition.split(':')
|
24
|
+
index_options = index_options.split(',') if index_options
|
25
|
+
opts = parse_raw_options(index_options)
|
26
|
+
|
27
|
+
new(name, opts)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def parse_raw_options(raw_opts)
|
32
|
+
raw_opts = [] if not raw_opts
|
33
|
+
raw_opts.map { |opt| get_option_value(opt) }.to_h
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_option_value(raw_option)
|
37
|
+
case raw_option
|
38
|
+
|
39
|
+
when /hkey\{(\w+)\}/
|
40
|
+
return :hash_key, $1
|
41
|
+
when /rkey\{(\w+)\}/
|
42
|
+
return :range_key, $1
|
43
|
+
when /proj_type\{(\w+)\}/
|
44
|
+
return :projection_type, $1
|
45
|
+
else
|
46
|
+
raise ArgumentError.new("Invalid option for secondary index #{raw_option}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(name, opts)
|
52
|
+
raise ArgumentError.new("You must provide a name") if not name
|
53
|
+
raise ArgumentError.new("You must provide a hash key") if not opts[:hash_key]
|
54
|
+
|
55
|
+
if opts.key? :projection_type
|
56
|
+
raise ArgumentError.new("Invalid projection type #{opts[:projection_type]}") if not PROJ_TYPES.include? opts[:projection_type]
|
57
|
+
raise NotImplementedError.new("ALL is the only projection type currently supported") if opts[:projection_type] != "ALL"
|
58
|
+
else
|
59
|
+
opts[:projection_type] = "ALL"
|
60
|
+
end
|
61
|
+
|
62
|
+
if opts[:hash_key] == opts[:range_key]
|
63
|
+
raise ArgumentError.new("#{opts[:hash_key]} cannot be both the rkey and hkey for gsi #{name}")
|
64
|
+
end
|
65
|
+
|
66
|
+
@name = name
|
67
|
+
@hash_key = opts[:hash_key]
|
68
|
+
@range_key = opts[:range_key]
|
69
|
+
@projection_type = '"' + "#{opts[:projection_type]}" + '"'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
require 'rails/generators/rails/app/app_generator'
|
15
|
+
require "rails/generators/testing/behaviour"
|
16
|
+
require "fileutils"
|
17
|
+
require "minitest/spec"
|
18
|
+
|
19
|
+
module AwsRecord
|
20
|
+
module Generators
|
21
|
+
class GeneratorTestHelper
|
22
|
+
include Minitest::Assertions
|
23
|
+
attr_accessor :assertions
|
24
|
+
|
25
|
+
include Rails::Generators::Testing::Behaviour
|
26
|
+
include FileUtils
|
27
|
+
|
28
|
+
def initialize(klass, dest)
|
29
|
+
@temp_root = File.expand_path(dest)
|
30
|
+
|
31
|
+
GeneratorTestHelper.tests klass
|
32
|
+
temp_app_dest = File.join(File.expand_path(@temp_root, __dir__), "test_app")
|
33
|
+
GeneratorTestHelper.destination temp_app_dest
|
34
|
+
|
35
|
+
destination_root_is_set?
|
36
|
+
prepare_destination
|
37
|
+
setup_test_app
|
38
|
+
ensure_current_path
|
39
|
+
|
40
|
+
self.assertions = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_in_test_app(cmd)
|
44
|
+
cd destination_root
|
45
|
+
a = `rails #{cmd}`
|
46
|
+
|
47
|
+
ensure_current_path
|
48
|
+
end
|
49
|
+
|
50
|
+
def assert_file(generated_file, actual_file)
|
51
|
+
assert File.exist?(generated_file), "Expected file #{generated_file.inspect} to exist, but does not"
|
52
|
+
assert File.exist?(actual_file), "Expected file #{actual_file.inspect} to exist, but does not"
|
53
|
+
assert identical? generated_file, actual_file
|
54
|
+
end
|
55
|
+
|
56
|
+
def assert_not_file(file_path)
|
57
|
+
assert !File.exist?(file_path), "Expected file #{file_path.inspect} to not exist, but does"
|
58
|
+
end
|
59
|
+
|
60
|
+
def cleanup
|
61
|
+
rm_rf @temp_root
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_generator(args = default_arguments, config = {})
|
65
|
+
capture(:stderr) do
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def setup_test_app
|
73
|
+
Rails::Generators::AppGenerator.start [destination_root, '--skip-bundle', '--skip-git', '--skip-spring', '--skip-test', '-d' , '--skip-javascript', '--force', '--quiet']
|
74
|
+
`echo 'gem "aws-record-generator", :path => "../../"' >> "#{destination_root}/Gemfile"`
|
75
|
+
`bundle install --gemfile "#{destination_root}/Gemfile"`
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
desc "Run all table configs in table_config folder"
|
15
|
+
|
16
|
+
namespace :aws_record do
|
17
|
+
task migrate: :environment do
|
18
|
+
Dir[File.join('db', 'table_config', '**/*.rb')].each do |filename|
|
19
|
+
puts "running #{filename}"
|
20
|
+
load(filename)
|
21
|
+
|
22
|
+
table_config = ModelTableConfig.config
|
23
|
+
table_config.migrate! unless table_config.compatible?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws-record-generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.pre.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amazon Web Services
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-record
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.2'
|
41
|
+
description: Rails generators for aws-record models
|
42
|
+
email:
|
43
|
+
- yasharm@amazon.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/aws-record-generator.rb
|
49
|
+
- lib/generators/aws_record/model/USAGE
|
50
|
+
- lib/generators/aws_record/model/model_generator.rb
|
51
|
+
- lib/generators/aws_record/model/templates/model.rb.tt
|
52
|
+
- lib/generators/aws_record/model/templates/table_config.rb.tt
|
53
|
+
- lib/generators/generated_attribute.rb
|
54
|
+
- lib/generators/secondary_index.rb
|
55
|
+
- lib/generators/test_helper.rb
|
56
|
+
- lib/tasks/table_config_migrate_task.rake
|
57
|
+
homepage: https://github.com/awslabs/aws-record-generator
|
58
|
+
licenses:
|
59
|
+
- Apache 2.0
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.3.1
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.6.13
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Rails generators for aws-record
|
81
|
+
test_files: []
|