Enabling Server Ping on a Rails App
Performance is User Experience
Our web app needs to be as fast as our users are. Competitive games such as League of Legends or CS:GO often have an option to display the current latency (or “lag”) directly on the HUD, typically aiming for <100ms response times.
On a web application, poor latency times can result in an userbase abandoning the site and never returning. So, how can we make sure if we’re performing at a competitive level ourselves?
Let’s code our own lag meter using Hotwire.
Setup
All we need for our server ping is a non-redirecting controller action and a route. We’ll be using the ping_path
route helper for the form_tag
that will be starting the response cycle.
#config/routes.rb
get '/ping', to: 'ping#pong', as: 'ping'
And the controller action to send back a basic response.
#app/controllers/ping_controller.rb
def pong
render status: :ok, body: ‘PONG’
end
Sending the request
Preparation
- Create a
_ping.html.haml
(or.erb
) partial in theshared
directory in your views folder. - Add
render "shared/ping"
to a view. - In our
_ping
partial, create a form to send a request (usingping-path
).
Attach Behavior
Turbo Drive makes this easy for us by allowing us to attach behavior at different points in the response cycle with Turbo Drive Events.
Start the timer
To our form_tag
, we'll attach the first behavior using a Turbo Drive Event as a trigger.
= form_tag ping_path ... data: { action: "turbo:before-fetch-request->ping#pauseRequest"} do |f|
In our data-action we can see the trigger before-fetch-request
triggering ping#pauseRequest
behavior
- Setup Data Controller
- Create the Stimulus controller.
rails g stimulus PingController
Next, we’ll establish the data-controller. Let’s embed the form_tag
in a div
with the data-controller
attribute.
%div{ :data=>{"controller": "ping"} }
form_tag_here
Setup #pauseRequest
in the Stimulus ping-controller
.
//app/javascript/controllers/ping_controller.rb
import { Controller } from "@hotwired/stimulus"// Connects to data-controller="ping"
export default class extends Controller {
pauseRequest(event){}
}
Recording the time of request
//app/javascript/controllers/ping_controller.rb
pauseRequest(event) {
event.preventDefault()setTimeout(() => this.saveRequestTime() )event.detail.resume()
}
saveRequestTime() {
this.requestTime = new Date().getTime()
}
Stopping the timer
turbo:submit-end
will stop the timer when the response cycle is complete.
= form_tag ping_path ... data: { action:
"... turbo:submit-end->ping#stopTimer"} do |f|
In the same Ping
data-controller, we'll attach a new series of behaviors.
stopTimer() {
this.saveResponseTime()this.latency = this.responseTime - this.requestTime
}
Within the div
with the data-controller
attribute, we'll add a tag with a data-ping-target
so the controller can display the current ping.
%p{ :data=>{"ping-target": "latencyValue"} }
And adding the target and display behavior to the controller.
export default class extends Controller {
static targets = ["pingForm", "latencyValue"]
//...
stopTimer() {
//...
this.valueTarget.textContent = `${this.latency} ms`
}
//...
Looping it
Now, we’ll have Stimulus trigger a loop by attaching a data-target to the form_tag
itself, causing it to resubmit.
Let’s add the behavior first.
rerun() {
this.pingResubmitTarget.requestSubmit()
}
And the data-target
to the form_tag
.
= form_tag ping_path ... data: { action: "...ping#stopTimer", "ping-target": "pingResubmit"} do |f|
In our stopTimer
behavior, let's add a 1s timeout before triggering the form submission rerun()
. And with that, our server ping feature will now be looping.
stopTimer() {
this.saveResponseTime()this.latency = this.responseTime - this.requestTimethis.latencyValueTarget.textContent = `${this.latency} ms`
setTimeout(() => this.rerun), 1000)
}
Now, we’re all done. Try out the feature and let’s see if the latency is below threshold.
What We Covered
We learned how to use Stimulus + Turbo Events to add a server ping feature on our Rails app.
This number not just indicates the speed of our app, but also the quality of it. We’re telling our users that their actions matter. A seamless experience where tech does not impair thought.
If you have any suggestions to improve this article, please leave a comment down below.