ユーザーがウェブページで操作をしたときの処理は、以下が典型的なものでしょう。
例えば、ユーザーが「新しい todo アイテムを todo リストに追加」するためにクリックしたときの処理は、「todo アイテムを表すオブジェクトを作成し、そのアイテムをリストの DOM に追加する」といったものになると思います。その際、オブジェクトと DOM の状態が確実に同期しておくようにするためには、多くの手間のかかる処理が必要です。
「リアクティブプログラミング」という手法は、DOM の管理をシンプルにするために使われます。この手法では、モデルとDOM を管理するイベントハンドラを持つ代わりに、リアクティブなデータモデルを管理するイベントハンドラを持ちます。このようなプログラミングでは、DOM のレイヤーを宣言型で記述することができます。そして、データモデルの内容をどのようにレンダリングして最新の状態に保つかは自動的に設定されます。
Web アプリケーションの中心的な仕事は状態を管理することです。多くのイベントが状態を変更します。例えば、フォーム要素へテキストを入力する、ボタンをクリックする、リンク、スクロール...などの操作はすべてアプリケーションの状態を変更するものです。かつては、ページに対するイベントは、ページが持っている状態をそれぞれ手動で変更していました。
Volt では、アプリケーションの状態管理をシンプルにするため、すべてのアプリケーションの状態を永続化されるモデルの中に保存します。保存のしかたはオプションで様々なものを指定可能です。 このようにアプリケーションの状態を一元管理することで、ページを更新するために本来必要となるはずの複雑なコードを、かなりの分量削減することができます。また、このことでページの HTML を宣言型で組み立てることができます。ページのモデルへの結びつきは、関数とメソッド呼び出しによってバインドされます。
モデルのデータが変更されたときには、それに応じて自動的に DOM を更新したいと考えることでしょう。それを可能にするために、Volt はメソッドや Proc を「監視 (watch)」し更新を行うことができます。
Volt を利用するにあたって、評価 (Computation) や依存関係 (Dependency) を直接扱う必要はほとんどありません。その代わりに、通常はモデルやバインディングに対して操作を行います。評価 (Computation) は見えないところで動いているものなので、それがどのような仕組みで動いているかについて完全に理解することは役に立つことですが、必ずしも必須ではありません。
では、評価について実例を見てみましょう。ここでは、page
コレクションを例とします。(後でより多くのコレクションを紹介します)
はじめに、評価のための監視設定を行います。計算 (Computation) は、Proc オブジェクトに対して .watch!
を実行ことで設定されます。ここでは、Ruby 1.9 の Proc の短縮シンタックス -> { .. }
を使います。これを一度実行すると、以後 page._name
が変更されたときに毎回実行されます。
page._name = 'Ryan'
-> { puts page._name }.watch!
# => Ryan
page._name = 'Jimmy'
# => Jimmy
page._name
に新しい値が代入されると再評価が実行されます。また、前回の実行でアクセスされたデータのいずれかに変更があったときにも再評価されます。これによって、メソッドを介してデータにアクセスしながらデータの監視を続けることができます。
page._first = 'Ryan'
page._last = 'Stout'
def lookup_name
return "#{page._first} #{page._last}"
end
-> do
puts lookup_name
end.watch!
# => Ryan Stout
page._first = 'Jimmy'
# => Jimmy Stout
page._last = 'Jones'
# => Jimmy Jones
.watch!
を実行すると、その戻り値として返ってくるのは Volt::Computation
(評価) オブジェクトです。もうこれ以上更新を知らせる必要がない場合には、Computation オブジェクトに対して .stop
メソッドを実行してください。
page._name = 'Ryan'
comp = -> { puts page._name }.watch!
# => Ryan
page._name = 'Jimmy'
# => Jimmy
comp.stop
page._name = 'Jo'
# (nothing)
前述のように、依存関係を直接扱うことはほとんどありませんが、理解しておくことは損にはならないでしょう。ここから概要を見ていきます。
TODO: 依存関係についての説明を書く