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