Create a live blog app using Ruby on Rails
You will need Ruby 2.2+ and Rails 4.2+ installed on your machine.
With the advancements of technology came new ways of doing things. About 20 years ago, the major source of obtaining information was from printed publications. Now, people don’t buy as many printed publications as before. They visit the websites of media publications, consume information and then continue with the rest of their days. This has come with numerous advantages with the major advantage being how fast it is to get the new articles to the readers.
What we’ll build
In this article, we are going to create a live blog application that allows readers to see new posts in realtime as editors publish them. To do this, we are going to use Ruby on Rails and Pusher.
Prerequisites
To follow through this tutorial, you’ll need the following:
- Ruby installed on your machine
- Rails on your machine
To confirm your installation, run the following command:
rails --version # minimum version 4.2
ruby --version # minimum version 2.2.2
If you get their version numbers as results then you’re good to go.
Getting started
Creating a new Ruby project
To create a new rails project, run the following command:
rails new ruby-live-blog
This creates a starter project for you with the following structure:
Now, you can view the demo application by running:
rails server --binding=127.0.0.1
Open your browser and navigate to http://127.0.0.1:3000/ to see the application at work
Configuring the database
To display blog posts to users, there has to be a place where the application stores blog posts. To store the blog posts, we need a database. For this live blog application, we are going to use SQLite as the database for storing information.
To create your database, run the command:
rake db:create
This creates the database in the db/
directory of the project if they weren’t automatically created before.
Creating the post model
To represent a sample blog post in our application, let’s create a model. A simple blog post will contain the following properties:
- Title - the title of the post
- Body - the post content
- Author - the name of the author of the post
- Media - a link to the post media
Create the model by running the command:
rails generate model Post title:text body:text author:text media:text
This creates the post model and a migration file for the new model located in the db/migrate/
directory. A migration file is a form of version control for the database. Your posts migration file will look like this:
# db/migrate/{timestamp}_create_posts.rb
class CreatePosts < ActiveRecord::Migration[5.2]
def change
create_table :posts do |t|
t.text :title
t.text :body
t.text :author
t.text :media
t.timestamps
end
end
end
To effect the changes in the database, run the migration by using the command:
rails db:migrate
On a successful run, you get the following output:
➜ rails db:migrate
== 20180609132632 CreatePosts: migrating ======================================
-- create_table(:posts)
-> 0.0016s
== 20180609132632 CreatePosts: migrated (0.0017s) =============================
At this stage, the database structure is all set and we have created the post model. The next thing to do now is to allow editors upload new blog posts.
Creating the homepage
The homepage displays created blog posts to readers when the page is loaded. Let’s create a controller that is responsible for fetching and displaying posts on the homepage. To do this, run the following command:
rails generate controller Home index
This creates a new controller and related template files. Update the home controller to look like this:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
@posts = Post.all.order("created_at DESC")
end
end
The next thing to do is to then update the index view as follows:
<!-- app/views/home/index.html.erb -->
<div class="wrapper">
<div class="title">
<h1>Live blog with Ruby and Pusher</h1>
<p>New posts will be displayed in realtime</p>
</div>
<div class="posts" id="post_section">
<% @posts.each do |post| %>
<div class="post-card">
<img class="image" src="<%= post.media %>">
<div class="title">
<h2><%= post.title %></h2>
</div>
<div class="body-trunc">
<p><%= post.body %></p>
</div>
</div>
<% end %>
</div>
</div>
The index view also has the following styling:
// app/assets/stylesheets/home.scss
* {
font-family: "Lora", serif;
}
.title {
text-align: center;
}
.posts {
margin-top: 50px;
display: flex;
flex-direction: row;
justify-content: space-around;
flex-wrap: wrap;
}
.post-card {
display: flex;
flex-direction: column;
min-width: 300px;
max-width: 300px;
height: 400px;
box-shadow: 1px 1px rgb(68, 68, 68);
border: 1px solid rgb(68, 68, 68);
border-radius: 5px;
margin: 20px 0px;
}
.image {
width: 100%;
height: 170px;
}
.body-trunc {
margin-left: 10px;
margin-right: 10px;
}
Creating and storing posts
The Post controller is responsible for accepting new post data and storing the post data in the database. To create the controller, run the following command:
rails generate controller Post create store
This command also creates route entries for the controller methods. At this point, your routes.rb
file should look like this:
# config/routes.rb
Rails.application.routes.draw do
get 'post/create'
post 'post/store'
root 'home#index'
end
The create
route is responsible for accepting the post data from the editor and the store
route is responsible for storing the data in the database.
Update the create view to look like this:
<!-- app/views/post/create.html.erb -->
<div class="title">
<h1>Live Blog</h1>
<p>Editor View</p>
</div>
<div style="text-align:center">
<p><strong>Fill and submit the form below to create a new post</strong></p>
<form method="POST" action="/post/store" enctype="multipart/form-data" class="post-form">
<%= token_tag %>
<div class="form-group">
<label for="title">Title</label>
<input type="text" name="title" class="form-control">
</div>
<div class="form-group">
<label for="media">Post Media</label>
<input type="file" name="media" class="form-control media-upload">
</div>
<div class="form-group">
<label for="author">Post Author</label>
<input type="text" name="author" class="form-control" />
</div>
<div class="form-group">
<label for="body">Post Content</label>
<textarea name="body" class="form-control" cols="30" rows="10"></textarea>
</div>
<div class="from-group">
<div><input type="submit" value="Create Post"></div>
</div>
</form>
</div>
The view also has the following styling:
// app/assets/stylesheets/posts.scss
.post-form{
display: flex;
width: 50%;
flex-direction: column;
margin: 20px auto;
}
.form-group{
display: flex;
justify-content: space-between;
margin: 5px 0px;
align-items: center;
}
input[type="submit"]{
background-color: #333333;
border: none;
color: white;
padding: 10px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
When you navigate to the /``post``/create
route on the web browser, you get the following view:
At this point, the application is almost complete. Notice that in the create view, we specified that the form should be submitted to the /post/store
route but we did not show how the post was saved. We will look at how to save posts in the next section.
Saving images with Cloudinary
To save images for the new posts, we will use Cloudinary. Cloudinary takes care of all your image and video manipulation needs when building applications.
Cloudinary has a Ruby gem that will be used to handle the file uploads.
Add the Cloudinary as a dependency in your Gemfile
:
# adding cloudinary to gemfile
gem 'cloudinary'
Install it by running the command:
bundle install
Configuring Cloudinary
To use the Cloudinary gem, you need to have a Cloudinary developer account to use it in your applications. Head over here to create one if you don’t have one already.
Cloudinary automatically checks for a cloudinary.yml
in the config
directory that contains the configuration file for your Cloudinary account. You can download your own customized file for your account here. Once it’s downloaded, place the file in the config
directory.
Saving posts
The module will be used in the post controller as follows:
# app/controllers/post_controller.rb
class PostController < ApplicationController
def create
end
def store
# upload image to cloudinary
@image = Cloudinary::Uploader.upload(params[:media])
# create a new post object and save to db
@post = Post.new({:title => params[:title], :text => params[:text], :author => params[:author], :media => @image['secure_url']})
@post.save
# trigger an event with pusher
[...]
redirect_to('/')
end
end
Now, when a post is created and saved, you can see it on the homepage:
Adding realtime functionality with Pusher
Currently, the only way users get access to new posts is if they refresh their browsers. We need a way to automatically add new posts to the page as they are created and this is where Pusher comes in. Pusher allows you to integrate realtime functionality in your applications with ease without worrying about infrastructure. To get started with Pusher, sign up for a developer account here. When you’re done with that, create a new application and note your credentials as you’ll need them as we progress.
Pusher has a Ruby gem which we are going to use. To install it, add the following to your Gemfile
:
# adding Pusher to Gemfile
gem 'pusher'
And then install the gem by running the command:
bundle install
To confirm your installation, run:
bundle info pusher
Configuring the Pusher package
Create a config/initializers/pusher.rb
file and add the following content to it:
# config/initializers/pusher.rb
require 'pusher'
Pusher.app_id = 'PUSHER_APP_ID'
Pusher.key = 'PUSHER_APP_KEY'
Pusher.secret = 'PUSHER_APP_SECRET'
Pusher.cluster = 'PUSHER_APP_CLUSTER'
Pusher.logger = Rails.logger
Pusher.encrypted = true
The Pusher credentials can be obtained from the pusher application dashboard.
Triggering events from controller
To trigger a new post event, update the post controller:
# app/controllers/post_controller.rb
class PostController < ApplicationController
def create
end
def store
# upload image to cloudinary
@image = Cloudinary::Uploader.upload(params[:media])
# create a new post object and save to db
@post = Post.new({:title => params[:title], :body => params[:body], :author => params[:author], :media => @image['secure_url']})
if @post.save
# broadcasting posts using pusher
Pusher.trigger('posts-channel','new-post', {
id: @post.id,
title: @post.title,
media: @post.media,
body: @post.body
})
end
redirect_to('/')
end
end
When a post is successfully saved in the database, a new-post
event is triggered in the posts-channel
. For debugging purposes, you can see events that have been triggered by your application on the Pusher dashboard.
Displaying new posts in realtime for readers
To use Pusher in the frontend of your application, you need to do the following:
Include the Pusher script in the header of the application layout:
# app/views/layouts/application.html.erb
[...]
<head>
[...]
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
[...]
</head>
[...]
Afterward, create a Pusher client in the home page view
# app/views/home/index.html.erb
<script>
var pusher = new Pusher('YOUR_APP_KEY', {
cluster: 'eu',
encrypted: true
});
</script>
<div class="wrapper">
[...]
Finally, you’ll need to subscribe to the post-channel
and listen for new-post
events. Update the app/views/home/index.html.erb
to look like this:
# app/views/home/index.html.erb
<script>
var pusher = new Pusher('YOUR_APP_KEY', {
cluster: 'eu',
encrypted: true
});
</script>
<div class="wrapper">
[...]
</div>
<script>
var channel = pusher.subscribe('posts-channel');
channel.bind('new-post', function(data) {
let post_media = data.media;
let post_id = data.id
let post_body = data.body;
let post_title = data.title;
let div = document.getElementById('post_section');
let new_content = document.createElement("div");
new_content.innerHTML = `
<div class="post-card">
<img class="image" src="${post_media}">
<div class="title">
<h2>${post_title}</h2>
</div>
<div class="body-trunc">
<p>${post_body}</p>
</div>
</div>`;
div.insertBefore(new_content, div.firstChild);
});
</script>
When an event is broadcast, we update the UI to show the new blog post. To see the application at work, start the rails server using the command:
rails server --binding=127.0.0.1
Navigate to the home page and the editor view to see the application:
Conclusion
In this article, we looked at how to use Ruby and Pusher to build a live blog with realtime updates when new posts are created. You can leverage the concepts shared here when building your own application that needs realtime functionality. The complete source code of this demo application is available on GitHub.
12 June 2018
by Oreoluwa Ogundipe