« 2007年01月 | メイン | 2008年05月 »

2008年04月 アーカイブ

2008年04月15日

Model-Glueのイベントとメッセージ

ColdFusionに、フレームワークが必要なのかどうかわからないけど、とりあえず、読んでみよう。
Model-Glue、ColdSpring、Transferの組み合わせで、何が便利なのか、確認。
Reactorではなく、Transferなのは、APIドキュメントに、“Transaction”があって、「トランザクションの記述が簡単なのかな?」と思っただけのこと。
実際には、その辺りについては、あんまり変わらないみたいな気がしてきている。
Transferの方が、いろいろと、ごてごて機能満載な感じがしている。

さて、フレームワークというものは、ちょっと動くものを作ってみようということが、なかなか難しい。
いろいろと、やらなきゃいけないことが多くて、「フォームでポストリクエストを受け取って、画面に表示」ということだけでも、めんどくさいだけにしか感じない。
こういったことは、ColdFusionの手軽で、軽快な作業性とは、大きく反している。
フレームワークの利点ってのは、巨大なプロジェクトで、大人数が作業する際に、真価が発揮されると思っているんだけど、そういったあたりも、確認できればいいな。

Model-Glueの設定ファイル“ModelGlue.xml”を見ただけでも、ちょっと引いてしまったのは、内緒。
ここで重要なのは、イベントと、メッセージだろうか。
イベントとは、文字通り、リクエストなどのイベント。URLの後ろに“event=<イベント名>”と付けたりする。
そうでなくても、構造上は、イベントは、“ModelGlue.xml”で任意に発生させることができる。
で、メッセージは、コントローラーへ処理を渡す際に発生させるという感じだろうか。
イベントは、そのメッセージを発生させたり、画面遷移させたり、追加でイベントを発生させたりできる。
メッセージは、単に、“function”を実行させるだけという認識でいいのではないだろうか。

もちろん、この2つ以外にも、Model-Glueには、色々な概念があるけど、一つ一つ、確認していかないと、混乱してしまうから、まずは、ここまで。

2008年04月16日

イベントとメッセージ その2

では、Model-Glue において、イベントやメッセージが発生した場合に何が引き起こされるのだろうか。

メッセージは、コントローラーが引き受けるもので、その挙動は、ModeGlue.xml の
 modelglue
  controllers
   controller
    message-listener
に定義されている。
メッセージは、メッセージリスナーが受け取ることになる。
メッセージリスナーがメッセージを受け取ると、messege-listener の messege 属性に一致するメッセージを探して、
その function 属性に設定されている関数が実行される。
関数は、modelglue => controllers => controller の type 属性に指定してあるコントローラー内に定義されている。

イベントが発生した場合の振る舞いは、ModelGlue.xml の
 modelglue
  event-handlers
   event-handler
に定義されている。

イベントが発生すると、event-handler の name 属性の中から一致しているものを探す。
そこで定義されている、broadcasts、results、views が処理される。
broadcasts は、message 子要素を持ち、メッセージを発生させる。
results は、result 子要素を持ち、追加的にイベントを発生させる。
result 要素は、name 属性を設定することができるが、設定されていない場合は、常に、do 属性のイベントが発生し、
設定されている場合は、コントローラー内で addResult() 関数によって登録された name が存在する場合に、イベントが発生する。
views は、include 子要素を持ち、文字通り、ユーザーが受け取る画面を構成する情報を指定する。
include は、画面のどこ(name 属性)に、どのファイル(template 属性)による情報を表示するかを指定する。
また、include は、value 子要素を持ち、value 要素は、画面を構成する際に必要となる引数を設定する。

これらの振る舞いがどのような順番で処理されるかははっきりわからないが、ロジックを処理するために
コントローラーへメッセージを渡し(broadcasts)、画面のベースとなるテンプレート(html、head、bodyタグで構成される)を
書き起こし(results)、各画面ごとに異なる情報や、ロジックになどにより処理された情報などを表示(views)するという
流れで、サンプルが作られているようだ。

2008年04月17日

イベントの続きと、viewstate

イベントは、どうやって発生させるのだろうか。
方法は、二通りあるようだ。
一つめは、以前にも書いたように、event-handler の results => result 要素で発生させる。
二つめは、さらに、以前に書いたように、「URLの後ろに“event=<イベント名>”」として、リクエストによって、発生させる。
このイベント名がそのまま、発生するイベントとなっている。

この“event=<イベント名>”を省略した場合は、どうなるのか。
http://~~~/~~~/index.cfm とした場合である。
どうやら、ColdSpring.xml で定義されている、

 <beans>
  <bean id="modelGlueConfiguration" class="ModelGlue.unity.framework.ModelGlueConfiguration">
   <property name="defaultEvent"><value>page.index</value></property>
  </bean>
 </beans>

page.index が発生するらしい。
確かに、“イベント指定なし”と“event=page.index”では、同じ挙動になる。

他にも、exception
とあるように、エラーが発生すると、exception というイベントが発生するようだ。

この部分を見つけるまで、ColdSpring.xml の modelGlueConfiguration は、あまり注意してみていなかったけど、どうやら、
結構重要らしい。

サンプルなどを見ると、このイベントをリンクなどのURLに指定する際に、画面表示プログラム埋め込み文字列ではなく、
views => include => value で引数として画面表示プログラムへ渡している。
果たして、これにどれほどの意味があるのかわからないけど、引数の渡し方は、ここから、わかる。

この“xe.cartinsertitem”が引数の変数名で、“CartInsert”がその値となる。
“xe”という単語が気になるが、“eXit Event”を示しているらしい。
ただし、この“xe”という単語が機能上特別な意味を持っているのかは、わからなかった。

では、画面側で、それらの値を受け取る場合は、どうなるのか。
viewstate.getValue("xe.cartinsertitem")
とすることで、値を受け取れる。
viewstate というのは、コントローラーなど、ロジック上から、値を渡す場合などにも、この空間?に設定されるものである。

また、myself や、self という予約後もあり、viewstate.getValue("myself") とすると、
リクエストページが index.cfm の場合「index.cfm?event=」という値を得ることができる。

2008年04月18日

viewstate と viewcollection

前回、viewstate というものの読み解いた。似たような単語で、viewcollection というものがある。
前者が view の state 状態ということであれば、後者は、view の集合となるのだろうか。余計わかりにくいか。

画面表示プログラム上で、viewcollection.getView("body") としてやることで、画面表示を埋め込むことができる。
event-handler => views の要素、include で画面に埋め込むプログラムを設定する。
<include name="body" template="dspCart.cfm" />

viewcollection.getView("body") に、dspCart.cfm が埋め込まれるわけである。

viewcollection.getView("body")この記述は、event-handler の results => result 要素で生成した画面上でしか、
機能しなかったが、実際のところは、どうなのだろうか。
動いてくれると、便利なんだが。

コントローラーから画面に値を渡す場合は、どうなるのか。
viewstate で受け取れるようにする方法である。
<cfset arguments.event.setValue("cartData", cartData) />
こうすることで、cartDate という変数の cartDate という値を渡すことができる。
arguments.event というのは、重要な概念で、HTTP でいうところのイベントを意味している。
これは、リクエストも、レスポンスも含んでいるようである。

つまり、レスポンスで画面に値を渡すわけであるから、レスポンスというイベントに値を設定(setValue)するということである。

わかりにくいかもしれないが、ポストリクエストのポストデータを受け取るときも、このイベントを利用する。
<cfset var itemId = arguments.event.getValue("itemid") />
こうすることで、フォームの itemid という値を取得することができる。

このイベントは、コントローラー上でのみ、扱える空間のようだ。
もちろん、引数などで渡してやれば、どこでだって使えるのであるが、コントローラー上では、イベントを受け取ることを
書いてしまえば、受け取ることができる。
他にも、コントローラ上でのみ扱える ModelGule API とか、コントローラーは、いろいろ便利に使えるようである。

2008年04月21日

とりあえず、動くもの

サンプルアプリケーション
これまでの情報から、ものすごくシンプルなアプリケーションを作ってみた。
商品リストから商品を選んで、カートに入れるというよくあるカートアプリケーション。

作成したイベントは3つ。

 CartInsert
 CartDeleteItem
 CartDeleteAll

メッセージは、4つ。

 ItemList
 CartInsertAction
 CartDeleteItemAction
 CartDeleteAllAction

これらは、ModelGule.xml から見つけることができる。
Controller.cfc に、メッセージに対応する関数を一つずつで、4つの関数。
ロジックとして、CartProcess.cfc に、ロジックの関数として3つ。
データベースを使わなかったために、商品リストを保持する Common.cfc を作成。

やはり、これぐらいの規模の仕組みでは、フレームワークのメリットを感じることはできなかったが、Model-Glueの基本的な作り方は、把握できた。

dspIndex.cfm で商品リストを表示する際に、Controller.cfc の getItemList 関数で、変数 itemMasterArray に、
arguments.event.setValue("itemMasterArray", ★商品の一覧データ★)で、レスポンスデータに保存。
dspIndex.cfm の viewstate.getValue("itemMasterArray") で商品の一覧データを取得する。

商品を選択すると、イベント CartInsert で、商品IDをポストリクエスト送信すると、メッセージ CartInsertAction が発生する。
Controller.cfc の関数 cartInsertItem によって、ロジックのモデル CartProcess.cfc のインスタンスを生成し、
arguments.event.getValue("itemid") でポストデータ itemid を取得して、関数 insertItem に渡す。関数 insertItem からは、
カートのデータが戻ってくるので、arguments.event.setValue("cartData", ★カートのデータ★) で、画面に変数 cartData で渡す。

他のイベントの場合も、似たような感じになっている。
いわゆる、MVCが構築されているわけになるが、当然、理想は、理想で、実際にシステムを作っていく上では、
こんなにきれいに分かれないだろう。
ここでポイントになるのは、VとCの流れの調整を ModelGlue.xml だけで行うということだろうか。
ModelGlue.xml では、Mのモデル(ロジック)部分のことは、制御外になっている。コントローラーの先は、作り手次第になっている。

2008年04月22日

ColdSpring

ColdSpring を活用してみよう。
すでに ColdSpring の機能である、“DI”というものについての記述は、たくさんあるので、どのように使うかを見ていく。
よい題材が思いつかなかったので、Common.cfc で生成していた商品リストを「強引に」ColdSpring でやってみよう。
ColdSpring.xml に下記のように記述する。

 <bean id="itemArrayBean" class="shopping.model.ItemArrayBean">
  <property name="itemArray">
   <list>
    <map>
     <entry key="name"><value>光学式マウス</value></entry>
     <entry key="price"><value>2600</value></entry>
    </map>
    <map>
     <entry key="name"><value>ボール式マウス</value></entry>
     <entry key="price"><value>1100</value></entry>
    </map>
    <map>
     <entry key="name"><value>レーザー式マウス</value></entry>
     <entry key="price"><value>4800</value></entry>
    </map>
   </list>
  </property>
 </bean>

ここで注意しなければならないのは、Model-Gule は、ColdSpring.xml などのファイルを、OSのデフォルトエンコーディングを前提に
読み込んでいるらしく、Windows で動作させる場合は、Shift_JIS で保存しなければ、文字化けしてしまった。

 <?xml version="1.0" encoding="UTF-8"?>

としても、だめだった。他のOSでは試していないので、あくまで、推測である。
自動で判別してくれるとか、先頭行をチェックしてから読み込むということは、してくれないようである。

shopping.model.ItemArrayBean は、商品リストデータを保持するコンポーネント。
Java でいうところの、Bean ということになる。ColdSpring でも、この Bean という単語を採用している。
デフォルトでは、リクエストごとに、インスタンスが生成されているようだ。
固定値を保持するような Bean は、アプリケーションで1インスタンスでいいのだから、何かしら、やり方があるのだろうか。
singleton とかの記述もマニュアルにあるし、できそうな雰囲気もあるが、試していない。

では、実際に、これを利用するためには、どうすればよいのか。
コントローラーでは、非常に簡単に利用できる。
getModelGlue().getBean("itemArrayBean") これだけで、取得できてしまうのである。
Controller.cfc にある、onRequestStart 関数に
<cfset variables.itemArrayBean = getModelGlue().getBean("itemArrayBean") /> とすれば、Controller.cfc 上では、いつでも、利用可能になる。

また、あたかも、AutoWire のように、

 <cffunction name="setItemArrayBean" access="public" returnType="void" output="false">
  <cfargument name="itemArrayBean" type="any">
  <cfset variables.itemArrayBean = arguments.itemArrayBean />
 </cffunction>

としてやるだけでも、取得できてしまう。
「あたかも、AutoWire のように」としたのは、ColdSpring.xml の bean タグに、AutoWire の設定することなく、
setter 関数が処理されたためにこのような表現をした。コントローラーは、本当に特別なもののようだ。
AutoWire については、また、後日、読み解いてみたい。

2008年04月23日

ColdSpring で Bean へデータを渡す

では、コントローラーではないコンポーネントに ColdSpring の Bean を渡すには、どうすればよいのか。
ロジックの、CartProcess.cfc に itemArrayBean を渡す方法を考えてみよう。

bean タグは、子要素に、constructor-arg と、property を持つことができる。
constructor-arg は、コンストラクタの引数として渡すためのタグ。
property は、setter 関数で渡すためのタグとなっている。


 <bean id="shopping" class="shopping.model.CartProcess">
  <constructor-arg name="itemAB">
   <ref bean="itemArrayBean" />
  </constructor-arg>
 </bean>

と ColdSpring.xml に記述してやることによって、CartProcess.cfc の初期化の際に、引数で自動的に渡してくれるのである。
初期化の関数は、デフォルトで、init になっているようで、


 <cffunction name="init" returntype="any" access="public" output="false">
  <cfargument name="itemAB" required="true">
  <cfset variables.itemArray = arguments.itemAB.getItemArray() />
  <cfreturn this />
 </cffunction>

という記述を、CartProcess.cfc にしてやると、CartProcess.cfc 上で、いつでも商品リストデータが利用可能になる。

一方、

  <constructor-arg name="itemAB">
   <ref bean="itemArrayBean" />
  </constructor-arg>

を、

  <property name="itemAB">
   <ref bean="itemArrayBean" />
  </property>

とすると、自動的に、CartProcess.cfc にある、setItemAB という関数を探して itemArrayBean を渡してくれるのである。

再び、サンプルアプリケーションを作ってみた。
できることは同じであるが、ColdSpring を利用して、コントローラーへ、自動的に、データを渡している。

サンプルColdSpring

2008年04月24日

ColdFusion 版 Ruby on Rails その名も ColdFusion on Wheels

やはり、あるんだな。
ColdFusion on Wheels
どんなんだろうか。
あ、ColdFusion8 限定。しかも、DBが、SQLServer と、MySQL5 だけだ・・・。

AutoWire

さて、たびたび出てきた AutoWire とは、何か。
ColdSpring のドキュメントによると、

The term "autowire" refers to the ability of the ColdSpring beanFactory to automatically wire dependent objects together without necessarily having to define those dependencies in the xml bean definitions.

とある。
簡単に言うと・・・、説明は止めておこう。実際に、どのように動くのかを見た方がわかりやすい。
検索すれば、AutoWire も、いっぱい出てくる。もちろん、ColdSpring での情報は少ないが。

前回、

 <bean id="shopping" class="shopping.model.CartProcess">
  <property name="itemAB">
   <ref bean="itemArrayBean" />
  </property>
 </bean>

このような記述をした。
ColdSpring.xml の Bean は、基本的にリクエストごとに、初期化される。
shopping.model.CartProcess を初期化する際に、setItemAB 関数を探して、引数として、itemAB という名前で、itemArrayBean を
渡して処理しなさいということであった。

AutoWire を使えば、この記述が以下のように簡単になる。

 <bean id="shopping" class="shopping.model.CartProcess" autowire="byName" />

autowire="byName" をautowire="byType" とすることもできる。
要するに、Bean を受け取りたい側(この場合は、CartProcess.cfc)で、setter 関数を記述さえすれば、取得できるわけである。

byName と、byType は、どう違うのか。名前と型なわけで、全く違うわけだが、byName であれは、setItemAB と名前で探すのに対し、

<bean id="shopping" class="shopping.model.CartProcess" autowire="byType" />


<cfargument name="itemArrayBean" type="shopping.model.ItemArrayBean" />

byType の場合は、setter 関数の名前は set で始まっていればよく、引数の受取の型指定が一致しているものを探すわけである。

同じ型で複数の Bean を作りたい場合には、byName にすることになるだろう。
byTypeの方は、すでに setter 関数があって、名前が重複してしまった場合などに利用することになるだろう。

基本的には、好みで決めればいいのだろう。byType の方が、好みだ。

2008年04月28日

ColdFusion on Wheels

ColdFusion on Wheels をちょっとやってみた。

http://ドメイン/アプリケーションディレクトリ/index.cfm/●●●●/▲▲▲▲?reload=development

?以下(URL引数)は、なしでもよい。

index.cfm で終わる場合、
/cfwheels/config/routes.cfm の最初の addRoute の Route が処理される。
デフォルト状態では、

のみなので、コントローラー main.cfc の welcomeToWheels という関数が処理され、
/cfwheels/view/main/welcomeToWheels.cfm という画面が表示される。

上記URLの場合、コントローラー ●●●●.cfc の ▲▲▲▲ という関数が処理され、
/cfwheels/view/●●●●/▲▲▲▲.cfm という画面が表示される。
このとき、routes.cfm の記述は要らないようだ。

最後にある ?reload=development のURL引数は、実際にはこのように使うのかわからないが、
こうすることで、application.settings.environment に値を設定できる。
もちろん、この記述をしないのが普通。
この値により、/cfwheels/config/environments/ の中の、それぞれ対応するファイルを読み込むようにできる。
現状、design とか、development、production などだ。

/cfwheels/config/environment.cfm の中で、この値を設定しているいるが、この値を変えても、
初回起動時(アプリケーションのスタート時)にしか変わってくれないので、強制的に変えたいときに使うのかもしれない。

コントローラーの関数内で設定した変数

は、view の画面ファイル内で、
#testValue#
など、そのまま使えるようにしてくれるようだ。
これは、簡単で便利なんだけど、逆に、想定できないようなバグを発生させる危険性もあるだろう。

バージョンが 0.7 ということで、まだまだ、これからのようだ。
ファイルの一覧を眺めても、基本的なことしかできないのかなぁと感じる。
これからに期待ってことで。

cfcUnit

テスティングフレームワークというものも、大規模開発向けなんだろうと思いつつ、流してきた。
折角、Model-Glue でCFコンポーネントにロジックをまとめることを調査しているので、
この機に、ちょっと調べてみよう。
cfcUnit と、CFUnit というものがあるらしいけど、最初に見つけた cfcUnit を調査。

http://sourceforge.net/project/showfiles.php?group_id=100854&package_id=126568

から、ファイルを拾ってくる。現在、1.2.0 beta1 らしい。
cfcUnit ディレクトリと、org ディレクトリを WEB のルートに入れる。
この2つに分かれているディレクトリにどういう意味があるのかは、わからない。

余談だが、cfcUnit は、Mac で開発してるようだ。

http://localhost/cfcunit/index.cfm でテスト実行用画面が表示される。
cfcunit.tests.AllTests というあらかじめ用意されているテストプログラムをテストしてみる。
「Run Test」ボタンを押すと、程なく、テスト完了。

では実際に、テストプログラムを作ってみよう。
今まで使ってきた shopping.model.CartProcess というCFコンポーネントのテストプログラムを作成する。

org.cfcunit.framework.TestCase を継承(extend)して、test で始まる関数を作ればいいようだ。
ここで問題が。この CartProcess コンポーネントは、ColdSpring によるDIを使っているので、そのままでは利用できない。

ColdSpring の機能を Model-Glue 経由で利用しているため、純粋に ColdSpring を利用する方法で初期化することになる。

 <cfset variables.cartProcess = "">
 <cffunction name="setUp" returntype="void" access="private">
  <cfset testBeanFactory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init()/>
  <cfset testBeanFactory.loadBeansFromXmlFile("/shopping/config/ColdSpring.xml",true)/>
  <cfset variables.cartProcess = testBeanFactory.getBean("cartProcess")>
 </cffunction>

この記述を使って CartProcess インスタンスを取得すればよい。setUp という初期化関数が使えるようだ。

そして、いろんなテストを作成する。
実際にテストを作成してみると、いろんなことを考えないといけないことがよくわかる。
予想はしていたけど、このテストプログラムを作るのは、仕様についても、プログラム言語のことも、
よく理解した上で作らないといけない。
このテストプログラムを作るのにもそれなりの工数がかかると思うのだが、実際の開発では、どれぐらい生産性が上がるものなのだろうか。

こうして作成した CartProcessTest.cfc を CartProces.cfc を同じディレクトリに入れる。
どのようなテストが入っているかは、http://localhost/shopping/model/CartProcessTest.cfc で一覧できる。
テスト実行用画面で、shopping.model.CartProcessTest をテストしてみる。

実際にテストしてみると、一つテストが失敗する。
カートに一つしか商品がない場合に削除ボタンを押しても、カートから削除できないという不具合がある。
CartProces での手抜きがばれてしまうわけだが、ちゃんと作り込んでやれば、全てのテストをクリアできる。

複数のテストプログラムをまとめるために、suite という概念があるようだ。
複数のコンポーネントをテストする場合に、便利なのだろう。

また、cfcUnit には、FLEX で作られたテスト実行用画面もある。
http://localhost/cfcunit/flex/index.html
でその画面が見られるだろう。FLEX だからどうなんだ。と言われても、困るというものだ。

2008年04月30日

ColdSpring によるAOP(Aspect Oriented Programming)

ColdSpring のDIコンテナとセットで、AOPというものが解説されることがある。
これも、たくさん記述があるので、それを読んでみても、何がどう便利でどう簡単なのかが、ピンと来ない。
というわけで、実践。

ColdSpring AOP Tutorial - Part Oneとか、ColdSpring ドキュメントや、ColdSpring のサンプルからちょっとコードを拝借。

ログを書き出す機能を、これまでのアプリケーションに、AOP手法で実装してみる。

とりあえず、どれぐらい簡単なのかを確認するために、ログ機能を利用するための諸設定などは、すでに行われているものとすれば、実に、簡単である。

やろうとしていることは、shopping.model.CartProcess コンポーネントの関数を実行するたびに、ログを書き出すようにしたい。
ということで、それには、ColdSpring.xml の中の、

 <bean id="shopping" class="shopping.model.CartProcess" autowire="byType" />

これを


 <bean id="cartProcessTarget" class="shopping.model.CartProcess" autowire="byType" />
 
 <bean id="shopping" class="coldspring.aop.framework.ProxyFactoryBean">
  <property name="target">
   <ref bean="cartProcessTarget" />
  </property>
  <property name="interceptorNames">
   <list>
    <value>loggingAdvisor</value>
   </list>
  </property>
 </bean>

とするだけである。
が、実際、簡単なんだと思えるまで、結構時間がかかった。

2段階でコンポーネントのインスタンスを取得していることになる。
こうして得られる shopping インスタンスは、coldspring.aop.framework.ProxyFactoryBean ではなく、
あたかも shopping.model.CartProcess そのもののように、扱うことができる。

そして、shopping 上の関数を利用すると、ログ書き出しが機能してしまうのである。
既存の shopping.model.CartProcess のコードを書き換えることなく、機能追加を実装できてしまった。

いやいや、ログを取る場合は、もっと細かいレベルで、見たいのではないかと。
例えば、shopping.model.CartProcess の関数は、「カートに商品を入れる」というものであり、
このログ機能が処理されるタイミングは、shopping.model.CartProcess の関数が実行される前、後、その両方、
そして、エラーが発生した場合で設定できる。
既存の関数にログをはき出す機能を付けても、それでは、たいした効果は期待できない。

AOPとは、再利用可能でシンプルな見やすいプログラムを作ることが目的の一つになっている。
例えば、データベースに対して、引数で与えたSQL文を発行するだけというような、再利用できる低レベルでの関数であれば、
その関数の前後などで、ログを書き出すことは、意味を持ってくるのだろう。

また、ログに限らず、AOPは、トランザクション処理や、キャッシュ処理、権限チェック処理などが利用例としてあげられており、どのようなものを実装するかで、意義は変わってくるのだろう。

About 2008年04月

2008年04月にブログ「気楽に行こう」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2007年01月です。

次のアーカイブは2008年05月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。