aws-record-rails 0.1.0

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