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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE +175 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-record-rails.rb +28 -0
  6. data/lib/generators/aws_record/active_model.rb +68 -0
  7. data/lib/generators/aws_record/base.rb +212 -0
  8. data/lib/generators/aws_record/erb/erb_generator.rb +55 -0
  9. data/lib/generators/aws_record/erb/templates/_form.html.erb.tt +34 -0
  10. data/lib/generators/aws_record/erb/templates/edit.html.erb.tt +6 -0
  11. data/lib/generators/aws_record/erb/templates/index.html.erb.tt +31 -0
  12. data/lib/generators/aws_record/erb/templates/new.html.erb.tt +5 -0
  13. data/lib/generators/aws_record/erb/templates/show.html.erb.tt +11 -0
  14. data/lib/generators/aws_record/model/USAGE +24 -0
  15. data/lib/generators/aws_record/model/model_generator.rb +25 -0
  16. data/lib/generators/aws_record/model/templates/model.rb.tt +76 -0
  17. data/lib/generators/aws_record/model/templates/table_config.rb.tt +20 -0
  18. data/lib/generators/aws_record/resource/USAGE +23 -0
  19. data/lib/generators/aws_record/resource/resource_generator.rb +21 -0
  20. data/lib/generators/aws_record/scaffold/USAGE +31 -0
  21. data/lib/generators/aws_record/scaffold/scaffold_generator.rb +46 -0
  22. data/lib/generators/aws_record/scaffold/templates/scaffold.css +80 -0
  23. data/lib/generators/aws_record/scaffold_controller/USAGE +15 -0
  24. data/lib/generators/aws_record/scaffold_controller/scaffold_controller_generator.rb +51 -0
  25. data/lib/generators/aws_record/scaffold_controller/templates/api_controller.rb.tt +63 -0
  26. data/lib/generators/aws_record/scaffold_controller/templates/controller.rb.tt +70 -0
  27. data/lib/generators/generated_attribute.rb +139 -0
  28. data/lib/generators/secondary_index.rb +67 -0
  29. data/lib/tasks/aws_record/migrate.rake +14 -0
  30. 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