Permalink
Browse files

Added support for updating game lists from the client.

Additionally, refactored game parsing logic into its own class.
  • Loading branch information...
justinweiss committed Mar 16, 2013
1 parent 6f313a2 commit 6a56e610125ef9eba5d99ea55fd44fe365f5f143
@@ -0,0 +1,19 @@
class GamesController < ApplicationController
respond_to :json
before_filter :load_player

def update_all
@player.games = params[:games].map do |dgs_game_id, game_params|
attributes = game_params.merge(dgs_game_id: dgs_game_id)
attributes.slice!(:dgs_game_id, :opponent_name, :updated_at, :player)
Game.new(attributes)
end

respond_with :nothing
end

private
def load_player
@player = Player.find_by_dgs_user_id!(params[:player_id])
end
end
@@ -4,23 +4,6 @@ class Game < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
belongs_to :player

def self.parse_from_csv(csv_string)
games = []
CSV.parse(csv_string, quote_char: "'") do |row|
games << Game.from_csv_row(row) if (row[0] == 'G')
end
games
end

def self.from_csv_row(row)
Game.new({
dgs_game_id: row[1],
opponent_name: row[2],
created_at: row[4],
updated_at: row[4],
})
end

def self.merge_games(old_games, new_games)
# TODO: abandon early where the newest existing game is newer / same as the newest new game
old_game_index = old_games.index_by(&:dgs_game_id)
@@ -0,0 +1,29 @@
class GameCSVParser

def initialize(csv_string)
@rows = CSV.parse(csv_string, quote_char: "'")
end

def games
game_rows.map { |row| Game.new(game_attributes(row)) }
end

private

def game_rows
@rows.select { |row| is_game_row?(row) }
end

def game_attributes(row)
{
dgs_game_id: row[1],
opponent_name: row[2],
created_at: row[4],
updated_at: row[4],
}
end

def is_game_row?(row)
row[0] == 'G'
end
end
@@ -22,7 +22,7 @@ def fetch_new_games!(session = self.session)
DGS::ConnectionPool.with do |dgs|
game_csv = dgs.get(session, '/quick_status.php?version=2')
end
new_games = Game.parse_from_csv(game_csv)
new_games = GameCSVParser.new(game_csv).games
added_games, removed_games, existing_games = Game.merge_games(self.games, new_games)
self.games = existing_games + added_games
create_notifications_for_games!(added_games, existing_games)
@@ -1,11 +1,13 @@
DgsPushServer::Application.routes.draw do
scope({format: true, constraints: {format: :json}}) do
resources :players, :only => [] do
resources :apns_devices, :only => [:create, :update, :destroy], :path => "devices"
resource :session, :only => [:create]
resources :players, only: [] do
resources :apns_devices, only: [:create, :update, :destroy], path: "devices"
resource :session, only: [:create]

end
put 'players/:player_id/games.:format' => 'games#update_all'
end

match '/test/fail' => 'test#fail'
match '/test/succeed' => 'test#succeed'
get '/test/fail'
get '/test/succeed'
end
@@ -7,3 +7,7 @@ justin:
player_without_tokens:
dgs_user_id: 1235
last_checked_at: 2013-01-17 22:26:16

player_without_games:
dgs_user_id: 1236
last_checked_at: 2013-01-17 22:26:16
@@ -0,0 +1,46 @@
require 'test_helper'

class GamesControllerTest < ActionController::TestCase
include JSONRequiredTest

setup do
@app_id = "net.uberweiss.DGS"
@request.env["X_BUNDLE_IDENTIFIER"] = @app_id
@request.accept = "application/json"
end

# Post data looks like this:
# {"games"=>{"789720"=>{"opponent_name"=>"SimpleHarry", "updated_at"=>"2013-03-13 12:44:15"}, "783006"=>{"updated_at"=>"2013-03-13 08:03:24", "opponent_name"=>"Catlemur"}, "795142"=>{"opponent_name"=>"Tatsumaki", "updated_at"=>"2013-03-13 13:23:10"}}, "player_id"=>"53292"}""}"}}
# GamesControllerTest#update_all (global)
test "If we get a list of games, they should be placed in the database" do
player = players(:player_without_games)
assert_difference "Game.count", 3 do
put :update_all, player_id: player.dgs_user_id, games: {
'1' => {opponent_name: 'Bob', updated_at: '2013-03-13 08:03:24'},
'2' => {opponent_name: 'Bill', updated_at: '2013-03-13 08:03:24'},
'3' => {opponent_name: 'Dave', updated_at: '2013-03-13 08:03:24'},
}
end
assert_equal 'Dave', Game.last.opponent_name
assert_equal 3, Game.last.dgs_game_id
assert_equal player.id, Game.last.player.id
end

test "When we get a list of games, we should replace the existing games with our games" do
player = players(:justin)
game = player.games.first
assert game, "Main player should have a game"
assert_difference "Game.count", 3 - player.games.length do
put :update_all, player_id: player.dgs_user_id, games: {
'1' => {opponent_name: 'Bob', updated_at: '2013-03-13 08:03:24'},
'2' => {opponent_name: 'Bill', updated_at: '2013-03-13 08:03:24'},
'3' => {opponent_name: 'Dave', updated_at: '2013-03-13 08:03:24'},
}
end

assert_equal 'Dave', Game.last.opponent_name
assert_equal 3, Game.last.dgs_game_id
assert_equal player.id, Game.last.player.id
refute Game.find_by_id(game.dgs_game_id), "Original game should not have been found."
end
end
@@ -2,7 +2,7 @@

class FetchGamesForPlayersTest < ActiveSupport::TestCase
test "All players who are ready for fetching get queued" do
assert_difference "FetchGamesForPlayer.jobs.size", 2 do
assert_difference "FetchGamesForPlayer.jobs.size", 3 do
FetchGamesForPlayers.new.perform
end
end
@@ -0,0 +1,12 @@
require 'test_helper'

class GameCSVParserTest < ActiveSupport::TestCase
test "The game csv parser can parse a list of games from a CSV" do
game_csv = File.read(File.expand_path("test/fixtures/sample_responses/game_list.csv"))
games = GameCSVParser.new(game_csv).games
assert_equal 2, games.length
assert_equal 783006, games.second.dgs_game_id
assert_equal 'Tirog', games.first.opponent_name
assert_equal Time.utc(2013, 1, 14, 23, 32, 00), games.first.updated_at
end
end
@@ -2,21 +2,12 @@

class GameTest < ActiveSupport::TestCase

test "The game class can parse a list of games from a CSV" do
game_csv = File.read(File.expand_path("test/fixtures/sample_responses/game_list.csv"))
games = Game.parse_from_csv(game_csv)
assert_equal 2, games.length
assert_equal 783006, games.second.dgs_game_id
assert_equal 'Tirog', games.first.opponent_name
assert_equal Time.utc(2013, 1, 14, 23, 32, 00), games.first.updated_at
end

test "The game class can merge two sets of games" do
game_csv = File.read(File.expand_path("test/fixtures/sample_responses/game_list.csv"))
new_games = Game.parse_from_csv(game_csv)
new_games = GameCSVParser.new(game_csv).games
old_game_data = [{dgs_game_id: new_games.first.dgs_game_id}, {}, {}]

old_games = Game.parse_from_csv(csv_for_game_data(old_game_data))
old_games = GameCSVParser.new(csv_for_game_data(old_game_data)).games
added, deleted, existing = Game.merge_games(old_games, new_games)

assert_equal 1, added.length
@@ -31,10 +22,10 @@ class GameTest < ActiveSupport::TestCase

test "A game is considered new if it's newer than an existing game" do
game_csv = File.read(File.expand_path("test/fixtures/sample_responses/game_list.csv"))
new_games = Game.parse_from_csv(game_csv)
new_games = GameCSVParser.new(game_csv).games
old_game_data = [{dgs_game_id: new_games.first.dgs_game_id, updated_at: new_games.first.updated_at - 1.day}, {}, {}]

old_games = Game.parse_from_csv(csv_for_game_data(old_game_data))
old_games = GameCSVParser.new(csv_for_game_data(old_game_data)).games
added, deleted, existing = Game.merge_games(old_games, new_games)

assert_equal 2, added.length
@@ -45,4 +36,9 @@ class GameTest < ActiveSupport::TestCase

assert_equal 0, existing.length
end

test "A game is smart enough to know that an updated_at string is in UTC" do
game = Game.new(updated_at: '2013-03-13 09:03:24') # (1-ish PDT)
assert_equal 'UTC', game.updated_at.zone
end
end
@@ -83,31 +83,31 @@ class PlayerTest < ActiveSupport::TestCase
end

test "game messages are generated correctly for a single game" do
game_list = Game.parse_from_csv(csv_for_game_data([{:opponent_name => 'justin'}]))
game_list = GameCSVParser.new(csv_for_game_data([{:opponent_name => 'justin'}])).games
assert_equal "justin is ready for you to move.", Player.new.send(:alert_message, game_list, [])
end

test "game messages are generated correctly for 3 new players" do
game_data = [{:opponent_name => 'justin'}, {:opponent_name => 'bob'}, {:opponent_name => 'bill'}]
game_list = Game.parse_from_csv(csv_for_game_data(game_data))
game_list = GameCSVParser.new(csv_for_game_data(game_data)).games
assert_equal "justin, bob, and bill are ready for you to move.", Player.new.send(:alert_message, game_list, [])
end

test "game messages are generated correctly for > 3 new games" do
game_list = Game.parse_from_csv(game_csv(4))
game_list = GameCSVParser.new(game_csv(4)).games
assert_equal "4 games are ready for you to move.", Player.new.send(:alert_message, game_list, [])
end

test "game messages are generated correctly for games with duplicate opponents" do
game_data = [{:opponent_name => 'justin'}, {:opponent_name => 'bob'}, {:opponent_name => 'bill'}, {:opponent_name => 'justin'}]
game_list = Game.parse_from_csv(csv_for_game_data(game_data))
game_list = GameCSVParser.new(csv_for_game_data(game_data)).games
assert_equal "justin, bob, and bill are ready for you to move.", Player.new.send(:alert_message, game_list, [])
end

test "only added opponents are taken into account" do
game_data = [{:opponent_name => 'justin'}, {:opponent_name => 'bob'}]
game_list = Game.parse_from_csv(csv_for_game_data(game_data))
assert_equal "justin and bob are ready for you to move.", Player.new.send(:alert_message, game_list, Game.parse_from_csv(game_csv(2)))
game_list = GameCSVParser.new(csv_for_game_data(game_data)).games
assert_equal "justin and bob are ready for you to move.", Player.new.send(:alert_message, game_list, GameCSVParser.new(game_csv(2)).games)
end

end

0 comments on commit 6a56e61

Please sign in to comment.