Содержание


Создание простого приложения для игры в слова с помощью Cloudant в облачной среде Bluemix

IBM Bluemix™ – это новая открытая платформа для разработки и развертывания веб- и мобильных приложений. В этой статье я опишу процесс создания простого приложения – игры «Угадай слово» – с помощью Bluemix и облачной среды разработки: DevOps Services. Мы начнем с нуля и придем к простой игре, в которую можно играть в веб-браузере, причем программа для сервера выполняется в облаке.

Выполнив все действия, описанные в этой статье, вы научитесь создавать собственные приложения Bluemix любого размера.

Хотя это простое приложение, мы рассмотрим аспекты, которые важны при разработке крупных приложений, такие как подготовка хорошей среды разработки для локальной отладки программ. Моя цель – показать, как создать простое Bluemix-приложение с помощью подхода, пригодного также и для разработки больших Bluemix-приложений. Выполнив все действия, описанные в этой статье, вы научитесь создавать собственные приложения Bluemix любого размера.

Запустить приложениеПолучить код

Что потребуется для приложения

Чтобы следовать указаниям этой статьи, вам понадобятся учетная запись Bluemix и учетная запись DevOps Services. Создайте их, если вы еще не сделали этого. Кроме того, если вы впервые слышите о Bluemix, вам будет полезно прочесть это краткое введение.

Приступая к разработке, важно выбрать правильные технологии. Bluemix поддерживает множество технологий для разработки приложений. Эта гибкость – одно из главных преимуществ данной платформы, так как она позволяет выбрать технологии, наиболее подходящие для разрабатываемого приложения. Для нашей игры «Угадай слово» мы будем использовать следующие технологии:

  • Node.js— Для серверной части кода мы будем использовать Node.js. Веб-серверы, реализованные в среде Node.js, быстро запускаются, и их легко разрабатывать и отлаживать локально. При этом можно использовать один и тот же язык, например, JavaScript, как для серверного, так и для клиентского кода. С Node.js мы будем использовать среду Express, так как она обеспечивает полезную функциональность при реализации веб-сервера.
  • Cloudant— Для хранения данных сервера между сеансами (поставленных при игре рекордов) мы будем использовать базу данных Cloudant. База данных NoSQL-типа, такая как Cloudant, проста в применении с программами JavaScript и данными в коде JSON.
  • HTML, CSS и JavaScript— Пользовательский интерфейс игры будет реализован с помощью HTML, CSS и JavaScript. Это естественный выбор с учетом поддержки HTML5 и CSS3 в современных веб-браузерах, так как он позволит играть в нашу игру не только на компьютере, но и телефонах и планшетах.
  • Bootstrap и JQuery— Мы также будем использовать среды Bootstrap и JQuery JavaScript, которые обеспечивают хорошую функциональность для разработки веб-интерфейса пользователя.
  • Jade— Чтобы меньше печатать, для своих HTML-страниц мы воспользуемся языком шаблонов. Существует несколько языков шаблонов для Node.js. Я остановился на Jade, так как он часто применяется в среде Express.

Приняв эти решения, мы готовы приступить к программированию.

Шаг 1. Программирование сервера

  1. Начнем с реализации ядра сервера Node.js, чтобы его можно было проверить до реализации пользовательского интерфейса. Для программирования можно использовать Eclipse, но на момент написания этой статьи поддержка Node.js в Eclipse остается на базовом уровне, особенно когда дело доходит до отладки. Поэтому для отладки своего серверного кода Node.js мы воспользуемся утилитой node-inspector. Для написания следующего кода вы можете использовать свой любимый текстовый редактор:
    /**
     * Серверная часть приложения GuessTheWord
     */
    
    var express = require('express');
    var app = express();
    var http = require('http');
    
    var host = "localhost";
    var port = 3030;
    
    // Указание пути к каталогу шаблонов Jade
    app.set('views', __dirname + '/views');
    
    // Указание пути к файлам JavaScript
    app.set('js', __dirname + '/js');
    
    // Указание пути к файлам CSS
    app.set('css', __dirname + '/css');
    
    // Указание пути к файлам изображений
    app.set('images', __dirname + '/images');
    
    // Set path to sound files
    app.set('sounds', __dirname + '/sounds');
    
    // Указание пути к статическим файлам
    app.use(express.static(__dirname + '/public'));
    
    // Привязка к странице hiscore корневого URL '/' 
    app.get('/', function(req, res){
      res.render('hiscores.jade', {title: 'Hiscores'});
    });
    
    // Привязка к главной странице игры URL '/play'
    app.get('/play', function(req, res){	
      res.render('main.jade', {title: 'Guess the Word'});
    });
    
    var server = app.listen(port, function() {
      console.log('Server running on port %d on host %s', server.address().port, host);
    });
    
    process.on('exit', function() {
      console.log('Server is shutting down!');
    });
  2. Сохраните этот код в файле server.js и создайте папку views для хранения двух файлов Jade (hiscores.jade и main.jade), которые упоминаются в предыдущем коде и приведены ниже. Эти Jade-файлы определяют содержимое двух веб-страниц игры.
  3. Файл hiscores.jade отображает рекорды. Это начальная страница игры, которая привязана к корневому URL '/'.
    doctype html
    html(lang="en")
      head
        title= title
        link(rel="stylesheet", href="css/bootstrap.css", type="text/css")
        link(rel="stylesheet", href="css/hiscores.css", type="text/css")
      body(style="background-image:url(/images/background.jpg)")
    
        div(class="container")
          h1(id="header") High Scores
          table(id="hiscore_table")
            tr()
              th(class="table-header") Score
              th(class="table-header") Name
              th(class="table-header") Date
          div(style="padding-top:30px;")
            a(class="btn btn-default", href="/play") Play!
    
        script(src="js/jquery.js", type="text/javascript")
        script(src="js/bootstrap.js", type="text/javascript")
        script(src="js/hiscores.js", type="text/javascript")
  4. Файл main.jade – это страница, на которой проходит игра. Она привязана к URL '/play'.
    doctype html
    html(lang="en")
      head
        title= title
        link(rel="stylesheet", href="css/bootstrap.css", type="text/css")
        link(rel="stylesheet", href="css/main.css", type="text/css")
      body(style="background-image:url(/images/background.jpg)")
    
        div(class="container")
          h1(class="game-text") Guess the secret word!
          div(class="row")
            div(class="col-xs-8")
              div(id="word-description")        
            div(class="col-xs-4")
               div(id="score") Score:        
    
          div(class="row")
            div(class="col-xs-8")
              div(class="word-area")
                table()
                  tr()        
            div(class="col-xs-4")
              div(id="power") Power:            
          div(class="row")
            div(class="col-xs-8")
              div(id="help-text", class="game-text") Click on a ?-box above and type a letter          
            div(class="col-xs-4")
              div(class="row")
                div(class="col-xs-12")
                  button(id="skip-button" class="btn btn-info btn-xs") Skip this word
                div(class="col-xs-12")
                  button(id="help-letter-button" class="btn btn-info btn-xs") Give me a letter
        audio(id="tick-sound", src="sounds/Tick.mp3")
        audio(id="skip-sound", src="sounds/Falcon.mp3")
        audio(id="applause-sound", src="sounds/Applause.mp3")
    
        script(src="js/jquery.js", type="text/javascript")
        script(src="js/bootstrap.js", type="text/javascript")
        script(src="js/main.js", type="text/javascript")

Как видите, эти Jade-файлы ссылаются на множество статических файлов, таких как файлы CSS и JavaScript. Мы добавим их позже, а сейчас проверим то, что у нас уже есть.

Шаг 2. Запуск и отладка кода сервера

  1. Если вы еще не установили Node.js локально, сейчас самое время это сделать. Загрузите его с сайта Nodejs.org и следуйте инструкциям. Мы установим Node.js локально, чтобы локально запускать и отлаживать код сервера перед его развертыванием в Bluemix.
  2. Вам нужно также установить модули Express и Jade для Node.js. Самый простой способ – создать в корневой папке файл package.json, который определяет зависимости для этих библиотек.
    {
    	"name": "GuessTheWord",
    	"version": "0.0.1",
    	"description": "GuessTheWord package.json file",
    	"dependencies": {
    		"express": ">=3.4.7 <4",
    		"jade": ">=1.1.4"
    	},
    	"engines": {
    		"node": ">=0.10.0"
    	},
    	"repository": {}
    }

    Теперь можно установить Express и Jade – откройте окно командной строки из корневой папки и введите следующую команду:

    > npm install

    К тому времени, когда вы будете читать эту статью, могут выйти новые версии Express и Jade. Конечно, вы можете использовать их вместо тех, которые указаны в коде. Поскольку все зависимости приложения указаны в файле package.json, при локальной разработке и при развертывании в Bluemix будут использоваться одни и те же версии библиотек. Это избавит нас от возможных сюрпризов, связанных с различиями в версиях библиотек, применяемых в разных средах.

  3. Выполните первый запуск приложения с помощью следующей команды:
    > node --debug server.js
  4. Откройте веб-браузер и перейдите по адресу: http://localhost:3030/. http://localhost:3030
  5. Прежде чем приступить к разработке игры, нужно организовать возможность отладки приложения. Сначала установите node-inspector. Откройте новую командную строку и введите следующую команду:
    > npm install -g node-inspector
  6. Мы запустили приложение с флагом --debug, поэтому теперь можем запустить node-inspector:
    > node-debug server.js

    Эта команда открывает отладчик JavaScript в веб-браузере по умолчанию – это должен быть Chrome или Opera. Если у вас другой веб-браузер по умолчанию, можно использовать следующую команду:

    > node-inspector

    Введите в Chrome или Opera адрес http://127.0.0.1:8080/debug?port=5858, чтобы приступить к отладке. Тем, кто никогда не использовал этот отладчик, будет полезно прочесть введение.

Шаг 3. Написание клиентского кода

  1. Теперь создадим отсутствующие файлы, на которые ссылаются файлы Jade. Все двоичные файлы (JPG и MP3) можно загрузить из готового проекта DevOps Services GuessTheWord, который содержит все файлы, фигурирующие в этой статье. Простейший способ загрузить их – открыть вкладку EDIT CODE, щелкнуть правой кнопкой мыши на содержащей папке и выбрать Export, чтобы экспортировать содержимое в zip-файл для загрузки. Создание отсутствующих файлов
    Создание отсутствующих файлов

    Конечно, можно создать свои собственные изображения и звуки, чтобы придать игре индивидуальность.

  2. Следующие файлы представляют собой библиотеки JavaScript JQuery и Bootstrap. Их также можно загрузить из проекта GuessTheWord DevOps или из Интернета.
    1. Загрузите в папку public/js:
      • jquery.js (последняя версия библиотеки JavaScript JQuery)
      • bootstrap.js (последняя версия библиотеки JavaScript Bootstrap)
    2. Загрузите в папку public/css:
      • bootstrap.css (последняя версия CSS-файла для Bootstrap)
  3. Теперь создайте следующие два CSS-файла, определяющие стили, используемые в игре:
  4. Снова запустите приложение локально, чтобы убедиться, что внесенные изменения вступили в силу. Перейдите в корневую папку приложения (к этой папке должен быть подключен локальный репозиторий Git) и выполните следующую команду:
    > node-debug server.js

    Теперь игра должна выглядеть немного лучше:

    Процесс создания игры
  5. Теперь напишем клиентский код JavaScript, реализующий логику игры. Реализацию hiscores.js пока отложим, так как для заполнения списка рекордов требуется база данных, которую мы добавим позже. Вместо этого создайте файл public/js/main.js, содержащий этот фрагмент кода со всей основной логикой игры. Он получает от сервера случайное английское слово с описанием, обрабатывает данные, введенные игроком с клавиатуры, подсчитывает очки, управляет питанием и т.д.

    Как видите, для того чтобы получить секретное слово, функция getNewSecretWord() обращается к службе /randomword на веб-сервере.

  6. Реализуем эту службу, добавив в файл server.js следующий код (после первоначального объявления переменных).
    /**
     * Поиск слова в онлайн-словаре wordnik и получение его описания.
     * @param word {String} – слово описание которого мы ищем 
     * @param cb_description {function} – Обратный вызов с описанием в качестве аргумента. 
     * Если слово в словаре не найдено, описание остается пустым.
     */
    function wordLookup(word, cb_description) {
      http.request(
        {
          host: "api.wordnik.com",
          path: "/v4/word.json/" + word +
            "/definitions?limit=1&api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5"
        },	function (res) {
          var str = '';
          res.on('data', function(d) {
            str += d;
          });
          res.on('end', function() {
          var wordList = JSON.parse(str);
          cb_description(wordList.length > 0 ? wordList[0].text : "");
        });
      }).end();	
    }
    
    app.get('/randomword', function(request, response) {
      http.request(
        {
          host: "api.wordnik.com",
          path: "/v4/words.json/randomWord?hasDictionaryDef=false&minCorpusCount=0&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=5&maxLength=-1&api_key=a2a73e7b926c924fad7001ca3111acd55af2ffabf50eb4ae5"
        }, function (res) {
          var str = '';
          res.on('data', function(d) {
            str += d;
          });
          res.on('end', function() {
            var wordObj = JSON.parse(str);
            wordLookup(wordObj.word, function(descr) {
            var randomWordObj = { word : wordObj.word, description : descr };
            response.send(JSON.stringify(randomWordObj));		
          });								
        });
      }).end();
    });

    Этот код связывает URL /randomword с кодом, использующим онлайн-словарь английского языка Wordnik.com, чтобы получить случайное слово. Затем он вызывает метод wordLookup(), который вновь обращается к API Wordnik, чтобы получить описание этого слова. Наконец, секретное слово и его описание кодируются в формат JSON и возвращаются в качестве ответа на HTTP-запрос.

    Примечание. Ключ API, входящий в приведенный выше фрагменте кода, относится к бесплатной учетной записи, которой пользуются многие люди, поэтому его можно применять лишь для небольшого количества запросов. Во избежание этого ограничения можно зарегистрироваться в Wordnik и получить свой собственный ключ API.

  7. Теперь можно локально выполнить несколько отладочных сеансов, чтобы пройтись по шагам клиентского и серверного кода и убедиться, что игра работает как нужно. Проверка игры
    Проверка игры

    Клиентский код JavaScript отлаживается с помощью встроенного отладчика Chrome. Серверный код JavaScript отлаживается с помощью node-inspector, как описано выше. Обратите внимание, что node-inspector использует для отладки серверного кода JavaScript Node.js тот же отладчик Chrome.

Шаг 4. Создание проекта DevOps Services

Теперь, когда у нас есть работающее приложение и мы можем запускать и отлаживать код локально, сохраним все написанное до сих пор в репозитории DevOps Services. Это обеспечит целый ряд преимуществ:

  • код будет храниться в облаке, то есть надежнее, чем в локальном компьютере;
  • другие могут вносить вклад в разработку приложения;
  • мы можем разрешить автоматическое развертывание в Bluemix.
  1. Войдите в DevOps Services и нажмите кнопку Start coding, чтобы создать проект приложения. Выбор между Jazz SCM и Git SCM произвольный и обычно зависит от того, с какой системой SCM вы лучше знакомы. Для нашего проекта мы выберем репозиторий Git и установим флажок Deploy to Bluemix, поскольку мы намерены развернуть свое приложение в облачной среде Bluemix. Выбрав организацию и пространство Bluemix, нажмите кнопку CREATE. Создание проекта
    Создание проекта
  2. Сначала ваш новый проект будет пустым, за исключением пары файлов по умолчанию. Чтобы добавить в проект код, щелкните на EDIT CODE, затем перетащите файлы из своей файловой системы в иерархию файлов проекта (дерево слева).

    Если код написан с помощью Eclipse, можно перетаскивать файлы прямо из Eclipse Project Explorer. После этого нужно выбрать отдельные файлы и папки в проекте Eclipse (а не сам проект) и перетащить их в верхний узел (помечен красным) в иерархии файлов проекта. Имейте в виду, что Eclipse скрывает некоторые файлы, например файл .project, так что, возможно, придется перетащить некоторые файлы из файловой системы.

    Теперь иерархия файлов проекта должна содержать все файлы приложения.

    Обратите внимание, что хотя вы только что скопировали файлы из локального компьютера в проект DevOps Services, внесенные изменения еще не зафиксированы и не перенесены в Git-репозиторий. Это означает, что добавленные файлы никто не увидит. Считайте иерархию файлов, отображаемую в проекте DevOps Services, своей личной рабочей областью, такой же, как локальная файловая система, за исключением того, что она хранится в облаке.

  3. Если нажать на значок репозитория, вы увидите несколько незафиксированных изменений, по одному для каждого добавленного файла. Файлы показаны как «Unstaged», что на языке Git означает изменения, еще не зафиксированные в репозитории. Значок репозитория
  4. Чтобы зафиксировать изменения, выполните команду Stage the change для каждого файла: Кнопка Stage

    Статус файлов изменится с Unstaged на Staged. Все изменения, показанные как Staged, будут выполнены как одно изменение, совершенное в момент нажатия кнопки COMMIT. В данном случае мы хотим добавить все файлы в одном наборе изменений, но позже, возможно, захотим внести некоторые изменения в рамках одного набора и некоторые другие изменения в рамках другого набора. В каждом наборе изменений группируются изменения, логически связанные друг с другом.

    Сообщение Commit message в диалоговом окне Commit должно содержать описание набора изменений и, возможно, причину этих изменений.

    Сообщение commit
    Сообщение commit
  5. При нажатии кнопки SUBMIT набор изменений фиксируется в Git и появляется как один набор изменений в разделе Commits. Зафиксированные, но еще не переданные изменения
    Зафиксированные, но еще не переданные изменения
  6. Последним шагом нужно передать этот набор изменений в основную ветвь, чтобы другие могли извлечь их оттуда. Нажмите кнопку PUSH, затем кнопку ОК, чтобы перенести изменения. Перенесенные изменения
  7. Теперь локальная копия кода – лишняя и ее можно удалить. Если вы будете продолжать разработку локальной копии, не забудьте переносить все измененные файлы в проект DevOps Services и вносить изменения так же, как мы только что это сделали. Работать таким образом можно, но это путано и неудобно.

    На практике лучше либо отказаться от локальной разработки и редактировать код с помощью веб-редактора в DevOps Services, либо создать локальный Git-репозиторий, сопряженный с Git-репозиторием DevOps Services. Я выбрал второй вариант, потому что нам нужна возможность локальной разработки и отладки.

    Существует несколько способов создания локального Git-репозитория, сопряженного с Git-репозиторием DevOps Services. Если вы используете интегрированную среду разработки, такую как Eclipse или Visual Studio, то можете применять плагин Git для этих IDE, импортировав Git-репозиторий DevOps Services в среду IDE. Если же вы разрабатываете в среде, где нет плагина Git, то можете запускать Git из командной строки. В любом случае потребуется URL Git-репозитория DevOps Services. Этот URL можно получить на странице обзора проекта DevOps Services.

    Чтобы попасть на страницу обзора проекта из вкладки EDIT CODE, нажмите на ссылку проекта в левом верхнем углу.

    Страница обзора проекта

    Затем нажмите на ссылку Git URL:

    Git URL
    Git URL
  8. Скопировав этот URL-адрес в локальный Git-репозиторий, можно продолжать локально разрабатывать приложение и фиксировать изменения непосредственно в Git-репозитории DevOps Services, не применяя веб-интерфейс DevOps Services. При использовании Eclipse с плагином EGit представление проекта будет выглядеть примерно так, как на следующем скриншоте. Если в Eclipse еще нет плагина EGit, его легко установить. Если вы никогда не работали с EGit, проконсультируйтесь в Руководстве пользователя.Проект Eclipse с файлами приложения в Git-репозитории
    Проект Eclipse с файлами приложения в Git-репозитории

Шаг 5. Развертывание приложения в Bluemix

Теперь пора сделать первый шаг по развертыванию в Bluemix, просто чтобы убедиться, что у нас все работает, прежде чем продолжить разработку игры.

  1. Процессом развертывания управляет файл manifest.yml, который необходимо создать в корневой папке приложения.
    ---
    applications:
    - name: GuessTheWord
      framework: node
      runtime: node08
      memory: 64M
      instances: 1
      host: GuessTheWord
      path: .     
      command: node server.js

    Эти параметры указывают Bluemix, как развернуть наше приложение. Мы указываем среду выполнения (Node.js), объем памяти, выделенный приложению, имя хоста, которое мы хотим использовать в качестве URL своего развернутого приложения и т.п. Обратите внимание, что имя хоста должно быть глобально уникальным, так что вам придется выбрать имя, отличное от GuessTheWord.

  2. Создаете ли вы этот файл в веб-редакторе DevOps Services или в локальной среде разработки, не забудьте перенести его в Git-репозиторий DevOps Services.
  3. Чтобы начать процесс развертывания, нажмите кнопку BUILD & DEPLOY в своем проекте DevOps Services. Вкладка Build & Deploy
    Вкладка Build & Deploy

    Затем переведите переключатель в верхней части страницы из положения OFF в положение SIMPLE для развертывания в Bluemix.

    Кнопки автоматического развертывания
  4. DevOps Services попытается развернуть приложение в Bluemix. Простое авторазвертывание
    Простое авторазвертывание

    Развертывание также будет происходить автоматически всякий раз, когда кто-то вносит изменения в Git-репозиторий DevOps Services. Это удобно, потому что обеспечивает постоянный доступ к последней версии развернутого приложения, например, для запуска тестов.

  5. Примерно через 30 секунд вы должны увидеть, что приложение развернуто без ошибок. Сообщение автоматического развертывания
    Сообщение автоматического развертывания
  6. Чтобы получить доступ к развернутому приложению в Bluemix, нажмите на ссылку над списком. Развернутое приложение
    Развернутое приложение

Шаг 6. Отладка приложения, развернутого в Bluemix

При попытке получить доступ к развернутому приложению вы, вероятно, увидите следующее сообщение:

404 Not Found: Requested route ('guesstheword.mybluemix.net') does not exist.

Это нехорошо. Очевидно, где-то есть проблема, и скорее всего, она связана с приложением, запущенным в Bluemix, так как локально все работало нормально.

Это иллюстрирует важный момент: приложение может быть успешно развернуто, но окажется недоступным при попытке обратиться к нему. Зеленый индикатор и OK в списке Recent Auto-Deployments просто означает, что приложение успешно развернуто и запущено. Однако оно, например, может остановиться вскоре после запуска и стать недоступным при попытке обращения к нему по его URL-адресу в Bluemix.

Для решения подобных проблем было бы неплохо иметь возможность отладки серверного кода в Bluemix. Скорее всего, такая возможность появится в будущем, но пока Bluemix не предоставляет ее. Что же делать?

  1. Просмотрим файлы журналов DevOps Services, чтобы выяснить, что пошло не так. Иногда дополнительную информацию можно получить с помощью инструмента Bluemix cf CLI. Например:
    > cf app GuessTheWord
    Showing health and status for app GuessTheWord in org mattias.mohlin@se.ibm.com
    / space dev as mattias.mohlin@se.ibm.com...
    OK
    
    requested state: started
    instances: 0/1
    usage: 64M x 1 instances
    urls: GuessTheWord.mybluemix.net
    
         state      since                    cpu    memory   disk
    #0   crashing   2014-04-24 01:10:37 PM   0.0%   0 of 0   0 of 0

    Мы видим, что приложение находится в состоянии crashing, что означает, что оно каким-то образом остановлено. Веб-сервер никогда не должен останавливаться, так что нужно выяснить, почему в нашем случае он останавливается при работе в Bluemix.

  2. В DevOps Services на вкладке EDIT CODE есть кнопка DEPLOY но пока не нажимайте ее. Кнопка Deploy

    Это кнопка ручного развертывания содержимого вашей личной области проекта DevOps Services в Bluemix. Она полезна при отладке, поскольку позволяет добавлять временную регистрацию событий или проводить другие эксперименты, которые не нужно переносить в основную ветвь проекта. Прежде чем нажать кнопку DEPLOY, нужно извлечь все входящие наборы изменений, чтобы привести область проекта DevOps Services в соответствие с основной с ветвью.

  3. Перейдите на страницу Git Status и нажмите кнопку FETCH. В разделе Commits показаны входящие наборы изменений. Нажмите кнопку MERGE, чтобы объединить эти входящие наборы изменений в области проекта DevOps Services.
  4. Теперь можно вручную развернуть проект, нажав кнопку DEPLOY. Когда приложение будет развернуто в Bluemix, вы увидите следующее сообщение: Ручное развертывание готово
    Ручное развертывание готово
  5. Нажмите на ссылку на страницу корневой папки в показанном выше сообщении, чтобы просмотреть сведения о ручном развертывании. Приложение, развернутое в Bluemix, не работает
    Приложение, развернутое в Bluemix, не работает

    Мы видим красный индикатор, подтверждающий, что приложение не выполняется. Нажмите на ссылку Logs, чтобы просмотреть журналы событий, созданных приложением перед его завершением.

    Файлы журналов
  6. Откройте файл stdout.log. Содержимое файла stdout.log
    Содержимое файла stdout.log

    Из этого журнала сразу становится ясно, что не так. Наш веб-сервер пытается использовать жестко запрограммированные имя хоста (localhost) и номер порта (3030). Это работало при локальном запуске сервера, но не в Bluemix.

  7. Решение заключается в том, чтобы добавить следующий фрагмент кода сразу после объявления переменных host и port в файле server.js:
    if (process.env.hasOwnProperty("VCAP_SERVICES")) {
      // Работа в Bluemix. Анализ имен порта и хоста, которые мы назначили.
      var env = JSON.parse(process.env.VCAP_SERVICES);
      var host = process.env.VCAP_APP_HOST;
      var port = process.env.VCAP_APP_PORT;	
    }

    Для передачи информации о среде на сервер Bluemix использует переменную среды VCAP_SERVICES. Примерами такой информации служат значения host и port. Обратите внимание, что оператор if не станет выполняться при локальном запуске сервера, потому что переменной VCAP_SERVICES тогда не будет. Следовательно, даже после этого изменения мы сможем запускать и отлаживать приложение локально.

  8. Перенесем это изменение в Git-репозиторий и дадим авторазвертыванию выполнить свою работу. Развертывание прошло успешно. Авторазвертывание изменений
    Авторазвертывание изменений
  9. Если теперь щелкнуть на ссылке на развернутое приложение, то у нас будет причина для маленького праздника. Игра GuessTheWord работает в Bluemix
    Игра GuessTheWord работает в Bluemix

    Есть! Наша игра работает в Bluemix.

  10. Теперь сделаем заслуженный перерыв и поиграем. Сколько слов вы успеете угадать, прежде чем кончится запас энергии?

Шаг 7. Добавление службы базы данных

Вам удалось достичь высокой оценки? Поздравляем! Жаль, что она не сохраняется, чтобы другие могли увидеть, как хорошо вы угадываете слова. Давайте исправим это, сохраняя оценки в базе данных. Может показаться, что база данных – это излишество для хранения столь малого объема информации. Не лучше ли было просто вести список рекордов в текстовом файле на сервере? Нет, это плохая идея, потому что сервер Bluemix не всегда работает на одной и той же физической машине (в целях выравнивания нагрузки). Так что хранить данные в файлах не рекомендуется, вместо этого лучше использовать какую-нибудь облачную службу персистентности.

Существует целый ряд облачных решений для хранения данных, но давайте поступим просто. Добавим у нашему приложению службу базы данных Cloudant.

  1. Войдите в веб-приложение Bluemix, нажав кнопку MANAGE на вкладке BUILD & DEPLOY. Кнопка Manage
    Кнопка Manage
  2. Это приведет нас в приложение GuessTheWord в Bluemix. Мы видим, что наше приложение работает, но с ним не связаны никакие службы. Нажмите кнопку Add a service, чтобы добавить новую службу. В разделе Data Management каталога Bluemix выберите Cloudant NoSQL DB. Значок Cloudant в Bluemix
  3. Нажмите на службу, затем нажмите кнопку Create в диалоговом окне. Приложение перезапустится, что должно занять всего несколько секунд. Нажмите кнопку Show Credentials в добавленной службе: Диалоговое окно для добавления службы базы данных Cloudant
    Диалоговое окно для добавления службы базы данных Cloudant

Автоматически создается учетная запись Cloudant со сгенерированным именем пользователя и паролем. Скопируйте значение поля URL, так как оно вам понадобится при написании кода доступа к базе данных.

Шаг 8. Использование базы данных Cloudant

  1. Щелкните на службе и нажмите кнопку Launch. Откроется веб-интерфейс Cloudant, где можно настраивать и администрировать базу данных. Кнопка Launch
  2. Создайте новую базу данных guess_the_word_hiscores. Затем нажмите кнопку для создания нового вторичного индекса. Сохраните его в документе top_scores и присвойте индексу имя top_scores_index. Создание вторичного индекса в БД Cloudant
    Создание вторичного индекса в БД Cloudant
  3. Функция map определяет, какие объекты в базе данных классифицируются по индексу и какую информацию нужно извлекать для этих объектов. Мы используем оценку в игре в качестве ключа индекса (первый аргумент команды emit), а затем командой emit создадим объект, содержащий оценку, имя игрока и дату получения оценки. Ниже приводится код JavaScript, реализующий функцию map, который можно скопировать и вставить.
    function(doc) {
     emit(doc.score, {score : doc.score, name : doc.name, date : doc.date});
    }
  4. Теперь напишем код, необходимый для сохранения оценок в базе данных Cloudant. На этот раз воспользуемся веб-редактором DevOps Services. Вам нужно решить, какую библиотеку Node.js использовать для доступа к Cloudant. Я выбрал библиотеку nano.

    Нажмите на файл package.json в иерархии файлов DevOps Services,а затем добавьте строку "nano" : "*", как показано ниже. Это приведет к включению в приложение последней версии библиотеки nano.

    	"dependencies": {
    		"express": ">=3.4.7 <4",
    		"jade": ">=1.1.4",
    		"nano" : "*"
    	},

    Как и в случае с хостом и портом, Bluemix использует переменную среды VCAP_SERVICES для передачи нашему приложению сведений о службе базы данных Cloudant (где она работает, какие учетные данные использовать для входа в нее и т.д.). Добавьте в файл server.js нижеследующие строки, отмеченные курсивом. Замените строку URL значением, предварительно скопированным из учетных данных службы.

    var express = require('express');
    var app = express();
    var http = require('http');
    
    var host = "localhost";
    var port = 3030;
    var cloudant = {
    		 		 url : "<url>" // TODO: Update		 		 
    };
    if (process.env.hasOwnProperty("VCAP_SERVICES")) {
      // Running on Bluemix. Parse out the port and host that we've been assigned.
      var env = JSON.parse(process.env.VCAP_SERVICES);
      var host = process.env.VCAP_APP_HOST;
      var port = process.env.VCAP_APP_PORT;
    
      // Also parse out Cloudant settings.
      cloudant = env['cloudantNoSQLDB'][0].credentials;  
    }
    var nano = require('nano')(cloudant.url);
    var db = nano.db.use('guess_the_word_hiscores');

    Как видите, этот приведенный код будет работать и тогда, когда приложение запускается не в Bluemix. Работая локально, приложение может использовать базу данных Cloudant, как если бы оно работало в Bluemix.

  5. Теперь давайте добавим к нашему серверу два новых URL:
    • /hiscores (для получения максимальной оценки из базы данных)
    • /save_score (для сохранения в базе данных новой максимальной оценки)

    Вот этот код:

    app.get('/hiscores', function(request, response) {
      db.view('top_scores', 'top_scores_index', function(err, body) {
      if (!err) {
        var scores = [];
          body.rows.forEach(function(doc) {
            scores.push(doc.value);		      
          });
          response.send(JSON.stringify(scores));
        }
      });
    });
    
    app.get('/save_score', function(request, response) {
      var name = request.query.name;
      var score = request.query.score;
    
      var scoreRecord = { 'name': name, 'score' : parseInt(score), 'date': new Date() };
      db.insert(scoreRecord, function(err, body, header) {
        if (!err) {       
          response.send('Successfully added one score to the DB');
        }
      });
    });
  6. У нас еще нет никаких обращений к этим новым URL, но мы все же можем проверить соединение с базой данных, вызывая эти URL непосредственно из браузера.

    Нажмите кнопку Git Status, перенесите все изменения в Git-репозиторий DevOps Services и дождитесь развертывания приложения в Bluemix. Затем введите в браузер следующий URL, заменив guesstheword именем хоста, который вы выбрали для своего приложения.

    http://guesstheword.mybluemix.net/save_score?name=Bob&score=4

    Должно появиться сообщение об успешной загрузке. Введите следующий URL, снова заменив guesstheword именем хоста приложения.

    http://guesstheword.mybluemix.net/hiscores

    Запись, которую вы только что добавили, должна появиться в коде JSON.

    [{"score":4,"name":"Bob","date":"2014-05-07T12:12:31.093Z"}]

    Это указывает на то, что наша программа использования базы данных работает правильно.

  7. Теперь давайте создадим отсутствующий файл JavaScript для заполнения таблицы оценок на главной странице нашей игры. В веб-редакторе щелкните правой кнопкой мыши на папке public/js и выберите New > File. Создание файла в веб-редакторе

    Назовите файл hiscores.js и введите в него следующий код.

    /**
     * Максимальные оценки
     */
    
    function createTableRow(name, score, date) {
      var dateObj = new Date(date);
      var formattedDate = dateObj.toLocaleDateString() + " " + dateObj.toLocaleTimeString();
      return '<tr> <td>' + score + '</td><td>' + name + '</td><td>' + formattedDate + '</td></tr>';
    }
    
    /**
     * Заполнение таблицы максимальных оценок с извлечением топ-10 оценок из БД. 
     * Вызывается, когда DOM полностью загружен.
     */
    function populateTable() {	
      var table = $("#hiscore_table tr");
      $.get("/hiscores", function (data) {
        var hiscores = JSON.parse(data);
        hiscores.forEach(function (hiscore) {
          var html = createTableRow(hiscore.name, hiscore.score, hiscore.date);
          table.last().after(html);		
        });
      });	
    }
    
    $(populateTable);

    Этот код заполняет таблицу лучшими результатами из базы данных. Перенесите изменения в Git-репозиторий DevOps Services и дождитесь, пока приложение будет заново развернуто в Bluemix. Теперь, когда у вас есть доступ к приложению, вы должны увидеть фиктивные оценки, добавленные в базу данных.

    Таблица рекордов
    Таблица рекордов
  8. Осталось всего несколько вещей, которые нужно поправить, чтобы по окончании игры оценка сохранялась. В файле main.js найдите следующую строку:
    // TODO: Save name and score in DB

    И замените ее строкой:

    saveScore(name, score);

    Добавьте функцию saveScore():

    function saveScore(name, score) {
      $.ajax({url: "/save_score?name=" + name + "&score=" + score, cache : false}).done(function(data) {
        window.location.replace("/"); // Go to hiscore page
      });
    }
  9. Наконец, создайте диалоговое окно Bootstrap «Game Over», добавив следующую строку в качестве первого элемента div в файле main.jade:
    div(id="name-dialog", class="modal fade")
      div(class="modal-dialog")
        div(class="modal-content")
          div(class="modal-header")
            h4(class="modal-title")
          div(class="modal-body")
            div(class="divDialogElements")
              label(for="name-input", style="display:table-cell; width:100%") Enter your name:
              input(class="xlarge", id="name-input", name="xlInput", type="text", style="display:table-cell; width:100%")            				
          div(class="modal-footer")
            button(type="button", class="btn btn-ok") OK
  10. Перенесите изменения, дождитесь автоматического развертывания, затем сыграйте в игру, чтобы убедиться, что оценка сохраняется в базе данных и отображается в таблице на главной странице. Финальная игра
    Финальная игра

Заключение

В ходе этой статьи вы создали игру GuesstheWord с помощью IBM DevOps Services и развернули приложение в Bluemix. Теперь вы знаете, как разработать код в веб-редакторе или в Eclipse (или в другой IDE) и перенести его в Git-репозиторий IBM DevOps Services. Вы научились настраивать процесс автоматического развертывания, чтобы приложение заново развертывалось при поступлении новых изменений. Вы также научились отлаживать приложение локально и решать проблемы, которые могут возникнуть при запуске приложения в Bluemix. Теперь вы готовы к разработке собственных Bluemix-приложений.


Ресурсы для скачивания

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Облачные вычисления
ArticleID=996947
ArticleTitle=Создание простого приложения для игры в слова с помощью Cloudant в облачной среде Bluemix
publish-date=09032014