Фреймворк Cappuccino – уникальная технология, позволяющая создавать веб-приложения десктопного качества. Он абстрагирует DOM и вместо него предоставляет Cocoa-подобный API. Вместо того, чтобы возиться с CSS-версткой и кроссбраузерными проблемами, вы используете интерфейсы, специально созданные для разработки приложений, а не статических страниц, интерфейсы, взятые с платформ Mac OS X и iOS.
Я заметил, что на русском языке почти нет обучающих материалов о Cappuccino, и решил восполнить пробел. Этот очерк рассчитан на то, чтобы прочитав его, можно было сразу приступать к разработке своего первого Cappuccino приложения. Я познакомился с фреймворком, когда искал средство для реализации онлайновой среды разработки для своего проекта Akshell. Мне нужно было сделать полнофункциональную IDE, работающую в окне браузера, и Cappuccino отлично справился с поставленной задачей.
В 2008 году Франциско Толмаски, Том Робинсон и Росс Баучер (Франциско и Росс – бывшие сотрудники Apple) создали компанию 280 North и выпустили 280 Slides, веб-приложение для подготовки презентаций. Оно и сейчас впечатляет своим look and feel’ом, а тогда это было просто что-то нереальное. Причем 280 Slides не был самостоятельным продуктом, это была лишь демонстрация возможностей нового веб-фреймворка, Cappuccino.
4-го сентября того же года Cappuccino был опубликован под лицензией LGPL и тут же стал хитом на GitHub. Вокруг фреймворка образовалось сообщество программистов, использующих его в своих продуктах. С тех пор это сообщество только увеличивается и принимает все более активное участие в разработке Cappuccino.
В 2009 году 280 North анонсировала новый продукт, Atlas, и он также оказался прорывной технологией. Atlas – это аналог Interface Builder для веб-приложений, в нем вы можете рисовать интерфейсы мышкой, перетаскивая нужные компоненты из библиотеки. Пара минут работы дает более впечатляющий результат, чем день мучений с CSS. В отличие от Cappuccino, Atlas – закрытая платная программа, для скачивания бета-версии нужно заплатить $20. Этот очерк посвящен Cappuccino, поэтому я не буду описывать Atlas более подробно. Скажу только, что он действительно экономит кучу времени при создании интерфейсов, позволяет сосредоточиться на юзабилити, а не на борьбе с различными версиями браузеров.
Cappuccino и Atlas активно развивались, когда летом 2010 года Motorola купила 280 North со всеми ее активами, потратив на это, по слухам, 20 миллионов долларов. Официальная цель покупки – развитие технологий разработки на платформе Android. Многие злые языки начали поговаривать о смерти Cappuccino, однако с тех пор прошло уже полгода, фреймворк активно разрабатывается, а 23-го февраля состоялся релиз версии 0.9.
Итак, Cappuccino является одной из самых продвинутых технологий создания веб-приложений, его развитие обеспечивает большая корпорация. Изучить или хотя бы ознакомиться с фреймворком будет полезно каждому веб-программисту, к чему и приступим.
Главная особенность Cappuccino – это созданный специально для него язык Objective-J. Он является такой же надстройкой над JavaScript, как Objective-C – над C. Для меня, как и для многих, вначале было совершенно неочевидно, зачем делать объектно-ориентированную надстройку над языком, который сам по себе является объектно-ориентированным. Однако в этом есть большой смысл.
Во-первых, благодаря Objective-J Cappuccino полностью повторяет Cocoa API. Это не фантазия на тему и даже не творческая переработка, это именно повторение, вплоть до сигнатур функций. За счет этого программисты, работавшие на Mac OS X и iOS могут безболезненно перейти на веб-разработку. Удобство всех API проверено долговременными использованием на десктопе, начиная с ОС NeXTSTEP. И наконец, при использовании фреймворка можно успешно пользоваться документацией Cocoa, которая значительно превосходит по качеству и проработанности Doxygen документацию Cappuccino.
Во-вторых, отсутствие гибкости иногда является плюсом. Большинство программистов посчитает, что я сейчас высказал большую ересь, поэтому сразу начну оправдываться. Дизайнеры знают, что введение сеток и других искусственных ограничений может улучшить дизайн. Objective-J интерфейсы Cappuccino на первый взгляд кажутся неповоротливыми и допотопными по сравнению с тем, что можно было бы сделать на JavaScript. Вместо парадигмы Target-Action зачастую можно было бы использовать замыкания, вместо явных геттеров и сеттеров –
Поработав с Cappuccino, я понял достоинства такого подхода, и они являются прямыми следствиями недостатков. Более жесткая объектная модель позволяет четче структурировать код, не потонуть в каше вложенных друг в друга функций. Длинные содержательные имена позволяют справляться с иерархиями классов Cocoa, в который один класс обычно имеет многие десятки методов. Вездесущие геттеры и сеттеры позволяют реализовать Key-Value Coding, Key-Value Observing и Key-Value Binding, техники переворачивающие представление о создании сложных интерфейсов пользователя (к сожалению, в этом очерке не хватит места для их описания).
Я начал пользоваться Cappuccino потому, что меня привлек внешний вид приложений, созданных с его помощью. Теперь я понимаю, что настоящая сила заключается в его API, которые сначала казались мне ужасными. Поэтому я призываю вас осознать философию фреймворка прежде, чем сделать окончательное суждение о нем.
Приступим к обзору Objective-J. Главное, что он вносит в JavaScript – это классы. Они определяются с помощью ключевого слова
Между
Взаимодействие между Objective-J объектами происходит с помощью пересылки сообщений, впервые эта техника появилась в языке Smalltalk. Синтаксически это выглядит так:
Например, вызов геттеров и сеттеров объекта класса
Методы объявляются после переменных-членов, до ключевого слова
Создание экземпляра класса в Objective-J происходит в два этапа: сначала метод
Названия конструкторов в Objective-J принято начинать со слова
Ради единства стиля Objective-J определяет три переменных, взятые из Objective-C, это
Как и многие читающие это очерк, я никогда не использовал Objective-C, поэтому для меня было совершенно неочевидно, почему класс определяется ключевым словом
Пора установить Cappuccino. Если вы используете Windows, сначала придется установить Cygwin (неприятное обстоятельство). Все остальные и те, кто все-таки решился на предыдущий шаг, просто скачивают Cappuccino Starter Package и запускают в нем скрипт bootstrap.sh.
После установки в системе должна появиться утилита capp. В качестве примера мы будем создавать (сюрприз!) приложение для поиска в твиттере. Сгенерируем его:
В появившейся папке лежит куча файлов, нас интересуют index-debug.html и AppController.j (*.j – стандартное расширение файлов с Objective-J кодом).
Откроем index-debug.html. Строгая политика безопасности Google Chrome не позволяет Cappuccino загружать нужные ей файлы при работе с протоколом file://, требуется поднимать веб-сервер, во всех же остальных браузерах мы увидим работающее приложение Hello World! Cappuccino компилирует Objective-J код в JavaScript на лету, прямо в браузере, поэтому при разработке можно просто обновлять страницу и не заботиться о пересборке. «Боевую» версию приложения можно скомпилировать заранее, чтобы ускорить загрузку.
Приступим к разработке. В файле AppController.j определен класс
Перед определением класса импортируем
Ключевое слово
Метод
Для инициализации приложения мы создадим окно и текстовое поле:
JavaScript функция
Раньше я был ярым сторонником ограничения длины строки 80-ю символами. Применение этого правила в Cappuccino превращает код в нечитаемую кашу, поэтому большинство разработчиков не ограничивают себя при работе как с Objective-J, так и с Objective-C. Единственным исключением являются гайдлайны Google, но на то он и Google.
Теперь сделаем текстовое поле редактируемым и добавим ему рамки:
Установим Target и Action, т.е. объект и метод, который должен у него вызваться при нажатии кнопки Enter в поле:
Добавляем поле к окну и фокусируем его:
И наконец, показываем окно:
С инициализацией приложения покончено, теперь определим его реакцию на нажатие Enter, т.е. метод
Мы извлекаем значение текстового поля и, если оно не пусто, создаем экземпляр
Создадим файл SearchWindowController.j и отнаследуем наш класс от
Определим конструктор, создающий окно и делающий запрос к твиттеру:
И наконец, напишем обработчик ответа от твиттера:
Для отображения твитов он использует библиотечные классы
Наверное, вы заметили в коде несколько вызовов метода
Осталось разобраться с классом
Метод
Теперь снова откроем index-debug.html и поищем несколько слов. Результат должен получиться примерно такой:
Вы можете просмотреть полный код примера и протестировать его.
Конечно, в этом очерке невозможно было описать и десятую долю функционала Cappuccino. Однако я уверен, что сейчас вы уже готовы начать разработку своих приложений, к чему вас и призываю. Для углубления знаний используйте документацию Cappuccino, также очень рекомендую набор скринкастов по теме. Надеюсь, что титанический труд Франциско, Тома и Росса, а также мой скромный очерк помогут кому-то из вас создать новое интересное веб-приложение.
Я заметил, что на русском языке почти нет обучающих материалов о Cappuccino, и решил восполнить пробел. Этот очерк рассчитан на то, чтобы прочитав его, можно было сразу приступать к разработке своего первого Cappuccino приложения. Я познакомился с фреймворком, когда искал средство для реализации онлайновой среды разработки для своего проекта Akshell. Мне нужно было сделать полнофункциональную IDE, работающую в окне браузера, и Cappuccino отлично справился с поставленной задачей.
История
В 2008 году Франциско Толмаски, Том Робинсон и Росс Баучер (Франциско и Росс – бывшие сотрудники Apple) создали компанию 280 North и выпустили 280 Slides, веб-приложение для подготовки презентаций. Оно и сейчас впечатляет своим look and feel’ом, а тогда это было просто что-то нереальное. Причем 280 Slides не был самостоятельным продуктом, это была лишь демонстрация возможностей нового веб-фреймворка, Cappuccino.
4-го сентября того же года Cappuccino был опубликован под лицензией LGPL и тут же стал хитом на GitHub. Вокруг фреймворка образовалось сообщество программистов, использующих его в своих продуктах. С тех пор это сообщество только увеличивается и принимает все более активное участие в разработке Cappuccino.
В 2009 году 280 North анонсировала новый продукт, Atlas, и он также оказался прорывной технологией. Atlas – это аналог Interface Builder для веб-приложений, в нем вы можете рисовать интерфейсы мышкой, перетаскивая нужные компоненты из библиотеки. Пара минут работы дает более впечатляющий результат, чем день мучений с CSS. В отличие от Cappuccino, Atlas – закрытая платная программа, для скачивания бета-версии нужно заплатить $20. Этот очерк посвящен Cappuccino, поэтому я не буду описывать Atlas более подробно. Скажу только, что он действительно экономит кучу времени при создании интерфейсов, позволяет сосредоточиться на юзабилити, а не на борьбе с различными версиями браузеров.
Cappuccino и Atlas активно развивались, когда летом 2010 года Motorola купила 280 North со всеми ее активами, потратив на это, по слухам, 20 миллионов долларов. Официальная цель покупки – развитие технологий разработки на платформе Android. Многие злые языки начали поговаривать о смерти Cappuccino, однако с тех пор прошло уже полгода, фреймворк активно разрабатывается, а 23-го февраля состоялся релиз версии 0.9.
Итак, Cappuccino является одной из самых продвинутых технологий создания веб-приложений, его развитие обеспечивает большая корпорация. Изучить или хотя бы ознакомиться с фреймворком будет полезно каждому веб-программисту, к чему и приступим.
Теория
Главная особенность Cappuccino – это созданный специально для него язык Objective-J. Он является такой же надстройкой над JavaScript, как Objective-C – над C. Для меня, как и для многих, вначале было совершенно неочевидно, зачем делать объектно-ориентированную надстройку над языком, который сам по себе является объектно-ориентированным. Однако в этом есть большой смысл.
Во-первых, благодаря Objective-J Cappuccino полностью повторяет Cocoa API. Это не фантазия на тему и даже не творческая переработка, это именно повторение, вплоть до сигнатур функций. За счет этого программисты, работавшие на Mac OS X и iOS могут безболезненно перейти на веб-разработку. Удобство всех API проверено долговременными использованием на десктопе, начиная с ОС NeXTSTEP. И наконец, при использовании фреймворка можно успешно пользоваться документацией Cocoa, которая значительно превосходит по качеству и проработанности Doxygen документацию Cappuccino.
Во-вторых, отсутствие гибкости иногда является плюсом. Большинство программистов посчитает, что я сейчас высказал большую ересь, поэтому сразу начну оправдываться. Дизайнеры знают, что введение сеток и других искусственных ограничений может улучшить дизайн. Objective-J интерфейсы Cappuccino на первый взгляд кажутся неповоротливыми и допотопными по сравнению с тем, что можно было бы сделать на JavaScript. Вместо парадигмы Target-Action зачастую можно было бы использовать замыкания, вместо явных геттеров и сеттеров –
__defineGetter__
и __defineSetter__
, вместо квадратных скобок – привычные точки, в конце концов. И самое ужасное: Objective-J методы не являются объектами первого класса!Поработав с Cappuccino, я понял достоинства такого подхода, и они являются прямыми следствиями недостатков. Более жесткая объектная модель позволяет четче структурировать код, не потонуть в каше вложенных друг в друга функций. Длинные содержательные имена позволяют справляться с иерархиями классов Cocoa, в который один класс обычно имеет многие десятки методов. Вездесущие геттеры и сеттеры позволяют реализовать Key-Value Coding, Key-Value Observing и Key-Value Binding, техники переворачивающие представление о создании сложных интерфейсов пользователя (к сожалению, в этом очерке не хватит места для их описания).
Я начал пользоваться Cappuccino потому, что меня привлек внешний вид приложений, созданных с его помощью. Теперь я понимаю, что настоящая сила заключается в его API, которые сначала казались мне ужасными. Поэтому я призываю вас осознать философию фреймворка прежде, чем сделать окончательное суждение о нем.
Приступим к обзору Objective-J. Главное, что он вносит в JavaScript – это классы. Они определяются с помощью ключевого слова
@implementation
:@implementation Person : CPObject { CPString name @accessors; } (id)initWithName:(CPString)aName { if (self = [super init]) name = aName; return self; } (Person)personWithName:(CPString)aName { return [[Person alloc] initWithName:aName]; } @end
Между
@implementation
и @end
находится описание класса. Класс Person
наследуется от CPObject
, вершины иерархии классов Cappuccino. Затем в фигурных скобках объявляются переменные-члены класса. name
– строковая переменная-член, для которой автоматически генерируются геттер и сеттер (благодаря указанию @accessors
). Правило именования аксессоров в Objective-J несколько нестандартно – это name
и setName:
. Этого формата стоит придерживаться, т.к. на него полагаются внутренние механизмы Cappuccino.Взаимодействие между Objective-J объектами происходит с помощью пересылки сообщений, впервые эта техника появилась в языке Smalltalk. Синтаксически это выглядит так:
[объект сообщение] [объект сообщениеСПараметром:значение] [объект сообщениеСПараметром1:значение1 параметром2:значение2] ...
Например, вызов геттеров и сеттеров объекта класса
Person
выглядит так:var name = [aPerson name]; [aPerson setName:”Вася”];
Методы объявляются после переменных-членов, до ключевого слова
@end
. Методы класса начинаются на плюс, методы экземпляра класса – на минус.Создание экземпляра класса в Objective-J происходит в два этапа: сначала метод
alloc
этого класса создает неинициализированный объект, а затем конструктор (init-метод) инициализирует его. Часто определяют методы класса, упрощающие этот процесс, personWithName:
– пример такого метода.Названия конструкторов в Objective-J принято начинать со слова
init
. Каждый конструктор должен сначала позаботится о вызове конструктора суперкласса, затем выполнить инициализацию и вернуть self
. Ошибка конструирования сигнализируется возвратом значения nil
(да, во времена создания Objective-C исключения еще не вошли в моду, поэтому Cocoa и, как следствие, Cappuccino обходятся без них).Ради единства стиля Objective-J определяет три переменных, взятые из Objective-C, это
nil
, YES
и NO
. Они идентичны null
, true
и false
, соответственно. Cappuccino Coding Style Guidelines рекомендуют использовать именно их.Как и многие читающие это очерк, я никогда не использовал Objective-C, поэтому для меня было совершенно неочевидно, почему класс определяется ключевым словом
@implementation
, а не, например, @class
. Специально для таких же любопытных: Objective-C, как надстройка над C, использует файлы объявлений (*.h от headers) и файлы определений (*.m от messages), поэтому классы должны объявляться (@interface
) и определяться (@implementation
). В Objective-J объявления не нужны, поэтому используется только @implementation
.Практика
Пора установить Cappuccino. Если вы используете Windows, сначала придется установить Cygwin (неприятное обстоятельство). Все остальные и те, кто все-таки решился на предыдущий шаг, просто скачивают Cappuccino Starter Package и запускают в нем скрипт bootstrap.sh.
После установки в системе должна появиться утилита capp. В качестве примера мы будем создавать (сюрприз!) приложение для поиска в твиттере. Сгенерируем его:
capp gen "Twitter Search"
В появившейся папке лежит куча файлов, нас интересуют index-debug.html и AppController.j (*.j – стандартное расширение файлов с Objective-J кодом).
Откроем index-debug.html. Строгая политика безопасности Google Chrome не позволяет Cappuccino загружать нужные ей файлы при работе с протоколом file://, требуется поднимать веб-сервер, во всех же остальных браузерах мы увидим работающее приложение Hello World! Cappuccino компилирует Objective-J код в JavaScript на лету, прямо в браузере, поэтому при разработке можно просто обновлять страницу и не заботиться о пересборке. «Боевую» версию приложения можно скомпилировать заранее, чтобы ускорить загрузку.
Приступим к разработке. В файле AppController.j определен класс
AppController
, в Objective-J принято размещать код каждого класса в одноименном файле. Cappuccino создает один экземпляр AppController
при запуске, он должен инициализировать приложение и управлять его дальнейшей работой.Перед определением класса импортируем
Foundation
и AppKit
, две основных части Cappuccino. Foundation
содержит классы для обеспечения бизнес-логики, а AppKit
– это библиотека классов пользовательского интерфейса. @import <Foundation/Foundation.j> @import <AppKit/AppKit.j>
Ключевое слово
@import
подключает указанный файл. Как и в C, при указании пути в угловых скобках файл будет искаться в системных папах, при использовании двойных кавычек – в текущей папке.Метод
applicationDidFinishLaunching:
вызывается у всех объектов непосредственно после запуска приложения. Содержательные имена методов Cappuccino делают код понятным даже для тех, кто не знаком с фреймворком, поэтому я буду давать пояснения только в неочевидных случаях. Cappuccino вряд ли сэкономит вам нажатия клавиш, так пусть хоть сэкономит их мне.Для инициализации приложения мы создадим окно и текстовое поле:
var window = [[CPWindow alloc] initWithContentRect:CGRectMake(100, 100, 250, 70) styleMask:CPTitledWindowMask], contentView = [window contentView], textField = [[CPTextField alloc] initWithFrame:CGRectMake(25, 20, 200, 30)];
JavaScript функция
CGRectMake(x, y, width, height)
описывает прямоугольник, в котором будет располагаться элемент управления. Метод contentView
возвращает представление внутренней области окна.Раньше я был ярым сторонником ограничения длины строки 80-ю символами. Применение этого правила в Cappuccino превращает код в нечитаемую кашу, поэтому большинство разработчиков не ограничивают себя при работе как с Objective-J, так и с Objective-C. Единственным исключением являются гайдлайны Google, но на то он и Google.
Теперь сделаем текстовое поле редактируемым и добавим ему рамки:
[textField setEditable:YES]; [textField setBezeled:YES];
Установим Target и Action, т.е. объект и метод, который должен у него вызваться при нажатии кнопки Enter в поле:
[textField setTarget:self]; [textField setAction:@selector(didSubmitTextField:)];
@selector
используется, чтобы превратить метод в передаваемое значение.Добавляем поле к окну и фокусируем его:
[contentView addSubview:textField]; [window makeFirstResponder:textField];
И наконец, показываем окно:
[window center]; [window setTitle:"Twitter Search"]; [window orderFront:self];
С инициализацией приложения покончено, теперь определим его реакцию на нажатие Enter, т.е. метод
didSubmitTextField:
- (void)didSubmitTextField:(CPTextField)textField { var searchString = [textField stringValue]; if (!searchString) return; [[[SearchWindowController alloc] initWithSearchString:searchString] showWindow:nil]; [textField setStringValue:""]; [[textField window] makeKeyAndOrderFront:nil]; }
Мы извлекаем значение текстового поля и, если оно не пусто, создаем экземпляр
SearchWindowController
, показываем его окно, опустошаем текстовое поле и выдвигаем его окно вперед. Дело за малым – разработать класс SearchWindowController
.Создадим файл SearchWindowController.j и отнаследуем наш класс от
CPWindowController
:@implementation SearchWindowController : CPWindowController { } @end
Определим конструктор, создающий окно и делающий запрос к твиттеру:
- (id)initWithSearchString:(CPString)searchString { if (self = [super init]) { var window = [[CPWindow alloc] initWithContentRect:CGRectMake(10, 30, 300, 400) styleMask:CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask]; [window setTitle:searchString]; [self setWindow:window]; var request = [CPURLRequest requestWithURL:"http://search.twitter.com/search.json?q=" + encodeURIComponent(searchString)]; [CPJSONPConnection sendRequest:request callback:"callback" delegate:self]; } return self; }
И наконец, напишем обработчик ответа от твиттера:
- (void)connection:(CPJSONPConnection)connection didReceiveData:(Object)data { var contentView = [[self window] contentView]; if (data.results.length) { var bounds = [contentView bounds], collectionView = [[CPCollectionView alloc] initWithFrame:bounds]; [collectionView setAutoresizingMask:CPViewWidthSizable]; [collectionView setMaxNumberOfColumns:1]; [collectionView setMinItemSize:CGSizeMake(200, 100)]; [collectionView setMaxItemSize:CGSizeMake(10000, 100)]; var itemPrototype = [[CPCollectionViewItem alloc] init]; [itemPrototype setView:[TweetView new]]; [collectionView setItemPrototype:itemPrototype]; [collectionView setContent:data.results]; var scrollView = [[CPScrollView alloc] initWithFrame:bounds]; [scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable] [scrollView setDocumentView:collectionView]; [contentView addSubview:scrollView]; } else { var label = [CPTextField labelWithTitle:"No tweets"], boundsSize = [contentView boundsSize]; [label setCenter:CGPointMake(boundsSize.width / 2, boundsSize.height / 2)]; [label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin]; [contentView addSubview:label]; } }
Для отображения твитов он использует библиотечные классы
CPCollectionView
, CPCollectionViewItem
и CPScrollView
. А вот класс TweetView
придется определить самостоятельно.Наверное, вы заметили в коде несколько вызовов метода
setAutoresizingMask:
. Этот метод заставит вас навсегда проклясть позиционирование CSS, если, конечно, вы этого еще не сделали. Позиционирование элементов управления в Cappuccino настолько просто и элегантно, что с ним справится даже ребенок.Осталось разобраться с классом
TweetView
:@implementation TweetView : CPView { CPImageView imageView; CPTextField userLabel; CPTextField tweetLabel; } - (void)setRepresentedObject:(id)tweet { if (!imageView) { imageView = [[CPImageView alloc] initWithFrame:CGRectMake(10, 5, 48, 48)]; [self addSubview:imageView]; userLabel = [[CPTextField alloc] initWithFrame:CGRectMake(65, 0, -60, 18)]; [userLabel setAutoresizingMask:CPViewWidthSizable]; [userLabel setFont:[CPFont boldSystemFontOfSize:12]]; [self addSubview:userLabel]; tweetLabel = [[CPTextField alloc] initWithFrame:CGRectMake(65, 18, -60, 100)]; [tweetLabel setAutoresizingMask:CPViewWidthSizable]; [tweetLabel setLineBreakMode:CPLineBreakByWordWrapping]; [self addSubview:tweetLabel]; } [imageView setImage:[[CPImage alloc] initWithContentsOfFile:tweet.profile_image_url size:CGSizeMake(48, 48)]]; [userLabel setStringValue:tweet.from_user]; [tweetLabel setStringValue:tweet.text]; } @end
Метод
setRepresentedObject:
создает изображение аватара и две метки, с именем пользователя и твитом.Теперь снова откроем index-debug.html и поищем несколько слов. Результат должен получиться примерно такой:
Вы можете просмотреть полный код примера и протестировать его.
Что дальше?
Конечно, в этом очерке невозможно было описать и десятую долю функционала Cappuccino. Однако я уверен, что сейчас вы уже готовы начать разработку своих приложений, к чему вас и призываю. Для углубления знаний используйте документацию Cappuccino, также очень рекомендую набор скринкастов по теме. Надеюсь, что титанический труд Франциско, Тома и Росса, а также мой скромный очерк помогут кому-то из вас создать новое интересное веб-приложение.