How to Build a Blog with Ruby on Rails

Based on Udemy The Complete Ruby on Rails Developer Course

David Allen
May 2, 2018 · 41 min read

Want to learn how to build a blog with Ruby on Rails? You’ve come to the right place. My goal with this tutorial was to be as accessible as possible to anyone beginning the Ruby on Rails learning journey, and so if you have any questions or suggestions, please don’t hesitate to let me know in the comments! With all that being said, please dive in and enjoy the learning process.

This tutorial is based on Udemy’s The Complete Ruby on rails Developer Course. I highly recommend it. Pro tip: Udemy frequently gives extreme discounts on their courses. If this course costs more than $10–15, Google a discount code before you buy it.

Table of Contents

  1. Create a new Ruby on Rails App

Step 1: Create a new Ruby on Rails App

$ rails new blog

Change directory into the app and then make the first commit to a new github repository.


<<< MAY 2019 UPDATE FROM THE AUTHOR >>>

Validating this documentation on my machine again in May 2019 and received error:

An error occurred while installing nokogiri (1.10.3), and Bundler
cannot continue.
Make sure that `gem install nokogiri -v '1.10.3'` succeeds before bundling.

To solve, I followed this documentation for MacOS:

gem update --system
xcode-select --install
#Then
gem install nokogiri

And then I added to my Gemfile in the new app for good measure.

gem 'nokogiri', '~> 1.6', '>= 1.6.8'

Then:

bundle install

<<< END UPDATE >>>

Step 2: Create a Pages Controller

There are several ways to do everything in Ruby on Rails. You can use the command line and generate a Controller or “scaffold” your up your MVC, you can create inside your text editor. I’m going to create the Controller inside my text editor.

Create a new file inside the Controller folder and name it:

pages_controller.rb

Inside the file, you must write:

class PagesController < ApplicationController def index
end
end

Step 3: Create a Pages View

Create a new folder under app/views called pages.

Create a new file under app/views/pages called

index.html.erb

Give the index page some HTML:

<h1>Welcome to Blog</h1>

<<< MAY 2019 UPDATE FROM THE AUTHOR >>>

Use the visual sublime editor for this, or navigate through the command line!

cd app/view
mkdir pages
cd pages
touch index.html.erb

Toss the HTML into the file through the editor and then save.

<<< END UPDATE >>>

Step 4: Create a Route to the Index Page

Inside the config/routes.rb file:

root 'pages#index'

The routes.rb file will look like:

Rails.application.routes.draw doroot 'pages#index'

end

Start the local server…

rails s

and then navigate to your local server…

http://localhost:3000/

You should see your page displaying the text “Welcome to Blog”

Step 5: Create an About Page, Update Routes and Create Links to Pages

Create a new file under views/pages called…

about.html.erb

Throw some HTML in there…

<h1>About Blog</h1>

Update the routes.rb file…

get 'about', to: 'pages#about'

Then create a new method in the pages_controller.rb…

class PagesController < ApplicationControllerdef index
end
def about
end
end

Add links to your index and about page…

<%= link_to 'About', about_path %>

And…

<%= link_to 'Home', root_path %>

Step 6: Build Articles Model and Controller

First thing we do is generate a migration for the articles…

$ rails g migration create_articles

Then, we jump into the migration file and add the table columns we want…

<<< MAY 2019 UPDATE FROM THE AUTHOR >>>

(File will be under db/migrate)

<<< END UPDATE >>>

Update this…

class CreateArticles < ActiveRecord::Migration[5.1]
def change
create_table :articles do |t|
end
end
end

To this…

class CreateArticles < ActiveRecord::Migration[5.1]
def change
create_table :articles do |t|
t.string :title
t.text :description
end
end
end

Then push the migration into a table by…

$ rake db:migrate

If you made a mistake and want to update the migration again…

$ rake db:rollback

Then proceed as usual.


<<< A DIFFERENT WAY OF DOING THINGS >>>

Or, another way of handling this update is to create a new migration…

$ rails g migration add_description_to_articles

This will give you a change method to update…

class AddDescriptionToArticles < ActiveRecord::Migration
def change
end
end

Update this to…

class AddDescriptionToArticles < ActiveRecord::Migration
def change
add_column :articles, :description, :text
add_column :articles, :created_at, :datetime
add_column :articles, :updated_at, :datetime
end
end

Then run the migration again…

$ rake db:migrate

and BOOM! You have an updated table scheme.rb file…

ActiveRecord::Schema.define(version: 20180502160956) docreate_table "articles", force: :cascade do |t|
t.string "title"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
end

<<< END DIFFERENT WAY OF DOING THINGS >>>


Next we need to create a model file under blog/app/models…

article.rb

<<< MAY 2019 UPDATE FROM THE AUTHOR >>>

Instead of using the visual text editor, you can also create the file through the terminal:

cd app/models
touch article.rb

<<< END UPDATE >>>

Now back in the visual text editor and inside the article.rb model…

class Article < ActiveRecord::Baseend

Step 7: Inside the Rails Console

Now, we can jump inside the rails console and test out the connection to the table…

$ rails c

Then call all the articles…

$ Article.all

You should see a SQL query that looks like…

(If you’ve been navigating inside the command line through the blog directory, you may get an error here. Make sure that you get back into the app directory or your Article.all command below will return an error.)

Typing in just ‘Article’ will give you all the table attributes…

More commands…

article = Article.new

Will create a new article stored inside the variable ‘article’ with the attributes:

Now, we can add a title and description manually through the rails console…

Now we need to save this data..

article.save

This command will generate a SQL query to insert these new values into the articles table…

We can create another article all at once by using “mass assignment”…

article = Article.new(title: "This is my second article", description: "This is my second description")

Again, we need to save the article..

article.save

Walah…

We can do this a third way using the create method…

Article.create(title: "This is my third title", description: "This is my third description")

And it will automatically save the article…

Now let’s pretend we want to edit one of these articles… First we need to grab the article…

article = Article.find(2)

Then we can edit the article and save the article…

article.title = "This is an edited second article"

And then save…

article.save

Will generate the necessary SQL query…

Now we are going to delete an article… First grab the article…

article = Article.find(3)

Then delete…

article.destroy

Walah…

Step 8: Add Validations to Enforce Constraints on Articles

In the article.rb model…

class Article < ActiveRecord::Base
validates :title, presence: true
end

This will require that all new articles contain a title. We can test this in the rails console (don’t forget to restart the rails console after you update the model! Thanks Mike Boyd and Andrew Linck for the reminders)…

article = Article.new

then try to save without update the title…

article.save

boom…

We can do the same thing for description…

class Article < ActiveRecord::Base
validates :title, presence: true
validates :description, presence: true
end

And it will prevent you from creating an article without a description even if it has a title… (again, make sure to restart the rails console after you update the model.)

You can check your errors with these commands…

article.errors.any?
article.errors.full_messages

Next, we’ll add length validations…

class Article < ActiveRecord::Base
validates :title, presence: true, length: { minimum: 3, maximum: 50 }
validates :description, presence: true, length: { minimum: 10, maximum: 300 }
end

Cool.

Next we want the users to be able to perform these same actions in the browser, so we’ll need to set up routes, controllers and views for creating and saving articles!

Step 9: Add Ability to Create Articles From Browser

The lifecycle of a new article…

Review Udemy Course first 2 minutes of Section 4, Lecture 63 for a complete overview…

First, let’s tackle our routes…

In the routes.rb file, add this line …

resources :articles

This gives us all sorts of paths to update, delete, create, show, etc.

Check new paths with… BUT WAIT, before you check paths make sure you are back out of the rails console by entering:

exit

then,

$ rake routes

Cool.

Now, we need a new controller… Create this file under app/controllers.

touch articles_controller.rb#or just create the file using the visual text editor 

And then inside the new controller file…

class ArticlesController < ApplicationControllerend

Now, we need to create a new folder under the views to save our articles view files… The folder should be called ‘articles’

<<< UPDATE FROM THE AUTHOR >>>

Try using the command line instead of the visual text editor:

cd views
mkdir articles
cd articles

<<< END UPDATE >>>

Now, we are going to create a new file under ‘articles’ called…

touch new.html.erb# or create using the visual text editor

And then we must update our articles_controller.rb:

class ArticlesController < ApplicationController
def new
end
end

Save.

Inside this new.html.erb page, we are going to store our form to capture title and description.

But first, let’s make sure the page is working… Add:

<h1>Create an article</h1>

And then load the server…

http://localhost:3000/articles/new

Gives us…

Great. It works.

Now, let’s create the model-backed form. Rails provides form-for helper methods here.

In the new.html.erb file…

<h1>Create an Article</h1><%= form_for @article do |f| %><% end %>

And we initiated an instance variable, so now we need to create the variable in our controller…

class ArticlesController < ApplicationControllerdef new
@article = Article.new
end
end

Great. Everything is working fine…

Now, let’s collect some data…

<h1>Create an Article</h1><%= form_for @article do |f| %>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<% end %>

And load the page to see what we’ve done…

Great. It works…

Now, let’s finish the form…

<h1>Create an Article</h1><%= form_for @article do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>

Great. Everything we need…

Now, we need to create a ‘create’ action in the controller…

class ArticlesController < ApplicationControllerdef new
@article = Article.new
end
def create
render plain: params[:article].inspect
end
end

When you fill out the form and then submit, you’ll see this data:

But we want this data to be saved into the database…

class ArticlesController < ApplicationControllerdef new
@article = Article.new
end
def create
#render plain: params[:article].inspect
@article = Article.new(article_params)
@article.save
end
private
def article_params
params.require(:article).permit(:title, :description)
end
end

But that’s not enough… We also want to show the article…

We’ll use a path that we already created, and pass in the instance variable for the new article…

redirect_to articles_show(@article)

All the code…

class ArticlesController < ApplicationControllerdef new
@article = Article.new
end
def create
#render plain: params[:article].inspect
@article = Article.new(article_params)
@article.save
redirect_to articles_show(@article)
end
private
def article_params
params.require(:article).permit(:title, :description)
end
end

But that’s not good enough, is it? We want to refactor this code to give a notice if the article was successfully created:

def create
@article = Article.new(article_params)
if @article.save
flash[:notice] = "Article was successfully created"
redirect_to article_path(@article)
else
render 'new'
end
end

And now we are going to make sure our flash messages can be seen in all pages…

In the body of the application.html.erb file…

<% flash.each do |name, msg| %>
<ul>
<li><%= msg %></li>
</ul>
<% end %>

And now we want to make sure errors are displayed as well…

In the new.html.erb file…

<% if @article.errors.any? %>
<h2>The following errors prevented the article from saving:</h2>
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>

Great. Now it will show us the errors in the articles/new URL:

Now make sure show action has been created…

def show
@article = Article.find(params[:id])
end

Now we need to create a page to show the information on…

We will call that file…

show.html.erb

Inside that page…

<h1>Showing selected article</h1><p>
Title: <%= @article.title %>
</p>
<p>
Description: <%= @article.description %>
</p>

And that will render…

(Get an error? Here is the solution to a common error: creating the show.html.erb file in the wrong place)

Hurrah. Next we will work on editing articles…

Step 10: Editing Articles

We need to create the edit action in the controller… Which controller? The articles controller of course!

def edit
end

And we need to create the edit template in the views… Under which folder? The articles view folder of course!

edit.html.erb

Now we need to build a form to edit…

We can actually copy/paste from the new.html.erb form. We’ll use the same form to update a current article that we used to create a new article.

<h1>Edit Article</h1><% if @article.errors.any? %>
<h2>The following errors prevented the article from saving:</h2>
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= form_for @article do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>

Now, let’s complete our edit action in the controller…

def edit
@article = Article.find(params[:id])
end

Now, we need to add an update action…

def update
@article = Article.find(params[:id])
if @article.update(article_params)
flash[:notice] = "Article was updated"
redirect_to article_path(@article)
else
flash[:notice] = "Article was not updated"
render 'edit'
end
end

Step 11: Add Index Listing of Articles, and Add Edit/Show Links

Add index action in articles_controller.rb…

def index
@articles = Article.all
end

Then we need to create the template… In which views directory will we place this file?

index.html.erb

And inside that file…

<h1>Listing all articles</h1><table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
</tr>
<% end %>
</table>

This gives us…

Cool. Now let’s add the edit and show links for each article…

<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Show', article_path(article) %></td>

The code looks like…

<h1>Listing all articles</h1>
<p>
<%= link_to "Create new article", new_article_path %>
</p>
<table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Show', article_path(article) %></td>
</tr>
<% end %>
</table>

That gives us…

Now let’s add some ‘Back’ links to the edit, show and new page… Go ahead and jump into the respective pages and add the new links like so:

<%= link_to 'Back', articles_path %>

I added mine to the very bottom of each file.

And let’s add an edit link on the show page…

<%= link_to "Edit", edit_article_path(@article) %>

Step 12: Refactor Forms into a Single Partial

Create a new file in the views/articles directly.

_form.html.erb

Copy/paste the form data into this file…

<% if @article.errors.any? %>
<h2>The following errors prevented the article from saving:</h2>
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= form_for @article do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>

Then render the form in the new and edit page…

<%= render 'form' %>

You should have scrapped out all the old form information and added the code snippet about to the new and edit page so that each looks something like this:

<h1>Edit Article</h1><%= render 'form' %><%= link_to 'Back', articles_path %>

Now, we will add the destroy action…

Step 13: Add the Destroy Action

In the controller…

Which controller? You already know.

def destroy
@article = Article.find(params[:id])
@article.destroy
flash[:notice] = "Article was deleted"
redirect_to articles_path
end

Now, we need to add the links to the pages from where we want to be able to delete. Using the link below…

<%= link_to 'Delete', article_path(article), method: :delete, data: {confirm: "Are you sure?"} %>

…add to the pages where you’d like the user to be able to delete.

If I add to our index page, it’ll look like…

<h1>Listing all articles</h1>
<p>
<%= link_to "Create new article", new_article_path %>
</p>
<table>
<tr>
<th>Title</th>
<th>Description</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.description %></td>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Show', article_path(article) %></td>
<td><%= link_to 'Delete', article_path(article), method: :delete, data: {confirm: "Are you sure?"} %></td>
</tr>
<% end %>
</table>

Add if we add to our show page, we’ll have to modify the destroy link just slightly to mimic the structure of the edit link. Do you see what I mean?

This:

<%= link_to 'Delete', article_path(@article), method: :delete, data: {confirm: "Are you sure?"} %>

Instead of this:

<%= link_to 'Delete', article_path(article), method: :delete, data: {confirm: "Are you sure?"} %>

Step 14: Refactor Articles Controller

One of the principles of development is DRY.

You may already know what it means: “DON’T REPEAT YOURSELF”

We’re repeating ourselves quite a bit, so let’s do a little refactoring.

This line of code appears in all of our actions…

@article = Article.find(params[:id])

So, we will create a private method and store this line there…

def set_article
@article = Article.find(params[:id])
end

Then, delete the line of code from the actions…

It’s included in destroy, show, update, edit…

Right below the first line of the controller…

before_action :set_article, only: [:edit, :update, :show, :destroy]

The updated controller file will look like:

class ArticlesController < ApplicationController
before_action :set_article, only: [:edit, :update, :show, :destroy]
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
flash[:notice] = "Article was successfully created"
redirect_to article_path(@article)
else
render 'new'
end
end
def show
end
def update
if @article.update(article_params)
flash[:notice] = "Article was updated"
redirect_to article_path(@article)
else
flash[:notice] = "Article was not updated"
render 'edit'
end
end
def edit
end
def index
@articles = Article.all
end
def destroy
@article.destroy
flash[:notice] = "Article was deleted"
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :description)
end
def set_article
@article = Article.find(params[:id])
end
end

Step 15: Install Bootstrap

Add gemfile from github page

gem 'bootstrap-sass', '~> 3.4.1'
gem 'sassc-rails', '>= 2.1.0'

Then in the command line, making sure you are in the app directory and not in one of the sub-directories, install…

$ bundle install

You’ll also need to create a new CSS file… The file name might look like this below and you’ll create it under app/assets/stylesheets

custom.css.scss

And add…

// "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables"
@import "bootstrap-sprockets";
@import "bootstrap";

Then, in the application.js file under app/assets/javascripts…

//= require jquery
//= require bootstrap-sprockets

And, if using Rails 5, install the jQuery gem…

gem 'jquery-rails'

Not sure what version you are using? Run the following in the terminal:

$ rails -v

Now you know. If you need to install jquery, add the gem above to the gemfile and bundle install.

$ bundle install

Step 16: Install Navigation Bar

Create partial under the layouts folder…

_navigation.html.erb

Now, go to bootstrap’s homepage

Grab the default navbar code… It’s the first example and looks something like this:

And paste into your _navigation partial… Save.

Now render the partial inside the <head> </head> of the application.html.erb file…

<%= render 'layouts/navigation' %>

The whole file now looks something like this:

<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render 'layouts/navigation' %>
<% flash.each do |name, msg| %>
<ul>
<li><%= msg %></li>
</ul>
<% end %>
<%= yield %>
</body>
</html>

Restart the server (on a mac: control + c) because you’ve recently added some new gems, and then start it up again:

$ rails s

And then when you refresh your local server, you’ll see:

Niiiiiiice…. Looks good, no?

Now, let’s start styling! Or rather, let’s get styling out of our way really quickly so we can keep our focus on building the app rather than styling the app :)

In the custom.css.scss file copy/past and then save:

$navbar-default-bg: black;@import "bootstrap-sprockets";
@import "bootstrap";
#logo {
float: left;
font-size: 1.7em;
color: white;
text-transform: uppercase;
letter-spacing: -1px;
font-weight: bold;
}
#logo:hover {
color: #fff;
text-decoration: none;
}
.article-actions {
border-top: 1px solid #eaeaea;
padding-top: 5px;
}
.article-title {
font-weight: bold;
font-size: 1.5em;
}
.article-body {
border-top: 1px solid #eaeaea;
padding-top: 15px;
padding-bottom: 15px;
}
.article-meta-details {
border-top: 1px solid #eaeaea;
margin-top: 15px;
}
.description {
margin-top: 0;
}
.listing {
list-style: none;
padding-left: 0;
}

Step 17: Add Links to the Navbar

Now, let’s tackle the Nav bar and make sure the navbar links point to the places we want them to point to!

This…

<a class="navbar-brand" href="#" id="logo">Alpha Blog</a>

becomes this:

<%= link_to "Alpha Blog", root_path, class: "navbar-brand", id: "logo" %>

Refresh your page and see:

And now on any page, you can navigate to the root_path (the index page) by clicking on ALPHA BLOG.

Next, let’s add a link to the Articles Index page:

Instead of the first “Link” in the Navbar:

<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>

Add:

<li><%= link_to "Articles", articles_path %></li>

And instead of the second “Link” in the Navbar:

<li><a href="#">Link</a></li>

Add:

<li><%= link_to "Create Article", new_article_path %></li>

Now we can remove the other link to “Create Article” in the index.html.erb file. Just track it down and delete it.

I’m also going to delete the search bar. We won’t be using that.

Rip out everything below:

<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>

We don’t need the first drop-down menu either, so out it goes. Just find this and delete it:

<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>

Save file. Refresh your localhost server and you’ll see:

Step 18: Style the Form Template

You can find the code on bootstrap here.

We are going to mix embedded ruby with bootstrap to get the result we want.

Inside the _form.html.erb partial, Change this…

<% if @article.errors.any? %>
<h2>The following errors prevented the article from saving:</h2>
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= form_for @article do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.submit %>
</p>
<% end %><%= link_to 'Back', articles_path %>

Into this…

<% if @article.errors.any? %>
<h2>The following errors prevented the article from saving:</h2>
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<div class="row">
<div class="col-xs-12">
<%= form_for(@article, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :title %>
</div>
<div class="col-sm-6">
<%= f.text_field :title, class: "form-control", placeholder: "Title of article", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :description %>
</div>
<div class="col-sm-6">
<%= f.text_area :description, rows: 10, class: "form-control", placeholder: "Body of article" %>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<%= f.submit class: 'btn btn-primary btn-large' %>
</div>
</div>
<% end %>
</div>
</div>

Note an important change we made to the beginning of the form do statement…

<%= form_for(@article, :html => {class: "form-horizontal", role: "form"}) do |f| %>

And note the syntax inside the form elements…

<%= f.text_field :title, class: "form-control", placeholder: "Title of article", autofocus: true %>

Step 19: Style the Messages & Refactor into Partials

Now let’s also update the styling for the error messages. Update the styling from…

<% if @article.errors.any? %>
<h2>The following errors prevented the article from saving:</h2>
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>

to:

<% if @article.errors.any? %>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="panel panel-danger">
<div class="panel-heading">
<h2 class="panel-title">
<%= pluralize(@article.errors.count, "error") %>
prohibited this article from being saved:
</h2>
<div class="panel-body">
<ul>
<% @article.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
</div>
</div>
</div>
<% end %>

That changes the error messages to:

Now we will extract to a partial..

Create a new folder under the views called ‘shared’…

Create a new file called _errors.html.erb…

Copy/paste the error message into that file… Save.

Now remove the error message from _form.html.erb, and replace the code with:

<%= render 'shared/errors' %>

Let’s also create a _messages.html.erb file and store it under the shared folder. Inside that file, add the flash messages code:

<% flash.each do |name, msg| %>
<ul>
<li><%= msg %></li>
</ul>
<% end %>

Save that file. Make sure to replace the code in the layouts/application.html.erb file with:

<%= render 'shared/errors' %>

The whole file currently looks like:

<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render 'layouts/navigation' %>
<%= render 'shared/messages' %>
<%= yield %>
</body>
</html>

Next, we’ll style the flash messages…

In the _messages.html.erb partial, change this…

<% flash.each do |name, msg| %>
<ul>
<li><%= msg %></li>
</ul>
<% end %>

to this:

<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<% flash.each do |name, msg| %>
<div class='alert alert-<%= "#{name}" %>'>
<a href="#" class="close" data-dismiss="alert">&#215;</a>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
</div>
<% end %>
</div>
</div>

We’ll also need to change all of the flash[:notice] in the articles_controller.rb file into flash[:success] to complete this step.

Step 20: Update Article Instance Variables To Work For Users

This is a bit premature as we haven’t even created users or user actions yet for our blog, but we will create users and we’ll want our error messages to work for users just like they work for articles. Know what I mean?

We have all of these functions that work for articles: creating new articles, editing old articles, and the flash error messages that let us know what went wrong… We want these flash error messages to communicate to us when we’re trying to create a new user or edit a current user.

We’ll accomplish this by updating the @article instance variable into one that will work for articles or users…

In the shared/_errors.html.erb file, update the @article instance variable to an @obj instance variable…

<% if obj.errors.any? %>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="panel panel-danger">
<div class="panel-heading">
<h2 class="panel-title">
<%= pluralize(obj.errors.count, "error") %>
prohibited this object from being saved:
</h2>
<div class="panel-body">
<ul>
<% obj.errors.full_messages.each do |msg|%>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
</div>
</div>
</div>
<% end %>

Now, we need to update the partial rendering statement… The one at articles/_form.html.erb

It was…

<%= render 'shared/errors' %>

and now it must read…

<%= render 'shared/errors', obj: @article %>

Next, we’re going to work on the “show” view…

Step 21: Style the Show Article Page

Update the views/articles/show.html.erb page to…

<h2 align="center">Title: <%= @article.title %></h2>
<div class="well col-xs-8 col-xs-offset-2">
<h4 class="center"><strong>Description:</strong></h4>
<hr>
<%= @article.description %>
<%= link_to "Edit", edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>

This will render:

Next, we’re going to add some ‘simple_format’…

Change this…

<%= @article.description %>

To this…

<%= simple_format(@article.description) %>

Then, some more style for the buttons…

<%= link_to "Edit", edit_article_path(@article), class: "btn btn-xs btn-primary" %> |
<%= link_to 'Back', articles_path, class: "btn btn-xs btn-success" %>

Will give us some beautiful little buttons…

Then, add a delete button…

<%= link_to "Delete", article_path(@article), method: :delete, data: { confirm: "Are you sure you want to delete this article?"}, class: "btn btn-xs btn-danger" %>

A nice colorful delete button…

The whole code block looks like this now:

<h2 align="center">Title: <%= @article.title %></h2>
<div class="well col-xs-8 col-xs-offset-2">
<h4 class="center"><strong>Description:</strong></h4>
<hr>
<%= simple_format(@article.description) %>
<%= link_to "Edit", edit_article_path(@article), class: "btn btn-xs btn-primary" %> |
<%= link_to 'Back', articles_path, class: "btn btn-xs btn-success" %>
<%= link_to "Delete", article_path(@article), method: :delete, data: { confirm: "Are you sure you want to delete this article?"}, class: "btn btn-xs btn-danger" %>

Step 22: Style Article Listing Index Page

We’re going to update this horribly janky looking page…

The index.html.erb page should be replaced with the following code:

<h1 align="center">Listing all articles</h1><% @articles.each do |article| %>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="well well-lg">
<div class="article-title">
<%= link_to article.title, article_path(article) %>
</div>
<div class="article-body">
<%= truncate(article.description, length: 100) %>
</div>
<div class="article-actions">
<%= link_to "Edit", edit_article_path(article), class: "btn btn-xs btn-primary" %>
<%= link_to "Delete", article_path(article), method: :delete, data: { confirm: "Are you sure you want to delete this article?"}, class: "btn btn-xs btn-danger" %>
</div>
</div>
</div>
</div>
<% end %>

Styling for the article body:

.article-body {
border-top: 1px solid #eaeaea;
padding-top: 15px;
padding-bottom: 15px;
}

Styling for the article title:

.article-title {
font-weight: bold;
font-size: 1.5em;
}

Your page will now look like this:

Lovely.

Step 23: Creating Users + Create a Feature Branch in Github

Next, we will be creating users! Cool. Our model for users will be called “User”. Below is a helpful chart that explains how the user table will be formatted and what the different naming structures for the files will be:

We’ve already built articles. We can create, read, edit and delete articles.

But right now anybody who accesses the blog can read, edit and delete any other article.

We only want users who have signed up to be able to do these functions, and we only want them to be able to edit and delete articles that they have created.

So we need users of course, and we also need a way to track users who create articles. This can be done by having the user id for the user creating the article stored in the article table in the article row. This is a foreign key association:

Adding the user_id column to the articles table will give us the ability to “v-lookup” to the rest of the user information.

One article that multiple people work on would be a many-to-many association…

One article that one person works on, where one person can create many articles, is one-to-many association…

Article categories, should we create them, would be a many-to-many association.

Ok, enough jib-jab, lets create the User’s table…


QUICK ASIDE: I haven’t been including anything about github in this tutorial. For this particular User feature, I am going to practice switching to a github feature branch so as to practice some development best-practices…

$ git checkout -b create-users

You can type in…

$ git branch

To view all of the repository branches and the one you’re working on…

If you want to switch back…

$ git checkout master

And switch again…

$ git checkout create-users

Now, to create the Users table…

$ rails g migration create_users

And then find the db/migrate file, for create_users, and update the columns from this…

class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
end
end
end

to this…

class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :username
t.string :email
t.timestamps
end
end
end

Save the file and then…

$ rake db:migrate

That created the user table. Next, create the model…

Create a new file under app/models called user.rb.

Inside that file, add:

class User < ActiveRecord::Baseend

Now, let’s jump into the console to test the connection…

$ rails c

A quick query will tell us if things are working properly…

> User.all

Should display a SQL query…

It’s working!

> user = User.create(username: "david", email: "david@example.com")

And this will automatically create an entry…

We can test with…

> User.all

You can update the entry by grabbing the entry you want to edit…

> user = User.find(1)

Then update the email…

> user.email = "david1@example.com"

Then save…

> user.save

Boom!

Now, let’s test delete…

> user = User.find(1)

Then destroy…

> user.destroy

And boom! Destroyed…

We can call all the Users just to confirm…

> User.all

Now exit the console…

> exit

If you are following along with git, time to merge the branches…

$ git status
$ git add .
$ git commit -m "create users table and model"
$ git checkout master
$ git merge create-users

Now we no longer need the feature branch…

So we delete…

$ git branch -d create-users

And now let’s push!

$ git push

Step 24: Add User Validations + Create a Feature Branch in Github

Validations for User class:

  • username must be present and unique

First, we’ll create a feature branch…

$ git checkout -b user-validations

A good place to start on validations here

In the user model, we’ll make sure email and username is present, is unique, and we’ll add some additional requirements…

In the user.rb file, add:

validates :username, presence: true, uniqueness: true, length: { minimum: 3, maximum: 25 }
validates :email, presence: true, uniqueness: true

The whole file currently looks like:

class User < ActiveRecord::Base
validates :username, presence: true, uniqueness: true, length: { minimum: 3, maximum: 25 }
validates :email, presence: true, uniqueness: true

end

You can check that everything is working in the rails console

$ rails c

Backing up, let’s make sure the uniqueness requirement is not case sensitive…

validates :username, presence: true, uniqueness: { case_sensitive: false }, length: { minimum: 3, maximum: 25 }

Next, we’ll work on the email validations…

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

And then…

validates :email, presence: true, length: { maximum: 105 }, uniqueness: { case_sensitive: false }, 
format: { with: VALID_EMAIL_REGEX }

The whole file looks like:

class User < ActiveRecord::Base
validates :username, presence: true, uniqueness: { case_sensitive: false }, length: { minimum: 3, maximum: 25 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 105 }, uniqueness: { case_sensitive: false },
format: { with: VALID_EMAIL_REGEX }
before_save { self.email = email.downcase }
end

Great. We’re done so it’s time to commit, merge, push and delete our feature branch…

$ git add .
$ git commit -m "added user validations"
$ git checkout master
$ git merge user-validations
$ git push
$ git branch -d user-validations

And we’re done with user validations!

Step 25: One-to-Many Association

We are going to add an association between user and articles

Create a new feature branch…

$ git checkout -b userarticle-association

Then create a new migration to add the column…

$ rails g migration add_user_id_to_articles

Then inside the migration file…

def change
add_column :articles, :user_id, :integer
end

The first item ‘:articles’ is the table to add the change to…

The second item ‘:user_id’ is the column name to add to the table…

The third item ‘:integer’ is the data type…

The whole file looks like:

class AddUserIdToArticles < ActiveRecord::Migration[5.1]
def change
add_column :articles, :user_id, :integer
end
end

Save. Then run the migration…

$ rake db:migrate

And then jump on the rails console to test it out!

$ rails c

If you query…

> Article.all

You can see that there is now a user_id column that is nil…

Now, back inside the model user.rb file…

Add an association with the articles…

has_many :articles

Then in the article.rb file…

Create an association with the user…

belongs_to :user

and that’s it to create the association!

Now, we’ll create a new validation to the article.rb file to make sure every article is saved with a user…

validates :user_id, presence: true

Boom!

And now, we’re going to add one more thing to our user.rb file. We are going to make sure the email address is transformed into lowercase before saving…

before_save { self.email = email.downcase }

And now jump back into the rails console to test out the association…

$ rails c

And then try to make a new article…

> article = Article.new(title: "this is a title", description: "this is a description)

If you try to save…

> article.save

You can’t! Our validations are working…

You can check the error on your creation attempt with…

> article.error.full_messages

Now, we’ll want to make sure that every new article has a user associated with it in the articles_controller.rb…

@article.user = User.first

This line is stored in the create action…

The whole action looks like:

def create
@article = Article.new(article_params)
@article.user = User.first
if @article.save
flash[:notice] = "Article was successfully created"
redirect_to article_path(@article)
else
render 'new'
end
end

Now, every time a new article is created, it is created with a User…

By the end of this section, your entire user.rb file should look something like:

class User < ActiveRecord::Base
has_many :articles
validates :username, presence: true, uniqueness: { case_sensitive: false }, length: { minimum: 3, maximum: 25 }
validates :email, presence: true, length: { maximum: 105 }, uniqueness: { case_sensitive: false },
format: { with: VALID_EMAIL_REGEX }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
before_save { self.email = email.downcase }
end

And your article.rb file should look something like:

class Article < ActiveRecord::Base
belongs_to :user
validates :title, presence: true, length: { minimum: 3, maximum: 50 }
validates :description, presence: true, length: { minimum: 10, maximum: 300 }
validates :user_id, presence: true
end

SIDE-NOTE ON DEBUGGER

Add this to your application.html.erb page to see useful debugging messages during the development process…

<%= debug(params) if Rails.env.development? %>

The message shown is different on each page and shows the action, controller and parameters at work…


To finish up this section, let’s commit…

$ git add .
$ git commit -m "add association between user and articles"
$ git checkout master
$ git merge userarticles-association
$ git push
$ git branch -d userarticles-association

Annnnnd we’re done…

Step 26: Show User Info in Articles

Allllright, so we want to show some user data in the articles index page…

Stuff like “who wrote this” and “when did they write this”…

Navigate to views/articles/index.html.erb…

And add this code in the div of class article-body…

<small>Created by: <%= article.user.username %>,
<%= time_ago_in_words(article.created_at) %> ago,
last updated: <%= time_ago_in_words(article.updated_at) %> ago</small>

If we start up the server, we’ll get an error…

This error is showing up because we’ve made a bunch of articles in the database without users…

No user, no username method…

We can fix this a couple of different ways, but the quickest is to add an if statement so that this new block of code is only executed IF user is present…

<small>Created by: <%= article.user.username if article.user %>,
<%= time_ago_in_words(article.created_at) %> ago,
last updated: <%= time_ago_in_words(article.updated_at) %> ago</small>

Once you’ve fixed this, you may also see a different error:

This is because our article table does not have timestamps! We need to add timestamps, and we’ll do that by creating a new migrations (I followed the directions here).

Run:

$ rails g migration add_timestamp_to_articles

And then, in the db/migrate file associated with this migration, updating the DateTime to today’s date:

class AddTimestampToArticles < ActiveRecord::Migration[5.1]
def change
# add new column but allow null values
add_timestamps :articles, null: true
# backfill existing record with created_at and updated_at
# values making clear that the records are faked
long_ago = DateTime.new(2000, 1, 1)
Article.update_all(created_at: long_ago, updated_at: long_ago)
# change not null constraints
change_column_null :articles, :created_at, false
change_column_null :articles, :updated_at, false
end
end

Save. Then:

rake db:migrate

Refresh the page, and…

Baboom!

Now, for some styling…

Wrap in a div…

<div class="article-meta-details">//stuff//</div>

And then in the custom.css.scss file, if you haven’t already:

.article-meta-details {
border-top: 1px solid #eaeaea;
margin-top: 15px;
}

Your whole index.html.erb file should look something like this:

<h1 align="center">Listing all articles</h1>
<% @articles.each do |article| %>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="well well-lg">
<div class="article-title">
<%= link_to article.title, article_path(article) %>
</div>
<div class="article-body">
<%= truncate(article.description, length: 100) %>
</div>
<div class="article-meta-details">
<small>Created by: <%= article.user.username if article.user %>, <%= time_ago_in_words(article.created_at) %> ago, last updated: <%= time_ago_in_words(article.updated_at) %> ago</small>
</div>
<div class="article-actions">
<%= link_to "Edit", edit_article_path(article), class: "btn btn-xs btn-primary" %>
<%= link_to "Delete", article_path(article), method: :delete, data: { confirm: "Are you sure you want to delete this article?"}, class: "btn btn-xs btn-danger" %>
</div>
</div>
</div>
</div>
<% end %>

Save and refresh your page!

Step 27: Signing Up Users

We are going to use the has_secure_password method for this section…

To execute this step, we need to…

  1. add has_secure_password method to the user.rb model

First step! In the user.rb model, add…

has_secure_password

then, in the gemfile…

gem 'bcrypt', '~> 3.1.7'

then bundle install the gem…

$ bundle install

Now that the gem is installed, generate a migration to add password_digest to users table…

$ rails g migration add_password_digest_to_users

Then in the migration file…

class AddPasswordDigestToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :password_digest, :string
end
end

we added the line…

add_column, :users, :password_digest, :string

Translation: *add_column, table name, column name, type of data in column*

Save…

Run the migration and add the column…

rake db:migrate

Now we jump into the console to play around…

$ rails c

Query…

> user = User.last

And you’ll see a SQL query that displays an empty password_digest field. Nice!

We can set a new password by…

> user.password = "password"

and then save…

> user.save

You can see that an encrypted password was created instead of “password”…

Now we can try authenticating the user with two false passwords and one true password to see how it works in the console…

Great. Now the backend is complete for Users!

Next we’ll work on signing up users in the UI.

Step 28: Signing Up Users in the Browser

In this section, we’ll add the new and create action to the users controller, and create a form to gather user data…

First, build the route for the sign-up page…

In the config/routes.rb file…

get 'signup', to: 'users#new'

Then we need to create a users_controller.rb file under app/controllers and add a new action…

class UsersController < ApplicationControllerdef new
end
end

Then, we need to create the view!

Create a ‘users’ folder under views, and then create a new.html.erb file under users.

The form will be a ‘model-backed form’, and we can steal a lot of work from our articles form partial to help create users.

Next, we’ll create a form partial called _form.html.erb inside the users folder.

Copy/paste the form partial under views/articles to re-use in the users/new.html.erb file, and we’ll update it to work with the user.rb model instead of the article.rb model:

<%= render 'shared/errors', obj: @user %><div class="row">
<div class="col-xs-12">
<%= form_for(@user, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :username %>
</div>
<div class="col-sm-8">
<%= f.text_field :username, class: "form-control", placeholder: "Enter username", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :email %>
</div>
<div class="col-sm-8">
<%= f.email_field :email, class: "form-control", placeholder: "Enter email" %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :password %>
</div>
<div class="col-sm-8">
<%= f.password_field :password, class: "form-control", placeholder: "Enter password" %>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<%= f.submit(@user.new_record? ? "Sign up" : "Update account", class: 'btn btn-primary btn-large') %>
</div>
</div>
<% end %>
<div class="col-xs-4 col-xs-offset-2">
[ <%= link_to 'Cancel request and Return to Articles Listing', articles_path %> ]
</div>
</div>
</div>

Notice we updated the object we reference in the errors partial to be user instead of articles…

<%= render 'shared/errors', obj: @user %>

instead of…

<%= render 'shared/errors', obj: @articles %>

And we updated the labels and field names.

Now inside the views/users/new.html.erb file:

<h1 align="center">Signup</h1><%= render 'form' %>

Now, let’s turn our attention to the users controller…

In the new action…

def new
@user = User.new
end

Then we need to create a path to handle the POST action for users…

In the routes.rb file…

resources :users, except: [:new]

and don’t forget to create the ‘create’ action in the users controller…

def create
end

Check it out!

Now in the users_controller.rb file…

def create
@user = User.new(user_params)
end

And then define the private method for user_params…

privatedef user_params
params.require(:user).permit(:username, :email, :password)
end

Then let’s add some more to our create action…

def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Welcome to the alpha blog #{@user.username}"
redirect_to articles_path
else
render 'new'
end
end

Niiiiiice…

Step 29: Editing Users

We’ll need an edit action, update action, and view for editing our users…

We can go ahead and create the view and the two actions…

The edit form submission is going to be handled by the update action…

The edit is going to have the user’s id in the params…

def edit
@user = User.find(params[:id])
end

Then we need to build our edit template…

Create a new page under views/users called edit.html.erb, and render the same form as in the new.html.erb form…

<h1 align="center">Edit User Account</h1><%= render 'form' %>

Then, we need to create our update action…

def update
@user = User.find(params[:id])
if @user.update(user_params)
flash[:success] = "Your account was updated successfully"
redirect_to articles_path
else
render 'edit'
end
end

Notice in the form partial that we had previously pasted in, that there is a bit of logic to handle the name of the submit button…

If it’s a new user, the button text should be “Sign up”. If not, the button text should be “Update account”.

<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<%= f.submit(@user.new_record? ? "Sign up" : "Update account", class: 'btn btn-primary btn-large') %>
</div>
</div>

Head to http://localhost:3000/users/1/edit to checkout your user profile’s edit page.

Step 30: Show User Profile Image

We will be adding the “show users page” to display the user information and a list of all articles this user has created…

We need a show action…

def show
@user = User.find(params[:id])
end

And then we need a show page… views/users/show.html.erb

<h1 align="center">Welcome to <%= @user.username %>'s page</h1>

Next, we’re going to use Gravatar to add an image of the user to associate with their email address…

<h1 align="center">Welcome to <%= @user.username %>'s page</h1>
<div class="row">
<div class="col-md-4 col-md-offset-4 center">
<%= gravatar_for @user %>
</div>
</div>
<h4 align="center"><%= @user.username %>'s articles</h4>

We prematurely created a gravatar_for method in our view, so let’s define in the application helper section under app/helpers/application_helper.rb…

module ApplicationHelper
def gravatar_for(user)
end
end

And then…

module ApplicationHelper
def gravatar_for(user)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
image_tag(gravatar_url, alt: user.username, class: "img-circle")
end
end

Now, we’ll make a few additional updates…

module ApplicationHelper
def gravatar_for(user, options = { size: 80})
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
size = options[:size]
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.username, class: "img-circle")
end
end

Now, we can change the size of the image easily in the show.html.erb file…

<%= gravatar_for @user, size: 150 %>

Instead of…

<%= gravatar_for @user %>

Now, to list the articles…

We’ve already written some code for listing articles in the Index page…

We’ll refactor that code into a partial called _article.html.erb…

Create a new file under views/shared called _article.html.erb, and inside that file write:

<% obj.each do |article| %>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="well well-lg">
<div class="article-title">
<%= link_to article.title, article_path(article) %>
</div>
<div class="article-body">
<%= truncate(article.description, length: 100) %>
<div class="article-meta-details">
<small>Created by: <%= article.user.username if article.user %>,
<%= time_ago_in_words(article.created_at) %> ago,
last updated: <%= time_ago_in_words(article.updated_at) %> ago</small>
</div>
</div>
<div class="article-actions">
<%= link_to "Edit", edit_article_path(article), class: "btn btn-xs btn-primary" %>
<%= link_to "Delete", article_path(article), method: :delete, data: { confirm: "Are you sure you want to delete this article?"}, class: "btn btn-xs btn-danger" %>
</div>
</div>
</div>
</div>
<% end %>

Notice that again we updated the @article instance variable to something more generic that we’ll be able to user for both the index view and the user profile view…

<% obj.each do |article| %>

instead of…

<% @articles.each do |article| %>

Then in the index page…

<%= render 'shared/article', obj: @articles %>

Instead of…

<%= render 'article' %>

Now in the show action…

<%= render 'shared/article', obj: @user.articles %>

The whole views/users/show.html.erb file should look like:

<h1 align="center">Welcome to <%= @user.username %>'s page</h1>
<div class="row">
<div class="col-md-4 col-md-offset-5 center">
<%= gravatar_for @user, size: 150 %>
</div>
</div>
<h4 align="center"><%= @user.username %>'s articles</h4><%= render 'shared/article', obj: @user.articles %>

That does it! Commit to github if that’s your thing and let’s move on…

Step 31: Add Users Index Page

First, we need an index action…

def index
@users = User.all
end

and then we need an index view under users… views/users/index.html.erb

Start with a do block…

<h1 align="center">All Users</h1><% @users.each do |user| %><% end %>

And fill in the block…

<h1 align="center">All Users</h1><% @users.each do |user| %>
<ul class="listing">
<div class="row">
<div class="well col-md-4 col-md-offset-4">
<li><%= link_to gravatar_for(user), user_path(user) %></li>
<li class="article-title"><%= link_to user.username, user_path(user) %></li>
<li><small><%= pluralize(user.articles.count, "article") if user.articles %></small></li>
</div>
</div>
</ul>
<% end %>

Now, for some styling inside the custom.css.sccs file, if you haven’t already…

.listing {
list-style: none;
padding-left: 0;
}

And then wrap the whole do block inside a centered div…

<div align="center">// all the stuff //</div>

The file will look like:

<h1 align="center">All Users</h1>
<div align="center">

<% @users.each do |user| %>
<ul class="listing">
<div class="row">
<div class="well col-md-4 col-md-offset-4">
<li><%= link_to gravatar_for(user), user_path(user) %></li>
<li class="article-title"><%= link_to user.username, user_path(user) %></li>
<li><small><%= pluralize(user.articles.count, "article") if user.articles %></small></li>
</div>
</div>
</ul>
<% end %>
</div>

You can see the index page at http://localhost:3000/users

It may look something like:

Step 32: Add Users to Articles Show Page

Now to tackle the articles show page. We want each article to show the user and the user’s article stats…

We accomplish this by adding the @article instance variable in front of all the database queries…

<ul class="listing">
<div class="row" align="center">
<div class="col-md-4 col-md-offset-4">
<li><%= link_to gravatar_for(@article.user), user_path(@article.user) %></li>
<li class="article-title"><%= link_to @article.user.username, user_path(@article.user) %></li>
<li><small><%= pluralize(@article.user.articles.count, "article") if @article.user.articles %></small></li>
</div>
</div>
</ul>

We have a problem, though…

If the article doesn’t have a user associated with it, it blows up…

but, we can address this with an embedded if statement…

<% if @article.user %>show the user profile...<% end %>

Boom! Now it works…

The whole articles/show.html.erb file should look like:

<h2 align="center">Title: <%= @article.title %></h2>
<div class="well col-xs-8 col-xs-offset-2">
<% if @article.user %>
<ul class="listing">
<div class="row" align="center">
<div class="col-md-4 col-md-offset-4">
<li><%= link_to gravatar_for(@article.user), user_path(@article.user) %></li>
<li class="article-title"><%= link_to @article.user.username, user_path(@article.user) %></li>
<li><small><%= pluralize(@article.user.articles.count, "article") if @article.user.articles %></small></li>
</div>
</div>
</ul>
<% end %>
<h4 class="center description"><strong>Description:</strong></h4>
<hr>
<%= simple_format(@article.description) %>
<div class="article-actions">
<%= link_to "Edit", edit_article_path(@article), class: "btn btn-xs btn-primary" %>
<%= link_to "Delete", article_path(@article), method: :delete, data: { confirm: "Are you sure you want to delete this article?"}, class: "btn btn-xs btn-danger" %>
<%= link_to "Back", articles_path, class: "btn btn-xs btn-success" %>
</div>
</div>

Step 33: Add Pagination to Views

Add the following gems…

gem 'will_paginate', '3.1.5'
gem 'bootstrap-will_paginate', '1.0.0'

Bundle install

$ bundle install

Now, in the articles controller…

We need to update the index action to…

@articles = Article.paginate(page: params[:page], per_page: 5)

It should look like:

def index
@articles = Article.paginate(page: params[:page], per_page: 5)
end

Now in the articles/index.html.erb view file we need to add display for the pagination…

<div align="center">
<%= will_paginate %>
</div>

You can stick pagination at the top, bottom, or both.

Here’s an example of it at the bottom:

Your articles/index.html.erb file would look like this:

<h1 align="center">Listing all articles</h1><%= render 'shared/article', obj: @articles %><div align="center">
<%= will_paginate %>
</div>

Now we are going to add to users page…

def index
@users = User.paginate(page: params[:page], per_page: 5)
end

Then in the users index.html.erb file…

<%= will_paginate %>

Whole file looks like:

<h1 align="center">All Users</h1>
<div align="center">

<% @users.each do |user| %>
<ul class="listing">
<div class="row">
<div class="well col-md-4 col-md-offset-4">
<li><%= link_to gravatar_for(user), user_path(user) %></li>
<li class="article-title"><%= link_to user.username, user_path(user) %></li>
<li><small><%= pluralize(user.articles.count, "article") if user.articles %></small></li>
</div>
</div>
</ul>
<% end %>
<%= will_paginate %>
</div>

Will_paginate will not work in the show.html.erb with the current configurations, so we need to update the users_controller.rb…

def show
@user = User.find(params[:id])
@user_articles = @user.articles.paginate(page: params[:page], per_page: 5)
end

Back in the users/show.html.erb view…

<div align="center">
<%= will_paginate @user_articles %>
</div>

The whole file looks like:

<h1 align="center">Welcome to <%= @user.username %>'s page</h1>
<div class="row">
<div class="col-md-4 col-md-offset-5 center">
<%= gravatar_for @user, size: 150 %>
</div>
</div>
<h4 align="center"><%= @user.username %>'s articles</h4><%= render 'shared/article', obj: @user.articles %><div align="center">
<%= will_paginate @user_articles %>
</div>

Step 34: Add Log-in and Log-out Functionality

In the routes.rb file…

get 'login', to: 'sessions#new'

Create a new controller called sessions_controller.rb…

class SessionsController < ApplicationControllerdef new
end
def create
end
def destroy
end
end

Add two more routes to handle the create and destroy actions…

post 'login', to: 'sessions#create'
delete 'logout', to: 'sessions#destroy'

The whole routes file currently looks lik :

Rails.application.routes.draw do
resources :articles
root 'pages#index'
get 'about', to: 'pages#about'
get 'signup', to: 'users#new'
resources :users, except: [:new]
get 'login', to: 'sessions#new'
post 'login', to: 'sessions#create'
delete 'logout', to: 'sessions#destroy'
end

Next we create the form…

Make a new folder called ‘sessions’ under views…

Then new file called new.html.erb under views/sessions…

Inside the file, we’ll recycle the user’s form partial with a few changes…

Update to…

<%= form_for(:session, :html => {class: "form-horizontal", role: "form"}, url: login_path) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :email %>
</div>
<div class="col-sm-8">
<%= f.text_field :email, class: "form-control", placeholder: "Enter email", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :password %>
</div>
<div class="col-sm-8">
<%= f.password_field :password, class: "form-control", autocomplete: "off" %>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<%= f.submit "Log in", class: 'btn btn-primary btn-large' %>
</div>
</div>
<% end %>

Carrying on…

Next we will work on the create action inside the sessions_controller.rb…

def create 
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
flash[:success] = "You have logged in"
redirect_to users_path(user)
else
flash.now[:danger] = "There was something wrong with your login information"
render 'new'
end
end

Now the destroy action…

def destroy
session[:user_id] = nil
flash[:success] = "You have logged out"
redirect_to root_path
end

And now we need a logout link that we will add to the Navbar in a few steps, but not just yet. Just to address the curiosity in the room, the link will be:

<li><%= link_to "Log out", logout_path, method: :delete %></li>

We will add to the Navbar with some additional logic in a minute.

Step 35: Authentication Methods for Restricting Access

We will build some methods in the application_controller.rb file to make sure the right user is seeing the right information…

The methods we will be adding..

def current_userenddef logged_in?enddef require_userend

By default, application controller methods are not available to the views, so we will need to specify them as “helper methods” so that the views can use them…

helper_method :current_user, :logged_in?

Now we need to use some optimization to prevent multiple database hits…

def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end

This will check if user is logged in or not…

def logged_in?
!!current_user
end

If not logged in…

def require_user
if !logged_in?
flash[:danger] = "You must be logged in to perform that action"
redirect_to root_path
end
end

The whole file looks like:

class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user, :logged_in?
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!!current_user
end
def require_user
if !logged_in?
flash[:danger] = "You must be logged in to perform that action"
redirect_to root_path
end
end
end

Now, in the navbar, we’ll add some logic to display a logout link if the user is logged in, and a login link with the user is not logged in…

<% if logged_in? %><% else %><% end %>

David Allen

Written by

Passionate about food, health, and learning new things.