Babitch – Une appli pour les matchs de babyfoot – Pourquoi ?



Babitch – Une appli pour les matchs de babyfoot – Pourquoi ?

0 0


talk-lft-babitch


On Github adriensamson / talk-lft-babitch

Babitch

Une appli pour les matchs de babyfoot

Pourquoi ?

  • Enregistrer les scores des matchs
  • Pouvoir faire des stats
  • S'amuser sur un side-project

Comment ?

Une API REST pour enregistrer et restituer les données

Une application web responsive pour saisir les données et afficher les stats

Stack technique

Une API REST

  • comme on en fait tous les jours (chez les Cytrons)
  • en Symfony2
  • avec FOSRestBundle, JMSSerializerBundle et NelmioApiDocBundle
  • stockage en BDD SQL (avec Doctrine)
  • tests fonctionnels avec Behat

Un joueur

{
    "name": "Adrien",
    "email": "adrien@example.com",
    "_links": {
        "self": {
            "href": "http://api.babitch.example.com/v1/players/1"
        }
    }
}

Un match

{
    "blue_score": 10,
    "red_score": 9,
    "composition": [
         { "...": "..." }
    ],
    "goals": [
        { "...": "..." }
    ],
    "_links": {
        "self": {
            "href": "http://api.babitch.example.com/v1/games/1"
        }
    }
}

Un joueur dans la compo

{
    "player_id": 1,
    "team": "blue",
    "position": "defense",
    "_links": {
        "player": {
            "href": "http://api.babitch.example.com/v1/players/1"
        }
    }
}

Un but

{
    "player_id": 1,
    "conceder_id": 1,
    "position": "defense",
    "autogoal": true
}

Tests fonctionnels avec behat

Feature: Players Ressource

    Scenario: POST a player, GET it, then GET player listing, finnaly DELETE it
        Given I add "CONTENT_TYPE" header equal to "application/json"
        When I send a POST request on "/v1/players" with body:
        """
        {"name" : "raoul", "email" : "raoul@test.com"}
        """
        Then the response status code should be 201
        And the header "location" should be equal to "http://localhost/v1/players/1"

        When I send a GET request on "/v1/players/1"
        Then the response status code should be 200
        And the response should be in JSON
        And the JSON node "name" should be equal to "raoul"
        And the JSON node "email" should be equal to "raoul@test.com"

Le FRONT

Une vue Match

Une vue Live

Une partie Stats

Techniquement ?

AngularJS

  • Framework MVW
  • Databinding bi-directionnel costaud
  • Webcomponents
  • Injection de dépendance \o/
  • Intégration du process de Tests

Databinding

Hello {{ name }}

<div ng-app="demo">
    <h3>Hello {{ name }}</h3>
    <input type="text" ng-model="name" />
</div>

Controller

Click : {{ count }}

click !
<div ng-controller='CounterCtrl'>
    <h3>Click : {{ count }}</h3>
    <button ng-click="increment()">click !</button>
</div>
angular.module('demo', [])
    .controller('CounterCtrl', ['$scope', function($scope) {
        $scope.count = 0;
        $scope.increment = function() {
            $scope.count++;
        };
    }]);

Restangular

{{ repository.slug }}

Status : {{ repository.last_build_result }}

Search
<div ng-controller='RestCtrl'>
    <h3>{{ repository.slug }}</h3>
    <p>Status : {{ repository.last_build_result }}</p>
    <input type="text" ng-model="repoId" />
    <button ng-click="search(repoId)">Search</button>
</div>
angular.module('demo', ['restangular'])
    .controller('RestCtrl', ['$scope', 'Restangular', function($scope, Restangular) {
        Restangular.setBaseUrl('https://api.travis-ci.org/');
        Restangular.setRequestSuffix('.json');
        $scope.search = function(repoId) {
            $scope.repository = Restangular.one('repos', repoId).get().$object;
        };
}]);

Tests

Tests Unitaire

  • Jasmine: Framework de tests JS
  • Karma: Tests Runner
  • Phantomjs: Browser headless
  • Mock de tous les appels HTTP (angule $httpBackend)

Tests Fonctionnels

  • Protractor
  • Mock de tous les appels au backends(angular httpBackend)
  • Chromedriver
  • Saucelabs

Faye

  • pub/sub temps réel
  • Serveur node ou ruby
  • Simple a mettre en place
  • Scalable et utilisé en production sur de grosses applications

Serveur (Nodejs)

var http = require('http'),
faye = require('faye');

var httpServer = http.createServer(),
fayeServer = new faye.NodeAdapter({mount: '/faye', timeout: 45});

fayeServer.attach(httpServer);
server.listen(8000);

Client

var client = new Faye.Client('http://localhost:8000/faye');

// Envoi d'un message
client.publish('/home-channel', {text: 'Hi there'});

// Réception d'un message
client.subscribe('/home-channel', function(message) {
    console.log(message);
});

Bilan

  • Les librairies *.js pas forcément très stables
  • Développement itératif avec périmètre restreint => utilisable rapidement

Le futur proche

  • Timestamps début/fin de match et à chaque but (en cours)
  • Création/modificaton des joueurs dans l'appli
  • Des stats qui prennent en compte les différences de niveau (ELO)
  • Composition à chaque but
  • D'autres idées ?

Contribuer

Merci