SlideShare una empresa de Scribd logo
1 de 37
Descargar para leer sin conexión
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取得 
curl get.pharo.org/30+vm | bash 
● Pharo起動 
./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側をロード 
bower install 
(ConfogurationOfTideで取得された Amberのディレクトリに移動して)
動作の確認 
● Pharoのワークスペースで 
TDDispatcher tideIndexPageUrl ”print it” 
● URLをWebブラウザにペーストでAmberが立ち上がる 
● Amberのワークスペースから 
TDCounterWidget new render 
● Seasideでおなじみのカウンターアプリが立ち上がる 
● 動作確認後、Pharoのイメージを保存しておく
クライアント側コード 
● Heliosのブラウザを上げてTDCounterWidgetを見てみる
TDCounterWidget 
● クラス定義 
Widget subclass: #TDCounterWidget 
instanceVariableNames: 'counter header' 
package: 'Tide-Amber-Examples' 
● レンダリング 
renderOn: html 
header := html h1 with: self counter count asString. 
html button 
with: '++'; 
onClick: [ self increase ]. 
html button 
with: '--'; 
onClick: [ self decrease ] 
なにやら Seaside っぽい
プロキシの存在 
● TDCounterWidget >> counter (‘accessing’) 
counter 
^ counter ifNil: [ counter := TDClientProxy on: '/counter' ] 
● TDCounterWidget >> increase (‘actions’) 
increase 
self counter increase. 
self counter then: [ self update ] 
● プロキシ経由でサーバにメッセージ送信 
● 非同期を待ってthen:でアップデートをかけている!
何が送られているのか? 
● Webブラウザの開発者用ツールで覗いてみる
こんな感じの JSON 
{"__id__":"cpacwb3c83uxa9icxs39o9g9r", 
"actions":{"increase":"/counter?_callback=798013338"," 
update":"/counter?_callback=784201227","decrease":" 
/counter?_callback=634370664"}, 
"state":{"tidePresenterString":"a TDPresenter","count": 
7}} 
● オブジェクトID(__id__) 
● アクションとコールバック用ID(actions) 
● 状態(state)
セッション情報はCookieに 
● 普通でいい感じ
サーバ側では 
● PharoのブラウザでTDCounterを確認
TDCounter 
● クラス定義 
TDPresenter subclass: #TDCounter 
instanceVariableNames: 'count' 
classVariableNames: '' 
category: 'Tide-Examples' 
● 初期化 
initialize 
super initialize. 
count := 0 
● TDPresenterを継承している 
● 状態保持用にcountを持つだけ
<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の最後の部分を書き換える 
function (smalltalk) { 
smalltalk.initialize({defaultAmdNamespace: 'tide'}); 
smalltalk.TDTodoWidget._new()._render(); 
● URL指定で Todoが立ち上がるようになる 
(TDTodoWidgetはこれから作ります!) 
}
TDTodoWidget の基本メソッド群 
● クラス定義 
● 
Widget subclass: #TDTodoWidget 
instanceVariableNames: 'todo listPart' 
package: 'Tide-Amber-Examples' 
● アクセッサ 
todo 
^ todo ifNil: [ todo := TDClientProxy on: '/todo' ] 
list 
^self todo todoList
TDTodoWidget レンダリング (1) 
render 
self todo connect. 
self todo then: [ self appendToJQuery: 'body' asJQuery ] 
● はじめにconnectしておくのが特徴 
● DOMにくっつけるところは通常のAmberと同じ 
(ただしthen:を使ってconnectの結果を待っている)
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 イベントハンドラ 
addItemLabelled: aString 
self list addItemLabelled: aString. 
self list then: [ self update ] 
● プロキシ経由で<action> addItemLabelled: を送信 
● then:後にアップデート 
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の中身 
update 
listPart contents: [:html | 
self renderListOn: html 
] 
● renderOn: で捕まえておいたlistPartのTagBrushを使い、 
特定DOM部分(<div class=’listPart’>)のみを再描画
レンダリング続き (1) 
renderListOn: html 
html ul: [ 
self list items do: [:each | html li:[self renderItem: each on: html]] 
] 
● プロキシ経由でitemsを得て、個々をrenderItem:on:で 
レンダリング
レンダリング続き (2) 
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 
● <action> completed:や<state>labelを利用 
● チェックボックスを即座に更新するためasJQueryでDOMを 
とらえて’checked’属性を変えている
演習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:を修正、削除ボタンをつける 
renderOn: html 
... 
html button 
with: '-'; 
onClick: [self removeCheckedItems]. 
● クリック時のコールバックでremoveCheckedItemsを 
指定
削除用のコールバックを実装 
removeCheckedItems 
self list removeCheckedItems. 
self list then: [ self update ] 
● プロキシ経由でremoveCheckedItemsをメッセージ送信 
● 結果を待って更新 
完成!!
おまけ: セッションの様子を見る 
TDDispatcher default sessionManager explore. 
● Pharo側で”do it” 
● セッションの状態、アクティブなプレゼンターなどを観察でき 
る
まとめ 
● TideはSmalltalkでSPAを作るための軽量フレームワーク 
● メッセージ送信と若干のpragmaで書け、AJAXの詳細は意識 
する必要がない 
● 定番のWeb技術を素直に使っているため拡張も楽 
○ CSSで見栄えの調整 
○ AmberのCanvasが嫌な場合は、Handlebarsなど、 
クライアントサイドJSONテンプレートも使える(はず) 
● Tideで作られたCMS、Marinaも見てみよう! 
https://github.com/tide-framework/marina

Más contenido relacionado

La actualidad más candente

日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
博文 斉藤
 
Hakodate - simple framework
Hakodate - simple frameworkHakodate - simple framework
Hakodate - simple framework
Hiroaki Murayama
 
Zabbix meeting 20101218_02-2 (Takanori Suzuki)
Zabbix meeting 20101218_02-2 (Takanori Suzuki)Zabbix meeting 20101218_02-2 (Takanori Suzuki)
Zabbix meeting 20101218_02-2 (Takanori Suzuki)
takanori suzuki
 
サーバー実装いろいろ
サーバー実装いろいろサーバー実装いろいろ
サーバー実装いろいろ
kjwtnb
 
Using SockJS(Websocket) with Sencha Ext JS
Using SockJS(Websocket) with Sencha Ext JSUsing SockJS(Websocket) with Sencha Ext JS
Using SockJS(Websocket) with Sencha Ext JS
Kazuhiro Kotsutsumi
 
Python Kyoto study LT
Python Kyoto study LTPython Kyoto study LT
Python Kyoto study LT
Naoya Inada
 
Open Source System Administration Framework - Func
Open Source System Administration Framework - FuncOpen Source System Administration Framework - Func
Open Source System Administration Framework - Func
Gosuke Miyashita
 
CategoLJについて
CategoLJについてCategoLJについて
CategoLJについて
Toshiaki Maki
 

La actualidad más candente (20)

日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
 
Hakodate - simple framework
Hakodate - simple frameworkHakodate - simple framework
Hakodate - simple framework
 
Zabbix meeting 20101218_02-2 (Takanori Suzuki)
Zabbix meeting 20101218_02-2 (Takanori Suzuki)Zabbix meeting 20101218_02-2 (Takanori Suzuki)
Zabbix meeting 20101218_02-2 (Takanori Suzuki)
 
サーバー実装いろいろ
サーバー実装いろいろサーバー実装いろいろ
サーバー実装いろいろ
 
Lt 111119
Lt 111119Lt 111119
Lt 111119
 
Nodejs
NodejsNodejs
Nodejs
 
第8回KPF発表資料
第8回KPF発表資料第8回KPF発表資料
第8回KPF発表資料
 
LAMP環境にDocker環境を追加してみた
LAMP環境にDocker環境を追加してみたLAMP環境にDocker環境を追加してみた
LAMP環境にDocker環境を追加してみた
 
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみたOPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
 
Fluentdへようこそ
FluentdへようこそFluentdへようこそ
Fluentdへようこそ
 
Using SockJS(Websocket) with Sencha Ext JS
Using SockJS(Websocket) with Sencha Ext JSUsing SockJS(Websocket) with Sencha Ext JS
Using SockJS(Websocket) with Sencha Ext JS
 
Python Kyoto study LT
Python Kyoto study LTPython Kyoto study LT
Python Kyoto study LT
 
最近の PHP の話
最近の PHP の話最近の PHP の話
最近の PHP の話
 
Open Source System Administration Framework - Func
Open Source System Administration Framework - FuncOpen Source System Administration Framework - Func
Open Source System Administration Framework - Func
 
CategoLJについて
CategoLJについてCategoLJについて
CategoLJについて
 
Apache Auroraの始めかた
Apache Auroraの始めかたApache Auroraの始めかた
Apache Auroraの始めかた
 
Chrome Extensionsの基本とデザインパターン
Chrome Extensionsの基本とデザインパターンChrome Extensionsの基本とデザインパターン
Chrome Extensionsの基本とデザインパターン
 
HascTool BlockDevelopment
HascTool BlockDevelopmentHascTool BlockDevelopment
HascTool BlockDevelopment
 
誰でも出来るosxでのローカルなウェブ開発環境構築
誰でも出来るosxでのローカルなウェブ開発環境構築誰でも出来るosxでのローカルなウェブ開発環境構築
誰でも出来るosxでのローカルなウェブ開発環境構築
 
PECL を数えてみた
PECL を数えてみたPECL を数えてみた
PECL を数えてみた
 

Similar a Tide - SmalltalkでSPA

データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
Naoyuki Yamada
 
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
Jumpei Ogawa
 
MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析
MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析
MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析
Takahiro Inoue
 
20110714 j queryベーシック
20110714 j queryベーシック20110714 j queryベーシック
20110714 j queryベーシック
良太 増子
 
node+socket.io+enchant.jsでチャットゲーを作る
node+socket.io+enchant.jsでチャットゲーを作るnode+socket.io+enchant.jsでチャットゲーを作る
node+socket.io+enchant.jsでチャットゲーを作る
Kiyoshi SATOH
 
Jqm20120210
Jqm20120210Jqm20120210
Jqm20120210
cmtomoda
 
More Better Nested Set
More Better Nested SetMore Better Nested Set
More Better Nested Set
xibbar
 
Silverlight Line-Of-Business Applications
Silverlight Line-Of-Business ApplicationsSilverlight Line-Of-Business Applications
Silverlight Line-Of-Business Applications
Yuya Yamaki
 

Similar a Tide - SmalltalkでSPA (20)

データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
 
レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】
 
20130924 Picomon CRH勉強会
20130924 Picomon CRH勉強会20130924 Picomon CRH勉強会
20130924 Picomon CRH勉強会
 
scala+liftで遊ぼう
scala+liftで遊ぼうscala+liftで遊ぼう
scala+liftで遊ぼう
 
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
QML を用いた YouTube クライアントの作成 - 関東 Qt 勉強会
 
「html5 boilerplate」から考える、これからのマークアップ
「html5 boilerplate」から考える、これからのマークアップ「html5 boilerplate」から考える、これからのマークアップ
「html5 boilerplate」から考える、これからのマークアップ
 
VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発
 
MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析
MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析
MongoDBとAjaxで作る解析フロントエンド&GraphDBを用いたソーシャルデータ解析
 
20110714 j queryベーシック
20110714 j queryベーシック20110714 j queryベーシック
20110714 j queryベーシック
 
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
Next2Dで始めるゲーム開発  - Game Development Starting with Next2DNext2Dで始めるゲーム開発  - Game Development Starting with Next2D
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
 
ScaLa+Liftとか
ScaLa+LiftとかScaLa+Liftとか
ScaLa+Liftとか
 
Chrome DevTools.next
Chrome DevTools.nextChrome DevTools.next
Chrome DevTools.next
 
Parse.comと始めるBackbone.js入門(jscafe7)
Parse.comと始めるBackbone.js入門(jscafe7)Parse.comと始めるBackbone.js入門(jscafe7)
Parse.comと始めるBackbone.js入門(jscafe7)
 
node+socket.io+enchant.jsでチャットゲーを作る
node+socket.io+enchant.jsでチャットゲーを作るnode+socket.io+enchant.jsでチャットゲーを作る
node+socket.io+enchant.jsでチャットゲーを作る
 
CodeIgniterによるPhwittr
CodeIgniterによるPhwittrCodeIgniterによるPhwittr
CodeIgniterによるPhwittr
 
Jqm20120210
Jqm20120210Jqm20120210
Jqm20120210
 
More Better Nested Set
More Better Nested SetMore Better Nested Set
More Better Nested Set
 
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
DEV-010_エンプラ系業務 Web アプリ開発に効く! 実践的 SPA 型モダン Web アプリ開発の選択手法
 
Visualforce + jQuery
Visualforce + jQueryVisualforce + jQuery
Visualforce + jQuery
 
Silverlight Line-Of-Business Applications
Silverlight Line-Of-Business ApplicationsSilverlight Line-Of-Business Applications
Silverlight Line-Of-Business Applications
 

Más de Masashi Umezawa

Más de Masashi Umezawa (20)

第142回Smalltalk勉強会 - PharoJSで作るWebアプリケーション
第142回Smalltalk勉強会 - PharoJSで作るWebアプリケーション第142回Smalltalk勉強会 - PharoJSで作るWebアプリケーション
第142回Smalltalk勉強会 - PharoJSで作るWebアプリケーション
 
FileManで楽々ファイル操作
FileManで楽々ファイル操作FileManで楽々ファイル操作
FileManで楽々ファイル操作
 
TruffleSqueakの紹介
TruffleSqueakの紹介TruffleSqueakの紹介
TruffleSqueakの紹介
 
TaskItの紹介
TaskItの紹介TaskItの紹介
TaskItの紹介
 
Smalltalk勉強会 - 過去、現在、そして未来へ のその後
Smalltalk勉強会 - 過去、現在、そして未来へ のその後Smalltalk勉強会 - 過去、現在、そして未来へ のその後
Smalltalk勉強会 - 過去、現在、そして未来へ のその後
 
Revealing ALLSTOCKER
Revealing ALLSTOCKERRevealing ALLSTOCKER
Revealing ALLSTOCKER
 
TarandocでJSONを永続化
TarandocでJSONを永続化TarandocでJSONを永続化
TarandocでJSONを永続化
 
Dockerizing pharo
Dockerizing pharoDockerizing pharo
Dockerizing pharo
 
今からでも遅くないSmalltalk入門
今からでも遅くないSmalltalk入門今からでも遅くないSmalltalk入門
今からでも遅くないSmalltalk入門
 
Tarantubeでメッセージキューを使い倒す
Tarantubeでメッセージキューを使い倒すTarantubeでメッセージキューを使い倒す
Tarantubeでメッセージキューを使い倒す
 
VerStixの紹介
VerStixの紹介VerStixの紹介
VerStixの紹介
 
Oldtalk - あのころの処理系は今
Oldtalk - あのころの処理系は今Oldtalk - あのころの処理系は今
Oldtalk - あのころの処理系は今
 
Pyonkeeを鳴らす
Pyonkeeを鳴らすPyonkeeを鳴らす
Pyonkeeを鳴らす
 
Smalltalk勉強会 - 過去、現在、そして未来へ
Smalltalk勉強会 - 過去、現在、そして未来へSmalltalk勉強会 - 過去、現在、そして未来へ
Smalltalk勉強会 - 過去、現在、そして未来へ
 
Tarantalk
TarantalkTarantalk
Tarantalk
 
Smalltalkと型について
Smalltalkと型についてSmalltalkと型について
Smalltalkと型について
 
Introduction of Pharo 5.0
Introduction of Pharo 5.0Introduction of Pharo 5.0
Introduction of Pharo 5.0
 
Why!? Smalltalk
Why!? SmalltalkWhy!? Smalltalk
Why!? Smalltalk
 
Pillarの紹介
Pillarの紹介Pillarの紹介
Pillarの紹介
 
Scrumの紹介とXPプロジェクトへの適用(Scrum and XP)
Scrumの紹介とXPプロジェクトへの適用(Scrum and XP)Scrumの紹介とXPプロジェクトへの適用(Scrum and XP)
Scrumの紹介とXPプロジェクトへの適用(Scrum and XP)
 

Tide - SmalltalkでSPA

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