Build an Instagram Clone with Ruby and Pusher
Tags: pushercontest, rubyrails, instagram, pusher
Published: 2018-06-01 20:44:37

---
title: Build an Instagram Clone with Ruby and Pusher
published: true
description: Build an Instagram clone app. Post pictures and like them all in realtime.
tags: pushercontest, ruby/rails, Instagram, Pusher
---

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

Last week, I entered the [first ever DEV Contest](https://dev.to/devteam/first-ever-dev-contest-build-a-realtime-app-with-pusher-4nhp?utm_source=additional_box&utm_medium=internal&utm_campaign=devteam_boosted&booster_org=devteam) 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.

{% link https://dev.to/troy34/entry-instagram-clone-2gbp %}


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](Devise) for user authentication and [Carrierwave](https://github.com/carrierwaveuploader/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](https://www.digitalocean.com/community/tutorials/how-to-setup-ruby-on-rails-with-postgres) 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](https://github.com/9jaswag/instaclone/blob/develop/app/views/layouts/application.html.erb)

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](https://dashboard.pusher.com/accounts/sign_up).
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](https://github.com/9jaswag/instaclone/blob/develop/app/assets/javascripts/application.js)
- Replace the code in `app/views/devise/registrations/new.html.erb` with the code [here](https://github.com/9jaswag/instaclone/blob/develop/app/views/devise/registrations/new.html.erb)
- Replace the code in `app/views/devise/sessions/new.html.erb` with the code [here](https://github.com/9jaswag/instaclone/blob/develop/app/views/devise/sessions/new.html.erb)
- Rename your `app/assets/stylesheets/application.css` file to `app/assets/stylesheets/application.scss` and replace the code there with the code [here](https://github.com/9jaswag/instaclone/blob/develop/app/assets/stylesheets/application.scss)
- Add the code [here](https://github.com/9jaswag/instaclone/blob/develop/app/controllers/posts_controller.rb) to your `app/controllers/posts_controller.rb` file.
- Add the code [here](https://github.com/9jaswag/instaclone/blob/develop/config/routes.rb) 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](https://github.com/9jaswag/instaclone/blob/develop/app/assets/javascripts/posts.coffee.erb) 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.
{% link https://dev.to/troy34/entry-instagram-clone-2gbp %}