Понемногу изучаю SammyJs - некоторый аналог Синатры на Js.
Есть хороший туториал -
http://sammyjs.org/docs/tutorials/json_store_1
http://sammyjs.org/docs/tutorials/json_store_2
Я перевёл код на CoffeeScript, шаблоны на haml, бэкэндом сделал
Синатру - которая выдаёт json, и разместил демо на CloudFoundry.
Исходный код:
https://github.com/nemilya/sammyjs-coffee-json-store
Демо:
http://sammyjs-coffee-json-store.cloudfoundry.com
Текущая версия SammyJs.
Использовался для html->haml
http://html2haml.heroku.com
(при этом атрибуты переводились из :name=>"value", в name: "value")
И замечательный http://js2coffee.org
Единственное добавил в блоке @around - return, иначе не корректно
работало (в CoffeeScript возвращается выражение, по умолчанию).
На бэкэнде и на фронтэнде - используется haml.
Для запуска локально - необходимы sinatra, haml, json.
и далее ruby app.rb
по умолчанию будет взять 4567 порт (это будет выведено в консоли), и
соответственно браузером открываем http://localhost:4567
Демо содержит получение ajax запросом json данных (которые хранятся в
items.json файле) - отображение таблицы, и переход на карточку, на
карточке - форма добавления в корзину, вверху справа отображение кол-
ва товаров в корзине. Так же используется модуль сессии для хранения
данных.
В связи с некоторыми проблемами кэширования - выдача haml и json -
была сделана через Синатру:
get '/:name.haml' do
file_path = "public/templates/#{params[:name]}.haml"
File.open(file_path).read if File.exists?(file_path)
end
get "/items.json" do
JSON.parse(File.read(DATA_SOURCE)).to_json
end
где DATA_SOURCE - это путь к файлу с json данными, ясно - что выдавать
можно из любого другого источника, я просто специально пропарсил -
чтобы это уже синатра выдавала сама json.
В views/layout.haml - идёт загрузка всех необходимых библиотек.
Для Sammy загружается haml.js библиотека, и оптом модуль Sammy.haml.
Так же загружается CoffeeScript интерпретатор - и после этого JS
приложение на базе SammyJS:
%script{ :src => "app/json_store.coffee", :type => "text/
coffeescript", :charset => "utf-8" }
А вот и всё содержимое приложения:
(($) ->
app = $.sammy("#main", ->
@use "Haml"
@use "Session"
@around (callback) ->
context = this
@load("/items.json").then((items) ->
context.items = items
).then callback
# important for CoffeeScript:
return
@get "#/", (context) ->
context.app.swap ""
$.each @items, (i, item) ->
context.render("/item.haml",
id: i
item: item
).appendTo context.$element()
@get "#/item/:id", (context) ->
@item = @items[@params["id"]]
return @notFound() unless @item
@partial "/item_detail.haml"
@post "#/cart", (context) ->
item_id = @params["item_id"]
# fetch the current cart
cart = @session("cart", ->
{}
)
# this item is not yet in our cart
# initialize its quantity with 0
cart[item_id] = 0 unless cart[item_id]
cart[item_id] += parseInt(@params["quantity"], 10)
# store the cart
@session "cart", cart
@trigger "update-cart"
@bind "update-cart", ->
sum = 0
$.each @session("cart") or {}, (id, quantity) ->
sum += quantity
$(".cart-info").find(".cart-
items").text(sum).end().animate(paddingTop: "30px").animate
paddingTop: "10px"
@bind "run", ->
# initialize the cart display
@trigger "update-cart"
)
$ ->
app.run "#/"
) jQuery
Из интересного можно отметить @around - это функция которая будет
выполнена перед любым запросом (роутером), а роутер выполняется дальше
- как вызов callback
Загрузка данных из бэкэнда происходит по этой команде
@load("/items.json")
она получает, и согласно расширению - автоматически парсит данные,
далее идёт ".then"
@load("/items.json").then((items) ->
context.items = items
).then callback
Это механизм приведения асинхронных запросов, к синхронному - в метод
then передаётся функция, которая получает параметр. И она будет
вызвана только после завершения асинхронного запроса, и в параметры к
ней будет передан результат функции - в нашем примере это будет
распарсенный json.
И значение присваивается в контекст (context.items = items) - и дальше
к нему обращаются из роутеров (@items)
Кстати в самом вверху, инициализация:
app = $.sammy("#main", ->
и здесь "#main" - это элемент в контексте которого app отображает
данные.
и строка :
context.app.swap ""
очищает содержимое этого элемента.
далее рендеринг шаблонов:
context.render("/item.haml",
id: i
item: item
)
возвращает dom элемент, который получился после обработки haml шаблона
item.haml, в контекст которого был передан хэш массив с ключами id и
item, вот содержимое haml шаблона:
.item
.item-image
%a{ href: "#/item/#{id}"}
%img{ alt: item.title, src: item.image}/
.item-artist= item.artist
.item-title= item.title
.item-price
$#{item.price}
Так же обратим внимание, что вверху подключены 2 плагина:
@use "Haml"
@use "Session"
предварительно были подключены соотв. js файлы в layout.haml:
sammy.haml.js
sammy.json.js
sammy.storage.js - содержит Session, и требует json плагин.
Session - обёртка над хранением данных - используется html5 storage,
если недоступен то куки, иначе в памяти.
Есть событийная модель, для этого определятся функция (обновляет
отображение корзины):
@bind "update-cart", ->
И далее она может быть вызвана с помощью @trigger, например:
@trigger "update-cart"
(вызывается в метода добавления пункта)
и так же:
@bind "run", ->
@trigger "update-cart"
Здесь "run" - это системное событие самого SammyJS - которое
вызывается при запуске (активации приложения), и соотв. в нём
происходит запуск события на обновление содержимого карзины.
Ну вот если кратко.
И для деплоя на CloudFoundry, надо:
1. получить аккаунт (бесплатно, при регистрации почтового ящика на
Cloudfoundry.com)
2. установить vmc - gem install vmc
3. настроить целевое облако
vmc target api.cloudfoundry.com
4. авторизоваться
vmc login
указать почту, и полученный по почте пароль
5. из папки приложения сделать
vmc push NAME_APP
NAME_APP - как вы назовёте приложение, в моём примере это было
"sammyjs-coffee-json-store" - будет создан домен 3го уровня
sammyjs-coffee-json-store.cloudfoundry.com
6. на все вопросы можно ответить по-умолчанию - приложение будет
запаковано и передано на cloudfoudnry - там распаковано, и стартовано.
7. для внесения изменения, надо находясь в корне приложения:
vmc update NAME_APP
система сама вычислит изменнения - запакует, запишет, и перезапустить
на сервере.
С уважением,
Илья