aws-record-generator 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|