tide - smalltalkでspa
Post on 18-Jun-2015
411 Views
Preview:
DESCRIPTION
TRANSCRIPT
Tide - SmalltalkでSPAシングルページWebアプリをSmalltalkで作る
第69回Smalltalk勉強会合同会社ソフトウメヤ 梅澤真史
Smalltalkで Web?
● 第一世代(90年前半)○ VisualWave (GUIがそのまま Webブラウザに)
○ Classic Blend (Java Appletと Server Smalltalkと通信)
● 第二世代(2012年くらいまで)○ Seaside (継続ベース。異端のフレームワーク)
○ AIDA/Web (RESTfulなオブジェクトの集合体)
概して進みすぎていて、理解されない!
● 詳しくは
○ 第9回Smalltalk勉強会の資料「SmalltalkでWeb!」
第三世代の波
● 今やシングルページWebアプリの時代
■ AJAXとDOMの操作ですべてをまかなう● ページの遷移がない
○ Gmailとか、Google Mapsとか
● 「継続とか、いらなかったんや...」● というかSPAに向いたWebアプリもたくさんある
● SPAに特化したSmalltalkのフレームワークの登場!○ Tide
■ https://github.com/tide-framework/tide○ Flow
■ https://github.com/flow-stack/flow
Tide - The missing web framework
● 軽量
○ サーバ側: Pharo■ 27クラス
○ クライアント側: Amber■ 14クラス
● Webの普通の仕組みを使う
○ XHRでAJAX○ Cookieでセッション管理
● 簡潔
○ オブジェクトのメッセージ送信を書くとAJAXしてくれる
インストール (1)
● githubから
○ https://github.com/tide-framework/tide● インストール時にbowerが必要となるので入れておく
○ Node.js のnpmですぐに入る
● Pharo取得
● Pharo起動
curl get.pharo.org/30+vm | bash
./pharo-ui Pharo.image
インストール (2)
● ConfogurationOfTideで Pharo側をロード
Metacello new configuration: 'Tide'; version: #development; repository: 'http://www.smalltalkhub.com/mc/Pharo/MetaRepoForPharo30/main'; load.
● bowerでAmber側をロード
(ConfogurationOfTideで取得された Amberのディレクトリに移動して)
bower install
動作の確認
● Pharoのワークスペースで
● URLをWebブラウザにペーストでAmberが立ち上がる
● Amberのワークスペースから
● Seasideでおなじみのカウンターアプリが立ち上がる
● 動作確認後、Pharoのイメージを保存しておく
TDDispatcher tideIndexPageUrl ”print it”
TDCounterWidget new render
クライアント側コード
● Heliosのブラウザを上げてTDCounterWidgetを見てみる
TDCounterWidget● クラス定義
● レンダリング
なにやら Seaside っぽい
Widget subclass: #TDCounterWidgetinstanceVariableNames: 'counter header'package: 'Tide-Amber-Examples'
renderOn: htmlheader := html h1 with: self counter count asString.html button
with: '++';onClick: [ self increase ].
html button with: '--';onClick: [ self decrease ]
プロキシの存在
● TDCounterWidget >> counter (‘accessing’)
● TDCounterWidget >> increase (‘actions’)
● プロキシ経由でサーバにメッセージ送信
● 非同期を待ってthen:でアップデートをかけている!
counter^ counter ifNil: [ counter := TDClientProxy on: '/counter' ]
increaseself counter increase.self counter then: [ self update ]
何が送られているのか?
● Webブラウザの開発者用ツールで覗いてみる
こんな感じの JSON
● オブジェクトID(__id__)● アクションとコールバック用ID(actions)● 状態(state)
{"__id__":"cpacwb3c83uxa9icxs39o9g9r","actions":{"increase":"/counter?_callback=798013338","update":"/counter?_callback=784201227","decrease":"/counter?_callback=634370664"},"state":{"tidePresenterString":"a TDPresenter","count":7}}
セッション情報はCookieに
● 普通でいい感じ
サーバ側では
● PharoのブラウザでTDCounterを確認
TDCounter
● クラス定義
● 初期化
● TDPresenterを継承している
● 状態保持用にcountを持つだけ
TDPresenter subclass: #TDCounterinstanceVariableNames: 'count'classVariableNames: ''category: 'Tide-Examples'
initializesuper initialize.count := 0
<state>と<action>
● TDCounter >> count (‘accessing’)
● TDCounter >> increase (‘actions’)
● モデル更新の処理を平凡に書く
● <state>, <action> のpragma指定でJSONに変換され、ク
ライアントに送られる
count<state>^ count
increase<action>count := count + 1
演習: Todoを作ってみる
● Pharo側にTDTodoPresenter, TDTodoList, TDTodoItemが用意されている
作れということ!
Todoのサーバ側 (1)
● TDTodoPresenter○ TDPresenter を継承
○ <state>toDoListとしてTDTodoListを保持
○ ‘todo’というURLで自己を登録している
● TDTodoList○ Objectを継承。単なるモデル
○ <state>itemsとしてTDTodoItemを保持
○ <action>addItemLabeled: aString○ <action>removeItem: anItem を定義
Todoのサーバ側 (2)
● TDTodoItem○ Objectを継承。単なるモデル
○ <state>label, <state>completed を持つ
サーバ側は以上。何の変哲もないつくり。素直。
● 以上をふまえ、クライアント側(Amber)を作ってみる!
Todo用のhtmlを用意
● いちいち Heliosからdo itするのが面倒なので、
Todo起動用のhtmlを作っておく
● index.htmlと同じ場所に置く(tide-framework-tide-xxx下)● index.htmlをコピーしてJSの最後の部分を書き換える
● URL指定で Todoが立ち上がるようになる
(TDTodoWidgetはこれから作ります!)
function (smalltalk) { smalltalk.initialize({defaultAmdNamespace: 'tide'}); smalltalk.TDTodoWidget._new()._render();}
TDTodoWidget の基本メソッド群
● クラス定義●
● アクセッサ
Widget subclass: #TDTodoWidgetinstanceVariableNames: 'todo listPart'package: 'Tide-Amber-Examples'
todo^ todo ifNil: [ todo := TDClientProxy on: '/todo' ]
list^self todo todoList
TDTodoWidget レンダリング (1)
● はじめにconnectしておくのが特徴
● DOMにくっつけるところは通常のAmberと同じ
(ただしthen:を使ってconnectの結果を待っている)
renderself todo connect.self todo then: [ self appendToJQuery: 'body' asJQuery ]
TDTodoWidget レンダリング (2)renderOn: html
html h1: 'Tide-Todo'.listPart := html div class: 'listPart'; with: [
self renderListOn: html].html button
with: '+';onClick: [self addItemLabelled: self askTodo].
● 後のアップデートのため、listPartインスタンス変数にTagBrushを入れておく
● onClick: イベントハンドラでTodo追加の処理を書く
TDTodoWidget イベントハンドラ
● プロキシ経由で<action> addItemLabelled: を送信
● then:後にアップデート
addItemLabelled: aStringself list addItemLabelled: aString.self list then: [ self update ]
askTodo^BrowserInterface new prompt: 'Todo?'
● Todo項目の入力用UIを作るのが面倒なので、BrowserInterface>>prompt:で対応
CSSで見栄えよく
● todo.cssを書いて、todo.htmlから参照させる
● 中身はご自由に
h1 {color: #333333;background-color:#cccccc;
}body {
background-color:#66ccff;}
ul {list-style-type: none;margin-left:10px;font-size:120%;
}.label {padding:10px;}.listPart{ background-color:#b2e5ff;}
updateの中身
updatelistPart contents: [:html |
self renderListOn: html]
● renderOn: で捕まえておいたlistPartのTagBrushを使い、特定DOM部分(<div class=’listPart’>)のみを再描画
レンダリング続き (1)
● プロキシ経由でitemsを得て、個々をrenderItem:on:でレンダリング
renderListOn: htmlhtml ul: [ self list items do: [:each | html li:[self renderItem: each on: html]]]
レンダリング続き (2)
● <action> completed:や<state>labelを利用
● チェックボックスを即座に更新するためasJQueryでDOMを
とらえて’checked’属性を変えている
renderItem: each on: html| chkbox |chkbox := html input class: 'completed'; type: 'checkbox'.chkbox onClick: [
each completed: each completed not.chkbox asJQuery attr: 'checked' to: each completed
].each completed ifTrue: [chkbox at: 'checked' put: 'checked'].html span class: 'label'; with: each label
演習2: Todo項目を削除できるように
● 今のところ、 Todo項目の削除機能がない
● チェックした項目を削除するには?
● Pharo側に<action> removeItem: anItem があるので
これを使うことを考えてみる
新たな<action>の追加
● 選択したTodo項目を一度に消すため、Pharo側に
<action>removeCheckedItemsを作ってみる
removeCheckedItems<action>self items do: [:each |
each completed ifTrue: [self removeItem: each]].
レンダリングコードの修正
● Amberに戻り、renderOn:を修正、削除ボタンをつける
● クリック時のコールバックでremoveCheckedItemsを指定
renderOn: html...html button
with: '-';onClick: [self removeCheckedItems].
削除用のコールバックを実装
● プロキシ経由でremoveCheckedItemsをメッセージ送信
● 結果を待って更新
完成!!
removeCheckedItemsself list removeCheckedItems.self list then: [ self update ]
● Pharo側で”do it”● セッションの状態、アクティブなプレゼンターなどを観察でき
る
おまけ: セッションの様子を見る
TDDispatcher default sessionManager explore.
まとめ
● TideはSmalltalkでSPAを作るための軽量フレームワーク
● メッセージ送信と若干のpragmaで書け、AJAXの詳細は意識
する必要がない
● 定番のWeb技術を素直に使っているため拡張も楽
○ CSSで見栄えの調整
○ AmberのCanvasが嫌な場合は、Handlebarsなど、
クライアントサイドJSONテンプレートも使える(はず)
● Tideで作られたCMS、Marinaも見てみよう!https://github.com/tide-framework/marina
top related