A Rails Blog In VS Code-Devise
How To Create A Blog in VS Code — Part V — RailsSeries#Episode 07
Let’s Use Devise.
In this post:
You will:
Learn how to use the Devise;
Devise come pre-built with
password field
that is already encrypted.
You won't even be able to see them,
which is good for your user
because it means
you can't form their password
and email combinations but also
means you don't have to create
all the encryption yourself.
It also may confirm
user registrations through emails,
if you set it up, which is a pretty
simple setup, you'll see...
You can recover accounts.
You can do all the basic stuff that
you'd expect with a user registration
system account in another app.
That just comes out of the
box 🕋️ here, with Devise,
with just a little bit of setup.
Welcome!
Let’s Get Started!
Note: If you get stuck, please see my repo.
0#step — Download the last post here and prepare your vscode
environment.
Let’s open a new branch: git checkout -b add_devise
1#step — GoTo Gemfile
and set Devise:
gem "devise"
2#step — Run bundle:
bundle install
output:
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies…
…
Fetching orm_adapter 0.5.0
…
Fetching bcrypt 3.1.19
…
Fetching warden 1.2.9
…
Fetching responders 3.1.0
Using stimulus-rails 1.2.1
Installing orm_adapter 0.5.0
Installing warden 1.2.9
Installing bcrypt 3.1.19 with native extensions
Installing responders 3.1.0
Fetching devise 4.9.2
Installing devise 4.9.2
Bundle complete! 17 Gemfile dependencies, 78 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Post-install message from devise:
[DEVISE] Please review the [changelog] and [upgrade guide] for more info on Hotwire / Turbo integration.
[changelog] https://github.com/heartcombo/devise/blob/main/CHANGELOG.md
[upgrade guide] https://github.com/heartcombo/devise/wiki/How-To:-Upgrade-to-Devise-4.9.0-%5BHotwire-Turbo-integration%5D
3#step —Run:
rails g devise:install
The code rails g devise:install
is a Rails command used to generate the necessary files and configuration for the Devise gem in a Rails 7 application.
Devise is a popular authentication solution for Rails applications, providing a set of pre-built controllers, views, and models to handle user registration, authentication, and session management. It simplifies the process of implementing user authentication features in a Rails application.
Running rails g devise:install
performs the following tasks:
- It generates an initializer file (
config/initializers/devise.rb
) that contains configuration options for Devise. You can modify this file to customize the behavior of Devise in your application. - It creates a migration file to add the necessary columns to the database table used for storing user information. This migration file is placed in the
db/migrate
directory and will be used to create the required tables when you runrails db:migrate
. - It inserts the necessary routes for Devise in the
config/routes.rb
file. These routes provide endpoints for user authentication, registration, password management, and more.
After running rails g devise:install
, you may need to run additional commands to generate other Devise-related components, such as the user model and views for authentication forms. These can be generated using commands like rails g devise User
or rails g devise:views
or rails g devise:controllers users
. Keep reading, please…
Note: It’s always recommended to refer to the official documentation or release notes for the most up-to-date information.
rails g devise:install
output:
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Depending on your application's configuration some manual setup may be required:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production, :host should be set to the actual host of your application.
* Required for all applications. *
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
* Not required for API-only Applications *
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
* Not required for API-only Applications *
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
* Not required *
===============================================================================
4#step — Run:
rails g devise User
When you run rails g devise User
, it performs the following tasks:
- It generates a model file for the User model (
app/models/user.rb
). This file will include the necessary code to integrate with Devise and handle user authentication-related functionality. - It creates a migration file to add additional columns to the
users
table in the database. These columns are required by Devise for storing user information such as encrypted passwords, timestamps, and other authentication-related data. The migration file is placed in thedb/migrate
directory and can be executed usingrails db:migrate
to apply the database changes. - It generates a locale file (
config/locales/devise.en.yml
) that contains translations for Devise error messages and other text displayed by Devise. - It creates a views directory for the User model (
app/views/devise/users
) containing a set of view templates for user authentication-related views such as sign-up, sign-in, password reset, and more. These views can be customized to match the look and feel of your application.
In addition to generating the User model and associated files, the rails g devise User
command will also update the config/routes.rb
file to include the necessary routes for user authentication, registration, and session management.
It’s worth noting that User
is just an example model name. You can replace User
with the name of your own model if you want to use a different name for your user model, such as Admin
or Customer
.
rails g devise User
output:
invoke active_record
create db/migrate/20230714162304_devise_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
insert app/models/user.rb
route devise_for :users
Before running the migration, you can see this file:/db/migrate/20230714162304_devise_create_users.rb.
We customize it in this post about email.
But for now, just migrate…
5#step — run:
rails db:migrate
output:
== 20230714162304 DeviseCreateUsers: migrating ================================
-- create_table(:users)
-> 0.0013s
-- add_index(:users, :email, {:unique=>true})
-> 0.0007s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0005s
== 20230714162304 DeviseCreateUsers: migrated (0.0026s) =======================
6
To generate the necessary files to apply the database changes required by the Devise User model.
It creates reset_password_token, which is unique. So what does it mean? It means if you have to recover the email you send to the applicant a hash to recover their account; it will find the user based on the reset password token. It is unique because it needs to only apply to that user of course.
Inform yourself about GDPR. Non-compliance with the GDPR can result in significant fines.
GDPR stands for General Data Protection Regulation. It is a comprehensive data protection and privacy law introduced by the European Union (EU) and came into effect on May 25, 2018. The GDPR was designed to provide individuals with greater control over their personal data and to harmonize data protection regulations across the EU member states.
In Brazil we have LGPD — Lei Geral de Proteção de Dados Pessoais (LGPD), Lei n° 13.709/2018.
6#step — Create a partial file to flash message decently at:
app/views/layouts/_messages.html.erb
<% if notice %>
<div class="alert alert-success" >
<button type="button" class="btn-close float-end" data-bs-dismiss="alert" aria-label="Close"></button>
<%# <button type="button" class="close" data-dismiss="alert" aria-label="Close"> %>
<span aria-hidden="true">×</span>
</button>
<%= notice %>
</div>
<% end %>
<% if alert %>
<div class="alert alert-danger">
<button type="button" class="btn-close float-end" data-bs-dismiss="alert" aria-label="Close"></button>
<%# <button type="button" class="close" data-dismiss="alert" aria-label="Close"> %>
<span aria-hidden="true">×</span>
</button>
<%= alert %>
</div>
<% end %>
This will add a flash message to your rails page. It is dismissible & colorized. when you click on the X
, it will close the message. Error is shown in red, regular action, in green. Just neat!
To take effect, please execute Step #7 below.
7#step — GoTo and inside <body>
type:
rails-blog-demo/app/views/layouts/application.html.erb
...
<header>
<%= render 'layouts/navbar' %>
<%= render 'layouts/messages'%>
</header>
<main>
<div class="container">
<%= yield %>
</div>
</main>
...
To streamline and prevent redundancy, you may remove the following line from both the show.html.erb and index.html.erb pages from app/views/posts/
and app/views/users/
directories:
<p style="color: green"><%= notice %></p>
This will help maintain consistency and improve the clarity of your code
8#step —Extract the code block from the last <li>
to </li>
within the partial file and replace it with <%= render 'user/session_manager' %>
. This will deal with session management.
rails-blog-demo/app/views/layouts/_navbar.html.erb
...
<ul class="navbar-nav">
<%= render 'user/session_manager' %>
</<ul>
...
Check out the finished design of our navbar!
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/home">Rails Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<%# <a class="nav-link active" aria-current="page" href="#">Home</a> %>
<%= link_to "Home", root_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to "Blog", posts_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to "About", about_path, class: 'nav-link' %>
</li>
</ul>
<ul class="navbar-nav">
<%= render 'user/session_manager' %>
</ul>
</div>
</div>
</nav>
We will substitute the dropdown text with User Functionality in the extracted code. This will serve as a placeholder until step #29 when we will address sessions.
Create a folder at app/views/user
.
9#step —Create a session management file within the new app/views/user
directory and Paste the content previously extracted to:
app/views/user/_session_manager.html.erb:
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
User Functionality
</a>
<ul class="dropdown-menu dropdown-menu-lg-end" aria-labelledby="navbarDropdown">
<li><%= link_to "New Post", new_post_path , class: "dropdown-item" %></li>
<li><a class="dropdown-item" href="#">Action</a></li>
<li><%= link_to "Edit Account", edit_user_registration_path, class:"dropdown-item" %></li>
<li><hr class="dropdown-divider"></li>
<li><%= button_to "Sign out", destroy_user_session_path, method: :delete, class:"dropdown-item" %></li>
</ul>
</li>
Note: Before navigating to app/views/user
and placing this partial inside, ensure you create the necessary folder and file. Moving forward, let’s proceed to manage the session; this is where we’ll handle individual sessions, which is why we set the directory user
in singular form.
Our blog’s navbar, particularly positioned at the end, directs users to the registration & authentication pages, and sessions & posts management actions. It provides easy access to different pages quickly.
10#step — Create a structure like this inside this partial:
app/views/user/_session_manager.html.erb:
<% if current_user %>
[IF USER IS LOGGED IN]
<% else %>
[IF USER IS NOT LOGGED IN]
<% end %>
<% if current_user %>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
User Functionality Goes here!
</a>
<ul class="dropdown-menu dropdown-menu-lg-end" aria-labelledby="navbarDropdown">
<li><%= link_to "New Post", new_post_path , class: "dropdown-item" %></li>
<li><a class="dropdown-item" href="#">Action</a></li>
<li><%= link_to "Edit Account", edit_user_registration_path, class:"dropdown-item" %></li>
<li><hr class="dropdown-divider"></li>
<li><%= button_to "Sign out", destroy_user_session_path, method: :delete, class:"dropdown-item" %></li>
</ul>
</li>
<% else %>
<li class="nav-item">
<%= link_to "Sign up", new_user_registration_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to "Login", new_user_session_path, class: 'nav-link' %>
</li>
<% end %>
When you enter rails routes | grep users
into the Terminal, concerning this post, you’ll encounter the following table:
Warning: if you destroy registration
(instead of destroying a session) you destroy the account entirely (new_user_registration_path); So take care!
If you destroy sessions
, the user’s session will be terminated, resulting in the user being logged out (redirected to new_user_session_path). Ensure a secure logout process with this approach!
11#step — Let’s make the sign_out (logout)
button inside dropdown menu,
right after the dropdown divider:
rails-blog-demo/app/views/user/_session_manager.html.erb
<li><%= button_to "Sign out", destroy_user_session_path, method: :delete, class:"dropdown-item" %></li>
12#step — Could you please confirm that the new post link is functioning correctly? So far, so good!
But there is a problem here: there’s nothing to stop us from going to this link /post/new
from anywhere;
Try it by typing CTRL + SHIFT + N
and opening an incognito window. Then, paste http://127.0.0.1:3000/posts/new
into the address bar, and you will see the form. No users are logged in!
It is better to control access to your system through user signing and authentication.
How can we accomplish this? inside posts_controller.rb file.
rails-blog-demo/app/controllers/posts_controller.rb
class PostsController < ApplicationController
...
before_action :authenticate_user!, except: %i[show index]
....
In Rails 7, the code snippet before_action :authenticate_user!, except: %i[show index]
is used to define a before_action
filter in a controller. This filter ensures that the authenticate_user!
method is called before executing any actions in the controller, except for the show
and index
actions.
The before_action
method is a common feature in Rails that allows you to specify a method or a block of code that should be executed before certain actions in a controller. In this case, authenticate_user!
is a common method used in authentication systems to ensure that a user is logged in before accessing certain actions or pages.
By specifying except: %i[show index]
, you are instructing Rails to skip the authenticate_user!
method for the show
and index
actions. This means that these actions can be accessed without requiring authentication, while other actions in the controller will still enforce the authentication requirement.
This can be useful in cases where you have certain public-facing pages or actions that don’t require user authentication, such as a blog’s index page or a public show page, while still enforcing authentication for other actions that deal with sensitive or protected data.
13#step — Now let’s associate user
with the posts
, on Terminal type:
rails g migration add_user_to_posts user:belongs_to
In Rails 7, the command rails g migration add_user_to_posts user:belongs_to
is used to generate a new migration file that adds a user_id
column to the posts
table, establishing a "belongs_to" association between the Post
model and the User
model.
14#step — Now association: ORM; GoTo:
rails-blog-demo/app/models/post.rb
class Post < ApplicationRecord
validates :title, presence: true, length: { maximum: 5, maximum: 50 }
validates :body, presence: true, length: { minimum: 10, maximum: 1000 }
belongs_to :user
end
15#step — And GoTo:
rails-blog-demo/app/models/user.rb
class User < ApplicationRecord
has_many :posts
...
end
16#step — GoTo Rails Console
and destroy all the post
to avoid this error 👇️ before running migration:
SQLite3::ConstraintException: NOT NULL constraint failed: posts.user_id
rails c
Post.destroy_all
exit
17#step — Now run migrate:
rails db:migrate
18#step — GoTo seeds.rb
and refactor it (now each post
must have user
id):
rails-blog-demo/db/seeds.rb
User.create(email: 'example@example.com', password: 'password', password_confirmation: 'password')
10.times do |i|
Post.create(title: "Title #{i}", body: "Body #{i} words goes here idk...", user_id: User.first.id)
end
19#step — Re-populate the table by running seed
:
rails db:seed
20 #step — To Identify the author’s post, GoTo and type <h4>
:
rails-blog-demo/app/views/posts/_post.html.erb
<div id="<%= dom_id post %>">
<h2><%= post.title %></h2>
<h4>Posted by <% post.user.email %></h4>
<h6><%= pluralize(post.views, "view")%></h6>
<div>
<%= post.body %>
</div>
</div>
21#step — We just have a column email. Let’s add a name to our User database. GoTo Terminal
type:
rails g migration add_name_to_user name:string
rails db:migrate
In Rails 7, the command rails g migration add_name_to_user name:string
is used to generate a new migration file that adds a name
column to the users
table.
This migration is typically used when you want to add a new attribute called name
to the User
model, allowing users to have a name associated with their account. Now:
rails db:migrate
By running this migration (rails db:migrate
), the name
column will be added to the users
table in the database, with the data type of string
. This means that each user record will have a corresponding name
field, where you can store the user's name as a string value.
After the migration is executed, you can access the name
attribute on the User
model just like any other attribute. For example, if you have an user
object, you can retrieve the name by calling user.name
.
This migration is useful when you want to extend the default attributes of the User
model in your application to include additional information like the user's name.
22#step — GoTo set edit link:
rails-blog-demo/app/views/user/_session_manager.html.erb
<li><%= link_to "Edit Account", edit_user_registration_path, class:"dropdown-item" %></li>
23#step — Now Devise views, Run:
rails g devise:views
output:
invoke Devise::Generators::SharedViewsGenerator
create app/views/devise/shared
create app/views/devise/shared/_error_messages.html.erb
create app/views/devise/shared/_links.html.erb
invoke form_for
create app/views/devise/confirmations
create app/views/devise/confirmations/new.html.erb
create app/views/devise/passwords
create app/views/devise/passwords/edit.html.erb
create app/views/devise/passwords/new.html.erb
create app/views/devise/registrations
create app/views/devise/registrations/edit.html.erb
create app/views/devise/registrations/new.html.erb
create app/views/devise/sessions
create app/views/devise/sessions/new.html.erb
create app/views/devise/unlocks
create app/views/devise/unlocks/new.html.erb
invoke erb
create app/views/devise/mailer
create app/views/devise/mailer/confirmation_instructions.html.erb
create app/views/devise/mailer/email_changed.html.erb
create app/views/devise/mailer/password_change.html.erb
create app/views/devise/mailer/reset_password_instructions.html.erb
create app/views/devise/mailer/unlock_instructions.html.erb
In Rails 7, the command rails g devise:views
is used to generate the views associated with the Devise gem.
When you run rails g devise:views
, it generates a set of view templates that can be customized to fit the design and functionality of your application. These views are stored in the app/views/devise
directory.
The generated views include files such as registrations/new.html.erb
(user registration form), sessions/new.html.erb
(user login form), passwords/new.html.erb
(password reset form), and others.
By running this command, you don’t need to create these views from scratch. Instead, Devise generates them for you, allowing you to quickly set up user authentication and customize the views as needed.
Once the views are generated, you can modify them to match your application’s styling, add additional fields, or customize the behavior according to your requirements. These views work in conjunction with the Devise controllers and models to provide the necessary authentication functionality in your Rails application (Devise is used for Registrations, Authentications, and Session Management — RASM).
24#step — Now Devise Controllers, Run:
rails g devise:controllers users
output:
create app/controllers/users/confirmations_controller.rb
create app/controllers/users/passwords_controller.rb
create app/controllers/users/registrations_controller.rb
create app/controllers/users/sessions_controller.rb
create app/controllers/users/unlocks_controller.rb
create app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================
Some setup you must do manually if you haven't yet:
Ensure you have overridden routes for generated controllers in your routes.rb.
For example:
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'users/sessions'
}
end
===============================================================================
By running rails g devise:controllers users
, you generate custom controller files specifically for the users
resource, which represents the user model in your application. These controller files will be placed in the app/controllers/users
directory.
The generated controller files include:
confirmations_controller.rb
: Handles account confirmation-related actions.passwords_controller.rb
: Handles password reset-related actions.registrations_controller.rb
: Handles user registration-related actions.sessions_controller.rb
: Handles user login and logout-related actions.unlocks_controller.rb
: Handles account unlocking-related actions.
But the two we want to take a look at are going to be registrations and session controllers.
These generated controller files allow you to override the default behavior of Devise controllers by adding or modifying the actions and their corresponding logic. This customization can include adding additional functionality, applying custom validation, or integrating with other parts of your application.
For example, you might want to add extra steps during the user registration process, implement additional authentication methods, or customize the behavior of password reset flows. By generating the custom controllers, you have a starting point to modify and extend the authentication behavior specifically for the users
resource in your Rails application.
After running this command, you can modify the generated controller files according to your specific requirements and customize the authentication behavior for the users
resource in your Rails application.
25#step — GoTo and create a name
field just below email:
rails-blog-demo/app/views/devise/registrations/edit.html.erb
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
26#step — Upon creating a new post, let’s promptly save the information of the user who authored the post.
GoTo, and below @post = Post.new(post_params), type:
rails-blog-demo/app/controllers/posts_controller.rb
def create
...
@post.user = current_user
...
We can do that because our authenticate user ( see Step#12 ) is making sure that we can never come to the create action unless we’re signed in so that will set the user of the post for us, which is neat!
27#step — To get everything working, please GoTo:
rails-blog-demo/config/routes.rb
devise_for :users, controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations'
}
This code snippet is configuring Devise to handle user authentication and registration routes for a User
model while using custom controllers to control the behavior of certain actions during the authentication and registration processes. In this case:
sessions: 'users/sessions'
: This points to the custom controller for handling user sessions (sign-in and sign-out). The controller would be located in theapp/controllers/users/sessions_controller.rb
file.registrations: 'users/registrations'
: This points to the custom controller for user registrations (sign-up). The controller would be located in theapp/controllers/users/registrations_controller.rb
file.
28#step — GoTo and uncomment these protected methods:
rails-blog-demo/app/controllers/users/registrations_controller.rb
...
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
...
# protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
end
# If you have extra params to permit, append them to the sanitizer.
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [:name])
end
...
Please remember to replace :attribute
with :name
as this is how we whitelist inputs in Devise.
Here we want to be able to update the account and white list a new parameter (name) so uncomment these too:
configure_sign_up_params
configure_account_update_params
To call each one, uncomment each before_action.
This is typically used in combination with strong parameters to prevent mass assignment vulnerabilities when you whitelist a new parameter in the controller using the permit
method within the params
hash, such as params.require(:post).permit(:title, :content, :new_parameter)
. But here, in Devise, you use sanitizer
method — devise_parameter_sanitizer.permit(:account_update, keys: [:name])
.
29#step — Now to render in the navbar the current user dot name
,
Substitute the text User Functionality Goes Here!
with the name of the logged-in user., GoTo:
rails-blog-demo/app/views/user/_session_manager.html.erb
<%= current_user.name %>
30#step —Now create a text input field where users can enter a value for the name
attribute below email, GoTo:
rails-blog-demo/app/views/devise/registrations/new.html.erb
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
31#step —Now functionality related to user profile, GoTo Terminal, type:
rails g controller users profile
output:
create app/controllers/users_controller.rb
route get 'users/profile'
invoke erb
create app/views/users
create app/views/users/profile.html.erb
invoke test_unit
create test/controllers/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
The code rails g controller users profile
in Rails 7 is a command used to generate a new controller named UsersController
with an action named profile
.
When you run this command, Rails generates several files for you:
app/controllers/users_controller.rb
: This file contains theUsersController
class definition. It is where you can define the actions and logic for handling requests related to theusers
resource.app/views/users/profile.html.erb
: This file is the view template for theprofile
action. It is an HTML.erb file where you can write the HTML and embedded Ruby code to define the content that will be displayed when theprofile
action is accessed.- Other files such as test files and helper files may also be generated, depending on your Rails configuration.
The purpose of generating this controller and action is to provide a starting point for implementing functionality related to user profiles within your Rails application. The UsersController
can include methods to handle user profile-related actions, such as displaying user information, allowing users to edit their profiles, or performing any other actions specific to user profiles.
You can customize the generated UsersController
and profile.html.erb
files to fit your application's requirements. You can add additional actions, define instance variables to pass data to the view, and modify the view template as needed to display the user profile information or perform other desired actions related to user profiles.
Note about the importance of profile pages:
Profile generally refers to a set of characteristics,
attributes, or information that describes an individual
or entity. In the context of software applications,
a “profile” often refers to a user’s personal or
professional information that they have provided or
that has been generated by their interactions within
the application. This information might include things
like personal details, preferences, settings,
activity history, or any other relevant data that helps
characterize or identify the user within the system.
Profiles are commonly used in social networking sites,
online forums, e-commerce platforms, and various other
types of applications to customize user experiences and
provide personalized content or services.
In Rails, a controller named users_profile typically
serves the purpose of managing actions related to user
profiles within an application. This controller would
handle requests and responses associated with viewing,
editing, updating, or deleting user profiles.
It helps to keep the code organized by separating
concerns related specifically to user profiles from
other parts of the application logic.
32#step — Add views count to the User table, GoTo Terminal, type:
rails g migration add_views_to_user views:integer
output:
invoke active_record
create db/migrate/20230714232351_add_views_to_user.rb
By running this command and then executing the migration (rails db:migrate
), the views
column will be added to the users
table in the database. The data type of the views
column is specified as integer
, indicating that it will store numeric values.
33#step — To create the field, GoTo Terminal, type:
rails db:migrate
output:
== 20230714232351 AddViewsToUser: migrating ===================================
-- add_column(:users, :views, :integer)
-> 0.0015s
== 20230714232351 AddViewsToUser: migrated (0.0016s) ==========================
34#step —Opps, let’s change the schema of the users
table to set the default value, on terminal type:
rails g migration change_views_for_users
output:
invoke active_record
create db/migrate/20230714232857_change_views_for_users.rb
The code rails g migration change_views_for_users
in Rails 7 is a command used to generate a new migration file for changing the schema of the users
table, specifically modifying the views
column.
The purpose of this migration file is to define the changes you want to make to the users
table regarding the views
column.
35#step — Now to add a default value, Navigate to db/migrate, select the latest migration file, and then enter:
class ChangeViewsForUsers < ActiveRecord::Migration[7.0]
def change
change_column :users, :views, :integer, default: 0
end
end
36#step — Run migration:
rails db:migrate
37#step — Now route configuration map, above to resources :posts, GoTo:
rails-blog-demo/config/routes.rb
get 'u/:id', to: 'users#profile', as: 'user'
This route configuration maps a GET request to the URL path 'u/:id'
to the profile
action of the UsersController
, and it gives the route a named route helper of 'user'
.
Here’s a breakdown of what each part of the code does:
get
: This specifies that this route will respond to HTTP GET requests.'u/:id'
: This is the URL pattern that the route will match. It uses a parameterized segment:id
to capture the user's ID as part of the URL path. For example, a request to/u/1
will match this route, with1
being captured as the:id
parameter.to: 'users#profile'
: This indicates that the request should be routed to theprofile
action within theUsersController
. It specifies the controller and action that will handle the request.as: 'user'
: This assigns the route a named route helper, which allows you to generate URLs and paths for this route using the specified name. In this case, the route helper is named'user'
, so you can use methods likeuser_path(id)
oruser_url(id)
to generate the URL or path for a specific user's profile. Theid
argument should be replaced with the actual user ID.
Overall, this route configuration sets up a URL pattern that captures the user’s ID and maps it to the profile
action in the UsersController
. The named route helper 'user'
makes it easier to generate URLs or paths for user profiles in your application.
38#step — Let’s implement the profile
action functionality; GoTo:
rails-blog-demo/app/controllers/users_controller.rb
and type:
class UsersController < ApplicationController
before_action :set_user
def profile
@user.update(views: @user.views + 1)
end
private
def set_user
@user = User.find(params[:id])
end
end
Here’s an explanation of what each part of the code does:
class UsersController < ApplicationController
: This line declares theUsersController
class, which is a subclass ofApplicationController
. Controllers in Rails handle incoming requests and define actions to perform specific tasks.before_action :set_user
: This line specifies that theset_user
method should be called as a before action before executing theprofile
action. It ensures that the@user
instance variable is set and available for use in theprofile
action.def profile
: This defines theprofile
action within theUsersController
. It is the action that will be executed when a request is made to theprofile
route for a specific user.@user.update(views: @user.views + 1)
: In theprofile
action, this line updates theviews
attribute of the@user
instance. It increments the value ofviews
by 1 each time theprofile
action is called, essentially tracking the number of views for a user's profile.private
: This keyword specifies that the methods defined after it (set_user
in this case) are private methods and cannot be accessed from outside the class. Private methods are typically used for internal implementation details.def set_user
: This is a private method that sets the@user
instance variable by finding the user record based on theid
parameter passed in the request. It usesUser.find(params[:id])
to retrieve the user record from the database and assigns it to@user
. Theparams[:id]
represents the value of theid
parameter in the request's URL.
Overall, this code sets up the UsersController
with a profile
action that increments the views
attribute of the user each time the profile is accessed. It uses a before_action
callback to ensure the @user
instance variable is set before executing the profile
action.
39#step — Let’s update all our users to have zero views; GoTo Rails Console by typing:
rails c
User.all.each do |user|
user.views = 0
user.save
end
output:
irb(main):005:1* User.all.each do |user|
irb(main):006:1* user.views = 0
irb(main):007:1* user.save
irb(main):008:0> end
User Load (0.1ms) SELECT "users".* FROM "users"
TRANSACTION (0.0ms) begin transaction
User Update (0.2ms) UPDATE "users" SET "updated_at" = ?, "views" = ? WHERE "users"."id" = ? [["updated_at", "2023-07-14 23:49:59.340777"], ["views", 0], ["id", 3]]
TRANSACTION (7.4ms) commit transaction
TRANSACTION (0.0ms) begin transaction
User Update (0.2ms) UPDATE "users" SET "updated_at" = ?, "views" = ? WHERE "users"."id" = ? [["updated_at", "2023-07-14 23:49:59.351222"], ["views", 0], ["id", 4]]
TRANSACTION (3.3ms) commit transaction
=>
[#<User views: 0, email: "giljr.2009@gmail.com", id: 3, created_at: "2023-07-14 21:26:23.681346000 +0000", updated_at: "2023-07-14 23:49:59.340777000 +0000", name: "gilberto">,
#<User views: 0, email: "test@test.com", id: 4, created_at: "2023-07-14 23:16:05.947085000 +0000", updated_at: "2023-07-14 23:49:59.351222000 +0000", name: "test">]
irb(main):009:0>
exit
This effectively resets the views
count to 0 for all users in the system. Conversely, certain post view attributes might be invalid or null.
40#step — Let’s do a static text indicating the author of the post, GoTo:
rails-blog-demo/app/views/posts/_post.html.erb
<h4>Posted by <%= link_to post.user.name, user_path(post.user) %></h4>
Let’s break down what each part of the code does:
Posted by
: This is a static text indicating the author of the post.<%= link_to post.user.name, user_path(post.user) %>
: This is an ERB code snippet that generates a link to the profile of the user who authored the post. It consists of two parts:post.user.name
: This accesses thename
attribute of theuser
associated with thepost
. Assuming that thepost
object has an association with aUser
model, it retrieves the name of the user who created the post.link_to
: This is a helper method provided by Rails to generate HTML anchor tags. It creates a hyperlink to the specified URL.user_path(post.user)
: This generates the URL path for the user's profile page, based on theuser_path
named route helper. It takes thepost.user
object (which represents the user who authored the post) as an argument and generates the appropriate URL.
Overall, this code generates an HTML heading (h4) that displays “Posted by” followed by a link to the profile of the user who authored the post. The user’s name is displayed as the link text, and clicking on it will direct the user to the profile page of the corresponding user.
41#step — The last modification, GoTo:
rails-blog-demo/app/controllers/posts_controller.rb
and type:
# GET /posts or /posts.json
def index
@posts = Post.all.order(created_at: :desc)
end
What does that do? well, if we now refresh the page we can see the most recent posts first!
That’s it:
42#step — Uploading to Heroku via GitHub merge request:
git status
git add -A
git commit -m ":lipstick: feat: Add Devise"
git push --set-upstream origin add_devise
Make this sequence.
Now GoTo Heroku and:
git checkout master
git status
git fetch
git pull
git status
git push heroku master
heroku addons:destroy
heroku run rails db:migrate
heroku run rails db:migrate
heroku open
That’s All, Folks!
See you in the next episode: Post’s comments.👋️👋️👋️!
For your convenience:
git checkout -b add_devise
bundle install
rails g devise:install
rails g devise User
rails db:migrate
rails s
rails g migration add_user_to_posts user:belongs_to
rails db:migrate
rails c
rails db:migrate
rails s
rails db:seed
rails s
rails c
rails s
rails g migration add_name_to_user name:string
rails db:migrate
rails s
rails g devise:views
rails s
rails g devise:controllers users
rails s
rails c
rails s
rails c
rails s
clear
rails g controller users profile
rails g migration add_views_to_user views:integer
rails db:migrate
rails g migration change_views_for_users
rails s
rails c
rails s
git status
git add -A
git commit -m ":lipstick: feat: Add Devise"
git push --set-upstream origin add_devise
git checkout master
git status
git fetch
git pull
git status
git push heroku master
heroku run rails db:migrate
heroku open
Credits & References
Based on: Deanin’s lessons:
Devise 4.8.1 + Rails 7.0.0 | Undefined method ‘user_url’ #5439
Related Posts:
00# Episode — RailsSeries — Installing Ruby on Rails Using ASDF — Why ASDF is Better Than RBENV for Rails Bootstrap App?
01# Episode — RailsSeries — How To Send Email In Rails 7? — User Registration and Onboarding.
02# Episode — RailsSeries — 14 Ruby Extensions 4 Vs Code — Based On This Deanin’s video.
03# Episode — RailsSeries — A Rails Blog In VS Code — Quick Start — How To Create A Blog in VS Code — Part I
04# Episode — RailsSeries — A Rails Blog In VS Code — Styling — How To Create A Blog in VS Code — Part II
05# Episode — RailsSeries — A Rails Blog In VS Code — Create Posts — How To Create A Blog in VS Code — Part III
06# Episode — RailsSeries — A Rails Blog In VS Code — Posts Tips&Tricks — How To Create A Blog in VS Code — Part IV
07# Episode — RailsSeries — A Rails Blog In VS Code — Devise — How To Create A Blog in VS Code — Part V (this one)
08# Episode — RailsSeries — A Rails Blog In VS Code — Add Comments to Post — How To Create A Blog in VS Code — Part VI
09# Episode — RailsSeries — Rails Blog In VS Code — Using Stimulus — How To Create A Blog in VS Code — Part VII
10# Episode — RailsSeries — Rails Blog In VS Code — Noticed V1 — Notifications for your Ruby on Rails app — Part VIII
11# Episode — RailsSeries — Rails Blog In VS Code — Noticed V2 — Notifications for your Ruby on Rails app — Part IXFor v5 let’s Tag it all!
git tag -a rails_blog_v5 -m "Blog in Rails 7 - v1.0: Go to https://j3-rails-blog-demo-5a0a55d44e12.herokuapp.com/" -m "0- Add Devise to the Gemfile & Run bundle install;" -m "1- Generate the Devise configuration files;" -m "2- Generate a Devise model;" -m "3- Run the database migration;" -m "4- Customize the Devise views;" -m "5- Mount Devise routes;" -m "5- Restart your Rails server;" -m "5- Upload Rails 7 project to heroku." -m "Thank you for downloading this project 😘️👌️👋️😍️"
git push origin rails_blog_v5