Adding Ping tool in Rails App
Published: 2022-08-04 15:30:00


You've heard of word ping or latency in network based applications. It's the time an application takes to respond to requests. For example, you're making a search on google. Your successful search will have following steps:

1. In search bar, you enter some text
2. Hit enter
3. Request is being processed
4. Response/results appear on webpage

Latency is the time it takes from step-2 to step-4.

**Problem**
In this blog, we will learn how to add a ping/latency time tool to our rails app. After setting this tool up, we will be able to monitor ping time for our rails app. The basic idea is to see how fast/slow our app is working.


**Solution**
Here is a prerequisite before we start adding this.

1. Rails App running with webpacker (rails version should be >=5)
2. Webpacker added

Let’s assume we have a rails app working with rails version => 5 and webpacker is added. Here’s how we will add this ping tool:

**1-Add Stimulus**
Run this command to add stimulus

`Yarn add stimulus`

Once it’s installed, use webpack to import it to the application. We will do this in `app/javascript/packs/application.js`. In javascript directory, create controllers folder and then add `index.js` there. Then reference this folder inside `application.js`. This is how your `index.js` will look like

```
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
const context = require.context(".", true, /\.js$/)
application.load(definitionsFromContext(context))


```

**2- Add turbo-rails and redis**
To setup `turbo-rails`,  use following commands
`bundle add turbo-rails`
`rails turbo:install`
`rails turbo:install:redis`

Now we’re all set and ready to use it. Let’s generate a `PingsController` with following command.
`rails g controller ping`
Let’s add `#pong` action which renders plain text response with `PONG`

```
class PingController < ApplicationController
  def pong
    render status: :ok, body: "PONG"
  end
end
```

Let’s add a `/ping` endpoint routed to the `#pong` action like this:
`get “/ping”, to: “ping#pong”, as: “ping”`

Let’s create a form now in `app/views/shared/_ping.html.erb` to hit this rout.

```
<div data-controller="ping" >
    <%= form_tag ping_path, method: :get, data: { action: "turbo:before-fetch-request->ping#pauseRequest turbo:submit-end->ping#measureLatency", "ping-target": "pingForm" } do %>
    <%= button_tag "Ping" %>
    <% end %>
    <span data-ping-target="latency"></span>
</div>
```

Let’s create a homepage and add this partial their so it would be usable from the homepage. First, let’s create `HomeController` with `show` action.

`rails g controller home show`

Now make this show action as root action in `route.rb`

`root to: “home#show”`

Now render the form partial on show page
Add following code in `app/views/home/show.html.erb`

`<%= render "shared/ping" %>`

Next step is to measure the time latency now.
For this, we need to generate a stimulus controller.

`rails g stimulus ping`

And then let’s define the getters and setters we need to serve our purpose. We have a `measureLatency()` fucntion that will measure the latency. Then there's a `displayLatency()` function that will display latency on page every second. All of this code will go in `app/javascript/controllers/ping_controller.js` as below.

```
import { Controller } from "stimulus";
export default class extends Controller {
  static targets = ["pingForm", "latency"]

  pauseRequest(event) {
    event.preventDefault();

    setTimeout(() => this.saveRequestTime());

    event.detail.resume();
  }

  saveRequestTime() {
    this.requestTime = new Date().getTime();
  }

  measureLatency() {

    this.saveResponseTime();

    this.latency = this.responseTime - this.requestTime;

    console.log(`${this.latency} ms`);
    this.displayLatency()

    setTimeout(() => this.ping(), 1000)
  }

  displayLatency() {
    this.latencyTarget.textContent = this.latency + " ms"
  }


  saveResponseTime() {
    this.responseTime = new Date().getTime();
  }

  ping() {
    this.pingFormTarget.requestSubmit()
  }

  get requestTime() {
    return this._requestTime;
  }

  set requestTime(requestTime) {
    this._requestTime = requestTime;
  }

  get responseTime() {
    return this._responseTime;
  }

  set responseTime(responseTime) {
    this._responseTime = responseTime;
  }

  get latency() {
    return this._latency;
  }

  set latency(latency) {
    this._latency = latency;
  }
}
```

If we submit the form ping time will be printed after each second like in this picture.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qavg1jprukt3td43sdqy.png)

**Discussion**
This is how you can tell the end user about the latency time. It is more helpful when you're working to scale your app and improving the app performance. To make the UI better, you can print a graph as well.

A simple Rails ping app is here in this [Repo](https://github.com/AdeelSarim/ping)