Stippleの紹介
JuliaのWebフレームワークにGenieがあります。Juliaの機能を活かしており、とても使い心地の良いパッケージなのですが、英語/日本語問わずまだまだサンプルが少ない状態です。そんな中でGenie.jlの関連情報をあさっていたらJuliaCon 2020でGenieの作者のAdrian SalceanuさんがStippleというDashboardsの作成にも使えそうなWeb UI用パッケージを紹介されていました。Webサーバー越しにデータバインディングを実現している面白いパッケージなので、簡単に紹介したいと思います。
インストール
GenieはWebサーバー用のフレームワークで、その上にStippleというパッケージがあります。この二つをインストールします。
pkg> add Genie
pkg> add Stipple
実行
Julia REPLで次のコードを実行します。
using Genie, Genie.Router, Genie.Renderer.Html, Stipple
# define (reactive) model
Base.@kwdef mutable struct Name <: ReactiveModel
name::R{String} = "World!"
end
# initialize the model
model = Stipple.init(Name)
# define UI
function ui()
page(
root(model), class="container", [
h1([
"Hello "
span("", @text(:name); style="color:red")
])
p([
"What is your name? "
input("", placeholder="Type your name", @bind(:name))
])
], title="Basic Stipple"
) |> html
end
# bind root "/" to ui
route("/", ui)
# start web server
up()
up()
を実行するとWebサーバーが起動します。環境にもよりますが、例えば下記のように表示されるので、ブラウザからアクセスしてみます。
Web Sockets server running at 127.0.0.1:8001
Web Server starting at http://127.0.0.1:8000
Web Server running at http://127.0.0.1:8000
Genie.AppServer.ServersCollection(Task (runnable) @0x0000000015eae850, Task (runnable) @0x00000000356d2e10)
この時、Julia REPLに下記のようなエラーが出ることがありますが、たぶんfavicon.icoがないために出ているだけなので無視してokです。
[ Info: / 200
[ Info: / 200
┌ Error: error handling request
│ exception =
│ IOError: stream is closed or unusable
│ Stacktrace:
│ [1] check_open at .\stream.jl:328 [inlined]
...
│ [12] (::HTTP.Servers.var"#13#14"{HTTP.Handlers.var"#4#5"{HTTP.Handlers.RequestHandlerFunction{Genie.AppServer.var"#6#12"}},HTTP.ConnectionPool.Transaction{Sockets.TCPSocket},HTTP.Streams.Stream{HTTP.Messages.Request,HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}})() at .\task.jl:356
└ @ HTTP.Servers C:\Users\dskkato\.julia\packages\HTTP\atT5q\src\Servers.jl:373
[ Info: /css/stipple/stipplecore.min.css 200
[ Info: /js/stipple/vue_filters.js 200
[ Info: /js/stipple/stipplecore.js 200
[ Info: /js/stipple/vue.js 200
[ Info: /js/stipple/underscore-min.js 200
[ Info: /__/channels.js?v=1.1.0 200
動かしてみる
StippleのUI部分にはVueが使われているようで、コンポーネント間のデータを同期してくれます。また、Julia-REPLと双方向のデータバインディングを提供実現しています。一つずつ試して行きましょう。
UIのコンポーネント間の同期
まずは先ほどの画面のテキストボックスにあるWorld!
を適当にJulia!
に書き換えて見ます。すると、赤い文字の部分も入力に追従して自動で変化していきます。VueやReact等を触ったことある方ならなじみのある操作かと思います。
Juliaとの双方向データバインディング
UI -> Julia
Stippleの目玉はこれかと思います。先ほどJulia!
に書き換えた状態でmodelの状態を確認してみます。ちゃんとJulia!
に更新されていますね。
julia> model
Name(Observable{String} with 1 listeners. Value:
"Julia!")
Julia -> UI
次にJulia側から操作してみます。この時、オリジナルのStringオブジェクトを書き換えたことが分かるように[]
で参照を取ってきて更新する必要がある見たいです。
julia> model.name[] = "Julia-REPL"
下記のように書くと、Stippleがオブジェクトの更新をトラッキングできなくなるようで、うまく動かなくなるので注意が必要です。
julia> model.name = "Julia-REPL"
すぐにUIにも反映されます。
カウントダウン
最後に、簡単なカウントダウンを動かしてみます。
for i in reverse(0:10)
model.name[] = "$i"
sleep(1)
end
(画面はそのうちupします...)
Stippleのポテンシャル
JuliaCon 2020での発表資料やDiscorseを見ているとダッシュボードを作れる程度には開発が進んでいるようです。多少HTMLのことを知らないと使いにくいかもしれませんが、簡単なGUIにも使えそうな印象を受けました。
Julia REPL->UIのフローはブロードキャストされているようで、マルチクライアントの状態でもすべての状態が更新されます。惜しむらくは、UI上での操作が他のクライアントに反映されませんでした。REPLからは出来ているので、大した問題ではないかもしれませんが。