From 9f43801032058d311cbc5f0ef3c4e363cb6a2c89 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 02:44:53 +0300 Subject: [PATCH 01/17] a10n --- .gitignore | 4 + Gemfile | 2 + Gemfile.lock | 16 + app/assets/stylesheets/style.scss | 4 + app/controllers/application_controller.rb | 13 + app/models/tweet_metric.rb | 15 +- app/models/user.rb | 32 ++ app/views/devise/confirmations/new.html.slim | 15 + .../mailer/confirmation_instructions.html.erb | 5 + .../devise/mailer/email_changed.html.erb | 7 + .../devise/mailer/password_change.html.erb | 3 + .../reset_password_instructions.html.erb | 8 + .../mailer/unlock_instructions.html.erb | 7 + app/views/devise/passwords/edit.html.slim | 23 ++ app/views/devise/passwords/new.html.slim | 15 + app/views/devise/registrations/edit.html.slim | 45 +++ app/views/devise/registrations/new.html.slim | 29 ++ app/views/devise/sessions/new.html.slim | 25 ++ .../devise/shared/_error_messages.html.slim | 9 + app/views/devise/shared/_links.html.slim | 25 ++ app/views/devise/unlocks/new.html.slim | 15 + app/views/layouts/devise.html.slim | 24 ++ app/views/shared/_navbar.html.slim | 30 +- config/credentials/development.yml.enc | 1 + .../production.yml.enc} | 0 config/credentials/test.key | 1 + config/initializers/devise.rb | 303 ++++++++++++++++++ config/locales/devise.en.yml | 65 ++++ config/routes.rb | 1 + db/data/20231007231119_create_admin_user.rb | 15 + db/data_schema.rb | 2 +- .../20231007213927_devise_create_users.rb | 17 + db/schema.rb | 11 +- spec/factories/tweet_metrics.rb | 15 +- spec/factories/users.rb | 20 ++ spec/models/tweet_metric_spec.rb | 15 +- spec/models/user_spec.rb | 20 ++ 37 files changed, 840 insertions(+), 17 deletions(-) create mode 100644 app/models/user.rb create mode 100644 app/views/devise/confirmations/new.html.slim create mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb create mode 100644 app/views/devise/mailer/email_changed.html.erb create mode 100644 app/views/devise/mailer/password_change.html.erb create mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/passwords/edit.html.slim create mode 100644 app/views/devise/passwords/new.html.slim create mode 100644 app/views/devise/registrations/edit.html.slim create mode 100644 app/views/devise/registrations/new.html.slim create mode 100644 app/views/devise/sessions/new.html.slim create mode 100644 app/views/devise/shared/_error_messages.html.slim create mode 100644 app/views/devise/shared/_links.html.slim create mode 100644 app/views/devise/unlocks/new.html.slim create mode 100644 app/views/layouts/devise.html.slim create mode 100644 config/credentials/development.yml.enc rename config/{credentials.yml.enc => credentials/production.yml.enc} (100%) create mode 100644 config/credentials/test.key create mode 100644 config/initializers/devise.rb create mode 100644 config/locales/devise.en.yml create mode 100644 db/data/20231007231119_create_admin_user.rb create mode 100644 db/migrate/20231007213927_devise_create_users.rb create mode 100644 spec/factories/users.rb create mode 100644 spec/models/user_spec.rb diff --git a/.gitignore b/.gitignore index f837556..af1e48f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,7 @@ !/app/assets/builds/.keep /node_modules + +/config/credentials/development.key + +/config/credentials/production.key diff --git a/Gemfile b/Gemfile index 79267be..2101171 100644 --- a/Gemfile +++ b/Gemfile @@ -32,8 +32,10 @@ gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby] gem "actionpack-action_caching", "~> 1.2" gem "awesome_print", "~> 1.9" +gem "cancancan", "~> 3.5" gem "chartkick", "~> 5.0" gem "data_migrate", "~> 9.2" +gem "devise", "~> 4.9" gem "draper", "~> 4.0" gem "groupdate", "~> 6.4" gem "pry", "~> 0.14.2" diff --git a/Gemfile.lock b/Gemfile.lock index 5ff96a4..0043a0f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -86,6 +86,7 @@ GEM ast (2.4.2) awesome_print (1.9.2) base64 (0.1.1) + bcrypt (3.1.19) better_errors (2.10.1) erubi (>= 1.0.0) rack (>= 0.9.0) @@ -95,6 +96,7 @@ GEM binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) builder (3.2.4) + cancancan (3.5.0) chartkick (5.0.4) coderay (1.1.3) concurrent-ruby (1.2.2) @@ -110,6 +112,12 @@ GEM irb (>= 1.5.0) reline (>= 0.3.1) debug_inspector (1.1.0) + devise (4.9.2) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0) + responders + warden (~> 1.2.3) diff-lcs (1.5.0) draper (4.0.2) actionpack (>= 5.0) @@ -173,6 +181,7 @@ GEM mini_portile2 (~> 2.8.2) racc (~> 1.4) ordinare (0.4.0) + orm_adapter (0.5.0) parallel (1.23.0) parser (3.2.2.4) ast (~> 2.4.1) @@ -236,6 +245,9 @@ GEM io-console (~> 0.5) request_store (1.5.1) rack (>= 1.4) + responders (3.1.0) + actionpack (>= 5.2) + railties (>= 5.2) rexml (3.2.6) rouge (4.1.3) rspec-core (3.12.2) @@ -317,6 +329,8 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) + warden (1.2.9) + rack (>= 2.0.9) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -337,10 +351,12 @@ DEPENDENCIES awesome_print (~> 1.9) better_errors (~> 2.10) binding_of_caller (~> 1.0) + cancancan (~> 3.5) chartkick (~> 5.0) cssbundling-rails data_migrate (~> 9.2) debug + devise (~> 4.9) draper (~> 4.0) factory_bot_rails (~> 6.2) ffaker (~> 2.23) diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss index b00e657..df1a01d 100644 --- a/app/assets/stylesheets/style.scss +++ b/app/assets/stylesheets/style.scss @@ -18,6 +18,10 @@ h1.rotated { font-size: 3rem; } +.hero.is-info .has-dark-text .title { + color: #000; +} + @media (max-width: 768px) { h1.rotated { transform: none; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d1..e91f867 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,15 @@ class ApplicationController < ActionController::Base + before_action :configure_permitted_parameters, if: :devise_controller? + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:username]) + devise_parameter_sanitizer.permit(:sign_in, keys: [:username]) + devise_parameter_sanitizer.permit(:account_update, keys: [:username]) + end + + def after_sign_in_path_for(resource) + tweets_path + end end diff --git a/app/models/tweet_metric.rb b/app/models/tweet_metric.rb index 1472ef6..599dd1c 100644 --- a/app/models/tweet_metric.rb +++ b/app/models/tweet_metric.rb @@ -3,14 +3,23 @@ # Table name: tweet_metrics # # id :bigint not null, primary key -# tweet_id :bigint not null +# bookmarks :integer default(0) +# likes :integer default(0) # replies :integer default(0) # reposts :integer default(0) -# likes :integer default(0) -# bookmarks :integer default(0) # views :integer default(0) # created_at :datetime not null # updated_at :datetime not null +# tweet_id :bigint not null +# +# Indexes +# +# index_tweet_metrics_on_tweet_id (tweet_id) +# index_tweet_metrics_on_tweet_id_and_created_at (tweet_id,created_at) UNIQUE +# +# Foreign Keys +# +# fk_rails_... (tweet_id => tweets.id) # class TweetMetric < ApplicationRecord belongs_to :tweet diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..7e85ba3 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,32 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint not null, primary key +# encrypted_password :string default(""), not null +# remember_created_at :datetime +# username :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_username (username) UNIQUE +# +class User < ApplicationRecord + devise :database_authenticatable, :registerable, + :rememberable, :validatable + validates :username, presence: true, uniqueness: {case_sensitive: false} + + def email_required? + false + end + + def email_changed? + false + end + + def will_save_change_to_email? + false + end +end diff --git a/app/views/devise/confirmations/new.html.slim b/app/views/devise/confirmations/new.html.slim new file mode 100644 index 0000000..01ba9fa --- /dev/null +++ b/app/views/devise/confirmations/new.html.slim @@ -0,0 +1,15 @@ +h2 Resend confirmation instructions + += form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| + = render "devise/shared/error_messages", resource: resource + + .field + = f.label :username, class: 'label' + .control + = f.text_field :username, autofocus: true, autocomplete: "username", class: 'input', value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.username) + + .field.is-grouped + .control + = f.submit "Resend confirmation instructions", class: 'button is-link' + += render "devise/shared/links" diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 0000000..dc55f64 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 0000000..32f4ba8 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 0000000..b41daf4 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..f667dc1 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..41e148b --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/devise/passwords/edit.html.slim b/app/views/devise/passwords/edit.html.slim new file mode 100644 index 0000000..fed2941 --- /dev/null +++ b/app/views/devise/passwords/edit.html.slim @@ -0,0 +1,23 @@ +h2.title Change your password + += form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| + = render "devise/shared/error_messages", resource: resource + = f.hidden_field :reset_password_token + + .field + = f.label :password, "New password", class: 'label' + - if @minimum_password_length + em (#{@minimum_password_length} characters minimum) + .control + = f.password_field :password, autofocus: true, autocomplete: "new-password", class: 'input' + + .field + = f.label :password_confirmation, "Confirm new password", class: 'label' + .control + = f.password_field :password_confirmation, autocomplete: "new-password", class: 'input' + + .field.is-grouped + .control + = f.submit "Change my password", class: 'button is-link' + += render "devise/shared/links" diff --git a/app/views/devise/passwords/new.html.slim b/app/views/devise/passwords/new.html.slim new file mode 100644 index 0000000..8250ee1 --- /dev/null +++ b/app/views/devise/passwords/new.html.slim @@ -0,0 +1,15 @@ +h2.title Forgot your password? + += form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| + = render "devise/shared/error_messages", resource: resource + + .field + = f.label :username, class: 'label' + .control + = f.text_field :username, autofocus: true, autocomplete: "username", class: 'input' + + .field.is-grouped + .control + = f.submit "Send me reset password instructions", class: 'button is-link' + += render "devise/shared/links" diff --git a/app/views/devise/registrations/edit.html.slim b/app/views/devise/registrations/edit.html.slim new file mode 100644 index 0000000..2f8d32a --- /dev/null +++ b/app/views/devise/registrations/edit.html.slim @@ -0,0 +1,45 @@ +h2.title Edit #{resource_name.to_s.humanize} + += form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| + = render "devise/shared/error_messages", resource: resource + + .field + = f.label :email, class: 'label' + .control + = f.email_field :email, autofocus: true, autocomplete: "email", class: 'input' + + - if devise_mapping.confirmable? && resource.pending_reconfirmation? + .notification + | Currently waiting confirmation for: #{resource.unconfirmed_email} + + .field + = f.label :password, class: 'label' + i (leave blank if you don't want to change it) + .control + = f.password_field :password, autocomplete: "new-password", class: 'input' + - if @minimum_password_length + br + em #{@minimum_password_length} characters minimum + + .field + = f.label :password_confirmation, class: 'label' + .control + = f.password_field :password_confirmation, autocomplete: "new-password", class: 'input' + + .field + = f.label :current_password, class: 'label' + i (we need your current password to confirm your changes) + .control + = f.password_field :current_password, autocomplete: "current-password", class: 'input' + + .field.is-grouped + .control + = f.submit "Update", class: 'button is-link' + +h3 Cancel my account + +.notification + | Unhappy? + = button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete, class: 'button is-danger' + += link_to "Back", :back, class: 'button is-light' diff --git a/app/views/devise/registrations/new.html.slim b/app/views/devise/registrations/new.html.slim new file mode 100644 index 0000000..5e00d16 --- /dev/null +++ b/app/views/devise/registrations/new.html.slim @@ -0,0 +1,29 @@ +h2.title Sign up + += form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| + = render "devise/shared/error_messages", resource: resource + + .field + = f.label :username, class: 'label' + .control + = f.text_field :username, autofocus: true, autocomplete: "username", class: 'input' + + .field + = f.label :password, class: 'label' + - if @minimum_password_length + em + = @minimum_password_length + | characters minimum + .control + = f.password_field :password, autocomplete: "new-password", class: 'input' + + .field + = f.label :password_confirmation, class: 'label' + .control + = f.password_field :password_confirmation, autocomplete: "new-password", class: 'input' + + .field.is-grouped + .control + = f.submit "Sign up", class: 'button is-link' + += render "devise/shared/links" diff --git a/app/views/devise/sessions/new.html.slim b/app/views/devise/sessions/new.html.slim new file mode 100644 index 0000000..3f0d127 --- /dev/null +++ b/app/views/devise/sessions/new.html.slim @@ -0,0 +1,25 @@ +h2.title Log in + += form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| + .field + = f.label :username, class: 'label' + .control + = f.text_field :username, autofocus: true, autocomplete: "username", class: 'input' + + .field + = f.label :password, class: 'label' + .control + = f.password_field :password, autocomplete: "current-password", class: 'input' + + - if devise_mapping.rememberable? + .field + .control + label.checkbox + = f.check_box :remember_me + | Remember me + + .field.is-grouped + .control + = f.submit "Log in", class: 'button is-link' + += render "devise/shared/links" diff --git a/app/views/devise/shared/_error_messages.html.slim b/app/views/devise/shared/_error_messages.html.slim new file mode 100644 index 0000000..36e0a40 --- /dev/null +++ b/app/views/devise/shared/_error_messages.html.slim @@ -0,0 +1,9 @@ +- if resource.errors.any? + #error_explanation data-turbo-cache="false" + h2 + = I18n.t("errors.messages.not_saved", + count: resource.errors.count, + resource: resource.class.model_name.human.downcase) + ul + - resource.errors.full_messages.each do |message| + li= message diff --git a/app/views/devise/shared/_links.html.slim b/app/views/devise/shared/_links.html.slim new file mode 100644 index 0000000..a0f6927 --- /dev/null +++ b/app/views/devise/shared/_links.html.slim @@ -0,0 +1,25 @@ +.nav.level + - if controller_name != 'sessions' + = link_to "Log in", new_session_path(resource_name), class: 'level-item' + br + + - if devise_mapping.registerable? && controller_name != 'registrations' + = link_to "Sign up", new_registration_path(resource_name), class: 'level-item' + br + + - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' + = link_to "Forgot your password?", new_password_path(resource_name), class: 'level-item' + br + + - if devise_mapping.confirmable? && controller_name != 'confirmations' + = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: 'level-item' + br + + - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' + = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: 'level-item' + br + + - if devise_mapping.omniauthable? + - resource_class.omniauth_providers.each do |provider| + = button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false }, class: 'button is-link level-item' + br diff --git a/app/views/devise/unlocks/new.html.slim b/app/views/devise/unlocks/new.html.slim new file mode 100644 index 0000000..354a560 --- /dev/null +++ b/app/views/devise/unlocks/new.html.slim @@ -0,0 +1,15 @@ +h2 Resend unlock instructions + += form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| + = render "devise/shared/error_messages", resource: resource + + .field + = f.label :email, class: 'label' + .control + = f.email_field :email, autofocus: true, autocomplete: "email", class: 'input' + + .field.is-grouped + .control + = f.submit "Resend unlock instructions", class: 'button is-link' + += render "devise/shared/links" diff --git a/app/views/layouts/devise.html.slim b/app/views/layouts/devise.html.slim new file mode 100644 index 0000000..d6383a9 --- /dev/null +++ b/app/views/layouts/devise.html.slim @@ -0,0 +1,24 @@ +doctype html +html + head + title XTracker + meta name="viewport" content="width=device-width,initial-scale=1" + = csrf_meta_tags + = csp_meta_tag + = stylesheet_link_tag 'application', 'data-turbo-track': 'reload' + = javascript_include_tag 'application', 'data-turbo-track': 'reload', defer: true + link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css" + link rel="preconnect" href="https://fonts.googleapis.com" + link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="crossorigin" + link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet" + script src="https://kit.fontawesome.com/08b1825a15.js" crossorigin="anonymous" + body + section.hero.is-info.is-fullheight + .hero-head + = render "shared/navbar" + .hero-body + .container + .columns.is-centered + .column.is-6 + .box.has-dark-text + == yield diff --git a/app/views/shared/_navbar.html.slim b/app/views/shared/_navbar.html.slim index f3ba378..a2616a5 100644 --- a/app/views/shared/_navbar.html.slim +++ b/app/views/shared/_navbar.html.slim @@ -6,10 +6,28 @@ header a.navbar-burger.burger data-target="navbarMenuHeroA" role="button" aria-label="menu" aria-expanded="false" span aria-hidden="true" span aria-hidden="true" - .navbar-menu id="navbarMenuHeroA" - .navbar-end - a.navbar-item href="/" - | Home + #navbarMenuHeroA.navbar-menu + .navbar-start + = link_to "Home", root_path, class: "navbar-item" = link_to "Tweets", tweets_path, class: "navbar-item" - / a.navbar-item href="/contact" - / | Contact + .navbar-end + .navbar-item + .buttons + - if user_signed_in? + = button_to destroy_user_session_path, method: :delete, class: "button is-info is-outlined is-inverted navbar-item" + span.icon + i.fas.fa-sign-out-alt aria-hidden="true" + span Sign out + / Optionally, you could add a link to the user's profile or settings + / = link_to edit_user_registration_path, class: 'navbar-item' do + / span.icon + / i.fas.fa-user-cog aria-hidden="true" + - else + = button_to new_user_session_path, method: :get, class: "button is-primary is-outlined is-inverted navbar-item mr-2" + span.icon + i.fas.fa-sign-in-alt aria-hidden="true" + span Log in + = button_to new_user_registration_path, method: :get, class: "button is-link is-outlined is-inverted navbar-item mr-2" + span.icon + i.fas.fa-user-plus aria-hidden="true" + span Sign up diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc new file mode 100644 index 0000000..20b82e7 --- /dev/null +++ b/config/credentials/development.yml.enc @@ -0,0 +1 @@ +zV9KXtcWS0Xn68LTNO5MSHGeSp6lCTGFf3ld+gvkmcpsq3UQQzYnFNxs3yU2DWApiEbKvWEwami9IOjxWUyu0oCuPzjqCK3U83qDu0eA5wVED/k4cbu7RqFrCJ8P91YzrDq42BH8Ho+oCmkj7wMEWpOOpOW/HYy6WdkZi7BliMKi6JhGw6fNFr5f8wku0SGyEshtUaXOAg7vQa5ZLLO61yK9LrHQw1bTZqIF4JQ6iuyu2Q04sRB3VC+jPr8gxkCdFpxP4BIHyIVEWDijOAgtRcUqgDulnp19xnXohQ/Igxfz87X8DJs/FIVwn9TYGUt/gI88ouEBka37hc9mFyEI4zcRClw5XBpYri4fV8hgfktd20tknhFtsZCsaovP/Vg3gbKRmwEKXV99X8jwHlccpzyrmXktW5d8RsqzRfOm3gcxzI50b9F804SD6s0RHfXFKDaBJufXlvfew+ISg8dS5YB9q+RpbL+X9w673hJ3Joyp7J9Jp+omKFIjSL7kxc+pfFFJl32EOfRuDfLxTwsrzPKx5e2ghMrO3l/oLx/i9yCZPFl2YjJGOrTVfeDYe8+gJ7gT/iG1uxa/aCP3pAC+yxrDWPJ8SbhAGJ92hy1/pxPpnDoeTzVec/4ryv2Q7cBO96kNus+Jh8Nk7o2we1o=--iqNPtOIYdydnVA+S--kGof11fRg4EwYSz13iLxMw== \ No newline at end of file diff --git a/config/credentials.yml.enc b/config/credentials/production.yml.enc similarity index 100% rename from config/credentials.yml.enc rename to config/credentials/production.yml.enc diff --git a/config/credentials/test.key b/config/credentials/test.key new file mode 100644 index 0000000..1b0eaaf --- /dev/null +++ b/config/credentials/test.key @@ -0,0 +1 @@ +a96a33fdff495ffd3f82bde545a3aa41 \ No newline at end of file diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 0000000..1bd6a7f --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,303 @@ +Devise.setup do |config| + # The secret key used by Devise. Devise uses this key to generate + # random tokens. Changing this key will render invalid all existing + # confirmation, reset password and unlock tokens in the database. + # Devise will use the `secret_key_base` as its `secret_key` + # by default. You can change it below and use your own secret key. + config.secret_key = Rails.application.credentials.devise.secret_key + + # ==> Controller configuration + # Configure the parent class to the devise controllers. + # config.parent_controller = 'DeviseController' + + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class + # with default "from" parameter. + # config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" + + # Configure the class responsible to send e-mails. + # config.mailer = 'Devise::Mailer' + + # Configure the parent class responsible to send e-mails. + # config.parent_mailer = 'ActionMailer::Base' + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require "devise/orm/active_record" + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + config.authentication_keys = [:username] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [:username] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [:username] + + # Tell if authentication through request.params is enabled. True by default. + # It can be set to an array that will enable params authentication only for the + # given strategies, for example, `config.params_authenticatable = [:database]` will + # enable it only for database (email + password) authentication. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Auth is enabled. False by default. + # It can be set to an array that will enable http authentication only for the + # given strategies, for example, `config.http_authenticatable = [:database]` will + # enable it only for database authentication. + # For API-only applications to support authentication "out-of-the-box", you will likely want to + # enable this with :database unless you are using a custom strategy. + # The supported strategies are: + # :database = Support basic authentication with authentication key + password + # config.http_authenticatable = false + + # If 401 status code should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. 'Application' by default. + # config.http_authentication_realm = 'Application' + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # By default Devise will store the user in session. You can skip storage for + # particular strategies by setting this option. + # Notice that if you are skipping storage for all authentication paths, you + # may want to disable generating routes to Devise's sessions controller by + # passing skip: :sessions to `devise_for` in your config/routes.rb + config.skip_session_storage = [:http_auth] + + # By default, Devise cleans up the CSRF token on authentication to + # avoid CSRF token fixation attacks. This means that, when using AJAX + # requests for sign in and sign up, you need to get a new CSRF token + # from the server. You can disable this option at your own risk. + # config.clean_up_csrf_token_on_authentication = true + + # When false, Devise will not attempt to reload routes on eager load. + # This can reduce the time taken to boot the app but if your application + # requires the Devise mappings to be loaded during boot time the application + # won't boot properly. + # config.reload_routes = true + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 12. If + # using other algorithms, it sets how many times you want the password to be hashed. + # The number of stretches used for generating the hashed password are stored + # with the hashed password. This allows you to change the stretches without + # invalidating existing passwords. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. Note that, for bcrypt (the default + # algorithm), the cost increases exponentially with the number of stretches (e.g. + # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). + config.stretches = Rails.env.test? ? 1 : 12 + + # Set up a pepper to generate the hashed password. + config.pepper = Rails.application.credentials.devise.pepper + + # Send a notification to the original email when the user's email is changed. + # config.send_email_changed_notification = false + + # Send a notification email when the user's password is changed. + # config.send_password_change_notification = false + + # ==> Configuration for :confirmable + # A period that the user is allowed to access the website even without + # confirming their account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming their account, + # access will be blocked just in the third day. + # You can also set it to nil, which will allow the user to access the website + # without confirming their account. + # Default is 0.days, meaning the user cannot access the website without + # confirming their account. + # config.allow_unconfirmed_access_for = 2.days + + # A period that the user is allowed to confirm their account before their + # token becomes invalid. For example, if set to 3.days, the user can confirm + # their account within 3 days after the mail was sent, but on the fourth day + # their account can't be confirmed with the token any more. + # Default is nil, meaning there is no restriction on how long a user can take + # before confirming their account. + # config.confirm_within = 3.days + + # If true, requires any email changes to be confirmed (exactly the same way as + # initial account confirmation) to be applied. Requires additional unconfirmed_email + # db field (see migrations). Until confirmed, new email is stored in + # unconfirmed_email column, and copied to email column on successful confirmation. + # config.reconfirmable = true + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [:email] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + config.remember_for = 2.weeks + + # Invalidates all the remember me tokens when the user signs out. + config.expire_all_remember_me_on_sign_out = true + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # Options to be passed to the created cookie. For instance, you can set + # secure: true in order to force SSL only cookies. + # config.rememberable_options = {} + + # ==> Configuration for :validatable + # Range for password length. + config.password_length = 7..128 + + # Email regex used to validate email formats. It simply asserts that + # one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + # config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [:email] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # Warn on the last attempt before the account is locked. + # config.last_attempt_warning = true + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [:email] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + # config.reset_password_within = 6.hours + + # When set to false, does not sign a user in automatically after their password is + # reset. Defaults to true, so a user is signed in automatically after a reset. + # config.sign_in_after_reset_password = true + + # ==> Configuration for :encryptable + # Allow you to use another hashing or encryption algorithm besides bcrypt (default). + # You can use :sha1, :sha512 or algorithms from others authentication tools as + # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 + # for default behavior) and :restful_authentication_sha1 (then you should set + # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). + # + # Require the `devise-encryptable` gem when using anything other than bcrypt + # config.encryptor = :sha512 + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Set this configuration to false if you want /users/sign_out to sign out + # only the current scope. By default, Devise signs out all scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The "*/*" below is required to match Internet Explorer requests. + config.navigational_formats = ['*/*', :html, :turbo_stream] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.intercept_401 = false + # manager.default_strategies(scope: :user).unshift :some_external_strategy + # end + + # ==> Mountable engine configurations + # When using Devise inside an engine, let's call it `MyEngine`, and this engine + # is mountable, there are some extra configurations to be taken into account. + # The following options are available, assuming the engine is mounted as: + # + # mount MyEngine, at: '/my_engine' + # + # The router that invoked `devise_for`, in the example above, would be: + # config.router_name = :my_engine + # + # When using OmniAuth, Devise cannot automatically set OmniAuth path, + # so you need to do it manually. For the users scope, it would be: + # config.omniauth_path_prefix = '/my_engine/users/auth' + + # ==> Hotwire/Turbo configuration + # When using Devise with Hotwire/Turbo, the http status for error responses + # and some redirects must match the following. The default in Devise for existing + # apps is `200 OK` and `302 Found respectively`, but new apps are generated with + # these new defaults that match Hotwire/Turbo behavior. + # Note: These might become the new default in future versions of Devise. + config.responder.error_status = :unprocessable_entity + config.responder.redirect_status = :see_other + + # ==> Configuration for :registerable + + # When set to false, does not sign a user in automatically after their password is + # changed. Defaults to true, so a user is signed in automatically after changing a password. + # config.sign_in_after_change_password = true +end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 0000000..260e1c4 --- /dev/null +++ b/config/locales/devise.en.yml @@ -0,0 +1,65 @@ +# Additional translations at https://github.com/heartcombo/devise/wiki/I18n + +en: + devise: + confirmations: + confirmed: "Your email address has been successfully confirmed." + send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." + failure: + already_authenticated: "You are already signed in." + inactive: "Your account is not activated yet." + invalid: "Invalid %{authentication_keys} or password." + locked: "Your account is locked." + last_attempt: "You have one more attempt before your account is locked." + not_found_in_database: "Invalid %{authentication_keys} or password." + timeout: "Your session expired. Please sign in again to continue." + unauthenticated: "You need to sign in or sign up before continuing." + unconfirmed: "You have to confirm your email address before continuing." + mailer: + confirmation_instructions: + subject: "Confirmation instructions" + reset_password_instructions: + subject: "Reset password instructions" + unlock_instructions: + subject: "Unlock instructions" + email_changed: + subject: "Email Changed" + password_change: + subject: "Password Changed" + omniauth_callbacks: + failure: "Could not authenticate you from %{kind} because \"%{reason}\"." + success: "Successfully authenticated from %{kind} account." + passwords: + no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." + send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." + updated: "Your password has been changed successfully. You are now signed in." + updated_not_active: "Your password has been changed successfully." + registrations: + destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." + signed_up: "Welcome! You have signed up successfully." + signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." + signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." + signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." + update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address." + updated: "Your account has been updated successfully." + updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again." + sessions: + signed_in: "Signed in successfully." + signed_out: "Signed out successfully." + already_signed_out: "Signed out successfully." + unlocks: + send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." + send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." + unlocked: "Your account has been unlocked successfully. Please sign in to continue." + errors: + messages: + already_confirmed: "was already confirmed, please try signing in" + confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" + expired: "has expired, please request a new one" + not_found: "not found" + not_locked: "was not locked" + not_saved: + one: "1 error prohibited this %{resource} from being saved:" + other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/config/routes.rb b/config/routes.rb index 0386a9c..874dd3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Rails.application.routes.draw do + devise_for :users resources :tweets, only: %i[index show] root "welcome#index" diff --git a/db/data/20231007231119_create_admin_user.rb b/db/data/20231007231119_create_admin_user.rb new file mode 100644 index 0000000..8e04e46 --- /dev/null +++ b/db/data/20231007231119_create_admin_user.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateAdminUser < ActiveRecord::Migration[7.1] + def up + User.create! do |u| + u.username = Rails.application.credentials.admin_user.username + u.password = Rails.application.credentials.admin_user.password + u.password_confirmation = Rails.application.credentials.admin_user.password + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb index 2f6bc6b..2c6937d 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1 +1 @@ -DataMigrate::Data.define(version: 20231007001635) +DataMigrate::Data.define(version: 20231007231119) diff --git a/db/migrate/20231007213927_devise_create_users.rb b/db/migrate/20231007213927_devise_create_users.rb new file mode 100644 index 0000000..1b35b61 --- /dev/null +++ b/db/migrate/20231007213927_devise_create_users.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class DeviseCreateUsers < ActiveRecord::Migration[7.1] + def change + create_table :users do |t| + ## Database authenticatable + t.string :username, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Rememberable + t.datetime :remember_created_at + t.timestamps null: false + end + + add_index :users, :username, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 942c0af..7ecf2e7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_10_07_023127) do +ActiveRecord::Schema[7.1].define(version: 2023_10_07_213927) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -38,5 +38,14 @@ t.datetime "updated_at", null: false end + create_table "users", force: :cascade do |t| + t.string "username", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.datetime "remember_created_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["username"], name: "index_users_on_username", unique: true + end + add_foreign_key "tweet_metrics", "tweets" end diff --git a/spec/factories/tweet_metrics.rb b/spec/factories/tweet_metrics.rb index bea660e..5bd7b22 100644 --- a/spec/factories/tweet_metrics.rb +++ b/spec/factories/tweet_metrics.rb @@ -3,14 +3,23 @@ # Table name: tweet_metrics # # id :bigint not null, primary key -# tweet_id :bigint not null +# bookmarks :integer default(0) +# likes :integer default(0) # replies :integer default(0) # reposts :integer default(0) -# likes :integer default(0) -# bookmarks :integer default(0) # views :integer default(0) # created_at :datetime not null # updated_at :datetime not null +# tweet_id :bigint not null +# +# Indexes +# +# index_tweet_metrics_on_tweet_id (tweet_id) +# index_tweet_metrics_on_tweet_id_and_created_at (tweet_id,created_at) UNIQUE +# +# Foreign Keys +# +# fk_rails_... (tweet_id => tweets.id) # FactoryBot.define do factory :tweet_metric do diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 0000000..1fd7f34 --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,20 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint not null, primary key +# encrypted_password :string default(""), not null +# remember_created_at :datetime +# username :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_username (username) UNIQUE +# +FactoryBot.define do + factory :user do + + end +end diff --git a/spec/models/tweet_metric_spec.rb b/spec/models/tweet_metric_spec.rb index 7068979..0400590 100644 --- a/spec/models/tweet_metric_spec.rb +++ b/spec/models/tweet_metric_spec.rb @@ -3,14 +3,23 @@ # Table name: tweet_metrics # # id :bigint not null, primary key -# tweet_id :bigint not null +# bookmarks :integer default(0) +# likes :integer default(0) # replies :integer default(0) # reposts :integer default(0) -# likes :integer default(0) -# bookmarks :integer default(0) # views :integer default(0) # created_at :datetime not null # updated_at :datetime not null +# tweet_id :bigint not null +# +# Indexes +# +# index_tweet_metrics_on_tweet_id (tweet_id) +# index_tweet_metrics_on_tweet_id_and_created_at (tweet_id,created_at) UNIQUE +# +# Foreign Keys +# +# fk_rails_... (tweet_id => tweets.id) # require "rails_helper" diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 0000000..8a71e0e --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,20 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint not null, primary key +# encrypted_password :string default(""), not null +# remember_created_at :datetime +# username :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_username (username) UNIQUE +# +require 'rails_helper' + +RSpec.describe User, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From c76642126d252b91568610e8888b48ec8b75eb33 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 03:44:38 +0300 Subject: [PATCH 02/17] Add missed test credentials --- .gitignore | 2 ++ config/credentials/test.yml.enc | 1 + spec/factories/users.rb | 4 +++- spec/models/user_spec.rb | 1 - spec/rails_helper.rb | 3 +++ 5 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 config/credentials/test.yml.enc diff --git a/.gitignore b/.gitignore index af1e48f..123359d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ /config/credentials/development.key /config/credentials/production.key + +/config/credentials/test.key diff --git a/config/credentials/test.yml.enc b/config/credentials/test.yml.enc new file mode 100644 index 0000000..4cea08f --- /dev/null +++ b/config/credentials/test.yml.enc @@ -0,0 +1 @@ +o8BtrpH9+d4R6BtusAtBDdsgSX1UcmHKG/EfPWcidxRD7AbDDUooP38VLGV99rbtcY4vmpGY18ikLCmsG0RFnGwubXHQuX95m6iVqCEYNJVuPcU1GyEyS63wD0boLNMcO1j2pu9z+ZO+XLNRTTkXQ2WtPTh+U0HRBOgLXponcmkQ9ggIWuKKtvec9/kmb6rFkSTC0B489hXxBcXgmuzgEOwsKT1TiN8/fTQBQxq94ksHM6DF0RQPj2Dwb+An5TlGQJ7joVyeAYYQJjiayZwgsajtvvS1d3tgQ3dgGzEFF6SRAdlscitDSJILaMDrWYwV49HgmemNaruPS7hFRVMfe1zhZyn1MYnmjRgwTBRinByC7mpyDvWJiSyy3ADku+yyBJXWbUkJ9m7oB1n8LegB0BGiQynmNeumLJpAhph9Ci1cRSAEvwZC1ejdmbvc71T4IpM1ex7yOLHQ0ijTJlRnaOyUNcvHsr0YZuRMUyb7lUi3Ab2tlFZKOQuMtHNRnTaSQb+OSvvfwZqmARNzARBgKZXVqT/3kocDC3kg6lVzbH2yDg6ppnZBpgUG03/3q+6nsgVF94VJGH2BheRRrb166abhAhM1+HFW7+h+o585Kz0qdwNYKqxrp8HGXgEdYvIw2HmJZxCZR64PoQh0wvA=--dLAJPfaMMv4tlcX6--FAz0h8gH8F91XnjlXOQR5w== \ No newline at end of file diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 1fd7f34..7de3041 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -15,6 +15,8 @@ # FactoryBot.define do factory :user do - + username { Faker::Internet.username } + password { Faker::Internet.password } + password_confirmation { password } end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8a71e0e..7485aab 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -16,5 +16,4 @@ require 'rails_helper' RSpec.describe User, type: :model do - pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index c508683..2259f70 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -13,6 +13,8 @@ end RSpec.configure do |config| config.include FactoryBot::Syntax::Methods + config.include Devise::Test::ControllerHelpers, type: :controller + config.include Devise::Test::IntegrationHelpers, type: :request # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false @@ -41,4 +43,5 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + end From 8777ed262d484acfc96ab1ce3f9e2774ccbc0ece Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 03:49:10 +0300 Subject: [PATCH 03/17] a10n specs --- spec/factories/users.rb | 4 ++-- spec/requests/tweets_spec.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 7de3041..c6b9e83 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -15,8 +15,8 @@ # FactoryBot.define do factory :user do - username { Faker::Internet.username } - password { Faker::Internet.password } + username { FFaker::Internet.user_name } + password { FFaker::Internet.password } password_confirmation { password } end end diff --git a/spec/requests/tweets_spec.rb b/spec/requests/tweets_spec.rb index 4030527..f1e0577 100644 --- a/spec/requests/tweets_spec.rb +++ b/spec/requests/tweets_spec.rb @@ -30,4 +30,34 @@ expect(response.body).to include(tweet.author) end end + + # Test signup/login/logout since we don't have a separate controller for users + describe "POST /users" do + let(:user_params) { attributes_for(:user) } + + it "creates a new user" do + expect { + post "/users", params: {user: user_params} + }.to change(User, :count).by(1) + end + end + + describe "POST /users/sign_in" do + let(:user) { create(:user) } + + it "signs in a user" do + post "/users/sign_in", params: {user: {username: user.username, password: user.password}} + expect(response).to redirect_to(tweets_path) + end + end + + describe "DELETE /users/sign_out" do + let(:user) { create(:user) } + + it "signs out a user" do + sign_in user + delete "/users/sign_out" + expect(response).to redirect_to(root_path) + end + end end From dbda541772d5771e6f0f1dbf7022ee00ade19b9a Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 03:57:04 +0300 Subject: [PATCH 04/17] Allow CI for PR to develop --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88d1ebc..e8bb0f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - main + - develop jobs: test: From 67849851746fac2e8c9b49083580ea174a172955 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 03:58:04 +0300 Subject: [PATCH 05/17] Linting fix --- config/initializers/devise.rb | 2 +- spec/models/user_spec.rb | 2 +- spec/rails_helper.rb | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 1bd6a7f..ec97d46 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -253,7 +253,7 @@ # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. - config.navigational_formats = ['*/*', :html, :turbo_stream] + config.navigational_formats = ["*/*", :html, :turbo_stream] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :delete diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 7485aab..c28932f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -13,7 +13,7 @@ # # index_users_on_username (username) UNIQUE # -require 'rails_helper' +require "rails_helper" RSpec.describe User, type: :model do end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 2259f70..0a69ea7 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -43,5 +43,4 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") - end From ba94b83e60a89f9b29d15e32eec6d8e534cad8ef Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 04:01:14 +0300 Subject: [PATCH 06/17] Remove unused devise mail templates --- .../devise/mailer/confirmation_instructions.html.erb | 5 ----- app/views/devise/mailer/email_changed.html.erb | 7 ------- app/views/devise/mailer/password_change.html.erb | 3 --- .../devise/mailer/reset_password_instructions.html.erb | 8 -------- app/views/devise/mailer/unlock_instructions.html.erb | 7 ------- 5 files changed, 30 deletions(-) delete mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb delete mode 100644 app/views/devise/mailer/email_changed.html.erb delete mode 100644 app/views/devise/mailer/password_change.html.erb delete mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb delete mode 100644 app/views/devise/mailer/unlock_instructions.html.erb diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb deleted file mode 100644 index dc55f64..0000000 --- a/app/views/devise/mailer/confirmation_instructions.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

Welcome <%= @email %>!

- -

You can confirm your account email through the link below:

- -

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb deleted file mode 100644 index 32f4ba8..0000000 --- a/app/views/devise/mailer/email_changed.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Hello <%= @email %>!

- -<% if @resource.try(:unconfirmed_email?) %> -

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

-<% else %> -

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

-<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb deleted file mode 100644 index b41daf4..0000000 --- a/app/views/devise/mailer/password_change.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -

Hello <%= @resource.email %>!

- -

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb deleted file mode 100644 index f667dc1..0000000 --- a/app/views/devise/mailer/reset_password_instructions.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -

Hello <%= @resource.email %>!

- -

Someone has requested a link to change your password. You can do this through the link below.

- -

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

- -

If you didn't request this, please ignore this email.

-

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb deleted file mode 100644 index 41e148b..0000000 --- a/app/views/devise/mailer/unlock_instructions.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Hello <%= @resource.email %>!

- -

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

- -

Click the link below to unlock your account:

- -

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

From 5859045091d8ee7d0a264a2a95ee6c407c56af47 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 04:11:22 +0300 Subject: [PATCH 07/17] Edit profile --- app/views/devise/registrations/edit.html.slim | 12 ++++-------- app/views/shared/_navbar.html.slim | 8 ++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/views/devise/registrations/edit.html.slim b/app/views/devise/registrations/edit.html.slim index 2f8d32a..c090103 100644 --- a/app/views/devise/registrations/edit.html.slim +++ b/app/views/devise/registrations/edit.html.slim @@ -4,17 +4,13 @@ h2.title Edit #{resource_name.to_s.humanize} = render "devise/shared/error_messages", resource: resource .field - = f.label :email, class: 'label' + = f.label :username, class: 'label' .control - = f.email_field :email, autofocus: true, autocomplete: "email", class: 'input' - - - if devise_mapping.confirmable? && resource.pending_reconfirmation? - .notification - | Currently waiting confirmation for: #{resource.unconfirmed_email} + = f.text_field :username, autofocus: true, autocomplete: "username", class: 'input' .field = f.label :password, class: 'label' - i (leave blank if you don't want to change it) + i #{"(leave blank if you don't want to change it)"} .control = f.password_field :password, autocomplete: "new-password", class: 'input' - if @minimum_password_length @@ -28,7 +24,7 @@ h2.title Edit #{resource_name.to_s.humanize} .field = f.label :current_password, class: 'label' - i (we need your current password to confirm your changes) + i #{"(we need your current password to confirm your changes)"} .control = f.password_field :current_password, autocomplete: "current-password", class: 'input' diff --git a/app/views/shared/_navbar.html.slim b/app/views/shared/_navbar.html.slim index a2616a5..1f74045 100644 --- a/app/views/shared/_navbar.html.slim +++ b/app/views/shared/_navbar.html.slim @@ -18,10 +18,10 @@ header span.icon i.fas.fa-sign-out-alt aria-hidden="true" span Sign out - / Optionally, you could add a link to the user's profile or settings - / = link_to edit_user_registration_path, class: 'navbar-item' do - / span.icon - / i.fas.fa-user-cog aria-hidden="true" + = button_to edit_user_registration_path, method: :get, class: "button is-link is-outlined is-inverted navbar-item mr-2" + span.icon + i.fas.fa-user-cog aria-hidden="true" + span Edit profile - else = button_to new_user_session_path, method: :get, class: "button is-primary is-outlined is-inverted navbar-item mr-2" span.icon From 3035b5729e5bb6968858f3b44f31245977926951 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 04:20:22 +0300 Subject: [PATCH 08/17] Add credentials test key to CI --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8bb0f5..7dd557a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,9 @@ jobs: bundle config path vendor/bundle bundle install --jobs 4 --retry 3 + - name: Create test credentials key file + run: echo "${{ secrets.RAILS_TEST_MASTER_KEY }}" > config/credentials/test.key + - name: Precompile assets run: bundle exec rails assets:precompile From 92ce6ce9dd0450865b011252b429565094baea47 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 04:37:14 +0300 Subject: [PATCH 09/17] Delete config/credentials/test.key --- config/credentials/test.key | 1 - 1 file changed, 1 deletion(-) delete mode 100644 config/credentials/test.key diff --git a/config/credentials/test.key b/config/credentials/test.key deleted file mode 100644 index 1b0eaaf..0000000 --- a/config/credentials/test.key +++ /dev/null @@ -1 +0,0 @@ -a96a33fdff495ffd3f82bde545a3aa41 \ No newline at end of file From a513e3910cef27f63b7111bed742cffc09160f76 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 04:41:32 +0300 Subject: [PATCH 10/17] Attempt to fix test credentials problem --- .github/workflows/ci.yml | 3 +++ config/credentials/test.yml.enc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7dd557a..d6a6d36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,9 @@ jobs: - name: Create test credentials key file run: echo "${{ secrets.RAILS_TEST_MASTER_KEY }}" > config/credentials/test.key + - name: Check test key file content + run: cat config/credentials/test.key + - name: Precompile assets run: bundle exec rails assets:precompile diff --git a/config/credentials/test.yml.enc b/config/credentials/test.yml.enc index 4cea08f..f945d03 100644 --- a/config/credentials/test.yml.enc +++ b/config/credentials/test.yml.enc @@ -1 +1 @@ -o8BtrpH9+d4R6BtusAtBDdsgSX1UcmHKG/EfPWcidxRD7AbDDUooP38VLGV99rbtcY4vmpGY18ikLCmsG0RFnGwubXHQuX95m6iVqCEYNJVuPcU1GyEyS63wD0boLNMcO1j2pu9z+ZO+XLNRTTkXQ2WtPTh+U0HRBOgLXponcmkQ9ggIWuKKtvec9/kmb6rFkSTC0B489hXxBcXgmuzgEOwsKT1TiN8/fTQBQxq94ksHM6DF0RQPj2Dwb+An5TlGQJ7joVyeAYYQJjiayZwgsajtvvS1d3tgQ3dgGzEFF6SRAdlscitDSJILaMDrWYwV49HgmemNaruPS7hFRVMfe1zhZyn1MYnmjRgwTBRinByC7mpyDvWJiSyy3ADku+yyBJXWbUkJ9m7oB1n8LegB0BGiQynmNeumLJpAhph9Ci1cRSAEvwZC1ejdmbvc71T4IpM1ex7yOLHQ0ijTJlRnaOyUNcvHsr0YZuRMUyb7lUi3Ab2tlFZKOQuMtHNRnTaSQb+OSvvfwZqmARNzARBgKZXVqT/3kocDC3kg6lVzbH2yDg6ppnZBpgUG03/3q+6nsgVF94VJGH2BheRRrb166abhAhM1+HFW7+h+o585Kz0qdwNYKqxrp8HGXgEdYvIw2HmJZxCZR64PoQh0wvA=--dLAJPfaMMv4tlcX6--FAz0h8gH8F91XnjlXOQR5w== \ No newline at end of file +iqunnjxyf91QhBwxRiKyL/CkK1QdTq0w7jp0x0NcltUe0JDwt+kSwiGm1a4PTLaF/MCttEmzJpHr6xpvKnmhzI2WrO3VQOrUmeYQDGA+cTF/d/wsgwL/w5oE0Mbje5iaFU6NfBPVQZB7GoESw5+4YPK03kVc38GIPsUDteuPYmZeKZ0QHl3stL5+RgFm6DyktMppancluqOZoBD5MhmgutJdoaxtIQA/L7GYR0hgfrt+q9mFO4oHEVYhoLeuMaYlrp0J6YtLlRq7ndxctNNCwT0B3/P7oIR/GyM1e0IHmqwnArmfTJ8A5UMVDVyp+5v6uI4b6o1Hpy+D9vuNDhkCufEbpb3O5Ko8tKHTBbWFIIXKfJZkNRgQ4r1gLGmcD6RF3fJimD75DS7EP1wj+B7oLkY1XTtRNEj35zpRCXwPGQt9NQJ5ANKykMSqomHgiLAtKpcWxpe/OXKzypAOiBe1jO6/132vBQAkHUF0gEnMPCIo9fW8gFYhJSA9sHAV6OxMGgpscX3TTiLzuWvOKuEP3zcjJrmRFnI807NoER0BBL7SaR/D9keiND+mmEi/cConky8mWgh7SYZUCXADbdFZvwbgvE6ilGuXlr8UP/6VjDT5SlCuRCNOtpWYHC4KlzTpxhKeBLDn7BDwJrEWkgA=--/nSstG5Qq7QD6o6K--Pnuo2dPPstRVc2GHyZWVXw== \ No newline at end of file From 8a810040a91f6b96732b10ff6fbea9135332ef78 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:23:45 +0300 Subject: [PATCH 11/17] LOL --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6a6d36..0f1e1ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,11 +43,10 @@ jobs: - name: Create test credentials key file run: echo "${{ secrets.RAILS_TEST_MASTER_KEY }}" > config/credentials/test.key - - name: Check test key file content - run: cat config/credentials/test.key - - name: Precompile assets run: bundle exec rails assets:precompile + env: + RAILS_ENV: test - name: Run tests run: bundle exec rspec From d7d1b86e94cc04e8953bf5d21f8eb62ae2f58d60 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:35:52 +0300 Subject: [PATCH 12/17] Uncomment devise mailer in initializer --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index ec97d46..f13b607 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -17,7 +17,7 @@ # config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. - # config.mailer = 'Devise::Mailer' + config.mailer = "Devise::Mailer" # Configure the parent class responsible to send e-mails. # config.parent_mailer = 'ActionMailer::Base' From 8c6f428218ab07904ff44efcdbfcfd36da626512 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:41:56 +0300 Subject: [PATCH 13/17] Attempt to ignore mailer --- config/initializers/devise.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index f13b607..5f0cf48 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,3 +1,7 @@ +if Rails.autoloaders.zeitwerk_enabled? + Rails.autoloaders.main.ignore("#{root}/app/mailers") +end + Devise.setup do |config| # The secret key used by Devise. Devise uses this key to generate # random tokens. Changing this key will render invalid all existing @@ -17,7 +21,7 @@ # config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. - config.mailer = "Devise::Mailer" + # config.mailer = "Devise::Mailer" # Configure the parent class responsible to send e-mails. # config.parent_mailer = 'ActionMailer::Base' From 9f231901b2de6333487f638760cb2d180e914b60 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:43:45 +0300 Subject: [PATCH 14/17] Follow up --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 5f0cf48..18e9c6d 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -21,7 +21,7 @@ # config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. - # config.mailer = "Devise::Mailer" + config.mailer = "Devise::Mailer" # Configure the parent class responsible to send e-mails. # config.parent_mailer = 'ActionMailer::Base' From 35c8f5e69c55e74b73150d2d05f3291da0d37305 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:44:25 +0300 Subject: [PATCH 15/17] Oops --- config/initializers/devise.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 18e9c6d..beacf57 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,5 +1,5 @@ if Rails.autoloaders.zeitwerk_enabled? - Rails.autoloaders.main.ignore("#{root}/app/mailers") + Rails.autoloaders.main.ignore("#{Rails.root}/app/mailers") end Devise.setup do |config| @@ -21,7 +21,7 @@ # config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. - config.mailer = "Devise::Mailer" + # config.mailer = "Devise::Mailer" # Configure the parent class responsible to send e-mails. # config.parent_mailer = 'ActionMailer::Base' From 76f7c19172464a143ede3043bd6af28deb369b83 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:46:51 +0300 Subject: [PATCH 16/17] Back and forth --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index beacf57..70bb4a2 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -21,7 +21,7 @@ # config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" # Configure the class responsible to send e-mails. - # config.mailer = "Devise::Mailer" + config.mailer = "Devise::Mailer" # Configure the parent class responsible to send e-mails. # config.parent_mailer = 'ActionMailer::Base' From e92a855514905e3db39d9903a18017f87b63f6f4 Mon Sep 17 00:00:00 2001 From: Yevhenii Hurin Date: Sun, 8 Oct 2023 05:50:47 +0300 Subject: [PATCH 17/17] And another one --- config/application.rb | 2 +- config/initializers/devise.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/config/application.rb b/config/application.rb index 2195d8c..08751e3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -7,7 +7,7 @@ require "active_record/railtie" # require "active_storage/engine" require "action_controller/railtie" -# require "action_mailer/railtie" +require "action_mailer/railtie" # require "action_mailbox/engine" # require "action_text/engine" require "action_view/railtie" diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 70bb4a2..f13b607 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,7 +1,3 @@ -if Rails.autoloaders.zeitwerk_enabled? - Rails.autoloaders.main.ignore("#{Rails.root}/app/mailers") -end - Devise.setup do |config| # The secret key used by Devise. Devise uses this key to generate # random tokens. Changing this key will render invalid all existing