maskat + Rails

NTTデータが公開したAjaxビジュアル開発ツール Maskat を使って、Railsと組み合わせてみた。

が、いまいちぱっとしない。

Maskatの仕組み

Maskatにおいては1画面は3つのファイルから構成される。

  • 入れ物であるHTML
  • HTMLに挿入されるAjaxコントロールの配置定義ファイル(レイアウト定義; XML)
  • コントロールに結びつける動作定義ファイル(イベント定義; XML)

HTMLはまず、フレームワークを構成するJavaScriptを大量に読み込む。次に、その外部JavaScriptで定義されている関数を使ってレイアウト定義を読み込む。読み込み時にDOMノードを渡して、レイアウト定義に書かれているコントロール群はそのノードの子孫として配置される。

それから、同様にしてイベント定義を読み込む。イベント定義に基づいて、指定したコントロールにローカルの関数かもしくはXMLHTTPRequest呼び出しが紐付けられる。

サイトに書いてある作成例を見ても、帳票みたいなアプリケーションへの適用を考えてるんだろうな。そのせいか、画面をあまり激しく書き換えるような処理はサポートされていない模様。ただ、タブで切替えたりするのを書くのは楽だ。

ヴィジュアル編集ツール

画面のメイン部分を別ファイルに定義するっていうやりかたは分業を考えれば良いやりかただ。レイアウト定義やイベント定義はEclipseプラグインで作成できる。レイアウト定義だけならブラウザから作成できるide.htmlというのも提供されてる。

ただなー。このide.htmlの存在異義はどこにあるんだろう。配置したコントロールをドラッグして位置を変えられると便利なんだけどそういう機能はないみたい。Eclipseプラグインの方ではそれができるんだけどね。おそらくはデザイナさんにEclipse入れろとは言えない状況もあるということでide.htmlがあるんだろうけれど、だったらもう少し機能をリッチにしてほしい。

Railsとの組み合わせ

基本的には、アーカイブを展開した中のmaskatFWディレクトリをRailsアプリケーションのpublicの下に入れれば良さそう。

Layout

Maskatの場合、HTMLは本当に器だけを提供して、ユーザーが主に操作するコントロールはレイアウト定義ファイルを使うのが普通らしい(?) だとすると、器HTMLはRailsではLayoutとして定義するのが良さそう。

app/views/layouts/calc.rhtml:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title><%=h @title || 'Calc' %></title>
<script type="text/javascript" src="/maskatFW/core/maskatConfig.js"></script>
<script type="text/javascript">
rialtoConfig.pathRialtoE = "/maskatFW/rialtoEngine/";
</script>
<link rel='stylesheet' type='text/css' href='/maskatFW/rialtoEngine/style/rialto.css'/>
<script type='text/javascript' src='/maskatFW/rialtoEngine/javascript/rialto.js'></script>
<script type="text/javascript" src="/maskatFW/core/crossBrowser.js"></script>
<script type="text/javascript" src="/maskatFW/core/dynamicLoad.js"></script>
<script type="text/javascript" src="/maskatFW/core/intpr.js"></script>
<script type="text/javascript" src="/maskatFW/core/layoutXMLEx.js"></script>
<script type="text/javascript" src="/maskatFW/core/layoutXMLInterpreter.js"></script>
<script type="text/javascript" src="/maskatFW/core/objWrapper.js"></script>
<script type="text/javascript" src="/maskatFW/core/soap.js"></script>
<script type="text/javascript" src="/maskatFW/core/validator.js"></script>
<script type="text/javascript">
function pageLoad(){
  maskat.loadLayoutFile('/<%=params[:action]%>.xml',  'document.getElementById("divConteiner")');
  maskat.loadEventFile('/<%=params[:action]%>_e.xml',  
}
</script>
</head>
<body onload="pageLoad();" onselectstart="return true">
 <%= @content_for_layout %>
</body>
</html>

SOAP

Maskatが生成するSOAPリクエストはActionWebServiceが理解してくれなかった。

イベント定義のheader要素でSoapActionヘッダを追加すればSOAPとして解析をするところまでは始めてくれるんだけど、その後Envelopeの下にBodyを探すのに失敗してる。

./script/../config/../vendor/rails/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb:59:
`undefined method `body' for)
        from ./script/../config/../vendor/rails/railties/lib/dispatcher.rb:41:in `dispatch'
        from ./script/../config/../vendor/rails/railties/lib/webrick_server.rb:110:in `handle_dispatch'
        from ./script/../config/../vendor/rails/railties/lib/webrick_server.rb:76:in `service'
        from /usr/lib/ruby/1.8/webrick/httpserver.rb:104:in `service'
        from /usr/lib/ruby/1.8/webrick/httpserver.rb:65:in `run'
        from /usr/lib/ruby/1.8/webrick/server.rb:173:in `start_thread'
        from /usr/lib/ruby/1.8/webrick/server.rb:162:in `start'
        from /usr/lib/ruby/1.8/webrick/server.rb:162:in `start_thread'
        from /usr/lib/ruby/1.8/webrick/server.rb:95:in `start'
        from /usr/lib/ruby/1.8/webrick/server.rb:92:in `each'
        from /usr/lib/ruby/1.8/webrick/server.rb:92:in `start'
        from /usr/lib/ruby/1.8/webrick/server.rb:23:in `start'
/usr/lib/ruby/1.8/webrick/httpservlet/filehandler.rb:250: ``/calc/api' not found.' (WEBrick::HTTPStatus::NotFound)
       from /usr/lib/ruby/1.8/webrick/server.rb:82:in `start'
       from /usr/lib/ruby/1.8/webrick/server.rb:82:in `start'
       from ./script/../config/../vendor/rails/railties/lib/webrick_server.rb:63:in `dispatch'
       from ./script/../config/../vendor/rails/railties/lib/webrick_server.rb:63:in `dispatch'
       from ./script/../config/../vendor/rails/railties/lib/commands/servers/webrick.rb:59
       from ./script/../config/../vendor/rails/railties/lib/commands/servers/webrick.rb:59
       from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
       from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'

c from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'

from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from ./script/../config/../vendor/rails/activesupport/lib/active_support/dependencies.rb:364:in `require'
from ./script/../config/../vendor/rails/activesupport/lib/active_support/dependencies.rb:364:in `require'
from ./script/../config/../vendor/rails/railties/lib/commands/server.rb:39
from ./script/../config/../vendor/rails/railties/lib/commands/server.rb:39
from script/server:3:in `require'
from script/server:3:in `require'
from script/server:3
from script/server:3

SOAPリクエス

SOAPリクエストにおいては、なぜかContent-Typeに"application/www-form-urlencoded"を指定して送信してる。送信してる中身はXMLなんだが。メーリングリストに質問投げといた。

*** maskatFW/core/intpr.js.orig 2006-09-24 00:47:59.000000000 +0900
--- maskatFW/core/intpr.js      2006-09-24 00:47:34.000000000 +0900
***************
*** 890,896 ****
         xhr.setRequestHeader("Content-Type", "application/soap+xml");
      }
      else {
!        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      }
      if (IDs.component){
        xhr.setRequestHeader("maskat_componentID", IDs.component);
--- 890,896 ----
         xhr.setRequestHeader("Content-Type", "application/soap+xml");
      }
      else {
!        xhr.setRequestHeader("Content-Type", "text/xml");
      }
      if (IDs.component){
        xhr.setRequestHeader("maskat_componentID", IDs.component); 

これだけ直せば、Rails EdgeではContent-Typeを元に、XMLとしてparseしてうまいことparamsに入れてくれるから、後はアクションで適当に処理すれば良い。

感想

なんかぱっとしないなー。というか、面白くない。

  • メーリングリストに今まで一通も流れてないってどうよ?

  • 私が上のパッチを投げたのが第一通目なんだけど

  • レポジトリを公開してないのってどうよ? いちいちアーカイブ落としてきて展開するの面倒なんだけど。

  • プロジェクト管理体制だけ 妙にかっちり決まってる んだが。
  • レイアウト定義にはXULみたいなありものを使っても良い気がする。まぁ、いいけど。
  • link_to_remote :updateみたいなのをヴィジュアルエディタで定義できるのは、まぁ、いいことなのかもしれん。
  • Ajax開発者向けにしては、用語集が必要以上に丁寧なんだが。
  • 開発者向けメーリングリストを非公開にする理由がよくわからないんだが。
  • SOAPメッセージの作成が、非SOAP版のメッセージをEnvelopeでくるむだけという激しくやる気の無い実装なんだけど。

これをオープンソース化するっていうのは面白い試みだし、公開から1週間の現段階で不完全な部分があるのはしかたがないのだけど、なんか、いまひとつ何かが欠けている気がする。

Railsと組み合わせようとするとき、ActionWebServiceAPI定義から上手いことイベント定義を自動生成できるとDRYで嬉しいんだけど、SOAPRailsがメッセージを認識してくれないしXMLRPCはMaskat側が対応してないし、ちょっと今日1日では終わらなかった。というか、なんか、これ以上関わる気力が失せてきた。

なんだかなー。XMLの設計がダサいのが良くないのかな。全体的にオープンさに欠けるのが魅力を削いでるのかな。NTTデータの中の人が優しい独裁者をやるのは別に構わんのだけど、オープンに運営してくつもりが無いならプロプライエタリに、Freesoftwareじゃなくて"free beerのfree"にしといた方が得だと思う。

まぁ、難はありつつも、でもとにかく叩き台として動くものがそこにあるというのは重要なこと。Railsのscaffoldの価値もそこにあると思うし。気が向いたらMaskat側にXML-RPCサポートと、Rails側にHTML部分作成用のヘルパーを書くかな。