Opal、Vue.js、Sinatra

気になっていたOpalを試してみるのに、どうせならとVue.jsを組み合わせよう! と気軽に始めてみたら、思いのほかのめり込んでしまったという話。(はまった、とも)

OpalはRubyで書いたコードをJavaScriptで実行するためのフレームワーク。すごそうだなとは思っていたのだけど、実際にそれなりのコードを書いてみたところ、すごかった。

他のJavaScriptフレームワーク(今回ならVue.js)と組み合わせるとなると気をつかうところが出てくるものの、単体で考えるとこれもうRubyなんじゃってくらいRubyらしく書けてしまう。がんばってるなあ。

で、実際に書いてみたコードなんだけど、こんなの感じのが動きます。

class Calc < Vue
  OP = { add: :+, sub: :-, mul: :*, div: :/, mod: :% }

  data waiting: -> { [] }
  data calculator: -> { OP.keys.to_n }

  def new_card
    waiting << { num1: nil, num2: nil, dragging: false }.to_n
  end
end

class CalcCard < VueComponent
  include DragAndDropHelper

  data :num1, :num2, dragging: false
  template '#card-template'

  def drag_start(ev)
    self.dragging = true
    set_dnd_data(ev, to_data);
  end

  def drag_end(ev)
    self.dragging = false
  end
end

class CalcPlace < VueComponent
  include DragAndDropHelper

  props :op
  data :num1, :num2
  template '#place-template'

  def drag_over(ev)
    set_drop_effect(ev, :copy)
  end

  def drop(ev)
    data = get_dnd_data(ev)
    apply_data(data)
  end

  computed

  def op_sym
    Calc::OP[op].to_n
  end

  def result
    sym = op_sym
    return unless sym
    return unless num1.is_a?(Numeric) && num2.is_a?(Numeric)

    num1.public_send(sym, num2)
  end
end

Document.ready? do
  CalcCard.activate!
  CalcPlace.activate!
  Calc.new('#calc') unless Element.find('#calc').empty?
end

(opal-vue-trialから一部抜粋)

Rubyに慣れている人なら、Rubyの知識はわりとそのまま使えちゃう。問題はRubyとJavaScriptの間の値の相互変換。

いや、Opalの実行環境はJavaScriptだから、どちらもJavaScriptなのだけど、OpalではRubyのためのクラス(というかオブジェクト)を用意しているので、それらの間で変換しなくてはらならない場合が出てくる。特に他のフレームワークといっしょに使うとき。

結局のところ、どういうときにどちらの値で持てばよいかの見極めなのだろうと思う。これは実際に動くものを書いてみるとだんだん分かってくるのだが、そのためには試行錯誤が必要となる。まあ、それもなかなか楽しい。久しぶりにがっつり時間をつっこんだ。

なお、フレームワークではなくてライブラリならそれほど苦労せずに使えと思う。また、jQueryなど有名なライブラリについてはバインディングがある。