まっつんです。今回は Web Velocity と Ruby on Rails (以下、Rails) を比較しながら、 ポテトバーガー注文アプリケーション を実装していきます。
ポテトバーガー注文アプリケーションとは?
最初に今回実装する ポテトバーガー注文アプリケーション について簡単に説明します。
ポテトバーガー注文アプリケーション は PHP のアプリケーションフレームワーク Piece Framework のクィックスタートドキュメント「Piece Framework で作る対話的なアプリケーション」で題材となっている、対話的にポテトバーガーを注文するアプリケーションで、メインメニューの選択、サイドメニューの選択、確認、完了の 4 ページで構成されています。
ページの遷移やデータベースアクセスといった Web アプリケーションで必ず出てくる処理が盛り込まれているため、このアプリケーションは評価対象のフレームワークの特徴を掴むためにもってこいの題材といえるでしょう。
リポジトリとアプリケーションの作成
Web Velocity では、最初にアプリケーションを格納するためのリポジトリを作成します。リポジトリの保存先には一般的な RDBMS を使うことができます。今回は PostgreSQL を使うことにし、リポジトリ用とアプリケーション用の 2 つのデータベースを作成します。データベースの情報は以下のとおりです。
リポジトリ用データベース
| データベース | repos |
|---|---|
| ユーザ | repos |
| パスワード | repos |
アプリケーション用データベース
| データベース | piece_conversation |
|---|---|
| ユーザ | piece |
| パスワード | piece |
ノート: Web Velocity では、PostgreSQL 以外にも Oracle や MySQL などさまざまな RDBMS を利用することができます。
それではリポジトリを作成しましょう。[New] -> [Repositry] をクリックすると、Add Repository ダイアログが表示されるので、以下のように入力します。
| Name | Repos |
|---|---|
| Platform | PostgreSQL |
| Address | localhost |
| Port | 5432 |
| Database | repos |
| Schema | public |
| Username | repos |
| Password | repos |
[Repositories] -> [Repos] をクリックし、作成したリポジトリを選択します。続いてアプリケーションを作成しましょう。[New] -> [Application] をクリックすると、Create Application ダイアログが表示されるので、以下のように入力します。
| Name | Conversation |
|---|---|
| Generate - Schema | オン |
ドメインモデルとテーブルの作成
Web Velocity では、Rails と同様にデータへアクセスするためのアーキテクチャとして Active Record が採用されています。Active Record の性質上ドメインモデルとテーブルの構造は深く関係します。そのため Web Velocity では、ドメインモデルの作成時にテーブル生成メソッドも合わせて作成できるようになっています。
それでは Order ドメインモデルを作成しましょう。[New] -> [New Model] をクリックすると、Create Persistent Model ダイアログが表示されるので、orders テーブルのジェネレータのみ生成するようにします。
| Name | order |
|---|---|
| Database Generation - #tableForOrder | オン |
| 上記以外 | オフ |
次にデータベースの接続情報を設定するため、[Database] をクリックし、以下のように入力します。
| Platform | PostgreSQL |
|---|---|
| Address | localhost |
| Port | 5432 |
| Database | piece_conversation |
| Schema | public |
| Username | piece |
| Password | piece |
入力が完了したら、[Test Login] をクリックし、データベースに接続できることを確認します。
続いて orders テーブルを生成するためのコードを追加します。[Go to Schema1] をクリックし、Source Code をクリックします。テーブル生成メソッドとして tableForORDERS が定義されているので、それを以下のように変更します。
Schema1 tables - tableForORDERS メソッド
tableForORDERS: aTable
(aTable createFieldNamed: 'id' type: platform serial) bePrimaryKey.
(aTable createFieldNamed: 'main' type: platform integer) beNullable: false.
(aTable createFieldNamed: 'side' type: platform integer) beNullable: false.
(aTable createFieldNamed: 'created_at' type: platform timestamp) beNullable: false.
(aTable createFieldNamed: 'updated_at' type: platform timestamp) beNullable: false.
これで orders テーブルを生成する準備が整いました。[Mappings] をクリックし、[Recreate all tables] をクリックすると、確認ダイアログが表示され、orders テーブルが生成されます。
このように Web Velocity はテーブルを定義する機能を提供しています。これは Rails のマイグレーションに似ていますが、Web Velocity はテーブルの変更点のみを定義する方法を提供しておらず、毎回テーブルを再作成する必要がある点が異なっています。
それでは、テーブルのフィールドとドメインモデルのプロパティのマッピングを定義しましょう。[Classes] -> [Order] をクリックし、[Mappings] をクリックします。Add instance variable named:xxxx リンクをクリックするとマッピングを定義することができます。今回は、すべてのフィールドに対してそれぞれ対応するプロパティをマッピングし、それらのアクセサメソッドも作成します。
ユーザインターフェイスの作成
最後にユーザインターフェイスを作成します。[New] -> [New Component] をクリックすると、Create Web Component ダイアログが表示されるので、以下のように入力します。
| Name | OrderUI |
|---|---|
| Register as application | オン |
| Configure for Glorp | オン |
| Use tamplate | オフ |
Glorp は Active Record ベースのデータアクセスフレームワークです。当初、筆者は Configure for Glorp を オフ にしていたことが原因で注文データを orders テーブルに登録することができず、原因究明に半日を費やすことになりました。
次にインスタンス変数を定義します。[Variables] をクリックし、Instance Variables の横の [Add Variable...] をクリックします。
| Name | Description | Create accessors |
|---|---|---|
| order | <Order> domain model. | オフ |
| currentState | <String> current state. | オフ |
| message | <String> validation result. | オフ |
次に、デフォルトのエントリポイントを削除し、新たに別のエントリポイントを登録します。[Component] をクリックし、既存の Conversation/OrderUI の [Remove] をクリックして削除します。そして [Add Entry Point...] をクリックし、Conversation/order エントリポイントを追加します。
それでは [Source Code] をクリックし、コードを追加していきましょう。まず、初期化コードの追加を行います。
OrderUI - initialize-release initialize メソッド
initialize
super initialize.
order := Order new bePersistent.
currentState := 'MainMenu'.
message := ''.
次に HTML 表示コードを追加します。最初に renderContentOn メソッドが呼び出されるので、このメソッドで各ページのステートに応じたメソッドを呼び出すようにしています。
OrderUI - rendering renderContentOn メソッド
renderContentOn: html
currentState = 'MainMenu' ifTrue: [
self renderMainMenu: html.
].
currentState = 'SideMenu' ifTrue: [
self renderSideMenu: html.
].
currentState = 'Confirmation' ifTrue: [
self renderConfirmation: html.
].
currentState = 'Finish' ifTrue: [
self renderFinish: html.
].
OrderUI - rendering renderMainMenu メソッド
renderMainMenu: html
html heading
level: 3;
with: 'メインメニューは何になさいますか?'.
html break.
html text: message.
html break.
html anchor
callback: [ self validateForMainMenu: 1 ];
with: 'ジャーマンポテトバーガー (650円)'.
html break.
html anchor
callback: [ self validateForMainMenu: 2 ];
with: 'ポテトコロッケバーガー (600円)'.
html break.
html anchor
callback: [ self validateForMainMenu: 3 ];
with: '肉じゃがバーガー (700円)'.
html break.
OrderUI - rendering renderSideMenu メソッド
renderSideMenu: html
html heading
level: 3;
with: 'サイドメニューは何になさいますか?'.
html break.
html text: message.
html break.
html anchor
callback: [ self validateForSideMenu: 1 ];
with: 'フライドポテト'.
html break.
html anchor
callback: [ self validateForSideMenu: 2 ];
with: 'ポテトサラダ'.
html break.
html anchor
callback: [ self validateForSideMenu: 3 ];
with: 'スイートポテト'.
html break.
OrderUI - rendering renderConfirmation メソッド
renderConfirmation: html
|mainMenu sideMenu price|
mainMenu := #('ジャーマンポテトバーガー'
'ポテトコロッケバーガー'
'肉じゃがバーガー'
) at: order main.
sideMenu := #('フライドポテト'
'ポテトサラダ'
'スイートポテト'
) at: order side.
price := #('650円'
'600円'
'700円'
) at: order main.
html heading
level: 3;
with: '注文内容は以上で宜しいですか?'.
html break.
html text: 'メインメニュー:', mainMenu.
html break.
html text: 'サイドメニュー:', sideMenu.
html break.
html text: '小計:', price.
html break.
html anchor
callback: [ self register ];
with: 'OK'.
html space.
html anchor
callback: [ currentState := 'MainMenu' ];
with: '選び直す'.
OrderUI - rendering renderFinish メソッド
renderFinish: html
html heading
level: 3;
with: 'ご注文ありがとうございました。'.
html break.
最後にバリデーションとデータベースアクセスのためのメソッドをそれぞれ追加します。
OrderUI - private validateForMainMenu メソッド
validateForMainMenu: mainMenu
message := ''.
(mainMenu >= 1) & (mainMenu <= 3) ifTrue: [
order main: mainMenu.
currentState := 'SideMenu'.
] ifFalse: [
message := '正しいメインメニューを選択してください。'.
currentState := 'MainMenu'.
]
OrderUI - private validateForSideMenu メソッド
validateForSideMenu: sideMenu
message := ''.
(sideMenu >= 1) & (sideMenu <= 3) ifTrue: [
order side: sideMenu.
currentState := 'Confirmation'.
] ifFalse: [
message := '正しいサイドメニューを選択してください。'.
currentState := 'SideMenu'.
]
OrderUI - private register メソッド
register
order createdAt: Timestamp now.
order updatedAt: Timestamp now.
order commitUnitOfWork.
currentState := 'Finish'.
Web Velocity では、データを保存するアーキテクチャとして Unit of Work が採用されています。ステートフルを特徴とする Seaside アプリケーションでは、その利点を生かすために、ビジネストランザクションが複数のリクエストにまたがるような実装を行うことも多いのではないかと思います。このような場合に並行性の問題を解決する Unit of Work は非常に有効なアーキテクチャであると言えます。
ノート: Unit of Work を使用せずにデータを保存することもできます。
動作確認
以上でアプリケーションの実装は完了です。[Browse] -> [/Conversation/order] をクリックして、動作確認を行いましょう。
問題なく動作することが確認できました。
おわりに
Web Velocity は、Seaside とRails それぞれの特徴と利点を上手く組み合わせているという印象を受けました。また、コードリポジトリがデータベースであることや、ブラウザベースの開発環境が提供されていることなど、言語ワークベンチ や IDE の観点からも興味深いフレームワークであると言えます。
しかし残念ながら、現時点では完成度が高いとは言えません。開発環境では親クラスのコードの検索ができないことをはじめ、苦労することが数多くありました。また、スタティックメソッドに不正なコードがあるとアプリケーションをロードすることができないため、それを修正することができず、一つ前のバージョンに戻さなければなりませんでした。
このようにいくつかの問題はあるものの、まだリリースされたばかりという点もあり今後が楽しみなフレームワークです。
使用したソフトウェアのバージョン
| WebVelocity Lite | 1.0 |
|---|
参考文献
トラックバック(0)
- このブログ記事のトラックバックURL:
