DEV Community

Chuks Opia
Chuks Opia

Posted on

Build an Instagram Clone with Ruby and Pusher

https://thepracticaldev.s3.amazonaws.com/i/nxqfrbmepu3pwj7fmb1d.gif

Last week, I entered the first ever DEV Contest to build a Realtime App with Pusher. After a very busy day and about 4 hours of coding, I submitted an Instagram clone.

You can check out my entry below to see the live app and Github repo. It'll be nice to also get your ❤️ & 🦄 on my entry post.

In this post, I'll be taking you through how I built the Instagram clone app.

Setting up the application

I built the app using Ruby on Rails. I made use of Devise for user authentication and Carrierwave for image upload.
If you have Ruby and Rails installed and want to follow along, run the following command to generate a new Rails app.

$    rails new instaclone -T --database=postgresql

In your app's root directory, open your Gemfile and add the following and then run bundle install in your terminal:

# Gemfile

gem 'bootstrap', '~> 4.1.0'
gem 'jquery-rails'
gem 'pusher'
gem 'figaro'
gem 'devise'
gem 'will_paginate', '~> 3.1.0'
gem 'carrierwave'
gem "fog-aws"

Database setup

To get our app up and running, we’ll create a database for it to work with. You can check out this article on how to create a Postgres database and an associated user and password.

Once you have your database details, in your database.yml file, under the development key, add the following code:

# config/database.yml

...
development:
  <<: *default
  database: instaclone_development // add this line if it isn't already there
  username: database_user // add this line
  password: user_password // add this line
...

Ensure that the username and password entered in the code above has access to the instaclone_development database. After that, run the following code to setup the database:

#    setup database
$    rails db:setup

User authentication

With our database set up, we'll set up user authentication with Devise. Devise is a flexible authentication solution for Ruby on Rails. It helps you set up user authentication in seconds. In your terminal, run the following command:

$    rails generate devise:install

The above command will install Devise. At this point, a number of instructions will appear in the console, one of which involves adding some code to your application.html.erb file. We’ll also add our Pusher script to the file.

Replace the code in your app/views/layouts/application.html.erb file with the one here

Next, we’ll generate our authentication view pages, like and then user model using Devise. In your terminal, run the following command:

#    generate Devise view pages
$    rails generate devise:views

#    generate user model
$    rails generate devise user

#    generate migration to add extra columns to the user model
$    rails generate migration add_username_to_users username:string:uniq

#    generate a likes model
$    rails generate model like like_count:integer post:references

Now that we have generated our migration files, let's make some modifications to some files before running our migrations.

Update the code in your application controller, with the following:

# app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :authenticate_user!

  protected

  def configure_permitted_parameters
    added_attrs = [:username, :email, :password, :password_confirmation, :remember_me]
    devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
    devise_parameter_sanitizer.permit :account_update, keys: added_attrs
  end
end

Also, update the code in your likes migration file with the following

# db/migrate/20180524215616_create_likes.rb

class CreateLikes < ActiveRecord::Migration[5.1]
  def change
    create_table :likes do |t|
      t.integer :like_count, default: 0
      t.references :post, foreign_key: true

      t.timestamps
    end
  end
end

Now, we’re ready to run our migration and see our app. In your terminal, run the following:

#    run database migrations
$    rails db:migrate

After running migrations, start the development server on your terminal by running rails s. Visit http://localhost:3000 in your browser to see your brand new application:

Pusher account setup

Now that our application is up and running, head over to Pusher and sign up for a free account.
After creating a Pusher account, create a new app by selecting Channels apps on the sidebar and clicking Create Channels app button on the bottom of the sidebar.
Configure your app by providing basic information requested in the form presented. You can also choose the environment you intend to integrate with Pusher, to be provided with some boilerplate setup code. After that, click the App Keys tab to retrieve your keys.

Styling the pages

To style our pages, we'll make use of Bootstrap. We have already added Bootstrap to our app while setting it up, so all we need to do is require it and add our markup and styles.
First we'll generate a controller for our posts; in your terminal, run the following command:

#    generate a posts controller
$    rails g controller posts

Update the code in the following files with the code in each link provided below:

  • Replace the code in app/assets/javascripts/application.js with the code here
  • Replace the code in app/views/devise/registrations/new.html.erb with the code here
  • Replace the code in app/views/devise/sessions/new.html.erb with the code here
  • Rename your app/assets/stylesheets/application.css file to app/assets/stylesheets/application.scss and replace the code there with the code here
  • Add the code here to your app/controllers/posts_controller.rb file.
  • Add the code here to your config/routes.rb file.

If we reload our app in the browser, we should be greeted with a lovely sight. Go ahead and create an account.

If you encounter any error related to application.html.erb, in config/boot.rb, change the ExecJS runtime from Duktape to Node.

# config/boot.rb
ENV['EXECJS_RUNTIME'] ='Node'

Realtime posts with Pusher

For users to see our posts realtime, whenever a user adds a post, we save it to the database and notify Pusher by broadcasting the new post and subscribing to it on the frontend of our app.
Let’s initialise our Pusher client. In the config/initializers folder, create a pusher.rb file and add the following code:

# config/initializers/pusher.rb

require 'pusher'

Pusher.app_id = ENV["PUSHER_APP_ID"]
Pusher.key = ENV["PUSHER_KEY"]
Pusher.secret = ENV["PUSHER_SECRET"]
Pusher.cluster = ENV["PUSHER_CLUSTER"]
Pusher.logger = Rails.logger
Pusher.encrypted = true

Since we don't want our Pusher keys to be visible to the public, we'll add save them as environmental variables. To do this, we'll make use of Figaro.
Run figaro install in your terminal. It will generate an application.yml file. In the application.yml file add your Pusher keys:

# config/application.yml

PUSHER_APP_ID: '11234'
PUSHER_KEY: '1234567890'
PUSHER_SECRET: '1234556677'
PUSHER_CLUSTER: 'us'

With Pusher now initialised, lets notify Pusher whenever a new post is saved. Add the code below to your post model:

# app/models/post.rb

class Post < ApplicationRecord
  belongs_to :user
  has_many :likes
  after_create :notify_pusher

  mount_uploader :image, ImageUploader

  validates :caption, presence: true

  def serialised
    {
      id: self.id,
      caption: self.caption,
      user: self.user.username,
      image: self.image_url,
      likes: self.likes[0].like_count
    }
  end

  def notify_pusher
    Pusher.trigger('post', 'new', self.serialised)
  end
end

In the code above, we have a notify_pusher action that is called after a new post is created. In the notify_pusher action, we notify pusher of the new post by triggering a new post event and passing in the new post.

Also, we have to notify Pusher whenever there's a new like for an image. Add the following code to the likes model

# app/models/like.rb

class Like < ApplicationRecord
  after_save :notify_pusher, on: :create
  belongs_to :post

  def notify_pusher
    Pusher.trigger('post', 'new-like', self.post.serialised)
  end
end

As with the user model, we also notify Pusher of a new like by broadcasting a new-like event.

For us to get the update realtime on the frontend of our app, we have to subscribe to the new post event we broadcasted on the server side. Lets rename our app/assets/javascripts/posts.coffee file to app/assets/javascripts/posts.coffee.erb and add the code here to the file.
In the code we added to the file above, we subscribe our Pusher client to the new and new-like events for posts and likes respectively. Whenever there's a new event, we update the page with the new post and likes.

Conclusion

There you have it, if you have followed this tutorial to this point, you should have your own fully functional Instagram clone.
Pusher is a very powerful software that can help you implement any realtime feature you can think of.

If you found this post useful, please add a ❤️ & 🦄 both here and in my entry post.

Top comments (1)

Collapse
 
kris profile image
kris

The tutorial is constantly driving my motivation . The overall tutorial is well-presented and great for beginners as well. Thanks for making this tutorial. if your looking for real world app. Your should check out some of full-fledge Instagram clone app templates in the market.