2022年4月30日土曜日

Cozy Web/Tag

前回紹介したとおりCozy WebではHTMLページを動的に生成するための機能として、テンプレートエンジンScalateによるSspやJade(Pug)を提供しています。

SspやJadeでは、基本的にプログラミング言語の断片をHTMLに埋め込む形になります。プログラミング機能をフルに使えるので、リッチな動的コンテンツを作成できるというメリットがある反面おまじない的なコードが必要になったり、アプリケーションロジックがプレゼンテーション層であるWebページに混入しがちになるといった問題がでてきます。

この問題に対してCozy Webではタグ機能を用意しました。タグ機能はCozy Webが提供する様々な機能をビュー層で簡単に使えるようにするための機能を提供します。拡張タグをHTML内に埋め込むことで、HTMLページの動的機能を簡単に実現することができます。

SSP

まずSSPによる動的ページについて確認しておきましょう。

以下はSSPによる動的ページです。

<%@ var context: ViewContext %>
<html>
    <head>
	<title>HelloTag</title>
    </head>
    <body>
	<p>現在の時刻は${context.datetime}です。</p>
    </body>
</html>

まずページ内で使用する変数として、以下の行でViewContextを格納する変数contextを定義しています。

<%@ var context: ViewContext %>

そして、以下の行でViewContextオブジェクトのdateTimeメソッドを呼び出し、その結果をpタグ内の文章に埋め込んでいます。dateTimeメソッドは現在時刻をロケール、タイムゾーンに対応した文字列で返します。

	<p>現在の時刻は${context.dateTime}です。</p>

HelloTag

Cozy Webのタグを使用したWebアプリケーションHelloTagを作成します。

アプリケーション

以下のHTMLはCozy Webタグを使用して前出のSSPと同じ動作をします。

<html>
    <head>
	<title>HelloTag</title>
    </head>
    <body>
	<p>現在の時刻は<c:datetime/>です。</p>
    </body>
</html>

以下の行で現在時刻を埋め込みたい場所にタグc:datetime配置しています。Cozy WebではHTML内に配置されたc:datetimeを現在時刻に変換します。

	<p>現在の時刻は<c:datetime/>です。</p>

SSPなどのようなプログラミング成分はなく、動的なコンテンツ部分を宣言的に定義することができます。

配備

cozyを起動するディレクトリにwebappsディレクトリに、アプリケーションのホームとなるディレクトリHelloTagを作成します。

このHelloTagディレクトリに前述のindex.htmlを作成すればOKです。

起動

Cozyをwebコマンドで起動するとWebプラットフォームとして起動します。

$ cozy web

実行

curlコマンドによってローカルホストの8080ポート上の /web/HelloTag/index.html を取得します。

/web はCozy上のWebアプリケーションのホームです。その配下のHelloTagが、先程作成したディレクトリHelloTagに対応するもので、ディレクトリ名がアプリケーション名になっています。

curl -v http://localhost:8080/web/HelloTag/index.html

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

> GET /web/HelloTag/index.html HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.76.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sat, 30 Mar 2022 18:30:00 GMT
< Content-Type: text/html;charset=utf-8
< Transfer-Encoding: chunked
< Server: Jetty(9.4.38.v20210224)
< 
<!DOCTYPE html>
<html>
    <head>
	<title>Hello Tag</title>
    </head>
    <body>
	<p>現在の時刻は2022年4月30日18時30分00秒です。</p>
    </body>
</html>

まとめ

今回はCozy WebのTag機能を説明しました

Tag機能を使えば、通常のHTMLにタグを埋め込むだけで動的コンテンツのWebページを作成することができます。

次回は動的コンテンツを実現する要の機能であるコントローラー機能を説明します。

諸元

Cozy
0.0.5

2022年3月31日木曜日

Cozy Web/HTMLフォーマット

Cozy Webでは通常のHTMLに加えて、以下の4つのフォーマットをサポートしています。

Mustache
Mustache派生の独自マークアップ
Scaml
HamlのScala版
Jade
Pug派生の独自マークアップ
Ssp
VelocityやJSPなどのHTML埋め込みフォーマットのScala版

これらのフォーマットはテンプレートエンジンScalateを使用して実現しており、フォーマットの仕様はScalateの提供するものになります。

今回はこの中でSspとJadeの2つのフォーマットを使ったWebページを試してみます。

HelloSsp

Ssp(Scala Server Pages)はVeloicyやJSPなどと同系統のHTMLにマークアップするフォーマットです。Scalate独自のフォーマットで、埋め込みプログラミング言語にScalaを使用しています。

HTMLをベースに必要箇所だけマークアップする方式なので習得が容易です。またお勧めはできませんが、プログラミング言語によるロジックをマークアップで記述することが可能です。

アプリケーション

以下のindex.sspを用意します。

基本的に通常のHTMLで、"${"と"}"で囲まれた「new java.util.Date()」がScala言語の埋め込み部分です。

<html>
    <head>
	<title>Hello SSP!</title>
    </head>
    <body>
	<h1>Hello SSP!</h1>
	<p>今日は ${new java.util.Date()} です。</p>
    </body>
</html>

配備

cozyを起動するディレクトリにwebappsディレクトリに、アプリケーションのホームとなるディレクトリHelloSspを作成します。

このHelloSspディレクトリに前述のindex.sspを作成すればOKです。

起動

Cozyをwebコマンドで起動するとWebプラットフォームとして起動します。

$ cozy web

実行

curlコマンドによってローカルホストの8080ポート上の /web/HelloSsp/index.html を取得します。

/web はCozy上のWebアプリケーションのホームです。その配下のHelloSspが、先程作成したディレクトリHelloSspに対応するもので、ディレクトリ名がアプリケーション名になっています。

curl -v http://localhost:8080/web/HelloSsp/index.html

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

> GET /web/HelloSsp/index.html HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.76.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 28 Mar 2022 22:02:47 GMT
< Content-Type: text/html;charset=utf-8
< Transfer-Encoding: chunked
< Server: Jetty(9.4.38.v20210224)
< 
<!DOCTYPE html>
<html>
    <head>
	<title>Hello SSP!</title>
    </head>
    <body>
	<h1>Hello SSP!</h1>
	<p>今日は 2022年3月29日 です。</p>
    </body>
</html>

HelloJade

SspはHTMLをベースにマークアップを入れる方式なので習得も容易ですが、HTML言語から引き継いでいる特性としてマークアップがやや煩雑です。

文章の中にマークアップする場合にはHTML言語が適していますが、HTML文書の構造などを記述する場合には冗長な表現になりがちです。

Jadeはタグの木構造を簡潔に記述することに適したフォーマットです。Jadeは現在ではPug( https://pugjs.org/ )と変名されていますが、ここではScalateの用語法を踏襲してJadeを使用します。Jadeはタグの木構造を簡潔に記述することに適したフォーマットです。Jadeは現在ではPug( https://pugjs.org/ )と変名されていますが、ここではScalateの用語法を踏襲してJadeを使用します。

アプリケーション

以下のindex.jadeを用意します。

タグの木構造をインデントで記述します。HTML文書を簡潔に記述することができます。

html(lang="ja")
  head
    title Hello Jade!
  body
    h1 Hello Jade!
    p
      | 今日は
      = new java.util.Date()
      | です。
    :markdown
      Jadeを使うとMarkdownで文章を書く事ができます。

配備

cozyを起動するディレクトリにwebappsディレクトリに、アプリケーションのホームとなるディレクトリHelloJadeを作成します。

このHelloJadeディレクトリに前述のindex.jadeを作成すればOKです。

起動

Cozyをwebコマンドで起動するとWebプラットフォームとして起動します。

$ cozy web

実行

curlコマンドによってローカルホストの8080ポート上の /web/HelloJade/index.html を取得します。

/web はCozy上のWebアプリケーションのホームです。その配下のHelloJadeが、先程作成したディレクトリHelloJadeに対応するもので、ディレクトリ名がアプリケーション名になっています。

curl -v http://localhost:8080/web/HelloJade/index.html

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

*   Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET /web/HelloJade/index.html HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.76.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 28 Mar 2022 22:04:28 GMT
< Content-Type: text/html;charset=utf-8
< Transfer-Encoding: chunked
< Server: Jetty(9.4.38.v20210224)
< 
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>Hello Jade!</title>
  </head>
  <body>
    <h1>Hello Jade!</h1>
    <p>
      今日は
      2022年3月29日
      です。
    </p>
    <p>Jadeを使うとMarkdownで文章を書く事ができます。</p>
  </body>
  </div>
</html>

まとめ

Cozy WebではHTML文書記述に以下の5つのフォーマットを使用することができます。

  • HTML
  • Mustache
  • Scaml
  • Jade
  • Ssp

前回HTML文書を使用する方法を紹介しました。

HTML文書をHTMLフォーマットで作成したものは、そのまま配備するだけでWebで表示することができます。

ただ、動的な要素を持っているページの記述の場合には動的生成が可能な文書フォーマットを使用する必要があります。Cozy WebではScalateがサポートしている4つのフォーマットが使用可能になっています。

今回は、その中のJadeとSspの2つについて使用方法を説明しました。MustacheやScamlも同様にそれぞれのフォーマットでページを記述するだけなので、簡単に使用できると思います。

次回はArcadiaが提供するタグ機能を紹介する予定です。

諸元

Cozy
0.0.4

2022年2月28日月曜日

Cozy Web

Modegramming Styleブログではモデリングとプログラミングの一体化を指向したModegrammingというコンセプトを提唱しており、その実現のための技術体系としてSimpleModelingを整備しています。

SimpleModelingを実現するためのプラットフォームとして以下の4つのプロダクトを集約したCozyを立ち上げたことを前回ご紹介しました。

SmartDox
文書処理系
SimpleModeler
モデルコンパイラ
Kaleidox
アクション言語
Arcadia
Webフレームワーク

CozyはGitHubで開発を進めています。

  • https://github.com/asami/cozy

Cozy Web

モデル駆動開発をWebアプリケーションまで広げるためには、モデル駆動対応したWebプラットフォームが必要です。このためのWebフレームワークとして開発を進めてきたのがArcadiaです。

CozyはこのArcadiaをベースに動作させることでモデル駆動の基盤のWebプラットフォームとして動作します。

CozyをWebプラットフォームとして動作させる形態をCozy Webと呼ぶことにします。

HelloWorld

Cozy Webの最小構成アプリケーションとしてHelloWorldを作成し、動作させてみます。

アプリケーション

最小構成のアプリケーションなので、index.htmlのみを配備します。以下のindex.htmlを用意します。

<html>
<head>
<title>Hello World!</title>
</head>
<body>
Hello World!
</body>
</html>

配備

cozyを起動するディレクトリにwebappsディレクトリを作成します。

ここにアプリケーションのホームとなるディレクトリHelloWorldを作成します。

このHelloWorldディレクトリに前述のindex.htmlを作成すればOKです。

起動

Cozyをwebコマンドで起動するとWebプラットフォームとして起動します。

$ cozy web

実行

curlコマンドによってローカルホストの8080ポート上の /web/HelloWorld/index.html を取得します。

/web はCozy上のWebアプリケーションのホームです。その配下のHelloWorldが、先程作成したディレクトリHelloWorldに対応するもので、ディレクトリ名がアプリケーション名になっています。

curl -v http://localhost:8080/web/HelloWorld/index.html

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

取得結果は以下になります。無事登録したHTMLファイルを取得することができました。

-vスイッチが指定されているので、HTTPのプロトコルヘッダーも表示されています。

> GET /web/HelloWorld/index.html HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.76.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Sun, 27 Feb 2022 04:12:53 GMT
< Content-Type: text/html;charset=utf-8
< Cache-Control: public,max-age=86400
< Expires: Mon, 28 Feb 2022 04:12:59 GMT
< Content-Length: 337
< Server: Jetty(9.4.38.v20210224)
< 
<html>
<head>
<title>Hello World!</title>
</head>
<body>
Hello World!
</body>
</html>

まとめ

今回はモデル駆動開発向けのWebプラットフォームCozy Webを紹介しました。

ごく簡単なWebアプリケーションをHelloWorldとして配備して実行することが確認できました。

次回以降Cozy Webの機能を紹介していく予定です。

諸元

Cozy
0.0.3

2022年1月31日月曜日

Cozy

Modegramming Styleブログではモデリングとプログラミングの一体化を指向したModegrammingというコンセプトを提唱しており、その実現のための技術体系としてSimpleModelingを整備しています。

プロダクト

ちょうど一年前の記事「SimpleModeling」では、このSimpleModelingの技術体系の構成要素である以下の4つのプロダクトの開発について説明しました。ちょうど一年前の記事「SimpleModeling」では、このSimpleModelingの技術体系の構成要素である以下の4つのプロダクトの開発について説明しました。

SmartDox
文書処理系
SimpleModeler
モデルコンパイラ
Kaleidox
アクション言語
Arcadia
Webフレームワーク

この中で、この一年は特にアクション言語であるKaleidoxの開発を進めてきました。

開発方法論

7月からオブジェクト指向開発方法論の講座を始めました。

UML(Unified Modeling Language)/UP(Unified Process)の基本を押さえつつ、オブジェクト指向開発方法論の現在地を整理する目的です。

その上で、最終的には上記のプロダクトを活用したモデル駆動開発と連携できればと考えています。

Cozy

Kaleidoxの開発も順調に進んできたので、Kaleidox, SmartDox, SimpleModeler, Arcadiaを統合した、モデル駆動開発のワークベンチとして新たなプロダクトCozyを立ち上げました。

モデルコンパイラにアクション言語、文書処理系、Webフレームワークを統合し連携することでより有効な活用を行うことができるでしょう。

状態機械図

Kaleidox, SmartDox, SimpleModeler, Arcadiaを統合することにより、KaleidoxからSimpleModelerのモデル生成機能などを直接使用できるようになりました。

今回はSimpleModelerの持つ状態機械図生成機能を使ってKaleidox上の状態機械モデルの状態機械図を生成してみます。

状態機械

まず「Kaleidox/状態機械:ヒストリー」で使用した状態機械の状態機械図を生成します。

以下のモデルでは状態機械purchaseを定義しています。

* event
event=[{
    name="confirm"
  },{
    name="reject"
  },{
    name="delivered"
  },{
    name="cancel"
  },{
    name="suspend"
  },{
    name="resume"
}]
* statemachine
statemachine={
  name="purchase"
  state=[{
    name=INIT
    transition=[{
      to=running
    }]
  },{
    name=canceled
    transition=[{
      to=FINAL
    }]
  },{
    name=suspended
    transition=[{
      guard=resume
      to=HISTORY
    }]
  }]
  statemachine=[{
    name="running"
    state=[{
      name=applying
      transition=[{
        to=confirming
      }]
    },{
      name=confirming
      transition=[{
        guard=confirm
        to=confirmed
      },{
        guard=reject
        to=rejected
      }]
    },{
      name=confirmed
      transition=[{
        to=delivering
      }]
    },{
      name=rejected
      transition=[{
        to=FINAL
      }]
    },{
     name=delivering
      transition=[{
        guard=delivered
        to=delivered
      }]
    },{
      name=delivered
      transition=[{
        to=FINAL
      }]
    }]
    transition=[{
      guard=cancel
      to=canceled
    },{
      guard=suspend
      to=suspended
    }]
  }]
}

statemachine-diagram関数で上記の状態機械purchaseから状態機械図を生成します。

cozy> statemachine-diagram 'purchase
Image

画像をviewコマンドで表示します。Kaleidox上で定義した状態機械の状態機械図として表示することができました。

cozy> :view

エンティティ

次は「Kaleidox状態機械/エンティティの状態遷移」で使用した状態機械を持つエンティティです。

エンティティsalesorderが状態機械statusを持っています。

* event
event=[{
    name="confirm"
  },{
    name="reject"
  },{
    name="delivered"
  },{
    name="cancel"
  },{
    name="suspend"
  },{
    name="resume"
}]
* entity
** salesorder
*** features
table=salesorder
*** attributes
| Name | Type  | Multiplicity |
|------+-------+--------------|
| id   | token |            1 |
| price| int   |            1 |
*** statemachines
**** status
state=[{
  name=INIT
  transition=[{
    to=running
  }]
},{
  name=canceled
  transition=[{
    to=FINAL
  }]
},{
  name=suspended
  transition=[{
    guard=resume
    to=HISTORY
  }]
}]
statemachine=[{
  name="running"
  state=[{
    name=applying
    transition=[{
      to=confirming
    }]
  },{
    name=confirming
    transition=[{
      guard=confirm
      to=confirmed
    },{
      guard=reject
      to=rejected
    }]
  },{
    name=confirmed
    transition=[{
      to=delivering
    }]
  },{
    name=rejected
    transition=[{
      to=FINAL
    }]
  },{
   name=delivering
    transition=[{
      guard=delivered
      to=delivered
    }]
  },{
    name=delivered
    transition=[{
      to=FINAL
    }]
  }]
  transition=[{
    guard=cancel
    to=canceled
  },{
    guard=suspend
    to=suspended
  }]
}]

statemachine-diagram関数で上記のエンティティpurchaseが持っている状態機械statusの状態機械図を生成します。

cozy> statemachine-diagram 'salesorder
Image

画像をviewコマンドで表示します。こちらもKaleidox上で定義した状態機械の状態機械図として表示することができました。

状態機械モデルとしては前出のpurchaseと同じなので、同じ状態機械図になっています。

cozy> :view

まとめ

Kaleidoxの開発もいい感じに進み、Cozyの枠組みの中でSimpleModelerと連携して状態機械図を生成することができるようになりました。

今年はCozyの各種機能の連携を強化し、効果的なモデル駆動開発の環境づくりに取り組んでいきたいと思います。

諸元

Cozy
0.0.2

2021年12月31日金曜日

Kaleidox状態機械/エンティティの状態遷移によるアクション

前回は状態機械を持ったエンティティを状態遷移させてみました。

今回は状態機械にアクションを設定し、状態遷移に伴うアクションの動作を確認します。

モデル

モデルは基本的には前回のものと同じですが、各アクションにアクションが実行されたことを示す文字列をコンソールに表示するようにしました。

アクションにはKaleidox言語を設定することができます。そこで、各アクションではKaledioxのprint関数を呼び出すようになっています。

* event
event=[{
    name="confirm"
  },{
    name="reject"
  },{
    name="delivered"
  },{
    name="cancel"
  },{
    name="suspend"
  },{
    name="resume"
}]
* entity
** salesorder
*** features
table=salesorder
*** attributes
| Name | Type  | Multiplicity |
|------+-------+--------------|
| id   | token |            1 |
| price| int   |            1 |
*** statemachines
**** status
state=[{
  name=INIT
  transition=[{
    to=running
    effect="println \"transition from INIT to running\""
  }]
  entry="println \"entry INIT\""
  exit="println \"exit INIT\""
  do.entry="println \"do.entry INIT\""
  do.exit="println \"do.exit INIT\""
},{
  name=canceled
  transition=[{
    to=FINAL
    effect="println \"transition from canceled to FINAL\""
  }]
  entry="println \"entry canceled\""
  exit="println \"exit canceled\""
  do.entry="println \"do.entry canceled\""
  do.exit="println \"do.exit canceled\""
},{
  name=suspended
  transition=[{
    guard=resume
    to=HISTORY
    effect="println \"transition from suspended to HISTORY\""
  }]
  entry="println \"entry suspended\""
  exit="println \"exit suspended\""
  do.entry="println \"do.entry suspended\""
  do.exit="println \"do.exit suspended\""
}]
statemachine=[{
  name="running"
  state=[{
    name=INIT
    transition=[{
      to=applying
      effect="println \"transition from running.INIT to running.applying\""
    }]
    entry="println \"entry running.INIT\""
    exit="println \"exit running.INIT\""
    do.entry="println \"do.entry running.INIT\""
    do.exit="println \"do.exit running.INIT\""
  },
  {
    name=applying
    transition=[{
      to=confirming,
      effect="println \"transition from running.applying to running.confirming\""
    }]
    entry="println \"entry running.applying\""
    exit="println \"exit running.applying\""
    do.entry="println \"do.entry running.applying\""
    do.exit="println \"do.exit running.applying\""
  },{
    name=confirming
    transition=[{
      guard=confirm
      to=confirmed
      effect="println \"transition from running.confirming to running.confirmed\""
    },{
      guard=reject
      to=rejected
      effect="println \"transition from running.confirming to running.rejected\""
    }]
    entry="println \"entry running.confirming\""
    exit="println \"exit running.confirming\""
    do.entry="println \"do.entry running.confirming\""
    do.exit="println \"do.exit running.confirming\""
  },{
    name=confirmed
    transition=[{
      to=delivering
      effect="println \"transition from running.confirmed to running.delivering\""
    }]
    entry="println \"entry running.confirmed\""
    exit="println \"exit running.confirmed\""
    do.entry="println \"do.entry running.confirmed\""
    do.exit="println \"do.exit running.confirmed\""
  },{
    name=rejected
    transition=[{
      to=FINAL
      effect="println \"transition from running.rejected to FINAL\""
    }]
    entry="println \"entry running.rejected\""
    exit="println \"exit running.rejected\""
    do.entry="println \"do.entry running.rejected\""
    do.exit="println \"do.exit running.rejected\""
  },{
   name=delivering
    transition=[{
      guard=delivered
      to=delivered
      effect="println \"transition from running.delivering to running.delivered\""
    }]
    entry="println \"entry running.delivering\""
    exit="println \"exit running.delivering\""
    do.entry="println \"do.entry running.delivering\""
    do.exit="println \"do.exit running.delivering\""
  },{
    name=delivered
    transition=[{
      to=FINAL
      effect="println \"transition from running.delivered to FINAL\""
    }]
    entry="println \"entry running.delivered\""
    exit="println \"exit running.delivered\""
    do.entry="println \"do.entry running.delivered\""
    do.exit="println \"do.exit running.delivered\""
  }]
  transition=[{
    guard=cancel
    to=canceled
    effect="println \"transition from running to canceled\""
  },{
    guard=suspend
    to=suspended
    effect="println \"transition from running to suspended\""
  }]
}]

イベント

以下の6つのイベントを定義しています。

confirm
確認OK
reject
確認却下
delivered
配送済み
cancel
キャンセル
suspend
保留
resume
再開

前回からの変更点はありません。

エンティティ

エンティティの定義も前回から変更点はありません。

entity節の下にエンティティ「salesorder」を定義しています。

エンティティ「salesorder」の下にfeatures節で特性、attributes節で属性、statemachines節で状態機械を定義しています。

statemachines節の下に状態機械「status」を定義しています。

状態機械

定義したモデルの状態機械図は前回と同じ以下となります。

アクション

状態機械の以下の場所にアクションを定義しました。

  • 状態のentry, exit, do.entry, do.exit
  • 遷移のeffect

アクションはKaleidoxスクリプトでアクションの設定位置をコンソールに表示するものです。

実行

それでは実行してみましょう。

準備

まず、entity-create-collection関数を使ってエンティティを格納する入れ物であるコレクションの作成(内部的にはDBテーブルの作成)を行います。

kaleidox> entity-create-collection 'salesorder
true

この処理ではエンティティの状態遷移は起こらないのでコンソールへの表示は行われません。

エンティティの作成

次にentity-create関数でエンティティを作成します。

kaleidox> entity-create 'salesorder price=100
entry INIT
do.entry INIT
do.exit INIT
exit INIT
transition from INIT to running
entry running.INIT
do.entry running.INIT
do.exit running.INIT
exit running.INIT
transition from running.INIT to running.applying
entry running.applying
do.entry running.applying
do.exit running.applying
exit running.applying
transition from running.applying to running.confirming
entry running.confirming
do.entry running.confirming
id:salesorder-67Yh9FdYry41hzuS9WDEtF;price:100;status:confirming

アクションの設定を行う前だと以下のようになっていたところですが、多数のアクションが実行されたことが分かります。この差分がアクションの動作ということになります。

kaleidox> entity-create 'salesorder price=100
id:salesorder-67Yh9FdYry41hzuS9WDEtF;price:100;status:confirming

それぞれ詳しく見ていきます。

オブジェクトが作成されると初期状態INITに入ります。初期状態INITへの進入時に呼ばれるentryアクションとdo.entryアクションが実行されました。

entry INIT
do.entry INIT

初期状態INITから最初の状態に自動的に移るので初期状態INITからの退出時に呼ばれるdo.exitアクションとexitアクションが実行されました。

do.exit INIT
exit INIT

次に初期状態INITから状態runningへの実際の遷移が行われます。

transition from INIT to running

状態runningは状態の入れ子になっているので状態機械running内で新たな状態遷移が始まります。このため状態機械runningの初期状態に入り最初の状態applyingに遷移します。状態機械runningの初期状態INITへの進入処理としてentry, do.entry、退出処理としてdo.exit, exitアクションが実行され、初期状態INITから最初の状態applyingに遷移する中でeffectアクションが実行されます。

entry running.INIT
do.entry running.INIT
do.exit running.INIT
exit running.INIT
transition from running.INIT to running.applying

状態applyingに入った後は自動的に状態confirmingに移ります。このため状態applyingへの進入処理、退出処理の各アクションが実行され、状態applyingから状態confirmingへの遷移アクションが実行されます。

entry running.applying
do.entry running.applying
do.exit running.applying
exit running.applying
transition from running.applying to running.confirming

最後に状態confirmingへの進入時のアクションとしてentry, do.entryアクションが実行され状態confirmingに落ち着きました。

entry running.confirming
do.entry running.confirming

オブジェクトの状態を確認するとconfirmingとなっています。

kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                            ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │salesorder-67Yh9FdYry41hzuS9WDEtF┃
┃price │100                              ┃
┃status│confirming                       ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

エベントの送出

event-call関数でCALLイベントconfirmをsalesorderエンティティ「67Yh9FdYry41hzuS9WDEtF」に対して送出します。

kaleidox> event-call :entity 'salesorder 'confirm "67Yh9FdYry41hzuS9WDEtF"
do.exit running.confirming
exit running.confirming
transition from running.confirming to running.confirmed
entry running.confirmed
do.entry running.confirmed
do.exit running.confirmed
exit running.confirmed
transition from running.confirmed to running.delivering
entry running.delivering
do.entry running.delivering
Event[confirm]

アクションの設定を行う前だと以下のようになっていたところです。この差分がアクションの動作ということになります。

kaleidox> event-call :entity 'salesorder 'confirm "67Yh9FdYry41hzuS9WDEtF"
Event[confirm]

それぞれ詳しく見ていきます。

まず状態confirmingから状態confirmedへの遷移が起こります。このため状態confirmingからの退出アクションdo.exit, exitが実行されます。続けて状態confirmingから状態confirmedへの遷移アクションが実行されます。

do.exit running.confirming
exit running.confirming
transition from running.confirming to running.confirmed

状態confirmedから状態deliveringへの遷移は無条件なので自動で遷移が起こります。

状態confirmedへの進入アクションentry, do.entryに続いて退出アクションdo.exit, exitが実行されます。続いて状態confirmedから状態deliveringへの遷移アクションが実行されます。

entry running.confirmed
do.entry running.confirmed
do.exit running.confirmed
exit running.confirmed
transition from running.confirmed to running.delivering

最後に状態deliveringへ進入するので状態deliveringのentry, do.entryアクションが実行されます。

entry running.delivering
do.entry running.delivering

ここで状態遷移は終わり状態deliveringに落ち着きました。

状態遷移の確認

confirmイベントを受信したsalesorderエンティティの状態をentity-get関数で確認します。

状態機械statusが状態confirmingから状態deliveringに遷移していることを確認できました。

kaleidox> entity-get 'salesorder "67Yh9FdYry41hzuS9WDEtF"
id:salesorder-67Yh9FdYry41hzuS9WDEtF;price:100;status:delivering
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                            ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │salesorder-67Yh9FdYry41hzuS9WDEtF┃
┃price │100                              ┃
┃status│delivering                       ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

まとめ

今回は状態機械のアクションの動作について確認しました。

状態遷移に伴って、設定したアクションが動作するのでイベント駆動の処理を自然に記述できます。

通常プログラミング言語で状態機械を実装するとそれなりのコード量になり、状態機械モデルとの関係も不明確になりがちですが、モデルそのものにアクションを設定することによりこのような問題が解消されます。

状態機械モデルを直接実行できることはモデル駆動開発の大きなアドバンテージになると思います。

諸元

Kaleidox
0.3.4

2021年11月30日火曜日

Kaleidox状態機械/エンティティの状態遷移

前回は状態機械を持ったエンティティをデータベースに格納、管理してみました。

今回は状態機械を持ったエンティティを状態遷移させてみます。

モデル

々回、前回とエンティティsalesorderを定義し状態機械の設定も行いました。

今回もこの定義をそのまま使います。

* event
event=[{
    name="confirm"
  },{
    name="reject"
  },{
    name="delivered"
  },{
    name="cancel"
  },{
    name="suspend"
  },{
    name="resume"
}]
* entity
** salesorder
*** features
table=salesorder
*** attributes
| Name  | Type  | Multiplicity |
|-------+-------+--------------|
| id    | token |            1 |
| price | int   |            1 |
*** statemachines
**** status
state=[{
  name=INIT
  transition=[{
    to=running
  }]
},{
  name=canceled
  transition=[{
    to=FINAL
  }]
},{
  name=suspended
  transition=[{
    guard=resume
    to=HISTORY
  }]
}]
statemachine=[{
  name="running"
  state=[{
    name=applying
    transition=[{
      to=confirming
    }]
  },{
    name=confirming
    transition=[{
      guard=confirm
      to=confirmed
    },{
      guard=reject
      to=rejected
    }]
  },{
    name=confirmed
    transition=[{
      to=delivering
    }]
  },{
    name=rejected
    transition=[{
      to=FINAL
    }]
  },{
   name=delivering
    transition=[{
      guard=delivered
      to=delivered
    }]
  },{
    name=delivered
    transition=[{
      to=FINAL
    }]
  }]
  transition=[{
    guard=cancel
    to=canceled
  },{
    guard=suspend
    to=suspended
  }]
}]

イベント

以下の6つのイベントを定義しています。

confirm
確認OK
reject
確認却下
delivered
配送済み
cancel
キャンセル
suspend
保留
resume
再開

前回からの変更点はありません。

エンティティ

エンティティの定義も前回から変更点はありません。

entity節の下にエンティティ「salesorder」を定義しています。

エンティティ「salesorder」の下にfeatures節で特性、attributes節で属性、statemachines節で状態機械を定義しています。

statemachines節の下に状態機械「status」を定義しています。

状態機械

定義したモデルの状態機械図は前回と同じ以下となります。

実行

それでは実行してみましょう。

準備

まず、前回記事までのおさらいです。

前回の記事でデータベース上に状態機械を持ったsalesorderオブジェクトを作成しました。手順を再現すると以下になります。

entity-create-collection関数でエンティティを格納するコレクションを作成します。コレクションのバックエンドはデータベースのテーブルになります。

kaleidox> entity-create-collection 'salesorder
true

次にentity-create関数でエンティティを作成します。

状態機械statusの状態はconfirmingになっています。

kaleidox> entity-create 'salesorder price=100
id:qXj7DyqEdu7rosSNdQ9R7;price:100;status:confirming
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                 ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │qXj7DyqEdu7rosSNdQ9R7 ┃
┃price │100                   ┃
┃status│confirming            ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━┛

salesorderエンティティのデータベースでの格納状況を確認するためにstore-get関数でデータベースから直接データを取得してみます。

kaleidox> store-get 'salesorder "qXj7DyqEdu7rosSNdQ9R7"
ID:qXj7DyqEdu7rosSNdQ9R7;PRICE:100;STATUS:501
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━┫
┃ID    │qXj7DyqEdu7rosSNdQ9R7┃
┃PRICE │100                  ┃
┃STATUS│201                  ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━┛

STATUSカラムには、状態confirmingに対応する数値201が格納されていることが確認できました。

エベントの送出

event-call関数でCALLイベントconfirmをsalesorderエンティティ「qXj7DyqEdu7rosSNdQ9R7」に対して送出します。

kaleidox> event-call :entity 'salesorder 'confirm "qXj7DyqEdu7rosSNdQ9R7"
Event[confirm]

状態遷移の確認

confirmイベントを受信したsalesorderエンティティの状態をentity-get関数で確認します。

状態機械statusが状態confirmingから状態deliveringに遷移していることを確認できました。

kaleidox> entity-get 'salesorder "qXj7DyqEdu7rosSNdQ9R7"
id:salesorder-qXj7DyqEdu7rosSNdQ9R7;price:100;status:delivering
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                 ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │qXj7DyqEdu7rosSNdQ9R7 ┃
┃price │100                   ┃
┃status│delivering            ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━┛

salesorderエンティティのデータベースでの格納状況を確認するためにstore-get関数でデータベースから直接データを取得してみます。

kaleidox> store-get 'salesorder "qXj7DyqEdu7rosSNdQ9R7"
ID:qXj7DyqEdu7rosSNdQ9R7;PRICE:100;STATUS:501
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━┫
┃ID    │qXj7DyqEdu7rosSNdQ9R7┃
┃PRICE │100                  ┃
┃STATUS│501                  ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━┛

STATUSカラムには、状態deliveringに対応する数値501が格納されていることが確認できました。

まとめ

今回はデータベース上に永続化されているエンティティに対してイベント送出によって状態遷移を起こし、その状態遷移がデータベース上に反映されていることを確認することができました。

永続オブジェクトであるエンティティの状態機械は、永続オブジェクトの読込みと書き戻しの処理が伴うので、プログラムで実装するのは少し手間がかかります。この手間をモデル駆動開発によって削減でできることが分かりました。

次回は状態機械のアクションについてみていきます。

諸元

Kaleidox
0.3.4

2021年10月31日日曜日

Kaleidox状態機械/エンティティ・ストア

今回より、エンティティの状態機械の操作について見ていきます。

今回は、エンティティをデータベースに保存、管理する方法について説明します。

モデル

前回、エンティティsalesorderを定義し、状態機械の設定も行いました。

今回はこの定義をそのまま使います。

* event
event=[{
    name="confirm"
  },{
    name="reject"
  },{
    name="delivered"
  },{
    name="cancel"
  },{
    name="suspend"
  },{
    name="resume"
}]
* entity
** salesorder
*** features
table=salesorder
*** attributes
| Name  | Type  | Multiplicity |
|-------+-------+--------------|
| id    | token |            1 |
| price | int   |            1 |
*** statemachines
**** status
state=[{
  name=INIT
  transition=[{
    to=running
  }]
},{
  name=canceled
  transition=[{
    to=FINAL
  }]
},{
  name=suspended
  transition=[{
    guard=resume
    to=HISTORY
  }]
}]
statemachine=[{
  name="running"
  state=[{
    name=applying
    transition=[{
      to=confirming
    }]
  },{
    name=confirming
    transition=[{
      guard=confirm
      to=confirmed
    },{
      guard=reject
      to=rejected
    }]
  },{
    name=confirmed
    transition=[{
      to=delivering
    }]
  },{
    name=rejected
    transition=[{
      to=FINAL
    }]
  },{
   name=delivering
    transition=[{
      guard=delivered
      to=delivered
    }]
  },{
    name=delivered
    transition=[{
      to=FINAL
    }]
  }]
  transition=[{
    guard=cancel
    to=canceled
  },{
    guard=suspend
    to=suspended
  }]
}]

イベント

以下の6つのイベントを定義しています。

confirm
確認OK
reject
確認却下
delivered
配送済み
cancel
キャンセル
suspend
保留
resume
再開

前回からの変更点はありません。

エンティティ

エンティティの定義も前回から変更点はありません。

entity節の下にエンティティ「salesorder」を定義しています。

エンティティ「salesorder」の下にfeatures節で特性、attributes節で属性、statemachines節で状態機械を定義しています。

statemachines節の下に状態機械「status」を定義しています。

状態機械

定義したモデルの状態機械図は前回と同じ以下となります。

実行

それでは実行してみましょう。

エンティティのテーブル作成

まずエンティティを格納するデータベースのテーブルを作成します。

最初はデータベースのテーブルは作成されていません。

このため以下のようにstore-select関数の実行はエラーとなります。

kaleidox> store-select 'salesorder
Error[org.h2.jdbc.JdbcSQLSyntaxErrorException: テーブル "SALESORDER" が見つかりません[NL]Table "SALESORDER" not found; SQL statement:[NL]SELECT * FROM salesorder WHERE 1 = 1 LIMIT 10 [42102-199]]

entity-create-collection関数でエンティティを格納するデータベースの作成を行うことができます。

デフォルトでは以下の設定が行われているため、H2のメモリデータベースを使用することができます。

db.default.driver="org.h2.Driver"
db.default.url="jdbc:h2:mem:"

以下のようにentity-create-collection関数を実行します。

kaleidox> entity-create-collection 'salesorder
true

store-select関数を用いてデータベースにエンティティsalesorderを格納するためのテーブルsalesorderが作成されていることを確認します。

kaleidox> store-select 'salesorder
Table[0x0]

実行の結果、テーブルは作成されておりデータが0件であることが確認できました。

entity-select関数を使ってエンティティsalesorderの一覧としても確認してみます。

kaleidox> entity-select 'salesorder
Table[3x0]

こちらも0件のンティティ・コレクションが作成されていることが確認できました。

エンティティの作成

データベースのテーブルが作成できたので、次はエンティティの作成を行います。

エンティティの作成はentity-create関数で行うことができます。第1引数にエンティティのクラス名、第2引数にプロパティをレコード形式で指定します。

kaleidox> entity-create 'salesorder price=100
id:51QK2CqGdgVLTqddOFdn6n;price:100;status:confirming
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                 ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │51QK2CqGdgVLTqddOFdn6n┃
┃price │100                   ┃
┃status│confirming            ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━┛

entity-create関数実行の結果、オブジェクトID「51QK2CqGdgVLTqddOFdn6n」のエンティティが作成されました。

状態機械statusは初期状態の「confirm」になっています。

エンティティの取得

entity-get関数を用いて、作成したエンティティの取得を行います。

kaleidox> entity-get 'salesorder "51QK2CqGdgVLTqddOFdn6n"
id:51QK2CqGdgVLTqddOFdn6n;price:100;status:confirming
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                 ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │51QK2CqGdgVLTqddOFdn6n┃
┃price │100                   ┃
┃status│confirming            ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━┛

先程、作成したエンティティを取得することができました。

エンティティの検索

次はエンティティの検索を行ってみます。

まず、データベースのテーブルを直接検索するstore-select関数で検索を行います。検索条件を指定していないので全件検索になります。

以下のようにテーブルの内容を取得することができました。状態機械statusはデータベース上では数値102として格納されています。

kaleidox> store-select 'salesorder
Table[3x1]
kaleidox> :show:pretty
┏━━━━━━━━━━━━━━━━━━━━━━┯━━━━━┯━━━━━━┓
┃ID                    │PRICE│STATUS┃
┣━━━━━━━━━━━━━━━━━━━━━━┿━━━━━┿━━━━━━┫
┃51QK2CqGdgVLTqddOFdn6n│100  │102   ┃
┗━━━━━━━━━━━━━━━━━━━━━━┷━━━━━┷━━━━━━┛

次はentity-select関数を用いて、エンティティsalesorderの一覧としても確認してみます。

以下のようにエンティティの内容を取得することができました。状態機械statusはconfirmingとして表示されています。

kaleidox> entity-select 'salesorder
Table[3x1]
kaleidox> :show:pretty
┏━━━━━━━━━━━━━━━━━━━━━━┯━━━━━┯━━━━━━━━━━┓
┃id                    │price│status    ┃
┣━━━━━━━━━━━━━━━━━━━━━━┿━━━━━┿━━━━━━━━━━┫
┃51QK2CqGdgVLTqddOFdn6n│100  │confirming┃
┗━━━━━━━━━━━━━━━━━━━━━━┷━━━━━┷━━━━━━━━━━┛

entity-select関数は、エンティティに対する検索を行い検索結果をテーブル形式で取得します。

entity-query関数は、エンティティに対する検索を行い、検索結果をエンティティのシーケンスとして取得します。

こちらもエンティティの内容を取得することができました。

kaleidox> entity-query 'salesorder
[id:51QK2CqGdgVLTqddOFdn6n;price:100;status:confirming]
kaleidox> .head
id:51QK2CqGdgVLTqddOFdn6n;price:100;status:confirming
kaleidox> :show:pretty
┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┓
┃Name  │Value                 ┃
┣━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━┫
┃id    │51QK2CqGdgVLTqddOFdn6n┃
┃price │100                   ┃
┃status│confirming            ┃
┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━┛

まとめ

今回は状態機械を持っているエンティティをデータベースに格納して管理する方法について説明しました。

次回はイベントに対するエンティティの振る舞いについて検証します。

諸元

Kaleidox
0.3.3