aws-record-rails 0.1.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 +7 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +175 -0
- data/VERSION +1 -0
- data/lib/aws-record-rails.rb +28 -0
- data/lib/generators/aws_record/active_model.rb +68 -0
- data/lib/generators/aws_record/base.rb +212 -0
- data/lib/generators/aws_record/erb/erb_generator.rb +55 -0
- data/lib/generators/aws_record/erb/templates/_form.html.erb.tt +34 -0
- data/lib/generators/aws_record/erb/templates/edit.html.erb.tt +6 -0
- data/lib/generators/aws_record/erb/templates/index.html.erb.tt +31 -0
- data/lib/generators/aws_record/erb/templates/new.html.erb.tt +5 -0
- data/lib/generators/aws_record/erb/templates/show.html.erb.tt +11 -0
- data/lib/generators/aws_record/model/USAGE +24 -0
- data/lib/generators/aws_record/model/model_generator.rb +25 -0
- data/lib/generators/aws_record/model/templates/model.rb.tt +76 -0
- data/lib/generators/aws_record/model/templates/table_config.rb.tt +20 -0
- data/lib/generators/aws_record/resource/USAGE +23 -0
- data/lib/generators/aws_record/resource/resource_generator.rb +21 -0
- data/lib/generators/aws_record/scaffold/USAGE +31 -0
- data/lib/generators/aws_record/scaffold/scaffold_generator.rb +46 -0
- data/lib/generators/aws_record/scaffold/templates/scaffold.css +80 -0
- data/lib/generators/aws_record/scaffold_controller/USAGE +15 -0
- data/lib/generators/aws_record/scaffold_controller/scaffold_controller_generator.rb +51 -0
- data/lib/generators/aws_record/scaffold_controller/templates/api_controller.rb.tt +63 -0
- data/lib/generators/aws_record/scaffold_controller/templates/controller.rb.tt +70 -0
- data/lib/generators/generated_attribute.rb +139 -0
- data/lib/generators/secondary_index.rb +67 -0
- data/lib/tasks/aws_record/migrate.rake +14 -0
- metadata +99 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'generators/aws_record/base'
|
4
|
+
|
5
|
+
module AwsRecord
|
6
|
+
module Generators
|
7
|
+
class ModelGenerator < Base
|
8
|
+
def initialize(args, *options)
|
9
|
+
self.class.source_root File.expand_path('templates', __dir__)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_model
|
14
|
+
template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_table_config
|
18
|
+
return unless options['table_config']
|
19
|
+
|
20
|
+
template 'table_config.rb',
|
21
|
+
File.join('db/table_config', class_path, "#{file_name}_config.rb")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-record'
|
4
|
+
<% if has_validations? -%>
|
5
|
+
require 'active_model'
|
6
|
+
<% end -%>
|
7
|
+
|
8
|
+
<% module_namespacing do -%>
|
9
|
+
class <%= class_name %>
|
10
|
+
include Aws::Record
|
11
|
+
<% if options.key? :scaffold -%>
|
12
|
+
extend ActiveModel::Naming
|
13
|
+
<% end -%>
|
14
|
+
<% if has_validations? -%>
|
15
|
+
include ActiveModel::Validations
|
16
|
+
<% end -%>
|
17
|
+
<% if options.key? :password_digest -%>
|
18
|
+
include ActiveModel::SecurePassword
|
19
|
+
<% end -%>
|
20
|
+
<% if mutation_tracking_disabled? -%>
|
21
|
+
disable_mutation_tracking
|
22
|
+
<% end -%>
|
23
|
+
|
24
|
+
<% attributes.each do |attribute| -%>
|
25
|
+
<%= 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 %>
|
26
|
+
<% end -%>
|
27
|
+
<% gsis.each do |index| %>
|
28
|
+
global_secondary_index(
|
29
|
+
:<%= index.name %>,
|
30
|
+
hash_key: :<%= index.hash_key -%>,<%- if index.range_key %>
|
31
|
+
range_key: :<%= index.range_key -%>,<%- end %>
|
32
|
+
projection: {
|
33
|
+
projection_type: <%= index.projection_type %>
|
34
|
+
}
|
35
|
+
)
|
36
|
+
<% end -%>
|
37
|
+
<% if !required_attrs.empty? -%>
|
38
|
+
validates_presence_of <% *req, last_req = required_attrs -%><% req.each do |required_validation|-%>:<%= required_validation %>, <% end -%>:<%= last_req %>
|
39
|
+
<% end -%>
|
40
|
+
<% length_validations.each do |attribute, validation| -%>
|
41
|
+
validates_length_of :<%= attribute %>, within: <%= validation %>
|
42
|
+
<% end -%>
|
43
|
+
<% if options.key? :scaffold -%>
|
44
|
+
|
45
|
+
# Scaffolding helpers
|
46
|
+
def initialize(args = {})
|
47
|
+
super
|
48
|
+
@errors = ActiveModel::Errors.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :errors
|
52
|
+
|
53
|
+
def to_model
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_param
|
58
|
+
return nil unless persisted?
|
59
|
+
|
60
|
+
hkey = public_send(self.class.hash_key)
|
61
|
+
if self.class.range_key
|
62
|
+
rkey = public_send(self.class.range_key)
|
63
|
+
"#{CGI.escape(hkey)}&#{CGI.escape(rkey)}"
|
64
|
+
else
|
65
|
+
"#{CGI.escape(hkey)}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
<% end -%>
|
69
|
+
<% if options['table_name'] -%>
|
70
|
+
set_table_name "<%= options['table_name'] %>"
|
71
|
+
<% end -%>
|
72
|
+
<% if options.key? :password_digest -%>
|
73
|
+
has_secure_password
|
74
|
+
<% end -%>
|
75
|
+
end
|
76
|
+
<% end -%>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-record'
|
4
|
+
|
5
|
+
module ModelTableConfig
|
6
|
+
def self.config
|
7
|
+
Aws::Record::TableConfig.define do |t|
|
8
|
+
t.model_class <%= class_name %>
|
9
|
+
|
10
|
+
t.read_capacity_units <%= primary_read_units %>
|
11
|
+
t.write_capacity_units <%= primary_write_units %>
|
12
|
+
<%- gsis.each do |index| %>
|
13
|
+
t.global_secondary_index(:<%= index.name %>) do |i|
|
14
|
+
i.read_capacity_units <%= gsi_rw_units[index.name][0] %>
|
15
|
+
i.write_capacity_units <%= gsi_rw_units[index.name][1] %>
|
16
|
+
end
|
17
|
+
<%- end -%>
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Description:
|
2
|
+
Stubs out a new resource including an empty model and controller suitable
|
3
|
+
for a RESTful, resource-oriented application. Pass the singular model name,
|
4
|
+
either CamelCased or under_scored, as the first argument, and an optional
|
5
|
+
list of attribute pairs.
|
6
|
+
|
7
|
+
Attribute pairs are field:type arguments specifying the
|
8
|
+
model's attributes. Timestamps are added by default, so you don't have to
|
9
|
+
specify them by hand as 'created_at:datetime updated_at:datetime'.
|
10
|
+
|
11
|
+
You don't have to think up every attribute up front, but it helps to
|
12
|
+
sketch out a few so you can start working with the model immediately.
|
13
|
+
|
14
|
+
This generator invokes your configured ORM and test framework, besides
|
15
|
+
creating helpers and add routes to config/routes.rb.
|
16
|
+
|
17
|
+
Unlike the scaffold generator, the resource generator does not create
|
18
|
+
views or add any methods to the generated controller.
|
19
|
+
|
20
|
+
Examples:
|
21
|
+
`rails generate resource post` # no attributes
|
22
|
+
`rails generate resource post title:string body:text published:boolean`
|
23
|
+
`rails generate resource purchase order_id:integer amount:decimal`
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/rails/resource_route/resource_route_generator'
|
4
|
+
require 'rails/generators/resource_helpers'
|
5
|
+
require 'generators/aws_record/active_model'
|
6
|
+
|
7
|
+
module AwsRecord
|
8
|
+
module Generators
|
9
|
+
class ResourceGenerator < ModelGenerator
|
10
|
+
include Rails::Generators::ResourceHelpers
|
11
|
+
|
12
|
+
hook_for :resource_route, in: :rails, required: true
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def orm_class
|
17
|
+
@orm_class = AwsRecord::Generators::ActiveModel
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Description:
|
2
|
+
Scaffolds an entire resource, from model and migration to controller and
|
3
|
+
views, along with a full test suite. The resource is ready to use as a
|
4
|
+
starting point for your RESTful, resource-oriented application.
|
5
|
+
|
6
|
+
Pass the name of the model (preferably in singular form), and an optional list of attributes
|
7
|
+
|
8
|
+
Attributes are declarations of the fields that you wish to store within a model. You can pass
|
9
|
+
a type and list of options for each attribtue in the form: `name:type:options` if you do not provide
|
10
|
+
a type, it is assumed that the attribute is of type `string_attr`
|
11
|
+
|
12
|
+
Each model should have an hkey, if one is not present a `uuid:hkey` will be created for you.
|
13
|
+
|
14
|
+
Timestamps are not added by default but you can add them using the `--timestamps` flag
|
15
|
+
More information can be found at: https://github.com/awslabs/aws-record-generator/blob/master/README.md
|
16
|
+
|
17
|
+
You don't have to think up every attribute up front, but it helps to
|
18
|
+
sketch out a few so you can start working with the resource immediately.
|
19
|
+
|
20
|
+
For example, 'scaffold post title:hkey body published:boolean' gives
|
21
|
+
you a model with those three attributes, a controller that handles
|
22
|
+
the create/show/update/destroy, forms to create and edit your posts, and
|
23
|
+
an index that lists them all, as well as a resources :posts declaration
|
24
|
+
in config/routes.rb.
|
25
|
+
|
26
|
+
If you want to remove all the generated files, run
|
27
|
+
'rails destroy scaffold ModelName'.
|
28
|
+
|
29
|
+
Examples:
|
30
|
+
`rails generate scaffold post title:hkey body published:boolean`
|
31
|
+
`rails generate scaffold purchase amount:float:hkey tracking_id:integer:rkey`
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/rails/scaffold/scaffold_generator'
|
4
|
+
require 'generators/aws_record/resource/resource_generator'
|
5
|
+
|
6
|
+
module AwsRecord
|
7
|
+
module Generators
|
8
|
+
class ScaffoldGenerator < ResourceGenerator
|
9
|
+
source_root File.expand_path('../model/templates', __dir__)
|
10
|
+
|
11
|
+
remove_class_option :orm
|
12
|
+
remove_class_option :actions
|
13
|
+
|
14
|
+
class_option :api, type: :boolean
|
15
|
+
class_option :stylesheets, type: :boolean, desc: 'Generate Stylesheets'
|
16
|
+
class_option :stylesheet_engine, desc: 'Engine for Stylesheets'
|
17
|
+
class_option :assets, type: :boolean
|
18
|
+
class_option :resource_route, type: :boolean
|
19
|
+
class_option :scaffold_stylesheet, type: :boolean
|
20
|
+
|
21
|
+
def handle_skip
|
22
|
+
@options = @options.merge(stylesheets: false) unless options[:assets]
|
23
|
+
return if options[:stylesheets] && options[:scaffold_stylesheet]
|
24
|
+
|
25
|
+
@options = @options.merge(stylesheet_engine: false)
|
26
|
+
end
|
27
|
+
|
28
|
+
hook_for :scaffold_controller, in: :aws_record, required: true
|
29
|
+
|
30
|
+
hook_for :assets, in: :rails do |assets|
|
31
|
+
invoke assets, [controller_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
hook_for :stylesheet_engine, in: :rails do |stylesheet_engine|
|
35
|
+
invoke stylesheet_engine, [controller_name] if behavior == :invoke
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def initialize(args, *options)
|
41
|
+
options[0] << '--scaffold'
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
body {
|
2
|
+
background-color: #fff;
|
3
|
+
color: #333;
|
4
|
+
margin: 33px;
|
5
|
+
}
|
6
|
+
|
7
|
+
body, p, ol, ul, td {
|
8
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
9
|
+
font-size: 13px;
|
10
|
+
line-height: 18px;
|
11
|
+
}
|
12
|
+
|
13
|
+
pre {
|
14
|
+
background-color: #eee;
|
15
|
+
padding: 10px;
|
16
|
+
font-size: 11px;
|
17
|
+
}
|
18
|
+
|
19
|
+
a {
|
20
|
+
color: #000;
|
21
|
+
}
|
22
|
+
|
23
|
+
a:visited {
|
24
|
+
color: #666;
|
25
|
+
}
|
26
|
+
|
27
|
+
a:hover {
|
28
|
+
color: #fff;
|
29
|
+
background-color: #000;
|
30
|
+
}
|
31
|
+
|
32
|
+
th {
|
33
|
+
padding-bottom: 5px;
|
34
|
+
}
|
35
|
+
|
36
|
+
td {
|
37
|
+
padding: 0 5px 7px;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.field,
|
41
|
+
div.actions {
|
42
|
+
margin-bottom: 10px;
|
43
|
+
}
|
44
|
+
|
45
|
+
#notice {
|
46
|
+
color: green;
|
47
|
+
}
|
48
|
+
|
49
|
+
.field_with_errors {
|
50
|
+
padding: 2px;
|
51
|
+
background-color: red;
|
52
|
+
display: table;
|
53
|
+
}
|
54
|
+
|
55
|
+
#error_explanation {
|
56
|
+
width: 450px;
|
57
|
+
border: 2px solid red;
|
58
|
+
padding: 7px 7px 0;
|
59
|
+
margin-bottom: 20px;
|
60
|
+
background-color: #f0f0f0;
|
61
|
+
}
|
62
|
+
|
63
|
+
#error_explanation h2 {
|
64
|
+
text-align: left;
|
65
|
+
font-weight: bold;
|
66
|
+
padding: 5px 5px 5px 15px;
|
67
|
+
font-size: 12px;
|
68
|
+
margin: -7px -7px 0;
|
69
|
+
background-color: #c00;
|
70
|
+
color: #fff;
|
71
|
+
}
|
72
|
+
|
73
|
+
#error_explanation ul li {
|
74
|
+
font-size: 12px;
|
75
|
+
list-style: square;
|
76
|
+
}
|
77
|
+
|
78
|
+
label {
|
79
|
+
display: block;
|
80
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Description:
|
2
|
+
Stubs out a scaffolded controller, its seven RESTful actions and related
|
3
|
+
views. Pass the model name, either CamelCased or under_scored. The
|
4
|
+
controller name is retrieved as a pluralized version of the model name.
|
5
|
+
|
6
|
+
This generates a controller class in app/controllers and invokes helper,
|
7
|
+
template engine framework generators.
|
8
|
+
|
9
|
+
Example:
|
10
|
+
`rails generate scaffold_controller CreditCard...`
|
11
|
+
|
12
|
+
Credit card controller with URLs like /credit_cards.
|
13
|
+
Controller: app/controllers/credit_cards_controller.rb
|
14
|
+
Views: app/views/credit_cards/index.html.erb [...]
|
15
|
+
Helper: app/helpers/credit_cards_helper.rb
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/resource_helpers'
|
4
|
+
require 'generators/aws_record/active_model'
|
5
|
+
|
6
|
+
module AwsRecord
|
7
|
+
module Generators
|
8
|
+
class ScaffoldControllerGenerator < Base
|
9
|
+
include Rails::Generators::ResourceHelpers
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
11
|
+
|
12
|
+
check_class_collision suffix: 'Controller'
|
13
|
+
|
14
|
+
class_option :helper, type: :boolean
|
15
|
+
class_option :orm, banner: 'NAME', type: :string, required: true,
|
16
|
+
desc: 'ORM to generate the controller for'
|
17
|
+
class_option :api, type: :boolean,
|
18
|
+
desc: 'Generates API controller'
|
19
|
+
|
20
|
+
argument :attributes, type: :array, default: [], banner: 'field:type field:type'
|
21
|
+
|
22
|
+
def initialize(args, *options)
|
23
|
+
options[0] << '--skip-table-config'
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_controller_files
|
28
|
+
template_file = options.api? ? 'api_controller.rb' : 'controller.rb'
|
29
|
+
template template_file,
|
30
|
+
File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
hook_for :template_engine, in: :aws_record do |template_engine|
|
34
|
+
invoke template_engine unless options.api?
|
35
|
+
end
|
36
|
+
|
37
|
+
hook_for :test_framework, as: :scaffold
|
38
|
+
|
39
|
+
# Invoke the helper using the controller name (pluralized)
|
40
|
+
hook_for :helper, as: :scaffold, in: :rails do |invoked|
|
41
|
+
invoke invoked, [controller_name]
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def orm_class
|
47
|
+
@orm_class = AwsRecord::Generators::ActiveModel
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<% if namespaced? -%>
|
2
|
+
require_dependency "<%= namespaced_path %>/application_controller"
|
3
|
+
|
4
|
+
<% end -%>
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
7
|
+
before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy]
|
8
|
+
|
9
|
+
# GET <%= route_url %>
|
10
|
+
def index
|
11
|
+
# Warning: This performs a full table scan, which can be very expensive if a table has many entries.
|
12
|
+
# It is strongly recommended to implement alternative approaches such as paginated queries.
|
13
|
+
@<%= plural_table_name %> = <%= orm_class.all(class_name) %>
|
14
|
+
|
15
|
+
render json: <%= "@#{plural_table_name}" %>
|
16
|
+
end
|
17
|
+
|
18
|
+
# GET <%= route_url %>/1
|
19
|
+
def show
|
20
|
+
render json: <%= "@#{singular_table_name}" %>
|
21
|
+
end
|
22
|
+
|
23
|
+
# POST <%= route_url %>
|
24
|
+
def create
|
25
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
|
26
|
+
|
27
|
+
if @<%= orm_instance.save %>
|
28
|
+
render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
|
29
|
+
else
|
30
|
+
render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# PATCH/PUT <%= route_url %>/1
|
35
|
+
def update
|
36
|
+
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
37
|
+
render json: <%= "@#{singular_table_name}" %>
|
38
|
+
else
|
39
|
+
render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# DELETE <%= route_url %>/1
|
44
|
+
def destroy
|
45
|
+
@<%= orm_instance.destroy %>
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# Use callbacks to share common setup or constraints between actions.
|
50
|
+
def set_<%= singular_table_name %>
|
51
|
+
@<%= singular_table_name %> = <%= orm_class.find(class_name, attributes) %>
|
52
|
+
end
|
53
|
+
|
54
|
+
# Only allow a trusted parameter "white list" through.
|
55
|
+
def <%= "#{singular_table_name}_params" %>
|
56
|
+
<%- if attributes_names.empty? -%>
|
57
|
+
params.fetch(:<%= singular_table_name %>, {})
|
58
|
+
<%- else -%>
|
59
|
+
params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
|
60
|
+
<%- end -%>
|
61
|
+
end
|
62
|
+
end
|
63
|
+
<% end -%>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<% if namespaced? -%>
|
2
|
+
require_dependency "<%= namespaced_path %>/application_controller"
|
3
|
+
|
4
|
+
<% end -%>
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
7
|
+
before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
|
8
|
+
|
9
|
+
# GET <%= route_url %>
|
10
|
+
def index
|
11
|
+
# Warning: This performs a full table scan, which can be very expensive if a table has many entries.
|
12
|
+
# It is strongly recommended to implement alternative approaches such as paginated queries.
|
13
|
+
@<%= plural_table_name %> = <%= orm_class.all(class_name) %>
|
14
|
+
end
|
15
|
+
|
16
|
+
# GET <%= route_url %>/1
|
17
|
+
def show
|
18
|
+
end
|
19
|
+
|
20
|
+
# GET <%= route_url %>/new
|
21
|
+
def new
|
22
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name) %>
|
23
|
+
end
|
24
|
+
|
25
|
+
# GET <%= route_url %>/1/edit
|
26
|
+
def edit
|
27
|
+
end
|
28
|
+
|
29
|
+
# POST <%= route_url %>
|
30
|
+
def create
|
31
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
|
32
|
+
|
33
|
+
if @<%= orm_instance.save %>
|
34
|
+
redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
|
35
|
+
else
|
36
|
+
render :new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# PATCH/PUT <%= route_url %>/1
|
41
|
+
def update
|
42
|
+
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
43
|
+
redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
|
44
|
+
else
|
45
|
+
render :edit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# DELETE <%= route_url %>/1
|
50
|
+
def destroy
|
51
|
+
@<%= orm_instance.destroy %>
|
52
|
+
redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
# Use callbacks to share common setup or constraints between actions.
|
57
|
+
def set_<%= singular_table_name %>
|
58
|
+
@<%= singular_table_name %> = <%= orm_class.find(class_name, attributes) %>
|
59
|
+
end
|
60
|
+
|
61
|
+
# Only allow a trusted parameter "white list" through.
|
62
|
+
def <%= "#{singular_table_name}_params" %>
|
63
|
+
<%- if attributes_names.empty? -%>
|
64
|
+
params.fetch(:<%= singular_table_name %>, {})
|
65
|
+
<%- else -%>
|
66
|
+
params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
|
67
|
+
<%- end -%>
|
68
|
+
end
|
69
|
+
end
|
70
|
+
<% end -%>
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AwsRecord
|
4
|
+
module Generators
|
5
|
+
# @api private
|
6
|
+
class GeneratedAttribute
|
7
|
+
OPTS = %w[hkey rkey persist_nil db_attr_name ddb_type default_value].freeze
|
8
|
+
INVALID_HKEY_TYPES = %i[map_attr list_attr numeric_set_attr string_set_attr].freeze
|
9
|
+
attr_reader :name, :type
|
10
|
+
attr_accessor :options
|
11
|
+
|
12
|
+
def field_type
|
13
|
+
case @type
|
14
|
+
when :integer_attr then :number_field
|
15
|
+
when :date_attr then :date_select
|
16
|
+
when :datetime_attr then :datetime_select
|
17
|
+
when :boolean_attr then :check_box
|
18
|
+
else :text_field
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def parse(field_definition)
|
24
|
+
name, type, opts = field_definition.split(':')
|
25
|
+
type ||= 'string'
|
26
|
+
if OPTS.any? { |opt| type.include? opt }
|
27
|
+
opts = type
|
28
|
+
type = 'string'
|
29
|
+
end
|
30
|
+
|
31
|
+
opts = opts.split(',') if opts
|
32
|
+
type, opts = parse_type_and_options(name, type, opts)
|
33
|
+
validate_opt_combs(name, type, opts)
|
34
|
+
|
35
|
+
new(name, type, opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def validate_opt_combs(name, type, opts)
|
41
|
+
return unless opts
|
42
|
+
|
43
|
+
is_hkey = opts.key?(:hash_key)
|
44
|
+
is_rkey = opts.key?(:range_key)
|
45
|
+
|
46
|
+
if is_hkey && is_rkey
|
47
|
+
raise ArgumentError,
|
48
|
+
"Field #{name} cannot be a range key and hash key simultaneously"
|
49
|
+
end
|
50
|
+
return unless is_hkey && INVALID_HKEY_TYPES.include?(type)
|
51
|
+
|
52
|
+
raise ArgumentError,
|
53
|
+
"Field #{name} cannot be a hash key and be of type #{type}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_type_and_options(name, type, opts)
|
57
|
+
opts ||= []
|
58
|
+
[parse_type(name, type), opts.to_h { |opt| parse_option(name, opt) }]
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_option(name, opt)
|
62
|
+
case opt
|
63
|
+
|
64
|
+
when 'hkey'
|
65
|
+
[:hash_key, true]
|
66
|
+
when 'rkey'
|
67
|
+
[:range_key, true]
|
68
|
+
when 'persist_nil'
|
69
|
+
[:persist_nil, true]
|
70
|
+
when /db_attr_name\{(\w+)\}/
|
71
|
+
[:database_attribute_name, "\"#{::Regexp.last_match(1)}\""]
|
72
|
+
when /ddb_type\{(S|N|B|BOOL|SS|NS|BS|M|L)\}/i
|
73
|
+
[:dynamodb_type, "\"#{::Regexp.last_match(1).upcase}\""]
|
74
|
+
when /default_value\{(.+)\}/
|
75
|
+
[:default_value, ::Regexp.last_match(1)]
|
76
|
+
else
|
77
|
+
raise ArgumentError, "You provided an invalid option for #{name}: #{opt}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_type(name, type)
|
82
|
+
case type.downcase
|
83
|
+
|
84
|
+
when 'bool', 'boolean'
|
85
|
+
:boolean_attr
|
86
|
+
when 'date'
|
87
|
+
:date_attr
|
88
|
+
when 'datetime'
|
89
|
+
:datetime_attr
|
90
|
+
when 'float'
|
91
|
+
:float_attr
|
92
|
+
when 'int', 'integer'
|
93
|
+
:integer_attr
|
94
|
+
when 'list'
|
95
|
+
:list_attr
|
96
|
+
when 'map'
|
97
|
+
:map_attr
|
98
|
+
when 'num_set', 'numeric_set', 'nset'
|
99
|
+
:numeric_set_attr
|
100
|
+
when 'string_set', 's_set', 'sset'
|
101
|
+
:string_set_attr
|
102
|
+
when 'string'
|
103
|
+
:string_attr
|
104
|
+
else
|
105
|
+
raise ArgumentError, "Invalid type for #{name}: #{type}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize(name, type = :string_attr, options = {})
|
111
|
+
@name = name
|
112
|
+
@type = type
|
113
|
+
@options = options
|
114
|
+
@digest = options.delete(:digest)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Methods used by rails scaffolding
|
118
|
+
def password_digest?
|
119
|
+
@digest
|
120
|
+
end
|
121
|
+
|
122
|
+
def polymorphic?
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
def column_name
|
127
|
+
if @name == 'password_digest'
|
128
|
+
'password'
|
129
|
+
else
|
130
|
+
@name
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def human_name
|
135
|
+
name.humanize
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|