Краткий экскурс
Меня часто спрашивают — чего ради ещё один фреймворк если есть Rails.
Я часто отвечаю — затем что поезд не решает те задачи которые решает автомобиль.
Ещё чаще меня спрашивают — чего ради Presto если есть Sinatra.
Также часто я отвечаю — потому что автомобили отличаются в плане скорости, комплектации и удобства.
Как работается?
В плане использования, Presto предлагает в полне стандартную логику создания приложений и такой же стандартный набор инструментов — контролеры, роутинг, редиректы, рендеринг etc.
В основном работается очень легко. Всё организовано натурально и просто. Легко подаётся пониманию.
В чём разница?
Скорость
Из особенностей, в первую очередь стоит отметить высокую скорость обработки запросов.
Эталонная скорость Presto — от 4000 до 5000 запросов в секунду, в зависимости от железа.
Что такое эталонная скорость и в чём она измеряется?
Очень просто — это скорость «Hello World» приложения.
С точки зрения фреймворка, все приложения одинаковы, с единственной разницей — сколько времени приложение тратит на обработку запроса.
И поскольку «Hello World» приложение тратит менее одного процента, эту скорость можно считать эталонной.
А скорость любого приложения это эталонная скорость разделить на время которое приложение тратит на обработку запроса.
Теперь, предположим что ваш сайт обращается к БД и рендерит HTML, тратя при этом 50% времени.
Если вы используете Presto, 50% от 4000-5000 это 2000-2500, так что примерная скорость вашего сайта будет составлять около 2000 запросов в секунду.
Представьте какая скорость у вашего сайта если вы используете фреймворк с эталонной скоростью 100 запросов в секунду.
Slices
Другой важной особенностью являются Slices.
Любой модуль содержавший контролеры можно вмонтировать в любое приложение и будет работать везде одинаково.
Можно один раз написать скажем блог или форум и использовать его на любом Presto сайте.
Дальше — лучше …
Оформив слайс как гем мы получаем распределённый модуль с возможностью синхронизации.
Многоуровневое конфигурирование
Из практичных приятностей выделяется возможность многоуровневого конфигурирования.
Любой контролер можно конфигурировать на уровне контролера или же на уровне слайса в котором он состоит.
Так например если ваш форум мигрирует с /forums/ на /forum/, всё что вам нужно сделать это перемонтировать форум слайс под /forum/.
Или же если нужно добавить миддлеваре, или поменять путь к темплэйтам, или даже template engine — всё это делается как на уровне контролера так и на уровне слайса.
Притом что конфиг контролера является персональным и имеет приоритет перед общим слайсовым конфигом.
Api
Ещё стоит отметить что Presto организован в виде множества Api.
В том смысле что инструменты не просто разбросаны внутри контролеров(или даже внутри Object класса!), а организованы по собственным Api.
Всё что связанно с HTTP находиться под http
методом — http.params
, http.session
, http.cookies
etc.
Всё что связанно с рендером находиться под view
методом — view.render
, view.render_partial
, view.render_layout
etc.
Таким образом, Presto добавляет всего-лищь 3 метода в ваши контролеры — http
, view
и node
.
Sandbox
Другой практичной особенностью является Sandbox.
Sandbox позволяет запретить доступ к session и cookies, а также запретить redirect, forward и halt.
Можно также дать доступ к session и cookies в режиме только чтение.
Таким образом, верстальщик, которому вы не совсем доверяете, не сможет исполнять код который будет читать/отправлять куки/сессии пользователей,
или переправлять на другие адреса.
К no-logic templates это конечно не относиться.
Inline Testing
Ну и в конце стоит отметить такую диковинку как Inline Testing.
Суть в том что можно писать тесты «не отходя от кассы»…
Тест для любого акшиона можно писать прямо в контролере, рядом с самим акшионом.
Спорный вопрос, но таким образом сохраняется визуальный контакт между логикой приложения и тестовыми действиями.
То есть, когда вы пишете тесты, текущая логика физически находится перед вами, а не в вашем воображении.
Впрочем, это сугубо субъективный подход. Лично я, за последние полгода так привык к такому подходу что использую стандартный метод лишь в крайних случаях.
Приступаем к освоению — Install
Для начала надо установить Ruby версии 1.9.2 или выше.
Или же JRuby 1.9 mode.
Потом в терминале пишем:
$ gem install presto
Presto имеет лишь две зависимости — Rack и Tilt.
Так что процесс установки обычно занимает меньше минуты.
Controllers
Создать контролер в Presto также просто как создать и расширить класс в Ruby.
Надо просто создать класс, включить Presto::Api и адресовать через http.map
class MyController
include Presto::Api
http.map
end
Routing
Routing в Presto начинается с http.map
http.map
устанавливает корневой адрес для всех акшионов в данном контролере.
В примере выше, http.map
вызван без параметров. Это означает что контролер будет обслуживать корневой адрес сайта — /
То есть http.map
идентичен http.map "/"
Типичный пример — News обслуживает /news/
class News
include Presto::Api
http.map :news
def index
# some logic
end
def edit
# some logic
end
end
Теперь News обслуживает 3 адреса:
- /news/
- /news/index
- /news/edit
Но http.map
не является окончательным словом в роутинге.
Также как http.map
устанавливает корневой адрес для акшионов в данном контролере,
аддрес на котором монтирован слайс устанавливает корневой адрес для контролеров в данном слайсе.
Скажем у нас такой слайс:
module Forum
class Posts
include Presto::Api
http.map :posts
# actions
end
class Users
include Presto::Api
http.map :users
# actions
end
end
Создаём приложение:
app = Presto::App.new
Монтируем форум в корневой адрес:
app.mount Forum
теперь Forum будет обслуживать все акшионы Posts и Users под /posts/ и /users/ соответственно.
Но мы также можем вмонтировать форумный слайс под любой другой адрес.
app.mount Forum, '/forum'
теперь Forum будет обслуживать все акшионы Posts и Users под /forum/posts/ и /forum/users/ соответственно.
Но и это ещё не всё.
Есть ещё возможность обслуживания множества корневых адресов одним и темже контролером.
Об этом в параграфе Canonical.
Canonical Routing
Допустим ваши новости мигрировали с /Novosti/ на /news/
Но вам нужно ещё какое-то время поддерживать новости на старом адресе.
Можно конечно сделать copy/paste, но в условиях нынешних тенденций это не то не целесообразно — это просто дико.
Presto предлагает решение по элегантнее — canonical routes.
Просто задаём http.map
множество параметров, каждый из которых является каноническим адресом,
кроме первого разумеется, который является корневым адресом.
Пример
class News
include Presto::Api
http.map :news, :Novosti
def index
http.canonical?.to_s
end
end
Теперь News будет обслуживать /Novosti/ также как и /news/
Обратите внимание на def index
, он содержит http.canonical?
,
который возвращает текущий канонический адрес если запрос сделан с канонического адреса или же nil если запрос сделан с корневого адреса.
В данном контексте, если ввести в браузер /news/, ответом будет пустая строка — http.canonical? возвращает nil потому что /news/ является корневым адресом.
Если же ввести /Novosti/, ответом будет /Novosti/
Сanonical работает как на уровне контролера так и на уровне слайса.
Пример
module Forum
class Posts
include Presto::Api
http.map :posts
# actions
end
class Users
include Presto::Api
http.map :users
# actions
end
end
app = Presto::App.new
app.mount Forum, '/forum', '/Forums'
Теперь Forum будет обслуживать следующие адреса:
- /forum/posts/
- /forum/users/
- /Forums/posts/
- /Forums/users/
Наверное вы задались вопросом — зачем же нельзя просто монтировать один и тот же слайс на множество адресов.
Можно, но не целесообразно.
— лишняя писанина.
— лишняя память для обслуживания ещё одного слайса.
— http.canonical? не будет работать так что вы не будете знать находитесь ли вы под рутом или под каноником.
Actions
Добавить акшион также легко как создать очередной метод в Ruby.
Пример: создаём 2 акшиона — index и edit
class MyController
include Presto::Api
http.map
def index
# some logic
end
def edit
# some logic
end
end
Теперь MyController обслуживает 3 адреса:
- /
- /index
- /edit
Действительно легко, но ведь акшионы не всегда такие тривиальные.
Как например обслуживать такой адрес: /forum/posts/top-100 или /forum/users/online/active-only
Про это детально рассказывается в параграфе Mapping.
Mapping
Идея позаимствована из Ramaze. Я использовал Ramaze фреймворк на протяжении долгово времени и нахожу превосходной идею формирования URL из имён методов.
Например, def edit; end
соответствует /edit в строке браузера.
А def users_online; end
соответствует /users_online
Но def users__online; end
соответствует /users/online
То есть __ преобразовывается в /
По умолчанию, у Presto 3 правила преобразования:
- "____" => ".",
- "___" => "-",
- "__" => "/",
Пример: def users___online; end
соответствует /users-online
Пример: def users____html; end
соответствует /users.html
Вы конечно можете изменить данные правила или даже добавить новые.
Для этого используется http.path_rules
на уровне контролера или слайса.
Пример: преобразовываем "_" в "/" на уровне контролера
class News
include Presto::Api
http.map :news
http.path_rules "_" => "/"
def read_active
end
end
Теперь read_active
будет обслуживать /news/read/active адрес.
Пример: преобразовываем "_" в "-" на уровне слайса
module Ctrl; end
class Ctrl::News
include Presto::Api
http.map :news
def search_popular
end
end
class Ctrl::Articles
include Presto::Api
http.map :articles
def show_latest
end
end
app = Presto::App.new
app.mount Ctrl do
http.path_rules "_" => "-"
end
Теперь Ctrl::News#search_popular
будет обслуживать /news/search/popular адрес.
Также как Ctrl::Articles#show_latest
будет обслуживать /articles/show/latest адрес.
Хорошо, но это всё статические имена. Как же на счёт динамических параметров?
Например /news/delete/100
Об этом в следующем параграфе.
Parametrization
Параметризация в Presto натуральна и проста.
HTTP параметры становятся аргументами для текущего акшиона.
Допустим у нас такой контролер:
class News
include Presto::Api
http.map :news
def edit id
return "ID Passed: #{id}"
end
end
Если в браузере набираем /news/edit/100, Presto сделает такой вызов — News#edit( 100 )
и в ответ получим «ID Passed: 100»
Если же набираем /news/edit/, вызов будет таким: News#edit
, и так как #edit не может быть вызван без параметров,
в ответ получим ошибку «404, Page Not Found»
Для того чтобы акшион работал и без ID, надо дать параметру значение по умолчанию:
class News
include Presto::Api
http.map :news
def edit id = 0
return "ID Passed: #{id}"
end
end
Если в браузере набираем /news/edit/, в ответ получим «ID Passed: 0»
Если же набираем /news/edit/100, в ответ получим «ID Passed: 100»
Можно также передать/принимать N-ое количество параметров:
class Users
include Presto::Api
http.map :users
def details id, *columns
return "ID: #{id} | Columns: #{columns.join(', ')} "
end
end
Если в браузере набираем /users/details/100/name/email/status, в ответ получим «ID: 100 | Columns: name, email, status»
Если же набираем /users/details/100/, в ответ получим «ID: 100 | Columns: „
/users/details/ вернёт ошибку “404, Page Not Found»
То есть если Ruby метод будет работать с данными аргументами, будет работать и HTTP запрос.
При таком раскладе, можно задать любую комбинацию HTTP параметров, единственное правило — чтобы акшион с этой комбинацией работал.
Alias
Как мы уже говорили, контролеры и слайсы могут обслуживать множество адресов.
Но как же на счёт акшионов? Что если я хочу отображать идентичный контент при запросе разных акшионов.
Всё очень просто — используем http.alias
Пример
class Users
include Presto::Api
http.map :users
def details
end
http.alias :details, :about, :features
end
Теперь /users/details будет обслуживать 2 дополнительных адреса — /users/about и /users/features
Стандартное «Hello World» приложение
Создаём app.rb file
require 'presto'
class HelloWorld
include Presto::Api
http.map
def index
"Hello World"
end
end
app = Presto::App.new
app.mount HelloWorld
app.run server: :Thin, Port: 7890
Файл
Результат в браузере
Тест на производительность
На этом заканчиваем первую часть знакомства.
В следующих частях узнаем про rendering, content_type, hooks, cache, middleware, error handling, authentication, sandbox и многое другое.
Автор: slivu
Пример использования Presto, в настоящее время Espresso – http://espresso.mosalt.ru/