ダッシュボードも作れるStipple.jlの紹介


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からは出来ているので、大した問題ではないかもしれませんが。

Demos (リポジトリから転載)

関連情報