JSSDK 
НАЧАЛО
Велосипед или продукт?
Основные проблемы 
- Устаревание 
- Зависимость от специфичной технологии 
- Узкая специализация 
- Отсутствие возможности расширения 
- Поддержка
http://mailru.github.io/FileAPI/
Спустя время 
- Большая и мобильная почта 
- Одноклассники 
- Облако, Ответы, Документы 
- и многие другие (~2К stars, ~260 forks)
«Тач» почта
Выбор решения 
- Использовать наработки большой почты 
- Взять популярный фреймворк 
- Написать самим
«Тач» почта 
- Grunt — сборка проекта 
- RequireJS — организация модулей 
- Backbone — работа с данными 
- fest — шаблонизация
Проблемы 
vs
Новое серверное API 
Проверка токена 
GET /folders/all/ 
RPC.call("folders/all") 
.then(doneFn, failFn) 
запрашиваем, 
токен POST /token/ 
проверка 
авторизации 
токен получен, 
идет за папками GET /folders/all/ 
Проверка токена, 
список папок 
resolve(body);
Базовый набор инструментов 
- Emitter — излучатель событий 
- Promise — обещания 
- request — отправка HTTP-запросов к 
серверу
Базовый набор инструментов 
- RPC — отвечает за логику работы с 
серверным API 
- Model — класс модели 
- Model.List — класс для работы со списком 
моделей (коллекция)
Что делать дальше?
Поиск готовых решений 
1. Составьте список требований 
2. Добавьте к нему пункт «расширяемость» 
3. Всё.
Готовые решения 
1. Составление списка готовых решений 
2. Изучение списка 
3. Если решение не подходит, пробуем 
изменить задачу 
4. Если ничего не подошло, то готовы ли вы…
Модели
Требования к моделям 
- Dot notation — доступ к свойствам модели 
- Getters — доступ к свойствам без `get` 
- Caching — возможность получения данных 
из localStorage или IndexedDB 
- Persist model — целостность модели
Сравнение 
Backbone Большая почта 
Dependencies jQuery, undescore jQuery 
Dot notation - - 
Getters - + 
Caching - - 
Persist model - +
Dot notation 
- Backbone.Nested 
- Backbone.DeepModel 
Getters 
- Backbone.Mutators 
- писать самим
Целостность 
что это?
Пример получения модели 
// Поиск модели 
function findOne(id) { 
var dfd = $.Deferred(), 
model = new Backbone.Model({ id: id }); 
model.fetch({ 
success: dfd.resolve, 
error: dfd.error 
}); 
return dfd.promise(); 
}
Пример получения модели 
// Где-то в коде #1 
findOne(123).then(function (model) { 
model.on("change:flag", function () { // Слушаем 
console.log(model.get("flag")); 
}); 
}); 
// Где-то #2 
findOne(123).then(function (model) { 
model.set("flag", true); // и ничего не происходит 
});
Добавляем целостность 
var _promises = {}; // список обещаний 
// Поиск модели 
function findOne(id) { 
if (_promises[id] === undefined) { 
var dfd = $.Deferred(); 
var model = new Backbone.Model({ id: id }); 
model.fetch({ success: dfd.resolve, 
error: dfd.reject }); 
_promises[id] = dfd.promise(); 
} 
return _promises[id]; 
}
А коллекции?
Коллекции 
// Отфильтруем и получим все id 
var ids = collection 
.where({ flag: true }) 
.pluck("id"); 
// TypeError: undefined is not a function
Итого 
- Dot notation —Nested / DeepModel 
- Getters — писать самим 
- Сaching — ничего вразумительного не 
нашел, т.е. писать самим 
- Persist model — писать самим
Если вам так ничего и 
не подошло, то готовы ли вы…
Готовы ли вы... 
- Писать общее решение, а не решать узкую 
задачу? 
- Писать тесты и документацию 
- Поддерживать 24/7? 
- Делать всё это бесплатно?
Я готов, 
с чего начать?
Главное не кодить
Инфраструктура
Инфраструктура 
- Сборка проекта 
- Тесты, контроль покрытия и code style 
- Поддержка браузерами 
- Автоматизация контроля изменений 
- Документирование кода и документация 
- Способ распространения
Инфраструктура 
- GruntJS для сборки проекта 
- JSHint, QUnit + Istanbul 
- grunt-autopolyfiller 
- git hooks + Travis CI 
- JSDoc3 для документирования кода 
- Private GitHub и подключение через subtree
С чего начинает 
разработчик?
Модуль 
> grunt module:create:MyModule 
- MyModule.js — код модуля 
- MyModule.tests.js — тесты 
- MyModule.banch.js — тесты 
производительности (если они нужны) 
- README.md — документация (по JSDoc3)
список изменённых файлов 
pre-commit 
grunt JSHint 
pre-push 
grunt QUnit
Веб-интерфейс
Веб-интерфейс
Веб-интерфейс
var CloudEntry = Backbone.Model.extend({ 
// ... 
}); 
var CloudEntries = Backbone.Collection.extend({ 
model: CloudEntry 
}); 
var entries = new CloudEntries({ id: "/" }); 
entries.fetch({ 
success: function () { 
entries.each(function (entry) { 
if (entry.isFile()) { 
} 
}); 
} 
}); 
Было
app.loadFolder = function (id) { 
return api.folder(id).then(function (entries) { 
_normalize(entries); 
return entries; 
}); 
}; 
app.loadFolder(123).then(function (/**Object[]*/entries) { 
entries.forEach(function (entry) { 
if (entry.is_file) { 
} 
}); 
}); 
Было
var CloudEntry = RPCModel.extend({ 
url: "...", 
defaults: { ... }, 
isFile: function () { 
// ... 
} 
}); 
CloudEntry.find("/").then(function (entries) { 
entries.each(function (entry) { 
if (entry.isFile()) { 
} 
}); 
}); 
Стало
CloudEntry.find("/").then(function (entries) { 
entries.each(function (entry) { 
if (entry.is_file) { 
} 
}); 
}); 
Но в Облаке должно быть...
var CloudEntry = RPCModel.extend({ 
url: "...", 
defaults: { ... }, 
getters: { 
is_file: "isFile" 
}, 
isFile: function () { 
// ... 
} 
}); 
Стало: Почта + Облако
x16 
x4 
x5 
x4 
x4 
x4
x4.6 
x4.6 
x4.4 
x4.2 
x4.2 
x1.9 
x1.9
Продолжение следует... 
https://github.com/mailru/ 
https://github.com/RubaXa/ 
@ibnRubaXa

JSSDK: Начало

  • 1.
  • 2.
  • 3.
    Основные проблемы -Устаревание - Зависимость от специфичной технологии - Узкая специализация - Отсутствие возможности расширения - Поддержка
  • 4.
  • 5.
    Спустя время -Большая и мобильная почта - Одноклассники - Облако, Ответы, Документы - и многие другие (~2К stars, ~260 forks)
  • 6.
  • 7.
    Выбор решения -Использовать наработки большой почты - Взять популярный фреймворк - Написать самим
  • 8.
    «Тач» почта -Grunt — сборка проекта - RequireJS — организация модулей - Backbone — работа с данными - fest — шаблонизация
  • 9.
  • 10.
    Новое серверное API Проверка токена GET /folders/all/ RPC.call("folders/all") .then(doneFn, failFn) запрашиваем, токен POST /token/ проверка авторизации токен получен, идет за папками GET /folders/all/ Проверка токена, список папок resolve(body);
  • 11.
    Базовый набор инструментов - Emitter — излучатель событий - Promise — обещания - request — отправка HTTP-запросов к серверу
  • 12.
    Базовый набор инструментов - RPC — отвечает за логику работы с серверным API - Model — класс модели - Model.List — класс для работы со списком моделей (коллекция)
  • 13.
  • 14.
    Поиск готовых решений 1. Составьте список требований 2. Добавьте к нему пункт «расширяемость» 3. Всё.
  • 15.
    Готовые решения 1.Составление списка готовых решений 2. Изучение списка 3. Если решение не подходит, пробуем изменить задачу 4. Если ничего не подошло, то готовы ли вы…
  • 16.
  • 17.
    Требования к моделям - Dot notation — доступ к свойствам модели - Getters — доступ к свойствам без `get` - Caching — возможность получения данных из localStorage или IndexedDB - Persist model — целостность модели
  • 20.
    Сравнение Backbone Большаяпочта Dependencies jQuery, undescore jQuery Dot notation - - Getters - + Caching - - Persist model - +
  • 21.
    Dot notation -Backbone.Nested - Backbone.DeepModel Getters - Backbone.Mutators - писать самим
  • 22.
  • 23.
    Пример получения модели // Поиск модели function findOne(id) { var dfd = $.Deferred(), model = new Backbone.Model({ id: id }); model.fetch({ success: dfd.resolve, error: dfd.error }); return dfd.promise(); }
  • 24.
    Пример получения модели // Где-то в коде #1 findOne(123).then(function (model) { model.on("change:flag", function () { // Слушаем console.log(model.get("flag")); }); }); // Где-то #2 findOne(123).then(function (model) { model.set("flag", true); // и ничего не происходит });
  • 25.
    Добавляем целостность var_promises = {}; // список обещаний // Поиск модели function findOne(id) { if (_promises[id] === undefined) { var dfd = $.Deferred(); var model = new Backbone.Model({ id: id }); model.fetch({ success: dfd.resolve, error: dfd.reject }); _promises[id] = dfd.promise(); } return _promises[id]; }
  • 26.
  • 27.
    Коллекции // Отфильтруеми получим все id var ids = collection .where({ flag: true }) .pluck("id"); // TypeError: undefined is not a function
  • 28.
    Итого - Dotnotation —Nested / DeepModel - Getters — писать самим - Сaching — ничего вразумительного не нашел, т.е. писать самим - Persist model — писать самим
  • 29.
    Если вам такничего и не подошло, то готовы ли вы…
  • 30.
    Готовы ли вы... - Писать общее решение, а не решать узкую задачу? - Писать тесты и документацию - Поддерживать 24/7? - Делать всё это бесплатно?
  • 31.
    Я готов, счего начать?
  • 32.
  • 33.
  • 34.
    Инфраструктура - Сборкапроекта - Тесты, контроль покрытия и code style - Поддержка браузерами - Автоматизация контроля изменений - Документирование кода и документация - Способ распространения
  • 35.
    Инфраструктура - GruntJSдля сборки проекта - JSHint, QUnit + Istanbul - grunt-autopolyfiller - git hooks + Travis CI - JSDoc3 для документирования кода - Private GitHub и подключение через subtree
  • 36.
    С чего начинает разработчик?
  • 38.
    Модуль > gruntmodule:create:MyModule - MyModule.js — код модуля - MyModule.tests.js — тесты - MyModule.banch.js — тесты производительности (если они нужны) - README.md — документация (по JSDoc3)
  • 39.
    список изменённых файлов pre-commit grunt JSHint pre-push grunt QUnit
  • 43.
  • 44.
  • 45.
  • 46.
    var CloudEntry =Backbone.Model.extend({ // ... }); var CloudEntries = Backbone.Collection.extend({ model: CloudEntry }); var entries = new CloudEntries({ id: "/" }); entries.fetch({ success: function () { entries.each(function (entry) { if (entry.isFile()) { } }); } }); Было
  • 47.
    app.loadFolder = function(id) { return api.folder(id).then(function (entries) { _normalize(entries); return entries; }); }; app.loadFolder(123).then(function (/**Object[]*/entries) { entries.forEach(function (entry) { if (entry.is_file) { } }); }); Было
  • 48.
    var CloudEntry =RPCModel.extend({ url: "...", defaults: { ... }, isFile: function () { // ... } }); CloudEntry.find("/").then(function (entries) { entries.each(function (entry) { if (entry.isFile()) { } }); }); Стало
  • 49.
    CloudEntry.find("/").then(function (entries) { entries.each(function (entry) { if (entry.is_file) { } }); }); Но в Облаке должно быть...
  • 50.
    var CloudEntry =RPCModel.extend({ url: "...", defaults: { ... }, getters: { is_file: "isFile" }, isFile: function () { // ... } }); Стало: Почта + Облако
  • 51.
    x16 x4 x5 x4 x4 x4
  • 52.
    x4.6 x4.6 x4.4 x4.2 x4.2 x1.9 x1.9
  • 53.