avocado 0.6.0 → 0.7.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -17
  3. data/Rakefile +0 -2
  4. data/app/controllers/avocado/affirmations_controller.rb +12 -8
  5. data/app/controllers/avocado/base_controller.rb +15 -6
  6. data/app/controllers/avocado/credentials_controller.rb +41 -0
  7. data/app/controllers/avocado/emails_controller.rb +5 -13
  8. data/app/controllers/avocado/events_controller.rb +3 -3
  9. data/app/controllers/avocado/passwords_controller.rb +4 -4
  10. data/app/controllers/avocado/recoveries_controller.rb +6 -35
  11. data/app/controllers/avocado/registrations_controller.rb +3 -10
  12. data/app/controllers/avocado/sessions_controller.rb +18 -10
  13. data/app/controllers/avocado/verifications_controller.rb +9 -13
  14. data/app/views/avocado/affirmations/_form.html.erb +4 -0
  15. data/app/views/avocado/affirmations/new.html.erb +1 -3
  16. data/app/views/avocado/{recoveries/edit.html.erb → credentials/_form.html.erb} +4 -13
  17. data/app/views/avocado/credentials/edit.html.erb +12 -0
  18. data/app/views/avocado/emails/_form.html.erb +8 -0
  19. data/app/views/avocado/emails/edit.html.erb +1 -6
  20. data/app/views/avocado/mailer/password_reset.text.erb +1 -1
  21. data/app/views/avocado/passwords/_form.html.erb +12 -0
  22. data/app/views/avocado/passwords/edit.html.erb +1 -9
  23. data/app/views/avocado/recoveries/_form.html.erb +4 -0
  24. data/app/views/avocado/recoveries/new.html.erb +1 -3
  25. data/app/views/avocado/registrations/_form.html.erb +12 -0
  26. data/app/views/avocado/registrations/new.html.erb +1 -12
  27. data/app/views/avocado/sessions/_form.html.erb +8 -0
  28. data/app/views/avocado/sessions/new.html.erb +1 -4
  29. data/config/locales/en.yml +45 -0
  30. data/config/routes/avocado.rb +2 -1
  31. data/config.ru +0 -2
  32. data/docs/USAGE.md +38 -29
  33. data/lib/avocado/authentication.rb +0 -2
  34. data/lib/avocado/current.rb +0 -2
  35. data/lib/avocado/engine.rb +1 -3
  36. data/lib/avocado/event.rb +0 -2
  37. data/lib/avocado/mailer.rb +0 -2
  38. data/lib/avocado/session.rb +0 -2
  39. data/lib/avocado/session_callbacks.rb +0 -2
  40. data/lib/avocado/user.rb +0 -2
  41. data/lib/avocado/user_callbacks.rb +0 -2
  42. data/lib/avocado/user_tokens.rb +0 -2
  43. data/lib/avocado/user_validations.rb +0 -2
  44. data/lib/avocado/version.rb +1 -3
  45. data/lib/avocado.rb +0 -2
  46. data/lib/generators/avocado/migrations/migrations_generator.rb +0 -2
  47. data/lib/generators/avocado/views/views_generator.rb +21 -0
  48. metadata +15 -6
  49. data/config/routes//360/237/245/221.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 393630ca933c51e34b00e2fe86bbcfb5caa4ea7764fc34882c21ef4f604bb938
4
- data.tar.gz: bfc5362c53ecee86c6369a1318c68fddf2bbd7e457287cb5b9c6b57970ded8aa
3
+ metadata.gz: e9ec8adfbc4563b5bbcddd5d5406963b4a9fe7d5a38765e35a86c9a3d35a09a0
4
+ data.tar.gz: 6db1dfa3a370661017533e44f2278a09daeece5c145267f05f0bced8943ea910
5
5
  SHA512:
6
- metadata.gz: efbe7bfe5b298b207e65e7958e3167be69d6612bd50e8a4a880483ec1a1d9e2c30d45fdae86e0516d34bb00442c0cd16416ad77a00a50ac31c707295d1538709
7
- data.tar.gz: b96ba925c445b78c92c560a01c395697dab736363a847c766e310879ded4ad58b97b7dae052a73ed1a267f7315bedd171d9f0097978760cb3de6a1e9b3c02969
6
+ metadata.gz: 9761662e0cf44fab571f6f6003aed2c0be2ae56424afbc1cc4b3ccb2f0d856a3e4d3b7a67c8a9e82b7d408977ebb30b4f1f61d9af0fd24db17c3fcb5c944cd89
7
+ data.tar.gz: a8021001d0f4864a36c981b2c5d18b71bca432f4f4b6fcde4ff6f07c38e7fc39f766dd39670041582cacb9a80526423eafd8156b5d8fc0580189679a1b67d144
data/CHANGELOG.md CHANGED
@@ -1,39 +1,78 @@
1
+ # Changelog
2
+
1
3
  ## [Unreleased]
2
4
 
5
+ ## [0.7.0] - 2023-08-10
6
+
7
+ ### Added
8
+
9
+ - An `avocado:views` generator copies engine views into application
10
+
11
+ ### Changed
12
+
13
+ - Flash messages for controllers moved to i18n yml
14
+ - View forms updated to use `_form` partials
15
+ - Move the reset phase of password recovery to a `Credentials` controller
16
+
3
17
  ## [0.6.0] - 2023-07-25
4
18
 
5
- - Change affirmations and verifications to require user action
6
- - Use session token instead of id for signed cookie value
7
- - Add migration generators
19
+ ### Added
20
+
21
+ - Migration generator
22
+
23
+ ### Changed
24
+
25
+ - Affirmation and Verification paths require user action
26
+ - Use session `token` instead of `id` for signed cookie value
8
27
 
9
28
  ## [0.5.0] - 2023-07-21
10
29
 
11
- - Add controller for "passwordless" email-link sign-in
12
- - Add event class to log user auth events
13
- - Add user-facing email and password edit pages
14
- - Add various event logging callbacks
15
- - Sign out all non current sessions when password changes
30
+ ### Added
31
+
32
+ - Controller for "passwordless" email-link sign-in
33
+ - `Event` class to log user auth events
34
+ - User-facing email and password edit pages
35
+ - Misc event logging callbacks
36
+
37
+ ### Changed
38
+
39
+ - Sign out all non-current sessions when password changes
16
40
 
17
41
  ## [0.4.0] - 2023-07-19
18
42
 
43
+ ### Added
44
+
45
+ - Controllers for signing up, signing in, password reset and email verification
46
+
47
+ ### Changed
48
+
19
49
  - Convert the `Avocado::Mailer` module into a class
20
- - Add controllers for signing up, signing in, password reset and email
21
- verification
22
50
 
23
51
  ## [0.3.0] - 2023-07-17
24
52
 
25
- - Add an `Avocado::Mailer` which generates each of the signed ids
53
+ ### Added
54
+
55
+ - `Avocado::Mailer` which generates each of the signed ids
56
+
57
+ ### Changed
58
+
26
59
  - Rename `password_recovery` to `password_reset`
27
60
 
28
61
  ## [0.2.0] - 2023-07-15
29
62
 
30
- - Validate presence, uniqueness, and format on `email` attribute
31
- - Normalize email value during save
32
- - Validate password format and length
33
- - Include a token generator for password recovery
63
+ ### Added
64
+
65
+ - Validations for presence, uniqueness, and format on `email` attribute
66
+ - Normalizer for email value during save
67
+ - Validations on password format and length
68
+ - Token generator for password recovery
69
+
70
+ ### Changed
71
+
34
72
  - Rename `Avocado::UserConcern` to `Avocado::User`
35
73
 
36
74
  ## [0.1.0] - 2023-07-14
37
75
 
38
- - Initial release
39
- - Add `Avocado::UserConcern` which calls `has_secure_password`
76
+ ### Added
77
+
78
+ - `Avocado::UserConcern` which calls `has_secure_password`
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "bundler/gem_tasks"
4
2
  require "rspec/core/rake_task"
5
3
 
@@ -1,18 +1,19 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class AffirmationsController < BaseController
5
3
  skip_before_action :authenticate
6
4
 
7
- before_action :set_user, only: %i[edit update]
8
- before_action :verify_user, only: :create
5
+ before_action :set_user,
6
+ only: %i[edit update]
7
+ before_action :verify_user,
8
+ only: :create
9
9
 
10
10
  def new
11
11
  end
12
12
 
13
13
  def create
14
14
  send_affirmation_email
15
- redirect_to new_session_path, notice: "Check your email for sign in instructions"
15
+ redirect_to new_session_path,
16
+ notice: t(".success")
16
17
  end
17
18
 
18
19
  def edit
@@ -20,7 +21,8 @@ module Avocado
20
21
 
21
22
  def update
22
23
  sign_in(@user)
23
- redirect_to(root_path, notice: "Signed in successfully")
24
+ redirect_to root_path,
25
+ notice: t(".success")
24
26
  end
25
27
 
26
28
  private
@@ -28,7 +30,8 @@ module Avocado
28
30
  def set_user
29
31
  @user = user_from_signed_affirmation_token
30
32
  rescue ActiveSupport::MessageVerifier::InvalidSignature
31
- redirect_to new_affirmation_path, alert: "That sign in link is invalid"
33
+ redirect_to new_affirmation_path,
34
+ alert: t(".errors.invalid_token")
32
35
  end
33
36
 
34
37
  def user_from_signed_affirmation_token
@@ -37,7 +40,8 @@ module Avocado
37
40
 
38
41
  def verify_user
39
42
  unless requested_verified_user
40
- redirect_to new_affirmation_path, alert: "You can't sign in until you verify your email"
43
+ redirect_to new_affirmation_path,
44
+ alert: t(".errors.unverified_email")
41
45
  end
42
46
  end
43
47
 
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class BaseController < ApplicationController
5
3
  FINDER_PARAMETERS = %i[email]
@@ -8,16 +6,20 @@ module Avocado
8
6
 
9
7
  def verify_password_challenge
10
8
  unless current_user.authenticate(params_password_challenge)
11
- redirect_back alert: "Password challenge failed.", fallback_location: root_path
9
+ redirect_back fallback_location: root_path,
10
+ alert: t("avocado.filters.invalid_password_challenge")
12
11
  end
13
12
  end
14
13
 
15
14
  def params_password_challenge
16
- params.dig(:user, :password_challenge)
15
+ params
16
+ .dig(:user, :password_challenge)
17
17
  end
18
18
 
19
19
  def requested_verified_user
20
- ::User.verified.find_by(email: finder_parameters[:email])
20
+ ::User
21
+ .verified
22
+ .find_by(email: finder_parameters[:email])
21
23
  end
22
24
 
23
25
  def finder_parameters
@@ -27,7 +29,14 @@ module Avocado
27
29
  end
28
30
 
29
31
  def mailer_for(user)
30
- Avocado::Mailer.with(user: user)
32
+ Avocado::Mailer
33
+ .with(user: user)
34
+ end
35
+
36
+ def send_email_verification(user)
37
+ mailer_for(user)
38
+ .email_verification
39
+ .deliver_later
31
40
  end
32
41
  end
33
42
  end
@@ -0,0 +1,41 @@
1
+ module Avocado
2
+ class CredentialsController < BaseController
3
+ UPDATE_PARAMETERS = %i[password password_confirmation]
4
+
5
+ skip_before_action :authenticate
6
+
7
+ before_action :set_user
8
+
9
+ def edit
10
+ end
11
+
12
+ def update
13
+ if @user.update(update_parameters)
14
+ redirect_to new_session_path,
15
+ notice: t(".success")
16
+ else
17
+ render :edit, status: :unprocessable_entity
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def set_user
24
+ @user = user_from_signed_password_reset_token
25
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
26
+ redirect_to new_recovery_path,
27
+ alert: t(".errors.invalid_token")
28
+ end
29
+
30
+ def user_from_signed_password_reset_token
31
+ ::User
32
+ .find_by_token_for!(:password_reset, params[:id])
33
+ end
34
+
35
+ def update_parameters
36
+ params
37
+ .require(:user)
38
+ .permit(UPDATE_PARAMETERS)
39
+ end
40
+ end
41
+ end
@@ -1,11 +1,10 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class EmailsController < BaseController
5
3
  UPDATE_PARAMETERS = %i[email]
6
4
 
7
5
  before_action :set_user
8
- before_action :verify_password_challenge, only: :update
6
+ before_action :verify_password_challenge,
7
+ only: :update
9
8
 
10
9
  def edit
11
10
  end
@@ -13,6 +12,8 @@ module Avocado
13
12
  def update
14
13
  if @user.update(update_parameters)
15
14
  process_email_update
15
+ redirect_to root_path,
16
+ notice: t(".success")
16
17
  else
17
18
  render :edit, status: :unprocessable_entity
18
19
  end
@@ -32,17 +33,8 @@ module Avocado
32
33
 
33
34
  def process_email_update
34
35
  if @user.email_previously_changed?
35
- resend_email_verification
36
- redirect_to root_path, notice: "Your email has been changed"
37
- else
38
- redirect_to root_path
36
+ send_email_verification(@user)
39
37
  end
40
38
  end
41
-
42
- def resend_email_verification
43
- mailer_for(@user)
44
- .email_verification
45
- .deliver_later
46
- end
47
39
  end
48
40
  end
@@ -1,9 +1,9 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class EventsController < BaseController
5
3
  def index
6
- @events = current_user.events.newest_first
4
+ @events = current_user
5
+ .events
6
+ .newest_first
7
7
  end
8
8
  end
9
9
  end
@@ -1,18 +1,18 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class PasswordsController < BaseController
5
3
  UPDATE_PARAMETERS = %i[password password_confirmation password_challenge]
6
4
 
7
5
  before_action :set_user
8
- before_action :verify_password_challenge, only: :update
6
+ before_action :verify_password_challenge,
7
+ only: :update
9
8
 
10
9
  def edit
11
10
  end
12
11
 
13
12
  def update
14
13
  if @user.update(update_parameters)
15
- redirect_to root_path, notice: "Your password has been changed"
14
+ redirect_to root_path,
15
+ notice: t(".success")
16
16
  else
17
17
  render :edit, status: :unprocessable_entity
18
18
  end
@@ -1,57 +1,28 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class RecoveriesController < BaseController
5
- UPDATE_PARAMETERS = %i[password password_confirmation]
6
-
7
3
  skip_before_action :authenticate
8
4
 
9
- before_action :set_user, only: %i[edit update]
10
- before_action :verify_user, only: :create
5
+ before_action :verify_user,
6
+ only: :create
11
7
 
12
8
  def new
13
9
  end
14
10
 
15
11
  def create
16
12
  send_password_reset_email
17
- redirect_to new_session_path, notice: "Check your email for reset instructions."
18
- end
19
-
20
- def edit
21
- end
22
-
23
- def update
24
- if @user.update(update_parameters)
25
- redirect_to new_session_path, notice: "Password reset successfully. Please sign in."
26
- else
27
- render :edit, status: :unprocessable_entity
28
- end
13
+ redirect_to new_session_path,
14
+ notice: t(".success")
29
15
  end
30
16
 
31
17
  private
32
18
 
33
- def set_user
34
- @user = user_from_signed_password_reset_token
35
- rescue ActiveSupport::MessageVerifier::InvalidSignature
36
- redirect_to new_recovery_path, alert: "Password reset link is invalid."
37
- end
38
-
39
- def user_from_signed_password_reset_token
40
- ::User.find_by_token_for!(:password_reset, params[:id])
41
- end
42
-
43
19
  def verify_user
44
20
  unless requested_verified_user
45
- redirect_to new_recovery_path, alert: "Verify email first before resetting password."
21
+ redirect_to new_recovery_path,
22
+ alert: t(".errors.unverified_email")
46
23
  end
47
24
  end
48
25
 
49
- def update_parameters
50
- params
51
- .require(:user)
52
- .permit(UPDATE_PARAMETERS)
53
- end
54
-
55
26
  def send_password_reset_email
56
27
  mailer_for(requested_verified_user)
57
28
  .password_reset
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class RegistrationsController < BaseController
5
3
  INITIALIZATION_PARAMETERS = %i[email password password_confirmation]
@@ -16,8 +14,9 @@ module Avocado
16
14
  if @user.save
17
15
  sign_in(@user)
18
16
 
19
- send_email_verification
20
- redirect_to root_path, notice: "Registration successful"
17
+ send_email_verification(@user)
18
+ redirect_to root_path,
19
+ notice: t(".success")
21
20
  else
22
21
  render :new, status: :unprocessable_entity
23
22
  end
@@ -30,11 +29,5 @@ module Avocado
30
29
  .require(:user)
31
30
  .permit(INITIALIZATION_PARAMETERS)
32
31
  end
33
-
34
- def send_email_verification
35
- mailer_for(@user)
36
- .email_verification
37
- .deliver_later
38
- end
39
32
  end
40
33
  end
@@ -1,19 +1,21 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class SessionsController < BaseController
5
3
  AUTHENTICATION_PARAMETERS = %i[email password]
6
4
 
7
- skip_before_action :authenticate, only: %i[new create]
5
+ skip_before_action :authenticate,
6
+ only: %i[new create]
8
7
 
9
8
  with_options only: :create do
10
9
  before_action :verify_authentication_attempt
11
10
  end
12
11
 
13
- before_action :set_session, only: :destroy
12
+ before_action :set_session,
13
+ only: :destroy
14
14
 
15
15
  def index
16
- @sessions = current_user.sessions.newest_first
16
+ @sessions = current_user
17
+ .sessions
18
+ .newest_first
17
19
  end
18
20
 
19
21
  def new
@@ -23,12 +25,14 @@ module Avocado
23
25
  def create
24
26
  sign_in(authenticated_user)
25
27
 
26
- redirect_to root_path, notice: "Session created"
28
+ redirect_to root_path,
29
+ notice: t(".success")
27
30
  end
28
31
 
29
32
  def destroy
30
33
  @session.destroy
31
- redirect_to sessions_path, notice: "Session destroyed"
34
+ redirect_to sessions_path,
35
+ notice: t(".success")
32
36
  end
33
37
 
34
38
  private
@@ -41,17 +45,21 @@ module Avocado
41
45
  end
42
46
 
43
47
  def authenticated_user
44
- @_authenticated_user ||= ::User.authenticate_by(authentication_parameters)
48
+ @_authenticated_user ||= ::User
49
+ .authenticate_by(authentication_parameters)
45
50
  end
46
51
 
47
52
  def verify_authentication_attempt
48
53
  if authenticated_user.blank?
49
- redirect_to new_session_path, alert: "Authentication failed"
54
+ redirect_to new_session_path,
55
+ alert: t(".errors.authentication")
50
56
  end
51
57
  end
52
58
 
53
59
  def set_session
54
- @session = current_user.sessions.find(params[:id])
60
+ @session = current_user
61
+ .sessions
62
+ .find(params[:id])
55
63
  end
56
64
  end
57
65
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class VerificationsController < BaseController
5
3
  with_options only: %i[edit update] do
@@ -12,12 +10,14 @@ module Avocado
12
10
 
13
11
  def update
14
12
  @user.update! verified: true
15
- redirect_to root_path, notice: "Email address verified."
13
+ redirect_to root_path,
14
+ notice: t(".success")
16
15
  end
17
16
 
18
17
  def create
19
- send_email_verification
20
- redirect_to root_path, notice: "Verification email sent to your address."
18
+ send_email_verification(current_user)
19
+ redirect_to root_path,
20
+ notice: t(".success")
21
21
  end
22
22
 
23
23
  private
@@ -25,17 +25,13 @@ module Avocado
25
25
  def set_user
26
26
  @user = user_from_signed_email_verification_token
27
27
  rescue ActiveSupport::MessageVerifier::InvalidSignature
28
- redirect_to root_path, alert: "Email verification link is invalid."
28
+ redirect_to root_path,
29
+ alert: t(".errors.invalid_token")
29
30
  end
30
31
 
31
32
  def user_from_signed_email_verification_token
32
- ::User.find_by_token_for!(:email_verification, params[:id])
33
- end
34
-
35
- def send_email_verification
36
- mailer_for(current_user)
37
- .email_verification
38
- .deliver_later
33
+ ::User
34
+ .find_by_token_for!(:email_verification, params[:id])
39
35
  end
40
36
  end
41
37
  end
@@ -0,0 +1,4 @@
1
+ <div>
2
+ <%= form.label :email %>
3
+ <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
4
+ </div>
@@ -7,8 +7,6 @@
7
7
  </p>
8
8
 
9
9
  <%= form_with url: affirmations_path, scope: :user do |form| %>
10
- <%= form.label :email %>
11
- <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
12
-
10
+ <%= render form %>
13
11
  <%= form.button "Submit", name: nil %>
14
12
  <% end -%>
@@ -1,17 +1,8 @@
1
- <h2>
2
- Change your password
3
- </h2>
4
-
5
- <p>
6
- <%= link_to "Sign in to your account" %>
7
- </p>
8
-
9
- <%= form_with model: @user, url: recovery_path(id: params[:id]), method: :patch do |form| %>
1
+ <div>
10
2
  <%= form.label :password %>
11
3
  <%= form.password_field :password, autocomplete: "new-password", required: true %>
12
-
4
+ </div>
5
+ <div>
13
6
  <%= form.label :password_confirmation %>
14
7
  <%= form.password_field :password_confirmation, autocomplete: "new-password", required: true %>
15
-
16
- <%= form.button "Submit", name: nil %>
17
- <% end %>
8
+ </div>
@@ -0,0 +1,12 @@
1
+ <h2>
2
+ Change your password
3
+ </h2>
4
+
5
+ <p>
6
+ <%= link_to "Sign in to your account" %>
7
+ </p>
8
+
9
+ <%= form_with model: @user, url: credential_path(id: params[:id]), method: :patch do |form| %>
10
+ <%= render form %>
11
+ <%= form.button "Submit", name: nil %>
12
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <div>
2
+ <%= form.label :email %>
3
+ <%= form.email_field :email, autocomplete: "email", required: true %>
4
+ </div>
5
+ <div>
6
+ <%= form.label :password_challenge, "Current password" %>
7
+ <%= form.password_field :password_challenge, autocomplete: "current-password", required: true %>
8
+ </div>
@@ -10,11 +10,6 @@
10
10
  <% end %>
11
11
 
12
12
  <%= form_with model: @user, url: email_path, method: :patch do |form| %>
13
- <%= form.label :email %>
14
- <%= form.email_field :email, autocomplete: "email", required: true %>
15
-
16
- <%= form.label :password_challenge, "Current password" %>
17
- <%= form.password_field :password_challenge, autocomplete: "current-password", required: true %>
18
-
13
+ <%= render form %>
19
14
  <%= form.button "Submit", name: nil %>
20
15
  <% end %>
@@ -1,3 +1,3 @@
1
1
  Reset your password by following this link:
2
2
 
3
- <%= edit_recovery_url(id: @signed_id) %>
3
+ <%= edit_credential_url(id: @signed_id) %>
@@ -0,0 +1,12 @@
1
+ <div>
2
+ <%= form.label :password_challenge, "Current password" %>
3
+ <%= form.password_field :password_challenge, autocomplete: "current-password", required: true %>
4
+ </div>
5
+ <div>
6
+ <%= form.label :password %>
7
+ <%= form.password_field :password, autocomplete: "new-password", required: true %>
8
+ </div>
9
+ <div>
10
+ <%= form.label :password_confirmation %>
11
+ <%= form.password_field :password_confirmation, autocomplete: "new-password", required: true %>
12
+ </div>
@@ -3,14 +3,6 @@
3
3
  </h2>
4
4
 
5
5
  <%= form_with model: @user, url: password_path, method: :patch do |form| %>
6
- <%= form.label :password_challenge, "Current password" %>
7
- <%= form.password_field :password_challenge, autocomplete: "current-password", required: true %>
8
-
9
- <%= form.label :password %>
10
- <%= form.password_field :password, autocomplete: "new-password", required: true %>
11
-
12
- <%= form.label :password_confirmation %>
13
- <%= form.password_field :password_confirmation, autocomplete: "new-password", required: true %>
14
-
6
+ <%= render form %>
15
7
  <%= form.button "Submit", name: nil %>
16
8
  <% end %>
@@ -0,0 +1,4 @@
1
+ <div>
2
+ <%= form.label :email %>
3
+ <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
4
+ </div>
@@ -7,8 +7,6 @@
7
7
  </p>
8
8
 
9
9
  <%= form_with url: recoveries_path, scope: :user do |form| %>
10
- <%= form.label :email %>
11
- <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
12
-
10
+ <%= render form %>
13
11
  <%= form.button "Submit", name: nil %>
14
12
  <% end -%>
@@ -0,0 +1,12 @@
1
+ <div>
2
+ <%= form.label :email %>
3
+ <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
4
+ </div>
5
+ <div>
6
+ <%= form.label :password %>
7
+ <%= form.password_field :password, autocomplete: "new-password", required: true %>
8
+ </div>
9
+ <div>
10
+ <%= form.label :password_confirmation %>
11
+ <%= form.password_field :password_confirmation, autocomplete: "new-password", required: true %>
12
+ </div>
@@ -7,17 +7,6 @@
7
7
  </p>
8
8
 
9
9
  <%= form_with model: @user, url: registrations_path do |form| %>
10
- <div>
11
- <%= form.label :email %>
12
- <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
13
- </div>
14
- <div>
15
- <%= form.label :password %>
16
- <%= form.password_field :password, autocomplete: "new-password", required: true %>
17
- </div>
18
- <div>
19
- <%= form.label :password_confirmation %>
20
- <%= form.password_field :password_confirmation, autocomplete: "new-password", required: true %>
21
- </div>
10
+ <%= render form %>
22
11
  <%= form.button "Submit", name: nil %>
23
12
  <% end %>
@@ -0,0 +1,8 @@
1
+ <div>
2
+ <%= form.label :email %>
3
+ <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
4
+ </div>
5
+ <div>
6
+ <%= form.label :password %>
7
+ <%= form.password_field :password, autocomplete: "current-password", required: true %>
8
+ </div>
@@ -7,9 +7,6 @@
7
7
  </p>
8
8
 
9
9
  <%= form_with model: @session do |form| %>
10
- <%= form.label :email %>
11
- <%= form.email_field :email, autofocus: true, autocomplete: "email", required: true %>
12
- <%= form.label :password %>
13
- <%= form.password_field :password, autocomplete: "current-password", required: true %>
10
+ <%= render form %>
14
11
  <%= form.button "Submit", name: nil %>
15
12
  <% end -%>
@@ -0,0 +1,45 @@
1
+ en:
2
+ avocado:
3
+ affirmations:
4
+ create:
5
+ success: Check email for sign in instructions.
6
+ errors:
7
+ invalid_token: Sign in link is invalid.
8
+ unverified_email: Verify email first before signing in.
9
+ update:
10
+ success: Signed in successfully.
11
+ credentials:
12
+ update:
13
+ success: Password reset successfully.
14
+ errors:
15
+ invalid_token: Password reset link is invalid.
16
+ emails:
17
+ update:
18
+ success: Email has been changed.
19
+ filters:
20
+ invalid_password_challenge: Password challenge failed.
21
+ passwords:
22
+ update:
23
+ success: Password has been changed.
24
+ recoveries:
25
+ create:
26
+ success: Check email for reset instructions.
27
+ errors:
28
+ unverified_email: Verify email first before resetting password.
29
+ registrations:
30
+ create:
31
+ success: Registration successful.
32
+ sessions:
33
+ create:
34
+ success: Session created.
35
+ destroy:
36
+ success: Session destroyed.
37
+ errors:
38
+ authentication: Authentication failed.
39
+ verifications:
40
+ create:
41
+ success: Verification email sent.
42
+ errors:
43
+ invalid_token: Email verification link is invalid.
44
+ update:
45
+ success: Email address verified.
@@ -2,8 +2,9 @@ scope module: :avocado do
2
2
  resource :email, only: %i[edit update]
3
3
  resource :password, only: %i[edit update]
4
4
  resources :affirmations, only: %i[new create edit update]
5
+ resources :credentials, only: %i[edit update]
5
6
  resources :events, only: %i[index]
6
- resources :recoveries, only: %i[new create edit update]
7
+ resources :recoveries, only: %i[new create]
7
8
  resources :registrations, only: %i[new create]
8
9
  resources :sessions, only: %i[index new create destroy]
9
10
  resources :verifications, only: %i[create edit update]
data/config.ru CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "rubygems"
4
2
  require "bundler"
5
3
 
data/docs/USAGE.md CHANGED
@@ -8,8 +8,8 @@ scenarios, and can be subclassed and overridden for special cases.
8
8
  ## Requirements
9
9
 
10
10
  Apps must be running Rails 7.1 or newer. The 🥑 gem uses features like
11
- `authenticate_by`, `has_secure_password`, `generates_token_for`, and
12
- `normalizes` which don't exist in earlier versions.
11
+ `authenticate_by`, the `password_challenge` feature of `has_secure_password`,
12
+ `generates_token_for`, and `normalizes` which don't exist in earlier versions.
13
13
 
14
14
  The database schema must have columns that match the `users`, `sessions`, and
15
15
  `events` tables from the [demo app schema]. More columns in each table are
@@ -21,9 +21,12 @@ Run `bin/rails g avocado:migrations` to generate migrations for the tables.
21
21
 
22
22
  The application must also have:
23
23
 
24
- - An `ApplicationController` base controller class
25
- - An `ApplicationMailer` base mailer class
26
- - A `root_path` method (typically generated by application routes)
24
+ - An `ApplicationController` base controller class for the controllers to
25
+ inherit from.
26
+ - An `ApplicationMailer` base mailer class which sets a layout and default
27
+ `from` value.
28
+ - A `root_path` route helper method (typically generated by routes configured in
29
+ the application).
27
30
 
28
31
  ## Usage
29
32
 
@@ -60,11 +63,11 @@ end
60
63
 
61
64
  ### Routes
62
65
 
63
- The 🥑 gem does not add any routes to the application when initialized. To hook
64
- up the controllers to routes, they must be added to the `config/routes.rb` of
65
- the application. It's possible to add all of the routes, or just a subset.
66
+ By defalt, 🥑 does not add any routes to the application when initialized. To
67
+ hook up the controllers to routes, they must be added to the `config/routes.rb`
68
+ of the application. It's possible to add all of the routes, or just a subset.
66
69
 
67
- Example that defines a root route and also pulls in every feature route:
70
+ This example defines a `root` route and also pulls in every feature route:
68
71
 
69
72
  ```ruby
70
73
  Rails.application.routes.draw do
@@ -73,12 +76,12 @@ Rails.application.routes.draw do
73
76
  end
74
77
  ```
75
78
 
76
- Example that adds only the sign-up, sign-in, and sign-out actions:
79
+ This example defines a `root` route and adds only a subset of feature routes:
77
80
 
78
81
  ```ruby
79
82
  Rails.application.routes.draw do
80
83
  root to: "records#index"
81
- scope module: :avocado do
84
+ scope module: 🥑 do
82
85
  resources :registrations, only: %i[new create]
83
86
  resources :sessions, only: %i[new create destroy]
84
87
  end
@@ -94,18 +97,19 @@ app initialization.
94
97
 
95
98
  These external (unauthenticated) features are available:
96
99
 
97
- - `Registrations` -- Fill out new form and create users
98
- - `Sessions` -- Sign in and sign out features
99
- - `Recoveries` -- Trigger password reset, click link, confirm
100
- - `Verifications` -- Email confirmation on account creation or email change
101
- - `Affirmations` -- Provides a "passwordless" auth via emailed link
100
+ - `Registrations` -- Fill out new user form and create users
101
+ - `Sessions` -- Sign in and sign out
102
+ - `Recoveries` -- Triggers password reset via emailed expiring link
103
+ - `Credentials` -- Use expiring token link from recovery to update password
104
+ - `Verifications` -- Confirm email address on account creation or email change
105
+ - `Affirmations` -- "Passwordless" authentication via emailed expiring link
102
106
 
103
107
  These internal (authenticated) features are available:
104
108
 
105
- - `Sessions` -- List active sessions, click to destroy untrusted ones
106
- - `Events` -- List view of user activity audit log
107
- - `Passwords` -- Edit and update user password
108
- - `Emails` -- Edit and update user email
109
+ - `Sessions` -- Index view of active sessions, ability to destroy sessions
110
+ - `Events` -- Index view of the user activity audit log
111
+ - `Passwords` -- Edit and update a user password
112
+ - `Emails` -- Edit and update a user email
109
113
 
110
114
  Linking to any of these internal pages is optional. Apps can use them as-is,
111
115
  override their views, or even ignore them entirely and make local versions.
@@ -118,14 +122,15 @@ within `app/views/avocado/mailer/` to make this happen.
118
122
 
119
123
  ### Before actions
120
124
 
121
- There is an `authenticate` method installed as a default `before_action`. Any
122
- actions which do not need to be authenticated should disable this with
123
- `skip_before_action`.
125
+ There is an `authenticate` method installed as default controller behavior using
126
+ `before_action :authenticate`. Any actions which do not need to be authenticated
127
+ should disable this with `skip_before_action :authenticate`, or inherit from a
128
+ controller which performs the skip.
124
129
 
125
- There is a `set_current_request_details` method installed as a default
126
- `before_action` which takes some loggable request meta information (user agent,
127
- IP address) and sets its value in `Current` so that its accesible to code
128
- elsewhere in the 🥑 gem.
130
+ There is also a `set_current_request_details` method installed as a default
131
+ `before_action` which takes some loggable request meta information (User Agent,
132
+ IP Address) and sets their values in `Current` so that they are accesible to
133
+ code elsewhere in the 🥑 gem.
129
134
 
130
135
  ### Helpers
131
136
 
@@ -144,7 +149,11 @@ There is not any configuration. To override functionality:
144
149
 
145
150
  - Redefine a method created in one of the models by the included module
146
151
  - Subclass a controller and update the routing to go to the subclass
147
- - Place views in the app where avocado expects them to override the defaults
152
+ - Place views in the app where avocado expects (`app/views/avocado`) them to
153
+ override the defaults
154
+
155
+ There is an `avocado:views` generator which will copy all the views as a
156
+ starting point for further modification.
148
157
 
149
158
  ## Examples
150
159
 
@@ -152,4 +161,4 @@ There is a [demo app] used by the specs which has some example usage.
152
161
 
153
162
  [demo app schema]: https://github.com/tcuwp/avocado/blob/main/spec/internal/db/schema.rb
154
163
  [demo app]: https://github.com/tcuwp/avocado/blob/main/spec/internal
155
- [Rails Engine]: https://guides.rubyonrails.org/engines.html#what-are-engines-questionmark
164
+ [Rails Engine]: https://guides.rubyonrails.org/engines.html#what-are-engines-questionmark
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module Authentication
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class Current < ActiveSupport::CurrentAttributes
5
3
  attribute :session,
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "avocado"
4
2
  require "rails/engine"
5
3
 
@@ -7,7 +5,7 @@ module Avocado
7
5
  class Engine < Rails::Engine
8
6
  initializer :avocado_routing do
9
7
  ActiveSupport.on_load(:action_dispatch_request) do
10
- ActionDispatch::Routing::Mapper.define_method(:🥑) { :🥑 }
8
+ ActionDispatch::Routing::Mapper.define_method(:🥑) { :avocado }
11
9
  end
12
10
  end
13
11
  end
data/lib/avocado/event.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module Event
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  class Mailer < ApplicationMailer
5
3
  before_action :set_user
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module Session
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module SessionCallbacks
5
3
  extend ActiveSupport::Concern
data/lib/avocado/user.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module User
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module UserCallbacks
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module UserTokens
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
2
  module UserValidations
5
3
  extend ActiveSupport::Concern
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Avocado
4
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
5
3
  end
data/lib/avocado.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "active_support"
4
2
  require_relative "avocado/engine"
5
3
 
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "rails/generators/base"
4
2
  require "rails/generators/active_record"
5
3
 
@@ -0,0 +1,21 @@
1
+ require "rails/generators/base"
2
+
3
+ module Avocado
4
+ class ViewsGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../../../..", __dir__)
6
+
7
+ def create_views
8
+ directory source_directory, target_directory
9
+ end
10
+
11
+ private
12
+
13
+ def source_directory
14
+ "app/views/avocado"
15
+ end
16
+
17
+ def target_directory
18
+ Rails.root.join("app/views/avocado")
19
+ end
20
+ end
21
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: avocado
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Jankowski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-25 00:00:00.000000000 Z
11
+ date: 2023-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -40,7 +40,7 @@ dependencies:
40
40
  version: 7.1.0.alpha
41
41
  description:
42
42
  email:
43
- - matt@jankowski.online
43
+ - info@tcuwp.co
44
44
  executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
@@ -54,6 +54,7 @@ files:
54
54
  - Rakefile
55
55
  - app/controllers/avocado/affirmations_controller.rb
56
56
  - app/controllers/avocado/base_controller.rb
57
+ - app/controllers/avocado/credentials_controller.rb
57
58
  - app/controllers/avocado/emails_controller.rb
58
59
  - app/controllers/avocado/events_controller.rb
59
60
  - app/controllers/avocado/passwords_controller.rb
@@ -61,25 +62,32 @@ files:
61
62
  - app/controllers/avocado/registrations_controller.rb
62
63
  - app/controllers/avocado/sessions_controller.rb
63
64
  - app/controllers/avocado/verifications_controller.rb
65
+ - app/views/avocado/affirmations/_form.html.erb
64
66
  - app/views/avocado/affirmations/edit.html.erb
65
67
  - app/views/avocado/affirmations/new.html.erb
68
+ - app/views/avocado/credentials/_form.html.erb
69
+ - app/views/avocado/credentials/edit.html.erb
70
+ - app/views/avocado/emails/_form.html.erb
66
71
  - app/views/avocado/emails/edit.html.erb
67
72
  - app/views/avocado/events/_event.html.erb
68
73
  - app/views/avocado/events/index.html.erb
69
74
  - app/views/avocado/mailer/email_affirmation.text.erb
70
75
  - app/views/avocado/mailer/email_verification.text.erb
71
76
  - app/views/avocado/mailer/password_reset.text.erb
77
+ - app/views/avocado/passwords/_form.html.erb
72
78
  - app/views/avocado/passwords/edit.html.erb
73
- - app/views/avocado/recoveries/edit.html.erb
79
+ - app/views/avocado/recoveries/_form.html.erb
74
80
  - app/views/avocado/recoveries/new.html.erb
81
+ - app/views/avocado/registrations/_form.html.erb
75
82
  - app/views/avocado/registrations/new.html.erb
83
+ - app/views/avocado/sessions/_form.html.erb
76
84
  - app/views/avocado/sessions/_session.html.erb
77
85
  - app/views/avocado/sessions/index.html.erb
78
86
  - app/views/avocado/sessions/new.html.erb
79
87
  - app/views/avocado/verifications/edit.html.erb
80
88
  - config.ru
89
+ - config/locales/en.yml
81
90
  - config/routes/avocado.rb
82
- - "config/routes/\U0001F951.rb"
83
91
  - docs/USAGE.md
84
92
  - lib/avocado.rb
85
93
  - lib/avocado/authentication.rb
@@ -98,6 +106,7 @@ files:
98
106
  - lib/generators/avocado/migrations/templates/create_events.rb.tt
99
107
  - lib/generators/avocado/migrations/templates/create_sessions.rb.tt
100
108
  - lib/generators/avocado/migrations/templates/create_users.rb.tt
109
+ - lib/generators/avocado/views/views_generator.rb
101
110
  - sig/avocado.rbs
102
111
  homepage: https://github.com/tcuwp/avocado
103
112
  licenses:
@@ -121,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
130
  - !ruby/object:Gem::Version
122
131
  version: '0'
123
132
  requirements: []
124
- rubygems_version: 3.4.15
133
+ rubygems_version: 3.4.18
125
134
  signing_key:
126
135
  specification_version: 4
127
136
  summary: Simple authentication for Rails (7.1+) applications
@@ -1 +0,0 @@
1
- avocado.rb