はじめに
RubyのWebAssemblyビルドを使ってSPA(single page application)を構築するためのパッケージ"Bormaŝino"を開発し、TodoMVCを移植してみました。
本記事ではパッケージの紹介及び移植されたコードを解説します。
デモ
- TodoMVC 移植版 https://bormashino-todomvc.vercel.app/
- テンプレートアプリケーション https://bormashino-app-template.vercel.app/
パッケージ"Bormaŝino"について
コンセプト
伝統的な「フォームを表示し」「POSTされる内容でデータストアを更新し」「データストアに保持されている内容を元に画面を表示する」だけのアプリケーションを通信時間が0に近い状態で走らせれば、いわゆるSPA的な操作感が得られるのでは?
という考えに基づき、Sinatra1を用いて構築された伝統的なWebアプリケーションをブラウザ内で動作させる環境を提供するパッケージが"Bormaŝino"です。
この構成により以下の項目を実現しました。
- アプリケーションの実装は伝統的なWebアプリケーションのそれと似通っており、学習負荷が低い
- 既存のrubygems等の資産を流用できる
現状出来ること
- いわゆるSPA的な画面遷移
- LocalStorageの読み書き
- ユーザ入力の取扱い
構成
次の2つのパッケージから構成されています。
また、アプリケーションのテンプレートを用意してあります。
https://github.com/keyasuda/bormashino-app-template
依存関係
主に以下のプロダクトに依存しています。
TodoMVC移植版のコードについて
ファイル構成
.
├── js
│ ├── app.js ページ読み込み後にruby.wasmを読み込み起動するブートストラップコード
│ └── index.html アプリケーションの外枠
├── spec
│ └── todo_spec.rb Todoクラスのユニットテスト
└── src
├── app.rb アプリケーションの中心部。Sinatra::Baseを継承したクラス
├── bootstrap.rb ruby.wasm起動後のブートストラップコード
├── todo.rb Todoクラス。LocalStorageへの読み書きを(Bormashino::LocalStorage経由で)行う
└── views
├── index.erb Todo一覧を内包する画面
└── todo.erb Todo一件を表示するERB断片
アプリケーションの実質的な実装はこれらのファイルに記述されています。
リンクの扱い/ルーティング
<li>
<a href="/" <% if request.path_info == '/' %>class="selected"<% end %>>All</a>
</li>
<li>
<a href="/active" <% if request.path_info == '/active' %>class="selected"<% end %>>Active</a>
</li>
<li>
<a href="/completed" <% if request.path_info == '/completed' %>class="selected"<% end %>>Completed</a>
</li>
ドメイン名を含まないリンクは画面更新時に自動でonclickイベントハンドラが設定され、クリック時にアプリケーションへのGETリクエストが行われます。
<p>Created by <a href="https://github.com/keyasuda">@keyasuda</a></p>
<p>An <a href="https://github.com/keyasuda/bormashino">Bormaŝino</a> port of <a href="http://todomvc.com">TodoMVC</a></p>
他ドメイン名へのリンクにはイベントハンドラは設定されず、通常のリンクとして機能します。
フォームの扱い
<form action="/todos/all" method="PUT">
formは拡張されており、methodに標準のget,postの他put,patch及びdeleteを指定可能です。
リンクと同様に自動でイベントハンドラが設定され、submitでアプリケーションへリクエストが行われます。
フォーム中の子要素に対する操作
<input
class="toggle"
data-bormashino-submit-on="change"
type="checkbox"
name="completed"
<% if todo.completed %>checked<% end %>>
フォーム中にdata-bormashino-submit-on
という属性を持った要素がある場合、その要素で指定されているイベント(この例ではonclick)が対象の要素で発生した場合に親となるフォームがsubmitされる仕組みを組み込んであります。これを利用してチェックボックスのクリックでTodoのactive/completed状態を更新しています。
ローカルストレージの利用
window.localStorageのラッパーとしてBormashino::LocalStorage
を用意してあります。Todoの内容はTodoMVCの仕様通りにtodos-bormashino
キー以下にJSONとして格納されます。
JavaScriptとの統合
window.bormashino = RubyApplication
window.bormashinoにアプリケーション操作用のオブジェクトがセットされています。
https://github.com/keyasuda/bormashino-todomvc/blob/main/src/views/todo.erb#L11
<label ondblclick='bormashino.router.pushState(<%= "#{request.path_info}?edit=#{todo.id}".to_json %>)'><%= todo.title %></label>
本TodoMVCデモ実装ではTodoのダブルクリックで編集モードに入れるためにondblclickハンドラが設定されています。
おわりに
パッケージ"Bormaŝino"を用いてRubyでTodoMVCと同様のSPAが構築できました。今後、以下のような追加機能開発を予定しています。
- Fetch APIのラッパー実装
- JavaScriptとの統合機能強化
- 画面更新時に発生するイベントの追加
Railsのように気の利いたコードジェネレータはありませんが、テンプレートを用意してありますのでどうぞお試し下さい。
2022-05-15追記: 続きを書きました。
-
に限らずRack protocol準拠のWebアプリケーションフレームワークであれば動く可能性はありますが未検証です ↩