Rails 5: Send reload to set of clients on data update

I'm using Rails 5 to make a simple turn based game tracker for an in-person social game (via phones/tablets/etc..)

I want to have all the 'players' in the game (list of sessions/users/...) to reload their browsers automatically once a player has taken an action.

I know that there are live update capabilities such as AJAX and websockets, but they all seem far too weighty for what seems to be a simple problem. Furthermore, I want to update other clients pages, not just the client initiating the action.

Is there a simple solution to send a reload? Or do I need to code something up in one of the more complicated APIs?

Upvotes: 2

Views: 410

Answers (2)

Thanks to @yeuem1vannam's answer, here is the final code I used that helps avoid the race condition of a page loading old information while the time is being updated and then the javascript updating the time and getting the new time, and hence missing the reload.

The javascript code:

var actionChecker;

function doneChecking () {
  clearInterval(actionChecker);
}

function checkLastAction () {
  // Get the game ID from the html access span
  var dataId = document.getElementById('javascript_data_access');
  if (!dataId) return doneChecking();
  var initActionTime = dataId.getAttribute('init_last_action_time');
  if (!initActionTime) return doneChecking();

  dataId = dataId.getAttribute('game_number');
  if (!dataId) return doneChecking();

  // Get the last action time
  var ret = $.getJSON("/get_last_action_time/"+dataId, function (data) {
    var lastActionTime = data.last_action_time;
    if (!lastActionTime) return doneChecking();
    if (lastActionTime>initActionTime) {
      location.reload();
    }
  })
}

window.onload = function() {
  // Check every 1 second, shouldn't be too short due to performance
  actionChecker = setInterval(checkLastAction, 1000);
}

The controller's action:

  def get_last_action_time
    last_time = nil
    begin
      @game = Game.find_by_id(params[:id])
      # Return the timestamp of the last action
      last_time = (@game && [email protected]) ? @game.reloadTime.to_i : 0
    rescue ActiveRecord::RecordNotFound
      last_time = 0
    end
    # Stop bugging us after 30m, we should have moved on from this page
    last_time==0 if (last_time!=0 && (milliseconds - last_time)>30*60*1000)
    render json: {last_action_time: last_time}
  end

And then in the html.erb:

<span id='javascript_data_access' game_number=<%= params[:id] %> init_last_action_time=<%= @game.reloadTime %>></span>

Obviously you need to add reloadTime to your model and also endTime if there's a time you no longer want to check for reloads anymore.

Seems to be working fine so far, you have to make sure that you're careful about who is in charge of setting reloadTime. If two pages set reloadTime everytime they reload, you'll be stuck in a reload loop battle between the two pages.

Upvotes: 0

yeuem1vannam
yeuem1vannam

Reputation: 957

For the simple trouble, you still can use AJAX to reload user client by making interval request for each XX seconds. The server can return the last action time which can be used for client to determine that it should reload itself or not.

For example, on the controller

# SomeController
def get_last_action_time
  # Return the timestamp of the last action
  render json: {last_action_time: "2017-12-29 10:00:42 UTC"}
end

on the client

function getLocalLastAction () {
  /* return timestamp of the last action on local */
}

function setLocalLastAction (time) {
  /* Store the `time` to somewhere, ex: localStorage */
}

function checkLastAction () {
  $.getJSON("/get_last_action_time", function (data) {
    if (getLocalLastAction() < data.last_action_time) {
      /* destroy the interval */
      setLocalLastAction(data.last_action_time)
      /* do the reload page */
    } else {
      /* do nothing */
    }
  })
}

// Check every 1 second, shouldn't be too short due to performance
var checking = setInterval(checkLastAction, 1000)

Then when user A do an action, the server last_action_time will change, hence client of other users will be reloaded at most after 1 second.

This way is old but quite easy to do in some simple case, and when you implement together with actions caching, the performance of app still acceptable. In the more complicated cases, I suggest using WebSocket solution for

  • Full control
  • Low latency
  • Better performance for app

Upvotes: 1

Related Questions