b4um_generators 0.3.0 → 0.4.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 (25) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.de.md +338 -4
  4. data/README.md +330 -5
  5. data/lib/b4um_generators/version.rb +1 -1
  6. data/lib/generators/b4um/admin/admin_generator.rb +614 -0
  7. data/lib/generators/b4um/admin/templates/add_admin_fields_migration.rb.tt +10 -0
  8. data/lib/generators/b4um/admin/templates/admin_base_controller.rb.tt +5 -0
  9. data/lib/generators/b4um/admin/templates/admin_dashboard.html.erb.tt +19 -0
  10. data/lib/generators/b4um/admin/templates/admin_dashboard_controller.rb.tt +6 -0
  11. data/lib/generators/b4um/admin/templates/admin_users_controller.rb.tt +70 -0
  12. data/lib/generators/b4um/admin/templates/admin_users_index.html.erb.tt +110 -0
  13. data/lib/generators/b4um/admin/templates/b4um_admin.rake.tt +106 -0
  14. data/lib/generators/b4um/authentication/authentication_generator.rb +90 -1
  15. data/lib/generators/b4um/guest/guest_generator.rb +673 -0
  16. data/lib/generators/b4um/guest/templates/add_guest_fields_migration.rb.tt +12 -0
  17. data/lib/generators/b4um/guest/templates/b4um_guests.rake.tt +12 -0
  18. data/lib/generators/b4um/guest/templates/edit.html.erb.tt +156 -0
  19. data/lib/generators/b4um/guest/templates/guest_expiration_warning.html.erb.tt +12 -0
  20. data/lib/generators/b4um/guest/templates/guests_controller.rb.tt +88 -0
  21. data/lib/generators/b4um/guest/templates/new.html.erb.tt +136 -0
  22. data/lib/generators/b4um/help/help_generator.rb +162 -0
  23. data/lib/generators/b4um/scaffold/scaffold_generator.rb +50 -0
  24. data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +18 -14
  25. metadata +16 -1
@@ -0,0 +1,156 @@
1
+ <div class="page-header">
2
+ <h1>Create your account</h1>
3
+ </div>
4
+
5
+ <div class="b4um-grid">
6
+ <section class="b4um-card b4um-card--full">
7
+ <%%= form_with model: @<%= authentication_model_name %>,
8
+ url: convert_guest_path,
9
+ method: :patch,
10
+ class: "form" do |form| %>
11
+ <%% if @<%= authentication_model_name %>.errors.any? %>
12
+ <div class="error-messages">
13
+ <h2>
14
+ <%%= pluralize(@<%= authentication_model_name %>.errors.count, "error") %>
15
+ prohibited this account from being saved:
16
+ </h2>
17
+
18
+ <ul>
19
+ <%% @<%= authentication_model_name %>.errors.each do |error| %>
20
+ <li><%%= error.full_message %></li>
21
+ <%% end %>
22
+ </ul>
23
+ </div>
24
+ <%% end %>
25
+
26
+ <% guest_available_fields.each do |field| -%>
27
+ <% field_type = guest_field_type(field) -%>
28
+ <% required = account_required_fields.include?(field) -%>
29
+ <% label = field.humanize + (required ? " *" : "") -%>
30
+ <% if field.match?(/(?:^|_)email(?:_|$)/) -%>
31
+ <div class="form-field">
32
+ <%%= form.email_field :<%= field %>,
33
+ class: "form-input",
34
+ placeholder: " ",
35
+ required: <%= required %> %>
36
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
37
+ </div>
38
+
39
+ <% elsif field.match?(/(?:^|_)(?:phone|telephone|mobile)(?:_|$)/) -%>
40
+ <div class="form-field">
41
+ <%%= form.telephone_field :<%= field %>,
42
+ class: "form-input",
43
+ placeholder: " ",
44
+ required: <%= required %> %>
45
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
46
+ </div>
47
+
48
+ <% elsif field.match?(/(?:^|_)(?:url|website|homepage)(?:_|$)/) -%>
49
+ <div class="form-field">
50
+ <%%= form.url_field :<%= field %>,
51
+ class: "form-input",
52
+ placeholder: " ",
53
+ required: <%= required %> %>
54
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
55
+ </div>
56
+
57
+ <% elsif field_type == :boolean -%>
58
+ <div class="form-field form-field--checkbox">
59
+ <%%= form.check_box :<%= field %>,
60
+ class: "form-checkbox",
61
+ required: <%= required %> %>
62
+ <%%= form.label :<%= field %>, "<%= label %>",
63
+ class: "form-label form-label--checkbox" %>
64
+ </div>
65
+
66
+ <% elsif field_type == :text -%>
67
+ <div class="form-field form-field--textarea">
68
+ <%%= form.text_area :<%= field %>,
69
+ class: "form-textarea",
70
+ placeholder: " ",
71
+ required: <%= required %> %>
72
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
73
+ </div>
74
+
75
+ <% elsif field_type == :integer -%>
76
+ <div class="form-field">
77
+ <%%= form.number_field :<%= field %>,
78
+ class: "form-input",
79
+ placeholder: " ",
80
+ step: 1,
81
+ required: <%= required %> %>
82
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
83
+ </div>
84
+
85
+ <% elsif [:float, :decimal].include?(field_type) -%>
86
+ <div class="form-field">
87
+ <%%= form.number_field :<%= field %>,
88
+ class: "form-input",
89
+ placeholder: " ",
90
+ step: "any",
91
+ required: <%= required %> %>
92
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
93
+ </div>
94
+
95
+ <% elsif field_type == :date -%>
96
+ <div class="form-field">
97
+ <%%= form.date_field :<%= field %>,
98
+ class: "form-input",
99
+ required: <%= required %> %>
100
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
101
+ </div>
102
+
103
+ <% elsif [:datetime, :timestamp].include?(field_type) -%>
104
+ <div class="form-field">
105
+ <%%= form.datetime_local_field :<%= field %>,
106
+ class: "form-input",
107
+ required: <%= required %> %>
108
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
109
+ </div>
110
+
111
+ <% elsif field_type == :time -%>
112
+ <div class="form-field">
113
+ <%%= form.time_field :<%= field %>,
114
+ class: "form-input",
115
+ required: <%= required %> %>
116
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
117
+ </div>
118
+
119
+ <% else -%>
120
+ <div class="form-field">
121
+ <%%= form.text_field :<%= field %>,
122
+ class: "form-input",
123
+ placeholder: " ",
124
+ required: <%= required %> %>
125
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
126
+ </div>
127
+
128
+ <% end -%>
129
+ <% end -%>
130
+
131
+ <div class="form-field">
132
+ <%%= form.password_field :password,
133
+ class: "form-input",
134
+ placeholder: " ",
135
+ required: true,
136
+ autocomplete: "new-password" %>
137
+ <%%= form.label :password, "Password *", class: "form-label" %>
138
+ </div>
139
+
140
+ <div class="form-field">
141
+ <%%= form.password_field :password_confirmation,
142
+ class: "form-input",
143
+ placeholder: " ",
144
+ required: true,
145
+ autocomplete: "new-password" %>
146
+ <%%= form.label :password_confirmation,
147
+ "Password confirmation *",
148
+ class: "form-label" %>
149
+ </div>
150
+
151
+ <div class="form-actions">
152
+ <%%= form.submit "Create account", class: "form-submit" %>
153
+ </div>
154
+ <%% end %>
155
+ </section>
156
+ </div>
@@ -0,0 +1,12 @@
1
+ <%% if guest_expiring_soon? %>
2
+ <div class="flash flash--alert">
3
+ <span>Your guest access expires soon.</span>
4
+
5
+ <%%= link_to "Create account", convert_guest_path %>
6
+
7
+ <%%= button_to "Delete guest account",
8
+ guest_path,
9
+ method: :delete,
10
+ data: { turbo_confirm: "Delete your guest account?" } %>
11
+ </div>
12
+ <%% end %>
@@ -0,0 +1,88 @@
1
+ require "securerandom"
2
+
3
+ class GuestsController < ApplicationController
4
+ before_action :require_guest, only: %i[edit update destroy]
5
+ before_action :require_logged_out, only: %i[new create]
6
+
7
+ def new
8
+ @<%= authentication_model_name %> = <%= authentication_class_name %>.new
9
+ end
10
+
11
+ def create
12
+ @<%= authentication_model_name %> = <%= authentication_class_name %>.new(guest_params)
13
+ @<%= authentication_model_name %>.guest = true
14
+ @<%= authentication_model_name %>.guest_expires_at = <%= options[:expires_in].to_i %>.days.from_now
15
+
16
+ guest_password = SecureRandom.base64(32)
17
+ @<%= authentication_model_name %>.password = guest_password
18
+ @<%= authentication_model_name %>.password_confirmation = guest_password
19
+
20
+ if @<%= authentication_model_name %>.save
21
+ session[:<%= authentication_model_name %>_id] = @<%= authentication_model_name %>.id
22
+ redirect_to root_path
23
+ else
24
+ render :new, status: :unprocessable_content
25
+ end
26
+ end
27
+
28
+ def edit
29
+ @<%= authentication_model_name %> = <%= current_authentication_method %>
30
+ end
31
+
32
+ def update
33
+ @<%= authentication_model_name %> = <%= current_authentication_method %>
34
+
35
+ if conversion_params[:password].blank?
36
+ @<%= authentication_model_name %>.errors.add(:password, "can't be blank")
37
+ render :edit, status: :unprocessable_content
38
+ return
39
+ end
40
+
41
+ guest_expires_at = @<%= authentication_model_name %>.guest_expires_at
42
+
43
+ @<%= authentication_model_name %>.assign_attributes(conversion_params)
44
+ @<%= authentication_model_name %>.guest = false
45
+ @<%= authentication_model_name %>.guest_expires_at = nil
46
+
47
+ if @<%= authentication_model_name %>.save
48
+ redirect_to root_path
49
+ else
50
+ @<%= authentication_model_name %>.guest = true
51
+ @<%= authentication_model_name %>.guest_expires_at = guest_expires_at
52
+ render :edit, status: :unprocessable_content
53
+ end
54
+ end
55
+
56
+ def destroy
57
+ @<%= authentication_model_name %> = <%= current_authentication_method %>
58
+
59
+ session.delete(:<%= authentication_model_name %>_id)
60
+ @<%= authentication_model_name %>.destroy!
61
+
62
+ redirect_to root_path
63
+ end
64
+
65
+ private
66
+
67
+ def guest_params
68
+ params.require(:guest).permit(<%= guest_available_fields.map { |field| ":#{field}" }.join(", ") %>)
69
+ end
70
+
71
+ def conversion_params
72
+ params.require(:<%= authentication_model_name %>).permit(
73
+ <%= (guest_available_fields + %w[password password_confirmation]).map { |field| ":#{field}" }.join(", ") %>
74
+ )
75
+ end
76
+
77
+ def require_guest
78
+ return if <%= current_authentication_method %>&.guest?
79
+
80
+ redirect_to root_path, alert: "Guest access required."
81
+ end
82
+
83
+ def require_logged_out
84
+ return unless <%= current_authentication_method %>
85
+
86
+ redirect_to root_path, alert: "You are already logged in."
87
+ end
88
+ end
@@ -0,0 +1,136 @@
1
+ <div class="page-header">
2
+ <h1>Guest access</h1>
3
+ </div>
4
+
5
+ <div class="b4um-grid">
6
+ <section class="b4um-card b4um-card--full">
7
+ <%%= form_with model: @<%= authentication_model_name %>,
8
+ url: guest_path,
9
+ scope: :guest,
10
+ class: "form" do |form| %>
11
+ <%% if @<%= authentication_model_name %>.errors.any? %>
12
+ <div class="error-messages">
13
+ <h2>
14
+ <%%= pluralize(@<%= authentication_model_name %>.errors.count, "error") %>
15
+ prohibited this guest from being saved:
16
+ </h2>
17
+
18
+ <ul>
19
+ <%% @<%= authentication_model_name %>.errors.each do |error| %>
20
+ <li><%%= error.full_message %></li>
21
+ <%% end %>
22
+ </ul>
23
+ </div>
24
+ <%% end %>
25
+
26
+ <% guest_available_fields.each do |field| -%>
27
+ <% field_type = guest_field_type(field) -%>
28
+ <% required = guest_required_fields.include?(field) -%>
29
+ <% label = field.humanize + (required ? " *" : "") -%>
30
+ <% if field.match?(/(?:^|_)email(?:_|$)/) -%>
31
+ <div class="form-field">
32
+ <%%= form.email_field :<%= field %>,
33
+ class: "form-input",
34
+ placeholder: " ",
35
+ required: <%= required %> %>
36
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
37
+ </div>
38
+
39
+ <% elsif field.match?(/(?:^|_)(?:phone|telephone|mobile)(?:_|$)/) -%>
40
+ <div class="form-field">
41
+ <%%= form.telephone_field :<%= field %>,
42
+ class: "form-input",
43
+ placeholder: " ",
44
+ required: <%= required %> %>
45
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
46
+ </div>
47
+
48
+ <% elsif field.match?(/(?:^|_)(?:url|website|homepage)(?:_|$)/) -%>
49
+ <div class="form-field">
50
+ <%%= form.url_field :<%= field %>,
51
+ class: "form-input",
52
+ placeholder: " ",
53
+ required: <%= required %> %>
54
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
55
+ </div>
56
+
57
+ <% elsif field_type == :boolean -%>
58
+ <div class="form-field form-field--checkbox">
59
+ <%%= form.check_box :<%= field %>,
60
+ class: "form-checkbox",
61
+ required: <%= required %> %>
62
+ <%%= form.label :<%= field %>, "<%= label %>",
63
+ class: "form-label form-label--checkbox" %>
64
+ </div>
65
+
66
+ <% elsif field_type == :text -%>
67
+ <div class="form-field form-field--textarea">
68
+ <%%= form.text_area :<%= field %>,
69
+ class: "form-textarea",
70
+ placeholder: " ",
71
+ required: <%= required %> %>
72
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
73
+ </div>
74
+
75
+ <% elsif field_type == :integer -%>
76
+ <div class="form-field">
77
+ <%%= form.number_field :<%= field %>,
78
+ class: "form-input",
79
+ placeholder: " ",
80
+ step: 1,
81
+ required: <%= required %> %>
82
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
83
+ </div>
84
+
85
+ <% elsif [:float, :decimal].include?(field_type) -%>
86
+ <div class="form-field">
87
+ <%%= form.number_field :<%= field %>,
88
+ class: "form-input",
89
+ placeholder: " ",
90
+ step: "any",
91
+ required: <%= required %> %>
92
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
93
+ </div>
94
+
95
+ <% elsif field_type == :date -%>
96
+ <div class="form-field">
97
+ <%%= form.date_field :<%= field %>,
98
+ class: "form-input",
99
+ required: <%= required %> %>
100
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
101
+ </div>
102
+
103
+ <% elsif [:datetime, :timestamp].include?(field_type) -%>
104
+ <div class="form-field">
105
+ <%%= form.datetime_local_field :<%= field %>,
106
+ class: "form-input",
107
+ required: <%= required %> %>
108
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
109
+ </div>
110
+
111
+ <% elsif field_type == :time -%>
112
+ <div class="form-field">
113
+ <%%= form.time_field :<%= field %>,
114
+ class: "form-input",
115
+ required: <%= required %> %>
116
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
117
+ </div>
118
+
119
+ <% else -%>
120
+ <div class="form-field">
121
+ <%%= form.text_field :<%= field %>,
122
+ class: "form-input",
123
+ placeholder: " ",
124
+ required: <%= required %> %>
125
+ <%%= form.label :<%= field %>, "<%= label %>", class: "form-label" %>
126
+ </div>
127
+
128
+ <% end -%>
129
+ <% end -%>
130
+
131
+ <div class="form-actions">
132
+ <%%= form.submit "Continue as guest", class: "form-submit" %>
133
+ </div>
134
+ <%% end %>
135
+ </section>
136
+ </div>
@@ -362,6 +362,7 @@ module B4um
362
362
  - b4um index, show, new and edit views
363
363
  - Bento index layout by default
364
364
  - Optional table index layout
365
+ - Required fields with presence validation
365
366
  - Select fields with custom labels
366
367
  - Radio fields with custom labels
367
368
  - Automatic navigation entry
@@ -371,6 +372,19 @@ module B4um
371
372
  - Password field support
372
373
  - Optional readable URL parameters
373
374
 
375
+ Required fields can be generated with:
376
+
377
+ --required=FIELD,FIELD
378
+
379
+ Example:
380
+
381
+ bin/rails generate b4um:scaffold Customer first_name:string last_name:string company:string email:string --required=first_name,last_name,email
382
+
383
+ This adds a presence validation for the selected fields
384
+ to the generated model.
385
+
386
+ Fields not listed in --required remain optional.
387
+
374
388
  Select fields can be generated with:
375
389
 
376
390
  --select='FIELD:VALUE=LABEL,VALUE=LABEL'
@@ -597,6 +611,154 @@ module B4um
597
611
  helpers, routes or navigation controls.
598
612
 
599
613
 
614
+ ADMIN
615
+
616
+ bin/rails generate b4um:admin MODEL
617
+
618
+ Adds administrator user management to an existing
619
+ b4um authentication model.
620
+
621
+ Example:
622
+
623
+ bin/rails generate b4um:admin User
624
+ bin/rails db:migrate
625
+
626
+ The admin generator requires b4um authentication to be
627
+ installed for the selected model first.
628
+
629
+ Example setup:
630
+
631
+ bin/rails generate b4um:scaffold User name:string email:string password_digest:string
632
+ bin/rails generate b4um:authentication User
633
+ bin/rails generate b4um:admin User
634
+ bin/rails db:migrate
635
+
636
+ The generator adds two fields to the authentication model:
637
+
638
+ admin
639
+ blocked
640
+
641
+ Both fields are boolean values and default to false.
642
+
643
+ The generator also adds:
644
+
645
+ - Admin dashboard
646
+ - User management
647
+ - Admin-only controller protection
648
+ - Administrator routes
649
+ - Blocking and unblocking users
650
+ - Granting and revoking administrator rights
651
+ - Deleting users
652
+ - Protection against blocking your own account
653
+ - Protection against deleting your own account
654
+ - Protection against removing the last administrator
655
+ - Blocked session invalidation
656
+ - Blocked login prevention
657
+
658
+ The admin area is available at:
659
+
660
+ /admin
661
+
662
+ Only logged-in users with administrator rights can access
663
+ the admin area.
664
+
665
+ The first administrator is created from the command line.
666
+ This prevents public registration from granting administrator
667
+ rights.
668
+
669
+ Run:
670
+
671
+ bin/rails b4um:admin:create
672
+
673
+ Run bin/rails db:migrate before creating the first administrator.
674
+
675
+ The task asks for the authentication model fields automatically.
676
+
677
+ Additional authentication model fields are requested automatically.
678
+ Fields with an unconditional presence validation are marked with * and are required.
679
+ Optional fields can be left empty.
680
+
681
+ Email, password and password confirmation are always required.
682
+
683
+ The password is not displayed while it is entered.
684
+
685
+ Existing users cannot be converted to administrators with
686
+ this task. Administrator rights for existing users are
687
+ managed from the admin user management interface.
688
+
689
+ From the admin user management interface, administrators can:
690
+
691
+ - Block a user
692
+ - Unblock a user
693
+ - Grant administrator rights
694
+ - Revoke administrator rights
695
+ - Delete a user
696
+
697
+ A blocked user cannot log in. If a logged-in user becomes
698
+ blocked, the existing session is invalidated.
699
+
700
+ Administrators cannot block or delete their own account
701
+ through the generated user management interface.
702
+
703
+ The last administrator cannot lose administrator rights.
704
+
705
+
706
+ GUEST
707
+
708
+ bin/rails generate b4um:guest MODEL --required=FIELDS
709
+
710
+ Adds temporary guest access to an existing
711
+ b4um authentication model.
712
+
713
+ Example:
714
+
715
+ bin/rails generate b4um:guest User --required=first_name,email
716
+ bin/rails db:migrate
717
+
718
+ The guest generator requires b4um authentication to be
719
+ installed for the selected model first.
720
+
721
+ At least one field must be supplied with --required.
722
+ These fields are required when creating a guest account.
723
+
724
+ Additional suitable model fields are offered automatically
725
+ and remain optional unless included in --required.
726
+
727
+ The generator adds two fields to the authentication model:
728
+
729
+ guest
730
+ guest_expires_at
731
+
732
+ Guest accounts use the same authentication model as regular
733
+ accounts. No separate guest model is created.
734
+
735
+ Guest accounts receive an internal password automatically.
736
+ The guest does not need to choose or enter this password.
737
+
738
+ By default, guest access expires after 7 days.
739
+
740
+ A different lifetime can be configured with --expires-in:
741
+
742
+ bin/rails generate b4um:guest User --required=first_name,email --expires-in=14
743
+
744
+ Guest accounts can be converted into regular accounts.
745
+ The existing record is retained so existing account data
746
+ remains associated with the user.
747
+
748
+ During conversion, missing required account fields and a
749
+ password are requested.
750
+
751
+ Guest accounts approaching expiration receive a warning.
752
+ Expired guest accounts can be removed with:
753
+
754
+ bin/rails b4um:guests:cleanup
755
+
756
+ Run the cleanup task regularly in production to remove
757
+ expired guest accounts.
758
+
759
+ Run bin/rails db:migrate after installing guest access.
760
+
761
+
600
762
  ACTIVE STORAGE / IMAGES
601
763
 
602
764
  Image attachments require Active Storage.
@@ -33,6 +33,39 @@ module B4um
33
33
  type: :string,
34
34
  desc: "Renders a field as radio buttons. Example: condition:new=Neu,used=Gebraucht"
35
35
 
36
+ class_option :required,
37
+ type: :string,
38
+ desc: "Adds presence validation to fields. Example: name,email"
39
+
40
+ def initialize(args, options, config)
41
+ super
42
+
43
+ validate_required_fields
44
+ end
45
+
46
+ no_tasks do
47
+ def validate_required_fields
48
+ return if required_fields.empty?
49
+
50
+ attribute_names = attributes.map(&:name)
51
+
52
+ unknown_fields = required_fields - attribute_names
53
+
54
+ return if unknown_fields.empty?
55
+
56
+ raise Thor::Error,
57
+ "Unknown required field: #{unknown_fields.first}"
58
+ end
59
+
60
+ def required_fields
61
+ options[:required]
62
+ .to_s
63
+ .split(",")
64
+ .map(&:strip)
65
+ .reject(&:empty?)
66
+ end
67
+ end
68
+
36
69
  def validate_editor_options
37
70
  configurations = {
38
71
  select: select_configuration,
@@ -147,6 +180,23 @@ module B4um
147
180
  )
148
181
  end
149
182
 
183
+ def add_required_validations
184
+ return if required_fields.empty?
185
+
186
+ validation = required_fields
187
+ .map { |field| ":#{field}" }
188
+ .join(", ")
189
+
190
+ inject_into_class(
191
+ File.join(
192
+ "app/models",
193
+ "#{singular_table_name}.rb"
194
+ ),
195
+ class_name,
196
+ " validates #{validation}, presence: true\n"
197
+ )
198
+ end
199
+
150
200
  def add_secure_password
151
201
  return unless attributes.any? { |attribute| attribute.name == "password_digest" }
152
202