├── .gitignore ├── README.md ├── mvc.jpg ├── index.html └── index.mkd /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-tutorial 2 | ============= 3 | -------------------------------------------------------------------------------- /mvc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidklassen/node-tutorial/HEAD/mvc.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | index 7 | 8 | 9 |

Web-разработка на node.js и express

10 |

Изучаем node.js на практике

11 |
12 |

Содержание

13 | 47 |
48 |

Вступление

49 |

Приветствую, перед вами небольшой учебник по практической разработке на node.js, с использованием фреймворка express. Я с большим энтузиазмом отношусь к node и сопутствующим технологиям. Node.js в первую очередь привлекает свежестью в подходах к разработке, смелостью и драйвом.

50 |

О том, что такое node.js вы можете прочитать на http://nodejs.org/, если коротко - то это серверная платформа, для выполнения javascript. Так же мы будем использовать express, web-фреймворк построеный на концепции middleware (о том, что это такое, поговорим поподробнее чуть позже)

51 |

В процессе изучения мы познакомимся с различными аспектами web-разработки, такими как использование системы контроля версий, автоматическое тестирование и так далее. В результате по ходу изучения мы разработаем вполне рабочее web-приложение (простенький аналог твиттера)

52 |

Хочется отметить, что очень большое влияние на меня оказал railstutorial, это лучшее пособие по web-разработке, которое я встречал, и мне очень хотелось бы создать нечто подобное для node.js.

53 |
54 |

Глава 1. Старт

55 |

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

56 |

1.1 Рабочее окружение

57 |

Так как я в основном использую в работе linux, а если точнее - Ubuntu 12.04, основная часть инструкций по установке тех или иных инструментов будет ориентирована на ползователей линукс, но я буду стараться по возможности давать ссылки на инструкции и для других ОС.

58 |

Для того чтобы точно следовать инструкциям в учебнике, вам нужно будет поставить систему контроля версий git, дело в том, что мы не только будем размещать код своих проектов в git, но и устанавливать многие инструменты из репозиториев на гитхабе.

59 |

Так что, первое что мы сделаем - это...

60 |

1.1.1 Установка git

61 |

Пользователи apt-based дистрибутивов могут выполнить в терминале:

62 |
$ sudo apt-get install git-core
 63 | 
64 |

Остальные отправляются читать инструкции по адресу http://git-scm.com/book/ch1-4.html

65 |

1.1.2 Установка node.js и npm

66 |

Теперь пришло время поставить последнюю стабильню версию node.js (на текущий момент это 0.8.1) и npm (установщик пакетов для node).

67 |

Инструкции по установке разных ОС можно найти здесь

68 |

Для начала устанавливаем все что необходимо для компиляции пакета из исходников:

69 |
$ sudo apt-get install python g++ curl libssl-dev apache2-utils
 70 | 
71 |

Теперь скачиваем исходный код:

72 |
$ cd /tmp
 73 | $ git clone https://github.com/joyent/node.git
 74 | $ cd node
 75 | $ git checkout v0.8.1
 76 | 
77 |

Посделний этап - компиляция и установка:

78 |
$ ./configure
 79 | $ make
 80 | $ sudo make install
 81 | 
82 |

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

83 |
$ node -v
 84 | v0.8.1
 85 | $ npm -v
 86 | 1.1.33
 87 | 
88 |

Если есть желание - можно запустить консоль node и поиграться с интерпретатором javascript.

89 |

1.1.3 Среда разработки

90 |

Тут каждый волен выбирать по своему вкусу, лично меня вполне устраивает gedit с установленным набором плагинов gmate. Вполне подходят Netbeans или Webstorm.

91 |

1.1.4 Express и первое приложение

92 |

Теперь пришло время познакомиться с фреймворком express. Фреймворк очень простой, и вполне приемлемо документированный. Единственный неудобный момент - это то, что мы будем использовать бета версию 3.0.x а документация на официальном сайте написана для 2.x, так что советую иметь под рукой migration guide

93 |

Устанавливаем express глобально:

94 |
$ sudo npm install -g express@3.0
 95 | 
96 |

Создаем директорию для наших учебных проектов:

97 |
$ mkdir -p ~/projects/node-tutorial
 98 | $ cd ~/projects/node-tutorial
 99 | 
100 |

Создаем проект и устанавливаем зависимости:

101 |
$ express node-demo-app
102 | $ cd node-demo-app && npm install
103 | 
104 |

Теперь можно заглянуть в то что нам сгенерировал генератор приложений, вот внутренности app.js:

105 |
/**
106 |  * Module dependencies.
107 |  */
108 | 
109 | var express = require('express')
110 |   , routes = require('./routes')
111 |   , http = require('http');
112 | 
113 | var app = express();
114 | 
115 | app.configure(function(){
116 |   app.set('port', process.env.PORT || 3000);
117 |   app.set('views', __dirname + '/views');
118 |   app.set('view engine', 'jade');
119 |   app.use(express.favicon());
120 |   app.use(express.logger('dev'));
121 |   app.use(express.bodyParser());
122 |   app.use(express.methodOverride());
123 |   app.use(app.router);
124 |   app.use(express.static(__dirname + '/public'));
125 | });
126 | 
127 | app.configure('development', function(){
128 |   app.use(express.errorHandler());
129 | });
130 | 
131 | app.get('/', routes.index);
132 | 
133 | http.createServer(app).listen(app.get('port'), function(){
134 |   console.log("Express server listening on port " + app.get('port'));
135 | });
136 | 
137 |

Думаю, что люди знакомые с javascript могут предположить что тут происходит. Подключаются необходимые модули, конфигурируется приложение и запускается на 3000-м порту.

138 |

Теперь приложение можно запустить:

139 |
$ node app
140 | 
141 |

И увидеть результат работы http://localhost:3000/

142 |

1.2 Система контроля версий

143 |

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

144 |

1.2.1 Настройка git

145 |

Для более комфортной работы с git стоит сначала указать свои личные данные:

146 |
$ git config --global user.name "Your Name"
147 | $ git config --global user.email your.email@example.com
148 | 
149 |

И настроить алиасы для наиболее часто используемых комманд:

150 |
$ git config --global alias.co checkout
151 | $ git config --global alias.ci commit
152 | 
153 |

1.2.2 Работа с git

154 |

Git настроен и можно размещать наше приложение в репозитории, инициализируем новый репозиторий:

155 |
$ git init
156 | 
157 |

Добавляем директорию с зависимостями приложения в gitignore:

158 |
$ echo '/node_modules' > .gitignore
159 | 
160 |

Помещаем все файлы в индекс и создаем первый коммит:

161 |
$ git add .
162 | $ git ci -m "Initial commit"
163 | 
164 |

1.2.3 github

165 |

После размещения кода проекта в репозитории пришло время выложить проект на GitHub. GitHub - это социальная сеть и хостинг для проектов. Огромное число opensource проектов хостится на гитхабе, так что если вы там еще не зарегистрированы - самое время сделать это (возможно вам придется сначала разобраться с ssh ключами)

166 |

Создаем новый репозиторий node-demo-app.

167 |

Выкладываем код на гитхаб:

168 |
$ git remote add origin git@github.com:ваш_ник_на_гитхабе/node-demo-app.git
169 | $ git push -u origin master
170 | 
171 |

1.3 Разворачиваем приложение

172 |

Теперь наступает самый волнующий этап, мы будем разворачивать приложение на хостинге. 173 | Для этого воспользуемся услугами облачной системы деплоя Heroku.

174 |

1.3.1 Настройка Heroku

175 |

Для начала нам надо зарегистрироваться и установить необходимый инструментарий.

176 |

Пользователи ubuntu выполняют:

177 |
$ wget -qO- https://toolbelt.heroku.com/install.sh | sh
178 | 
179 |

Когда установка завершится, нужно будет залогиниться из коммандной строки:

180 |
$ heroku login
181 | 
182 |

1.3.2 Размещаем приложение на heroku

183 |

Теперь наше окружение полностью готов к выкладке на хостинг. Размещение node.js проекта на Heroku требует еще нескольких действий, вы можете почитать об этом в документации или просто выполнить инструкции.

184 |

В файле package.json нашего проекта, нужно указать версии ноды и npm, package.json должен выглядеть так:

185 |
{
186 |   "name": "application-name",
187 |   "version": "0.0.1",
188 |   "private": true,
189 |   "scripts": {
190 |     "start": "node app"
191 |   },
192 |   "dependencies": {
193 |     "express": "3.0.0beta4",
194 |     "jade": "*"
195 |   },
196 |   "engines": {
197 |     "node": "0.8.x",
198 |     "npm": "1.1.x"
199 |   }
200 | }
201 | 
202 |

Теперь в корне проекта создаем файл Procfile:

203 |
$ echo 'web: node app.js'  > Procfile
204 | 
205 |

Проверяем что все запускается с помощью менеджера процессов:

206 |
$ foreman start
207 | 
208 |

Приложение должно быть доступно на http://localhost:5000/

209 |

Добавляем файлы в репозиторий:

210 |
$ git add .
211 | $ git ci -m "Added Procfile and engines"
212 | $ git push
213 | 
214 |

Создаем приложение на heroku:

215 |
$ heroku create
216 | $ git push heroku master
217 | 
218 |

Набираем:

219 |
$ heroku open
220 | 
221 |

и любуемся задеплоеным приложением.

222 |
223 |

Глава 2. Статические страницы

224 |

Suspendisse hendrerit quam mollis magna pharetra ac convallis justo laoreet. Morbi sit amet malesuada arcu. Sed adipiscing tempus rutrum. Aenean lacinia metus et augue aliquam pulvinar. Praesent nulla ante, ullamcorper vitae varius quis, ullamcorper sit amet risus. Nulla facilisi. Ut risus arcu, convallis a ornare eu, tempor sed elit. Mauris auctor, tellus cursus congue convallis, lorem neque hendrerit turpis, at viverra erat ipsum ut nunc. Fusce non lectus massa, vitae imperdiet lorem. Curabitur dapibus ullamcorper est, ut vestibulum 225 | diam sollicitudin sit amet.

226 |
227 |

Глава 3. Модель пользователя

228 |

Suspendisse hendrerit quam mollis magna pharetra ac convallis justo laoreet. Morbi sit amet malesuada arcu. Sed adipiscing tempus rutrum. Aenean lacinia metus et augue aliquam pulvinar. Praesent nulla ante, ullamcorper vitae varius quis, ullamcorper sit amet risus. Nulla facilisi. Ut risus arcu, convallis a ornare eu, tempor sed elit. Mauris auctor, tellus cursus congue convallis, lorem neque hendrerit turpis, at viverra erat ipsum ut nunc. Fusce non lectus massa, vitae imperdiet lorem. Curabitur dapibus ullamcorper est, ut vestibulum 229 | diam sollicitudin sit amet.

230 |
231 |

Copyright David Klassen, 2012.

232 | 233 | 234 | -------------------------------------------------------------------------------- /index.mkd: -------------------------------------------------------------------------------- 1 | #Web-разработка на node.js и express 2 | ###Изучаем node.js на практике 3 | --- 4 | 5 | ##Содержание 6 | 7 | * ####[Вступление](#introduction) 8 | 9 | * ####[Глава 1. Старт](#start) 10 | * [1.1 Рабочее окружение](#environment) 11 | * [1.1.1 Установка git](#git-install) 12 | * [1.1.2 Установка node.js и npm](#node-install) 13 | * [1.1.3 Среда разработки](#ide) 14 | * [1.1.4 Express и первое приложение](#hello-world) 15 | * [1.2 Система контроля версий](#version-control) 16 | * [1.2.1 Настройка git](#git-setup) 17 | * [1.2.2 Работа с git](#git-workflow) 18 | * [1.2.3 github](#github) 19 | * [1.3 Разворачиваем приложение](#deploying) 20 | * [1.3.1 Настройка Heroku](#heroku-setup) 21 | * [1.3.2 Разворачиваем приложение](#deploy-hello-world) 22 | 23 | * ####[Глава 2. Статические страницы](#static-pages) 24 | * [2.1 Model-View-Controller (MVC)](#mvc) 25 | * [2.2 Демонстрационное приложение](#sample-app) 26 | * [2.2.1 package.json](#package.json) 27 | * [2.2.2 Hello, World!](#hello-world) 28 | * [2.2.3 Структура приложения](#app-structure) 29 | * [2.3 Тестирование приложения](#tests) 30 | * [2.4 Статические страницы](#static-pages-sub) 31 | * [2.4.1 Контроллер статических страниц](#pages-controller) 32 | * [2.4.2 Шаблонизация и Views](#templates-and-views) 33 | * [2.4.3 Layout и Twitter Bootstrap](#layout-and-twitter-bootstrap) 34 | * [2.4.4 Деплой на Heroku](#templates-and-views) 35 | 36 | * ####[Глава 3. Модель пользователя](#user-model) 37 | 38 | --- 39 | 40 | ##Вступление 41 | 42 | Приветствую, перед вами небольшой учебник по практической разработке на node.js, с использованием фреймворка express. Я с большим энтузиазмом отношусь к node и сопутствующим технологиям. Node.js в первую очередь привлекает свежестью в подходах к разработке, смелостью и драйвом. 43 | 44 | О том, что такое node.js вы можете прочитать на [http://nodejs.org/](http://nodejs.org/), если коротко - то это серверная платформа, для выполнения javascript. Так же мы будем использовать express, web-фреймворк построеный на концепции [middleware](http://stephensugden.com/middleware_guide/) (о том, что это такое, поговорим поподробнее чуть позже) 45 | 46 | В процессе изучения мы познакомимся с различными аспектами web-разработки, такими как использование системы контроля версий, автоматическое тестирование и так далее. В результате по ходу изучения мы разработаем вполне рабочее web-приложение (простенький аналог твиттера) 47 | 48 | Хочется отметить, что очень большое влияние на меня оказал [railstutorial](http://ruby.railstutorial.org/ruby-on-rails-tutorial-book), это лучшее пособие по web-разработке, которое я встречал, и мне очень хотелось бы создать нечто подобное для node.js. 49 | 50 | --- 51 | 52 | ##Глава 1. Старт 53 | 54 | В этой главе мы развернем рабочее окружение с нуля, установим все необходимые инструменты, создадим простенькое приложение и даже запустим его на облачном хостинге. 55 | 56 | ###1.1 Рабочее окружение 57 | 58 | Так как я в основном использую в работе linux, а если точнее - Ubuntu 12.04, основная часть инструкций по установке тех или иных инструментов будет ориентирована на ползователей линукс, но я буду стараться по возможности давать ссылки на инструкции и для других ОС. 59 | 60 | Для того чтобы точно следовать инструкциям в учебнике, вам нужно будет поставить систему контроля версий [git](http://ru.wikipedia.org/wiki/Git), дело в том, что мы не только будем размещать код своих проектов в git, но и устанавливать многие инструменты из репозиториев на [гитхабе](https://github.com/). 61 | 62 | Так что, первое что мы сделаем - это... 63 | 64 | ####1.1.1 Установка git 65 | 66 | Пользователи apt-based дистрибутивов могут выполнить в терминале: 67 | 68 | $ sudo apt-get install git-core 69 | 70 | Остальные отправляются читать инструкции по адресу [http://git-scm.com/book/ch1-4.html](http://git-scm.com/book/ch1-4.html) 71 | 72 | ####1.1.2 Установка node.js и npm 73 | 74 | Теперь пришло время поставить последнюю стабильню версию node.js (на текущий момент это 0.8.1) и npm (установщик пакетов для node). 75 | 76 | Инструкции по установке разных ОС можно найти [здесь](https://github.com/joyent/node/wiki/Installation) 77 | 78 | Для установки на ubuntu выполняем: 79 | 80 | $ sudo apt-get install python-software-properties 81 | $ sudo add-apt-repository ppa:chris-lea/node.js 82 | $ sudo apt-get update 83 | $ sudo apt-get install nodejs npm 84 | 85 | Если есть желание - можно запустить консоль node и поиграться с интерпретатором javascript. 86 | 87 | ####1.1.3 Среда разработки 88 | 89 | Тут каждый волен выбирать по своему вкусу, лично меня вполне устраивает gedit с установленным набором плагинов [gmate](https://github.com/gmate/gmate/). Вполне подходят Netbeans или Webstorm. 90 | 91 | ####1.1.4 Express и первое приложение 92 | 93 | Теперь пришло время познакомиться с фреймворком [express](http://expressjs.com/). Фреймворк очень простой, и вполне приемлемо документированный. Единственный неудобный момент - это то, что мы будем использовать бета версию 3.0.x а документация на официальном сайте написана для 2.x, так что советую иметь под рукой [migration guide](https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x) 94 | 95 | Устанавливаем express глобально: 96 | 97 | $ sudo npm install -g express 98 | 99 | Создаем директорию для наших учебных проектов: 100 | 101 | $ mkdir -p ~/projects/node-tutorial 102 | $ cd ~/projects/node-tutorial 103 | 104 | Создаем проект и устанавливаем зависимости: 105 | 106 | $ express first-app 107 | $ cd first-app && npm install 108 | 109 | Желающие могут покопаться в том что нам сгенерировал генератор приложений, думаю, что люди знакомые с javascript могут предположить что там происходит. 110 | 111 | Теперь приложение можно запустить: 112 | 113 | $ node app 114 | 115 | И увидеть результат работы [http://localhost:3000/](http://localhost:3000/) 116 | 117 | ###1.2 Система контроля версий 118 | 119 | Теперь, когда у нас уже есть рабочее приложение, более подробно коснемся работы с сисемой контроля версий. Для того чтобы лучше познакомиться с работой git, стоит почитать книжку [Pro Git](http://git-scm.com/book), но можно и обойтись инструкциями в данном учебнике. 120 | 121 | ####1.2.1 Настройка git 122 | 123 | Для более комфортной работы с git стоит сначала указать свои личные данные: 124 | 125 | $ git config --global user.name "Your Name" 126 | $ git config --global user.email your.email@example.com 127 | 128 | И настроить алиасы для наиболее часто используемых комманд: 129 | 130 | $ git config --global alias.co checkout 131 | $ git config --global alias.ci commit 132 | 133 | ####1.2.2 Работа с git 134 | 135 | Git настроен и можно размещать наше приложение в репозитории, инициализируем новый репозиторий: 136 | 137 | $ git init 138 | 139 | Добавляем директорию с зависимостями приложения в gitignore: 140 | 141 | $ echo 'node_modules' > .gitignore 142 | 143 | Помещаем все файлы в индекс и создаем первый коммит: 144 | 145 | $ git add . 146 | $ git ci -m "Initial commit" 147 | 148 | ####1.2.3 github 149 | 150 | После размещения кода проекта в репозитории пришло время выложить проект на [GitHub](http://github.com/). GitHub - это социальная сеть и хостинг для проектов. Огромное число opensource проектов хостится на гитхабе, так что если вы там еще не зарегистрированы - самое время [сделать это](http://github.com/signup/free). 151 | 152 | Перед тем как работать с GitHub нужно будет создать RSA ключи для доступа по ssh. Процедура описана [тут](https://help.github.com/articles/generating-ssh-keys). Для пользователей linux привожу инструкцию по созданию ключей если их у вас еще нет. 153 | 154 | $ ssh-keygen -t rsa -C "your_email@youremail.com" 155 | 156 | Отвечаем на вопросы генератора, после чего копируем содержимое файла `~/.ssh/id_rsa.pub`: 157 | 158 | $ sudo apt-get install xclip 159 | $ xclip -sel clip < ~/.ssh/id_rsa.pub 160 | 161 | После этого нужно пройти по ссылке [Account Settings](https://github.com/settings/profile), зайти в раздел SSH Keys и нажать кнопку Add SSH Key и вставить ключ из буфера обмена в поле Key. Затем сохранить. 162 | 163 | Проверить что ключ работает можно так: 164 | 165 | $ ssh -T git@github.com 166 | 167 | Возможно вы увидете предупреждение: 168 | 169 | The authenticity of host 'github.com (207.97.227.239)' can't be established. 170 | # RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48. 171 | # Are you sure you want to continue connecting (yes/no)? 172 | 173 | Нужно просто ответить 'yes' и тогда, если ключ успешно добавился, вы увидите ответ сервера: 174 | 175 | Hi username! You've successfully authenticated, but GitHub does not 176 | # provide shell access. 177 | 178 | Когда ключи настроены [создаем](https://github.com/new) новый репозиторий с названием first-app и дефолтными настройками, после чего выкладываем код на гитхаб: 179 | 180 | $ git remote add origin git@github.com:ваш_ник_на_гитхабе/first-app.git 181 | $ git push -u origin master 182 | 183 | ###1.3 Разворачиваем приложение 184 | 185 | Теперь наступает самый волнующий этап, мы будем разворачивать приложение на хостинге. 186 | Для этого воспользуемся услугами облачной системы деплоя [Heroku](http://www.heroku.com/). Если вам интересно как работает хостинг Heroku, советую поизучать их раздел [How it Works](http://www.heroku.com/how) 187 | 188 | ####1.3.1 Настройка Heroku 189 | 190 | Для начала нам надо [зарегистрироваться](https://api.heroku.com/signup) и установить необходимый [инструментарий](https://toolbelt.heroku.com/). 191 | 192 | Пользователи ubuntu выполняют: 193 | 194 | $ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh 195 | 196 | Когда установка завершится, нужно будет залогиниться из коммандной строки: 197 | 198 | $ heroku login 199 | 200 | ####1.3.2 Размещаем приложение на heroku 201 | 202 | Теперь наше окружение полностью готов к выкладке на хостинг. Размещение node.js проекта на Heroku требует еще нескольких действий, вы можете почитать об этом в [документации](https://devcenter.heroku.com/articles/nodejs) или просто выполнить инструкции. 203 | 204 | В файле package.json нашего проекта, нужно указать версии ноды и npm, package.json должен выглядеть так: 205 | 206 | { 207 | "name": "application-name", 208 | "version": "0.0.1", 209 | "private": true, 210 | "scripts": { 211 | "start": "node app" 212 | }, 213 | "dependencies": { 214 | "express": "3.0.0beta4", 215 | "jade": "*" 216 | }, 217 | "engines": { 218 | "node": "0.8.x", 219 | "npm": "1.1.x" 220 | } 221 | } 222 | 223 | Теперь в корне проекта создаем файл Procfile: 224 | 225 | $ echo 'web: node app.js' > Procfile 226 | 227 | Проверяем что все запускается с помощью менеджера процессов: 228 | 229 | $ foreman start 230 | 231 | Приложение должно быть доступно на [http://localhost:5000/](http://localhost:5000/) 232 | 233 | Добавляем файлы в репозиторий: 234 | 235 | $ git add . 236 | $ git ci -m "Added Procfile and engines" 237 | $ git push 238 | 239 | Создаем приложение на heroku: 240 | 241 | $ heroku create 242 | $ heroku keys:add ~/.ssh/id_rsa.pub 243 | $ git push heroku master 244 | 245 | Набираем: 246 | 247 | $ heroku open 248 | 249 | и любуемся задеплоеным приложением. 250 | 251 | --- 252 | 253 | ##Глава 2. Статические страницы 254 | 255 | В этой главе мы приступим к разработке нашего приложения, которое мы будем использовать в качестве примера на протяжении всего учебника, и начнем с самого простого, а именно со статических страниц. 256 | 257 | ###2.1 Model-View-Controller (MVC) 258 | 259 | Перед тем как приступать собственно к разработке приложения, полезно поговорить о том, что из себя представляет типичная архитектура web-приложения на наиболее высоком уровне абстракции. Самым популярным архитектурным паттерном на сегодняшний день является [model-view-controller](http://ссылка.на.википедию) (MVC), общий смысл паттерна заключается в том, чтобы разделить бизнес логику приложения (её привязывают к моделям) и представление - view. Кроме того, модели реализуют интерфейс к базе данных. Контроллер играет роль посредника между моделью и представлением. В случае web-приложения - это выглядит так: браузер пользователя отправляет запрос на сервер, контроллер обрабатывает запрос, получает необходимые данные из модели и отправляет их во view. View получает данные из контроллера и превращает их в красивую HTML страничку, которую контроллер в итоге отправит пользователю. 260 | 261 | ![](mvc.jpg) 262 | 263 | ###2.2 Демонстрационное приложение 264 | 265 | Пришло время приступить к разработке нашего демонстрационного приложения. В первой главе мы уже развернули тестовое приложение, но воспользовались при этом генератором express и не написали ни строчки кода. Теперь мы будем писать наше приложение сами и начнем с "Hello, World". 266 | 267 | $ cd ~/projects/node-tutorial 268 | $ mkdir node-demo-app 269 | $ cd node-sample-app 270 | 271 | ####2.2.1 package.json 272 | 273 | Для того чтобы воспользоваться всеми прелестями, которые нам может предоставить npm (зачем, что это такое и ссылка), мы создадим в корневой лиректории нашего проекта файлик package.json: 274 | 275 | { 276 | "name": "node-demo-app" 277 | , "version": "0.0.1" 278 | , "scripts": { 279 | "start": "node server.js" 280 | } 281 | , "dependencies": { 282 | "express": "3.0.1" 283 | } 284 | } 285 | 286 | TODO: Описание полей и ссылка на доки по package.json 287 | 288 | Теперь можно выполнить 289 | 290 | $ npm install 291 | 292 | В результате npm создаст директорию node_modules в которую поместит все модули от которых зависит наш проект. 293 | 294 | ####2.2.2 Hello, World! 295 | 296 | Основной файл назовем server.js: 297 | 298 | var express = require('express') 299 | , app = express() 300 | , port = process.env.PORT || 3000 301 | 302 | app.get('/', function (req, res) { 303 | res.send('Hello, World!') 304 | }) 305 | 306 | app.listen(port, function () { 307 | console.log('Listening on port ', port) 308 | }) 309 | 310 | Для того, чтобы полюбоваться результатом нашего труда, есть два способа: 311 | 312 | $ node server.js 313 | 314 | либо 315 | 316 | $ npm start 317 | 318 | Второй способ доступен потому что мы добавили соответствующую строчку в файл конфигурации package.json. 319 | 320 | Теперь по адресу http://localhost:3000/ можно получить строчку 'Hello, World!'. 321 | 322 | Настало время залить что-нибудь в GitHub. Создаем новый репозиторий и выполняем в директории проекта следующий набор комманд, сперва создадим файл README.md (правило хорошего тона) 323 | 324 | $ echo '# Node.js demo app' > README.md 325 | 326 | Создадим файл .gitignore для того чтобы не коммитить лишние файлы в git, а именно директорию node_modules: 327 | 328 | $ echo 'node_modules' > .gitignore 329 | 330 | Создаем репозиторий, коммитимся и заливаем все на GitHub 331 | 332 | $ git init 333 | $ git add . 334 | $ git commit -m 'Hello, World' 335 | $ git remote add origin git@github.com:/node-demo-app.git 336 | $ git push -u origin master 337 | 338 | ####2.2.3 Структура приложения 339 | 340 | Express пока не диктует строгой структуры для файлов приложения, так что мы придумаем свою. Предлагаю такой вариант: 341 | 342 | /node-demo-app 343 | |- /controllers - директория с контроллерами 344 | |- /models - модели приложения 345 | |- /public - статика - картинки, клиентские скрипты, стили и т.д. 346 | |- /tests - автоматические тесты 347 | |- /views - html темплейты 348 | |- app.js - основной файл приложения 349 | |- config.js - файл с настройками приложения 350 | |- server.js - http сервер 351 | 352 | Думаю, что для начала этого хватит. 353 | 354 | ###2.3 Тестирование приложения 355 | 356 | О том что такое TDD и зачем нужно писать тесты вы наверняка уже слышали, а если нет, то можете прочитать об этом здесь .... В этом учебнике, для тестирования приложения мы воспользуемся подходом который называется BDD (behavior-driven development). В тестах мы будем описывать предполагаемое поведение приложения. Сами тесты разделим на две категории: integration тесты - они будут имитировать поведение пользователя и тестировать систему целиком, и unit тесты - для тестирования отдельных модулей приложения. В качестве фреймворков мы будем использовать библиотеки Mocha (читается как мокка, кофе-мокка :)), should.js, и supertest. Mocha служит для организации описаний тест-кейсов, should.js предоставляет синтаксис для осуществления различных проверок, а supertest - это надстройка над простеньким http-клиентом, которая позволяет проверять результаты http-запросов. Для подключения библиотек сделаем необходимые изменения в package.json 357 | 358 | { 359 | "name": "node-demo-app" 360 | , "version": "0.0.1" 361 | , "scripts": { 362 | "start": "node server.js" 363 | } 364 | , "dependencies": { 365 | "express": "3.0.1" 366 | } 367 | , "devDependencies": { 368 | "mocha": "1.7.0" 369 | , "should": "1.2.1" 370 | , "supertest": "0.4.0" 371 | } 372 | } 373 | 374 | Зависимости мы разместили в разделе "devDependencies", так как нет никакой необходимости тащить эти библиотеки на продакшн сервер. Для установки библиотек выполняем 375 | 376 | $ npm install 377 | 378 | Для того что бы понять как это работает, попробуем создать свой первый тест и прогнать его через наш фреймворк 379 | 380 | $ mkdir tests 381 | $ touch tests/test.js 382 | 383 | В test.js положим такой тест 384 | 385 | describe('Truth', function () { 386 | it('should be true', function () { 387 | true.should.be.true 388 | }) 389 | 390 | it('should not be false', function () { 391 | true.should.not.be.false 392 | }) 393 | }) 394 | 395 | и запустим его 396 | 397 | $ ./node_modules/.bin/mocha --require should --reporter spec tests 398 | 399 | Вполне естественно, что такой тест пройдет, так что заменим его на что-то неработающее 400 | 401 | describe('foo variable', function () { 402 | it('should equal bar', function () { 403 | foo.should.equal('bar') 404 | }) 405 | }) 406 | 407 | запускаем 408 | 409 | $ ./node_modules/.bin/mocha --require should --reporter spec tests 410 | 411 | и видим, что тесты не прошли, придется чинить код, добавляем объявление переменной 412 | 413 | var foo = 'bar' 414 | 415 | describe('foo variable', function () { 416 | it('should equal bar', function () { 417 | foo.should.equal('bar') 418 | }) 419 | }) 420 | 421 | запускаем 422 | 423 | $ ./node_modules/.bin/mocha --require should --reporter spec tests 424 | 425 | и видим что код рабочий. 426 | 427 | Основной принцип TDD состоит в том, чтобы напсать тесты до того как написан код, таким образом мы можем убедиться в том, что тесты действительно что-то тестируют, а не просто запускают код на выполнение и делают проверки в стиле true.should.be.true. То есть процесс разработки выглядит следующим образом: 428 | 429 | 1. Пишем тест 430 | 2. Выполняем тест и убеждаемся в том что он падает 431 | 3. Пишем код 432 | 4. Выполняем тест и убеждаемся в том что он проходит, если нет, возвращаемся в п.3 433 | 434 | И так много раз. 435 | 436 | Чтобы упростить запуск тестов добавим таск прогоняющий тесты в Makefile 437 | 438 | $ touch Makefile 439 | 440 | Содержимое Makefile: 441 | 442 | REPORTER=spec 443 | TESTS=$(shell find ./tests -type f -name "*.js") 444 | 445 | test: 446 | @./node_modules/.bin/mocha \ 447 | --require should \ 448 | --reporter $(REPORTER) \ 449 | $(TESTS) 450 | 451 | .PHONY: test 452 | 453 | Обращаю внимание на то, что отступы после названия таска должны быть сделаны табами, а не пробелами. Теперь test-suite можно запускать коммандой 454 | 455 | $ make test 456 | 457 | Попробуем потестировать http запросы. Для того чтобы сделать тестирование более удобным проведем небольшой рефакторинг кода и вынесем приложение express из файла server.js в отдельный модуль app.js 458 | 459 | $ touch app.js 460 | 461 | app.js: 462 | 463 | var express = require('express') 464 | , app = express() 465 | 466 | // mount routes 467 | app.get('/', function (req, res) { 468 | res.send('Hello, World!') 469 | }) 470 | 471 | module.exports = app 472 | 473 | TODO: Написать про систему модулей в node.js 474 | 475 | server.js заменяем на 476 | 477 | var app = require(__dirname + '/app') 478 | 479 | app.listen(3000, function () { 480 | console.log('Listening on port ', 3000) 481 | }) 482 | 483 | Для того, чтобы проверить корректность http запроса напишем в test.js следующий код 484 | 485 | var request = require('supertest') 486 | , app = require(__dirname + '/../app') 487 | 488 | describe('GET /', function () { 489 | it('should contain text "Hello, Express!"', function (done) { 490 | request(app) 491 | .get('/') 492 | .expect(/Hello, Express!/, done) 493 | }) 494 | }) 495 | 496 | В этом тесте мы проверяем, что сервер отвечает нам строчкой "Hello, Express!". Так как вместо этого сервер отвечает "Hello, World!", тест упадет. Важный момент, на который нужно обратить внимание, запросы к http серверу происходят асинхронно, по-этому нам нужно будет назначить callback на завешение теста. Mocha предоставляет такую возможность с помощью функции done, которую можно опционально передать в функцию с тест-кейсом. Чтобы тест прошел, нужно заменить строчку "Hello, World!" на "Hello, Express!" в файле app.js и выполнить `make test`. 497 | 498 | С тестами мы разобрались, так что удаляем тестовый тест 499 | 500 | $ rm tests/test.js 501 | 502 | И коммитимся 503 | 504 | $ git add . 505 | $ git ci -m "Added testing framework" 506 | $ git push 507 | 508 | ###2.4 Статические страницы 509 | 510 | Конечно статические страницы можно сделать по настоящему статическими, то есть разместить файл к примеру index.html со следующим содержанием: 511 | 512 | 513 | 514 | 515 | 516 | Hello, World! 517 | 518 | 519 |

Hello, World!

520 | 521 | 522 | 523 | в директории public, и научить приложение отдавать его как статику. Делается это с помощью добавления строчки `app.use(express.static(__dirname + '/public'))` в app.js 524 | 525 | var express = require('express') 526 | , app = express() 527 | 528 | app.use(express.static(__dirname + '/public')) 529 | 530 | app.get('/', function (req, res) { 531 | res.send('Hello, World!') 532 | }) 533 | 534 | module.exports = app 535 | 536 | Все файлы в директории /public после этого будут отдаваться как статика (http://localhost:3000/index.html). Но нам это не очень интересно, так что стираем ненужный index.html 537 | 538 | $ rm public/index.html 539 | 540 | и... 541 | 542 | ####2.4.1 Контроллер статических страниц 543 | 544 | Раз уж мы решили придерживаться TDD, то первым делом напишем тест для еще не созданного контроллера pages 545 | 546 | $ mkdir tests/integration 547 | $ touch tests/integration/pages.js 548 | 549 | pages.js: 550 | 551 | var request = require('supertest') 552 | , app = require(__dirname + '/../../app') 553 | 554 | describe('Pages', function () { 555 | describe('GET /', function () { 556 | it('should redirect to "home"', function (done) { 557 | request(app) 558 | .get('/') 559 | .expect('location', '/home') 560 | .expect(302, done) 561 | }) 562 | }) 563 | 564 | describe('GET /home', function () { 565 | it('should return status code 200', function (done) { 566 | request(app) 567 | .get('/home') 568 | .expect(200, done) 569 | }) 570 | 571 | it('should contain text "Home page"', function (done) { 572 | request(app) 573 | .get('/home') 574 | .expect(/Home page/, done) 575 | }) 576 | }) 577 | }) 578 | 579 | Тут мы описали такие сценарии: 580 | 581 | * GET '/' должен редиректить на '/home' 582 | * GET '/home' должен быть успешным 583 | * GET '/home' должен в теле ответа содержать строку "Home page" 584 | 585 | Запускаем тесты 586 | 587 | $ make test 588 | 589 | Убеждаемся в том что они все падают. 590 | 591 | Наша цель в том, чтобы тесты прошли. Создаем контроллер для раздачи статичных страничек: 592 | 593 | $ mkdir controllers 594 | $ touch controllers/pages.js 595 | 596 | В этом контроллере создадим экшн, который будет отдавать нам домашнюю страничку приложения 597 | 598 | exports.home = function (req, res) { 599 | res.send('Home page') 600 | } 601 | 602 | Теперь подключим контроллер страниц, экшн примонтируем к пути '/home', а для пути '/' настроим редирект на '/home' в app.js 603 | 604 | var express = require('express') 605 | , app = express() 606 | , pages = require(__dirname + '/controllers/pages') 607 | 608 | // configuration settings 609 | app.use(express.static(__dirname + '/public')) 610 | 611 | // mount routes 612 | app.get('/', function (req, res) { res.redirect('home') }) 613 | app.get('/home', pages.home) 614 | 615 | module.exports = app 616 | 617 | Запускаем тесты, если мы все сделали правильно, они должны пройти. 618 | 619 | $ make test 620 | 621 | При попытке зайти на http://localhost:3000/ нас теперь перекинет на страничку home. С контроллером разобрались, теперь возьмемся за вьюхи. 622 | 623 | ####2.4.2 Шаблонизация и Views 624 | 625 | Express в качестве движка дял шаблонизации позоляет подключать разные бибилотеки, такие как ..placeholder.. Мы воспользуемся ejs т.к. как ее синтаксис приближен к html и возможно привычен большинству. Для этого в package.json добавим зависимость "ejs": "0.8.3" 626 | 627 | { 628 | "name": "node-demo-app" 629 | , "version": "0.0.1" 630 | , "scripts": { 631 | "start": "node server.js" 632 | } 633 | , "dependencies": { 634 | "express": "3.0.1" 635 | , "ejs": "0.8.3" 636 | } 637 | , "devDependencies": { 638 | "mocha": "1.7.0" 639 | , "should": "1.2.1" 640 | , "supertest": "0.4.0" 641 | } 642 | } 643 | 644 | И выполним 645 | 646 | $ npm install 647 | 648 | EJS нужно подключить к приложению в app.js 649 | 650 | var express = require('express') 651 | , app = express() 652 | , pages = require(__dirname + '/controllers/pages') 653 | 654 | // configuration settings 655 | app.set('views', __dirname + '/views') 656 | app.set('view engine', 'ejs') 657 | app.use(express.static(__dirname + '/public')) 658 | 659 | // mount routes 660 | app.get('/', function (req, res) { res.redirect('home') }) 661 | app.get('/home', pages.home) 662 | 663 | module.exports = app 664 | 665 | Шаблоны мы будем хранить в директории '/views' с поддиректорией для каждого контроллера и начнем с шаблона для страницы home 666 | 667 | $ mkdir -p views/pages 668 | $ touch views/pages/home.ejs 669 | 670 | В этом файле можно разместить шаблон для странички и использовать переменные, которые туда передаст контроллер, например 671 | 672 | 673 | 674 | 675 | 676 | <%= title %> 677 | 678 | 679 |

<%= title %>

680 |

<%= message %>

681 | 682 | 683 | 684 | В данном случае используются переменные `title` и `message`. И поменяем экшн home в контроллере pages 685 | 686 | exports.home = function (req, res) { 687 | res.render('pages/home', { 688 | title: 'Home page' 689 | , message: 'This is the "home" action of "pages" controller' 690 | }) 691 | } 692 | 693 | Наша "статическая" страница стала уже слегка "динамической". Любуемся результатом по адресу http://localhost:3000/home 694 | 695 | В принципе в этот момент было бы неплохо закомититься, предварительно прогнав тесты и убедившись, что наши улучшения ничего не поломали. Вообще это нужно принять за правило, перед коммитом всегда прогоняем test-suite. 696 | 697 | $ make test 698 | $ git add . 699 | $ git ci -m "Added home page" 700 | $ git push 701 | 702 | Предлагаю в качестве упражнения самостоятельно сделать страничку about, добавив необходимый экшн в контроллер pages и создав шаблон для неё. Не забываем примонтировать путь '/about' в app.js. Ну а начать нужно с тестов! 703 | 704 | ####2.4.3 Layout и Twitter Bootstrap 705 | 706 | Если у вас получилось создать страницу "/about" то теперь у вас две страницы, если не получилось, можете выкачать готовый вариант из гитхаба 707 | 708 | $ git clone blabla 709 | $ git checkout blabla 710 | 711 | Как вы могли заметить, в наших вьюшках дублируется код и хотелось бы устранить этот недочет. Для этого создадим layout с базовым каркасом страницы. К сожалению ejs пока что не поддерживает layout-ы, но существует библиотека ejs-locals, которая добавляет этот функционал в шаблонизатор. Подключаем её в проект. 712 | 713 | package.json: 714 | 715 | { 716 | "name": "node-demo-app" 717 | , "version": "0.0.1" 718 | , "scripts": { 719 | "start": "node server.js" 720 | } 721 | , "dependencies": { 722 | "express": "3.0.1" 723 | , "ejs": "0.8.3" 724 | , "ejs-locals": "0.2.5" 725 | } 726 | , "devDependencies": { 727 | "mocha": "1.7.0" 728 | , "should": "1.2.1" 729 | , "supertest": "0.4.0" 730 | } 731 | } 732 | 733 | Инсталируем: 734 | 735 | $ npm install 736 | 737 | Добавляем в приложение app.js: 738 | 739 | var express = require('express') 740 | , ejsLocals = require('ejs-locals') 741 | , app = express() 742 | , pages = require(__dirname + '/controllers/pages') 743 | 744 | // configuration settings 745 | app.engine('ejs', ejsLocals) 746 | app.set('views', __dirname + '/views') 747 | app.set('view engine', 'ejs') 748 | app.use(express.static(__dirname + '/public')) 749 | 750 | // mount routes 751 | app.get('/', function (req, res) { res.redirect('home') }) 752 | app.get('/home', pages.home) 753 | app.get('/about', pages.about) 754 | 755 | module.exports = app 756 | 757 | Создаем layout: 758 | 759 | $ touch views/layout.ejs 760 | 761 | layout.ejs: 762 | 763 | 764 | 765 | 766 | 767 | <%= title %> 768 | 769 | 770 | <%- body -%> 771 | 772 | 773 | 774 | Этот файл будет общим каркасом для страниц, а конкретная вьюха будет рендериться на месте тега `<%- body -%>`. 775 | 776 | Теперь можно убрать лишний код из шаблонов home.ejs и about.ejs. Сейчас они идентичны и мы могли бы воспользоваться одним шаблоном для обоих экшнов, но как правило разные страницы имеют разный код так что представим себе, что он различается. 777 | 778 | home.ejs и about.ejs: 779 | 780 | <% layout('../layout') -%> 781 |

<%= title %>

782 |

<%= message %>

783 | 784 | Внешне ничего не должно поменяться, чтобы убедиться в этом запустим tet-suite, а потом закоммитимся 785 | 786 | $ make test 787 | $ git add . 788 | $ git ci -m "Added layout" 789 | $ git push 790 | 791 | Осталось навести красоту, в этом нам поможет фреймворк для прототипирования под названием Twitter Bootstrap, его нужно скачать и положить в /public 792 | 793 | $ wget http://twitter.github.com/bootstrap/assets/bootstrap.zip 794 | $ unzip bootstrap.zip -d public/ 795 | $ rm bootstrap.zip 796 | 797 | Теперь воспользуемся шаблоном Bootstrap starter template и сделаем layout на его основе: 798 | 799 | layout.js: 800 | 801 | 802 | 803 | 804 | 805 | <%= title %> 806 | 807 | 808 | 813 | 814 | 815 | 816 | 819 | 820 | 821 | 822 | 823 | 836 | 837 |
838 | <%- body -%> 839 |
840 | 841 | 842 | 843 | 844 | 845 | 846 | Чтобы добавить в шаблон переменную `route`, которую мы используем для подсветки ссылки на текущую страницу, добавим немножко кода в app.js. 847 | 848 | var express = require('express') 849 | , ejsLocals = require('ejs-locals') 850 | , app = express() 851 | , pages = require(__dirname + '/controllers/pages') 852 | 853 | // configuration settings 854 | app.engine('ejs', ejsLocals) 855 | app.set('views', __dirname + '/views') 856 | app.set('view engine', 'ejs') 857 | app.use(express.static(__dirname + '/public')) 858 | 859 | // set view locals 860 | app.use(function (req, res, next) { 861 | app.locals.route = req.url 862 | next() 863 | }) 864 | 865 | // mount routes 866 | app.get('/', function (req, res) { res.redirect('home') }) 867 | app.get('/home', pages.home) 868 | app.get('/about', pages.about) 869 | 870 | module.exports = app 871 | 872 | Выполняем стандартную процедуру: 873 | 874 | $ make test 875 | $ git add . 876 | $ git ci -m "Added twitter bootstrap" 877 | $ git push 878 | 879 | Запускаем сервер: 880 | 881 | $ npm start 882 | 883 | Любуемся получившейся красотой на http://localhost:3000/. 884 | 885 | ####2.4.4 Деплой на Heroku 886 | 887 | Мы уже разворачивали приложение в первой главе, так что просто повторим процесс. Добавляем версии node.js и npm в package.json: 888 | 889 | { 890 | "name": "node-demo-app" 891 | , "version": "0.0.1" 892 | , "scripts": { "start": "node server.js" } 893 | , "dependencies": { 894 | "express": "3.0.1" 895 | , "ejs": "0.8.3" 896 | , "ejs-locals": "0.2.5" 897 | } 898 | , "devDependencies": { 899 | "mocha": "1.7.0" 900 | , "should": "1.2.1" 901 | , "supertest": "0.4.0" 902 | } 903 | , "engines": { 904 | "node": "0.8.x" 905 | , "npm": "1.1.x" 906 | } 907 | } 908 | 909 | Создаем Procfile: 910 | 911 | $ echo 'web: node server.js' > Procfile 912 | 913 | Отправляем приложение на heroku: 914 | 915 | $ heroku create 916 | $ git push heroku master 917 | $ heroku open 918 | 919 | --- 920 | 921 | ##Глава 3. Модель пользователя 922 | 923 | Suspendisse hendrerit quam mollis magna pharetra ac convallis justo laoreet. Morbi sit amet malesuada arcu. Sed adipiscing tempus rutrum. Aenean lacinia metus et augue aliquam pulvinar. Praesent nulla ante, ullamcorper vitae varius quis, ullamcorper sit amet risus. Nulla facilisi. Ut risus arcu, convallis a ornare eu, tempor sed elit. Mauris auctor, tellus cursus congue convallis, lorem neque hendrerit turpis, at viverra erat ipsum ut nunc. Fusce non lectus massa, vitae imperdiet lorem. Curabitur dapibus ullamcorper est, ut vestibulum 924 | diam sollicitudin sit amet. 925 | 926 | --- 927 | Copyright David Klassen, 2012. --------------------------------------------------------------------------------