2024年3月31日日曜日

Cozyモデル駆動開発/成果物の管理

今回はCozyモデル駆動開発で作成する成果物の管理方法について考えてみます。

ファイル構成

図はCozyモデル駆動開発におけるプロジェクトのファイル構成の案です。

プログラムはScalaによる記述を想定しているのでsbtのファイル構成を踏襲しています。

srcディレクトリの配下に以下のディレクトリを配置します。

main
メインのソースコード
test
テストのソースコード

さらにmain配下に以下のディレクトリ配置します。

cozy
Cozyモデル
scala
Scalaプログラム

以下でそれぞれの内容について説明します。

Cozyモデル

src/main/cozyディレクトリにCozyモデルを格納します。

Cozyでは、Cozyモデルと後述のScalaモデルの両方のコンテンツの情報を統合してプロジェクトのCozyモデルを作成します。

CozyモデルはScalaモデルで記述しきれない仕様をCozy形式で記述します。

cozyディレクトリには以下のディレクトリを配置します。それぞれのディレクトリが対応するモデルを格納します。

  • business : ビジネス・モデル
  • requirement : 要求モデル
  • analysis : 分析モデル
  • design : 設計モデル

ビジネス・モデル

businessディレクトリにはビジネス・モデルを格納します。

ソフトウェア開発におけるビジネス・モデルでは、開発するソフトウェアのビジネス上の位置づけや用途などのビジネス文脈を記述します。

ビジネス・システムを駆動するビジネス・モデルを作成し、これをソフトウェア開発の入力にするのが理想ですが、そのような本格的なアプローチが取れない場合はソフトウェア開発に必要な範囲でビジネス文脈を整理するのがよいでしょう。

前回はEssenseのアルファとしてビジネス・システム・アルファとして定義しました。

記述するモデルの候補としては以下のものが考えられます。

  • ビジョン
  • ゴール
  • ビジネス・コンテキスト
  • ビジネス・ユースケース
  • ドメイン・モデル

また、ビジネスの計測と結果のフィードバック・ループに対応するため以下のモデルについても扱っていく予定です。

  • KGI(Key Goal Indicator)
  • KPI(Key Performance Indicator)

モデルは基本的には通常ファイルに自然言語のテキストとして記述する形になりますが、コンテキスト・モデルは図として記述するのが適切なので、これをどのように記述するのかは工夫が必要そうです。

要求

requirementディレクトリには要求モデルを格納します。

記述するモデルの候補としては以下のものが考えられます。

  • ビジョン
  • ゴール
  • コンテキスト・モデル
  • ユースケース

ビジョン、ゴール、コンテキスト・モデル、ユースケースはビジネス・モデルと同じ項目ですが、開発対象となるソフトウェア・システムのビジョン、ゴール、コンテキスト・モデル、ユースケースという点が異なります。

またビジネス・モデルで定義したKGIやKPIも、必要に応じて要求モデルに引き継がれるでしょう。

モデルの記述方法はビジネス・モデルと同様に通常ファイルに自然言語で記述するものが中心となります。

分析

analysisディレクトリには分析モデルを格納します。

作業分野「分析」と「設計」はEssense標準ではShape the Systemアクティビティ空間に所属するアクティビティということになるでしょう。

分析はPIM(Platform Independent Model)を、設計はPSM(Platform Specific Model)を記述するというモデルの抽象度に違いがあります。

しかし、実装主導の開発では開発ではPIMを意識しつつPSM主体の設計になると思うので、実務的には要求から直接設計に進み、必要に応じて分析も行う、というところが実際のところかもしれません。この点も加味して分析について検討していく予定です。

設計

designディレクトリには設計モデルを格納します。

設計モデルでは、実装に直結したモデルを構築します。

設計モデルを考える上で重要なのはプログラム自身が設計になっているといる点です。特にオブジェクト指向プログラミングで記述した場合には、オブジェクト・モデルとのインピーダンス・ミスマッチが低いので、場合によってはそのままモデルと考えてもよいかもしれません。

しかし、プログラミングの場合はプログラム動作上のさまざまな処理を追加していくため、抽象的なモデルとずれてきたり、境界が曖昧になってきます。この問題に対応するためアノテーションの仕組みを使ってモデルとして扱う箇所とモデルとして位置づけを記述していくことになるでしょう。また、JavaやScalaではプログラム内に仕様を自然言語で記述する仕組みがあるので、これを用いてより詳細にモデルの情報を記述することができます。

すべてのモデルをアノテーション付きプログラムで記述することはできないので、この記述できない部分は別の形でモデルを記述することになります。

一つはドメイン・モデルなど定型化して形式で記述したモデルです。

このモデルからはインタープリタで直接実行したり、実装を自動生成することができます。

最後にアーキテクチャ全体の情報やコンポーネントやクラスなどの仕様に関して自然言語による記述を行う場合があります。このような記述は通常ファイルに記述した上でプログラム・モデルと統合して設計モデルを組み上げていくことになるでしょう。

Scalaプログラム

本シリーズではプログラミング言語にScalaを用います。

Scalaプログラムとして開発するソフトウェアの本体のプログラムとテスト・プログラムを作成します。

本体

開発ターゲットの本体のScalaプログラムはsrc/main/scalaディレクトリに格納します。

前述したようにScalaプログラムにアノテーションとScaladocを併用することで、モデルとして扱うことができます。

プログラミング言語としてScalaを使用するので、Scaladoc形式でインタフェース仕様を記述します。

Cozyは、モデル、プログラム、テスト・ケースからそれぞれに記述された仕様を集め、マージしたものをプロジェクトの仕様として統合します。

テスト

Scalaによるテスト・プログラムはsrc/test/scalaディレクトリに格納します。

Cozyモデル駆動開発ではTDD(Test-Driven Development)、BDD(Behavior-Driven Development)でテストを動く仕様(executable specification)として扱っていきます。

これはテストを要求仕様、コンポーネント仕様として作成して活用していくという意図があります。

この場合は、自然言語で記述した要求モデルとどのように連携していくのかがポイントとなるでしょう。

TDD, BDDの基盤としてscalatestを使用する想定です。

まとめ

今回はCozyモデル駆動開発で作成する成果物の管理方法について考えてみました。

次回はビジネス・モデルについて具体的に考えていく予定です。

2024年2月29日木曜日

Cozyモデル駆動開発/ビジネス・モデリング

モデル駆動開発ワークベンチCozyを使用したモデル駆動開発を具体的に考えて、Cozyへの機能要件を洗い出しています。

Cozyを使用したモデル駆動開発をCozyモデル駆動開発 (Cozy Model-Driven Development)、略してCMDDと呼ぶことにします。

ビジネス・モデリングの位置付け

まず最初の作業分野はビジネス・モデリングです。

ビジネス・モデリングの関心分野の候補としては、カスタマー(Customer)とソルーション(Solution)が考えられます。

Essenceカーネルではカスタマーには以下のアルファが定義されています。

  • ステークホルダー(Stakeholders)
  • 機会(Opportunity)

またソルーションには以下のアルファが定義されています。

  • 要求(Requirements)
  • ソフトウェア・システム(Software System)

ビジネス・モデリングをどの関心分野のどのアルファにどのような形で組み込むかは、開発プロセス内でのビジネス・モデリングの目的によって変わってくるところでしょう。

CMDDでは小規模チームで、中小規模アプリケーションまたは大規模アプリケーションのサブシステムの開発をターゲットにします。

ビジネス・モデリングは要求アルファの入力の情報になるものなので、ソルーションの補助的な活動と考えると要求アルファにアクティビティと成果物を追加するアプローチも考えられます。

また、ビジネス・モデリングはソルーションの外側、カスタマー側の情報と考えるとカスタマー側に配置することも考えられます。その場合はステークホルダー、機会といったアルファに入れるよりも、新しくビジネス・モデリングを行うアルファを追加するのがよさそうです。

今回は関心分野カスタマーに新規のアルファとしてビジネス・システム・アルファを追加して検討を進めることにします。

ビジネス・システム・アルファ

ビジネス・モデリングではビジネス・システムをモデリングします。

このビジネス・システムをモデリングするアルファをビジネス・システム・アルファとします。ビジネス・システム・アルファの成果物(Work Product)として以下のモデルを考えます。具体的なモデルを作ってみて調整していく予定です。

  • ビジョン
  • ビジネス・コンテキスト
  • ビジネス・ユースケース
  • ドメイン・モデル

ビジョン

ビジョン(Vision)はプロジェクトのビジネス・システム上のビジョンです。

プロジェクトの全体の方向性を定めます。

このビジョンをベースに、要求アルファでソフトウェア開発上のビジョンを定める想定です。

ビジネス・コンテキスト

ビジネス・システムのおかれている環境をビジネス・コンテキストとしてモデル化します。

ビジネス・システムと関係する外部組織の存在とインタラクションを明確にします。

ビジネス・ユースケース

ビジネス・ユースケース(Business UseCase)はビジネス・システムにおけるユースケースです。

ビジネス・コンテキストの舞台の上で、ビジネス・システムの各種利用者が体験する物語を記述します。

開発対象のソフトウェア・システムはこの物語の中に登場し、物語の遂行を助けます。

要求アルファで作成する(システム)ユースケースは、ビジネス・ユースケースの物語の中での利用方法を切り口に・ソフトウェア・システムと利用者の対話の物語を記述することになります。

ドメイン・モデル

ドメイン・モデル(Domain Model)はソフトウェア・システムの操作対象となる問題領域(problem domain)のモデルです。

ドメイン・モデルはビジネス・システムとソフトウェア・システムで一本化したいので、その方向で検討を進めます。

ただ、ビジネス・システムとソフトウェア・システムのモデルの規模が違いすぎる場合は別立てで管理した方がよいかもしれません。そのあたりの考察も進めていきたいと思います。

まとめ

今回は要求アルファの入力となるビジネス・モデリングの扱いについて考えてみました。

ここでは、関心分野カスタマー側にビジネス・システム・アルファとして入れることにしましたが、その是非は今後の考察で検討していきたいと思います。

次回は、ビジネス・システム・アルファの具体的な検討に入る前に、モデルなどの成果物の管理方法について検討します。

2024年1月31日水曜日

Cozyによるモデル駆動開発

モデル駆動開発のエコシステムを支えるキーパーツとして、Webアプリケーション・フレームワークのCozy Webを開発してきました。

今回からCozyを使用したモデル駆動開発について具体的に考えていきたいと思います。

Cozyは現在開発途中ですが、モデル駆動開発に必要な機能を拡張していく上でどのような機能が必要かの洗い出しが目的です。要求モデルにおけるユースケース分析的なアプローチですね。

EssenceというOMGが標準化している開発方法論フレームワークの枠組み、用語を使用して考えていくことにします。

実際の検討に入る前に、今回は開発方法論を記述する言語として使用するEssenceについてみていきます。

Essence

EssenceはSoftware Engineering Methods and TheoryObject Management Group®によって作成された開発方法論のフレームワークです。

  • https://www.omg.org/spec/Essence/

Essenceでは開発方法論を記述する言語(Language)と基本的な枠組み(Kernel)を定めており、この骨組みにプラクティスで肉付けすることで開発方法論を定義します。

Essenceの言語では、従来のモデリングの用語に加えて、新しい用語が導入されています。その点も含めて用語の整理をします。

基本概念

アルファ

アルファ(alpha)は開発作業を進める上で必要な各種概念を表現したものです。アルファの具体例として要求(requirement)やソフトウェア・システム(software system)があります。

アルファに対応する日本語用語はないと思うので、本Blogではアルファの用語を使います。

ワーク・プロダクト

ワーク・プロダクト(work product)はソフトウェア開発の各種成果物です。

ワーク・プロダクトの例としては要求仕様(要求アルファ)やコード(ソフトウェア・システム・アルファ)があります。

アクティビティ

アクティビティ(activity)はソフトウェア開発の活動を表現します。

アクティビティの例としては「ミーティングを開く(Holding a meeting)や「コードを書く(Writing Code)」があります。

アクティビティ・スペース

アクティビティ・スペース(activity space)はソフトウェア開発の活動分野を表現したものです。

アクティビティ・スペース内でアクティビティが実行されます。

アクティビティ・スペースの例としては「ステークホルダー・ニーズを理解する」や「システムを実装する」などがあります。

コンピテンシィ

コンピテンシィ(competency)は作業を進めるのに必要な各種能力です。

コンピテンシィの具体例としてはステークホルダー代表(カスタマー関心分野)や開発(ソルーション関心分野)などがあります。

関心分野

Essenceではソフトウェア開発の関心分野(area of concern)として以下の3つを定義しています。

  • カスタマー
  • ソルーション
  • エンデバー

Essenceのアクティビティはこの関心分野のどれかに所属することになります。

カスタマー

カスタマー(customer)はソフトぅエア・システムの利用方法についての関心分野です。

Essenceカーネルではカスタマーの関心分野で以下のアルファを定義しています。

  • ステークホルダー (stakeholder)
  • 機会 (opportunity)

また以下のアクティビティ・スペースを定義しています。

  • 可能性を探る (Explore Possibilities)
  • ステークホルダー・ニーズを理解する (Understand Stakeholder Needs)
  • ステークホルダーの満足を確保する (Ensure Stakeholder Satisfaction)
  • システムを利用する (Use the System)

ソリューション

ソルーション(solution)はソフトウェアの実現に関しての関心分野です。

Essenceカーネルではソルーションの関心分野で以下のアルファを定義しています。

  • 要求 (requirement)
  • ソフトウェア・システム (software system)

また以下のアクティビティ・スペースを定義しています。

  • 要求を理解する (Understand the Requirements)
  • システムを形作る (Shape the System)
  • システムを実装する (Implement the System)
  • システムをテストする (Test the System)
  • システムを配備する (Deploy the System)
  • システムを運用する (Operate the System)

エンデバー

エンデバー(endevor)は直訳すると「努力」ですが、ここではチームが努力して進める活動というような意味合いから選ばれた用語ではないかと思います。

エンデバーに対応する日本語用語はないと思うので、本Blogではエンデバーの用語を使います。

Essenceカーネルではソルーションの関心分野で以下のアルファを定義しています。

  • チーム (team)
  • ワーク (work)
  • 仕事の進め方 (way of working)

また以下のアクティビティ・スペースを定義しています。

  • ワークの準備をする (Prepare to do the Work)
  • アクティビティを協調させる (Coordinate Activity)
  • チームをサポートする (Support the Team)
  • 進捗を追尾する (Track Progress)
  • ワークを止める (Stop the Work)

まとめ

本ブログでCozyを使用したモデル駆動開発に対する分析を始めることにしました。

開発プロセスを記述するためのフレームワークとしてOMGのEssenceを使用します。今回はその準備としてEssenceの用語の整理をしました。

次回はビジネス・モデリングんいついて考えます。

2023年12月31日日曜日

Cozy Web/グリッド内カードのカスタマイズ

モデル駆動開発のエコシステムのキーパーツとして、Webアプリケーション・フレームワークのCozy Webを紹介しています。

前回はWebフロントエンド・フレームワークBootstrapの上でグリッド表示を行いました。

今回はグリッド内に配置されるカードをカスタマイズする方法について説明します。

HelloGrid

前回作成したリソースのグリッド表示を行うHelloGridを使用します。

HelloGridはBootstrap 5を使ってグリッド表示を行います。

Cozy Web/Webライブラリで紹介したWebライブラリ機能を使用してBootstrap 5をサポートするBootstrap5ライブラリを使用しています。

準備

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

モデル

WEB-INF/models/model.orgにアプリケーションで使用するモデルが定義します。これはHelloResourceと同じ設定です。

* entity  
** product
*** features  
table=product
*** attributes  
| Name  | Type   | Multiplicity |
|-------+--------+--------------|
| id    | token  |            1 |
| name  | string |            1 |
| price | int    |            1 |

エンティティproductを定義しており、Webのリソースproductとなります。

webapp.conf

ホームディレクトリのWEB-INFディレクトリ配下にwebapp.confを配置し、Bootstrap5ライブラリの名前である「bootstrap5」をextend属性に設定します。この設定を行うことで、Bootstrap5ライブラリがWebアプリケーションのベースとして設定されます。

またthemeにbootstrap-gridを設定します。この設定により一覧表示がグリッドで表示されます。

ここまでが前回の設定です。

extend: bootstrap5
theme: bootstrap-grid

今回はグリッドの表示方法をカスタマイズします。

このためにwebapp.confに以下のようにrender.card_kind_in_gridを設定します。

render.card_kind_in_gridに"grid"を指定しているので、card__gridという名前のウィジェットがグリッド内のカードの表示に使用されます。

extend: bootstrap5
theme: bootstrap-grid
render.card_kind_in_grid: "grid"

カードの表示内容の設定

グリッド内にカードを表示するときに使用するウィジェットcard_gridを作成します。

以下のJade形式の card__grid.jade 作成し、WEB-INF/widgets配下に配置します。

-@val card: ViewCard
div.card
  div.card-header
    h4.card-title
      =card.header

ファイル名card__grid.jadeの「card」はウィジェット種別を表しこの場合はカードを示します。「__」の後のgridはウィジェットの名前を表します。

カードを記述するDIV要素以下をカードの部品として定義しています。部品内ではHTMLの要素を自由に使うことができます。

部品内のHTML要素ではBootstrapで定義されているcard, card-header, card-titleといったクラス属性を指定しているので、Bootstrapによって適切な表示が行われます。

また、カードの表示に必要な情報はViewCardオブジェクトとして渡されてくるので、ここから必要な情報を取り出してウィジェット内のHTML要素に埋め込んでいきます。ここではカードのヘッダ情報をH4要素に入れています。

グリッド表示

それでは、グリッド表示を行います。

Webブラウザ

productリソースに対するアクセスを行います。

http://localhost:8080/web/HelloGrid/product

webページの表示のために出力されたHTML文書は以下になります。

<?xml version="1.0"?>
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
    <link crossorigin="anonymous" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"/>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"/>
    <style>
      .card a {
      color: inherit;
      text-decoration: none;
      }
      .card:hover {
      background-color: #f8f9fa;
      border-color: #007bff;
      }
    </style>
  </head>
  <body>
    <script crossorigin="anonymous" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"/>
    <nav class="navbar navbar-light bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand mb-0 h1">Cozy Web</span>
      </div>
    </nav>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"/>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Features</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Pricing</a>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <div class="container">
      <div class="row">
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <div class="card-header">
              <h4 class="card-title">
          Apple
        </h4>
            </div>
          </div>
        </div>
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <div class="card-header">
              <h4 class="card-title">
          Orange
        </h4>
            </div>
          </div>
        </div>
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <div class="card-header">
              <h4 class="card-title">
          Peach
        </h4>
            </div>
          </div>
        </div>
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <div class="card-header">
              <h4 class="card-title">
          Berry
        </h4>
            </div>
          </div>
        </div>
      </div>
    </div>
    <nav class="navbar navbar-light bg-secondary">
      <span>Cozy Web</span>
    </nav>
  </body>
</html>

前回のグリッド表示ではカード部分は以下のHTML断片になっていました。

          <div class="card">
            <a href="product/1.html" class="">
              <img src="assets/img/no-image-icon.png" class="card-img-top"/>
              <div class="card-header">
                <h4 class="card-title">Apple</h4>
              </div>
            </a>
          </div>

今回は、以下のHTML断片になっており、グリッド内のカードはcard__grid.jadeで定義したものが使用されています。

          <div class="card">
            <div class="card-header">
              <h4 class="card-title">
          Apple
        </h4>
            </div>
          </div>

まとめ

Cozy Webでリソースの定義をすることで、Webアプリケーションが簡単に作成できることを確認してきました。

今回はBootstrapによるレスポンシブル・デザインによるグリッド表示のカードをカスタマイズする方法について説明しました。

モデルの定義とカスタマイズしたカードの定義以外はほとんどノープログラミングでレスポンシブルなWebアプリケーションの作成ができることを確認することができました。

諸元

Cozy
0.0.16

2023年11月30日木曜日

Cozy Web/グリッド

モデル駆動開発のエコシステムのキーパーツとして、Webアプリケーション・フレームワークのCozy Webを紹介しています。

数回に渡りリソースの一覧表示の方法について見てきました。その一環でグリッドとカードによる一覧表示を取り上げます。グリッドとカードによる覧表表示はHTMLの標準機能では用意されていないので、この機能をサポートしているWebフロントエンド・フレームワークBootstrapを導入し、その上でグリッドとカードによるリソースの一覧表示を行うことにします。

HelloGrid

リソースのグリッド表示を行うHelloGridを作成します。

HelloGridはBootstrap 5を使ってグリッド表示を行います。

Cozy Web/Webライブラリで紹介したWebライブラリ機能を使用してBootstrap 5をサポートするBootstrap5ライブラリを使用します。Cozy Web/Webライブラリで紹介したWebライブラリ機能を使用してBootstrap 5をサポートするBootstrap5ライブラリを使用します。

準備

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

モデル

WEB-INF/models/model.orgにアプリケーションで使用するモデルが定義します。これは前回まで使用していたHelloResourceと同じ設定です。

* entity  
** product
*** features  
table=product
*** attributes  
| Name  | Type   | Multiplicity |
|-------+--------+--------------|
| id    | token  |            1 |
| name  | string |            1 |
| price | int    |            1 |

エンティティproductを定義しており、Webのリソースproductとなります。

webapp.conf

ホームディレクトリのWEB-INFディレクトリ配下にwebapp.confを配置し、Bootstrap5ライブラリの名前である「bootstrap5」をextend属性に設定します。この設定を行うことで、Bootstrap5ライブラリがWebアプリケーションのベースとして設定されます。

extend: bootstrap5

一覧表示

CozyのWebライブラリBootstrap5はデフォルトでは、tableタグを使った一覧表示を行います。

Webブラウザ

前回と同様にproductリソースに対するアクセスを行います。

http://localhost:8080/web/HelloGrid/product

Cozy Web/Webライブラリで説明したとおり、WebライブラリBootstrapでは、ナビゲーションバーやヘッダー、フッターを使用した画面レイアウトになります。Cozy Web/Webライブラリで説明したとおり、WebライブラリBootstrapでは、ナビゲーションバーやヘッダー、フッターを使用した画面レイアウトになります。

画面内のコンテンツの部分にBootstrapによって綺麗にレイアウトされた表が表示されました。

HTML

webページの表示のために出力されたHTMLは以下になります。

tableタグにはBootstrap用のclass属性が設定されているため、Bootstrap標準の綺麗な表が出力されます。

<?xml version="1.0"?>
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
    <link crossorigin="anonymous" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"/>
    <style>
      .card a {
      color: inherit;
      text-decoration: none;
      }
      .card:hover {
      background-color: #f8f9fa;
      border-color: #007bff;
      }
    </style>
  </head>
  <body>
    <script crossorigin="anonymous" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"/>
    <nav class="navbar navbar-light bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand mb-0 h1">Cozy Web</span>
      </div>
    </nav>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"/>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Features</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Pricing</a>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <div class="table-responsive">
      <table class="table">
        <caption class="">product</caption>
        <thead class="">
          <tr class="">
            <th class="" scope="col">Id</th>
            <th class="" scope="col">Name</th>
            <th class="" scope="col">Price</th>
          </tr>
        </thead>
        <tbody class="">
          <tr data-href="product/1.html">
            <td class="">1</td>
            <td class="">Apple</td>
            <td class="">300</td>
          </tr>
          <tr data-href="product/2.html">
            <td class="">2</td>
            <td class="">Orange</td>
            <td class="">350</td>
          </tr>
          <tr data-href="product/3.html">
            <td class="">3</td>
            <td class="">Peach</td>
            <td class="">400</td>
          </tr>
          <tr data-href="product/4.html">
            <td class="">4</td>
            <td class="">Berry</td>
            <td class="">450</td>
          </tr>
        </tbody>
      </table>
    </div>
    <nav class="navbar navbar-light bg-secondary">
      <span>Cozy Web</span>
    </nav>
  </body>
</html>

グリッド

それでは、グリッド表示を行います。

webapp.conf

Bootstrapアプリケーションの一覧表示のデフォルト設定をwebapp.confに設定することができます。

webapp.confのthemeにテーマbootstrap-gridを設定します。

テーマbootstrap-gridは一覧表示のデフォルト設定をグリッドにしたテーマです。

extend: bootstrap5
theme: bootstrap-grid

Webブラウザ

productリソースに対するアクセスを行います。

http://localhost:8080/web/HelloGrid/product

表示結果は以下になります。

無事Bootstrapのグリッド表示になります。グリッド内にはリソースの内容を記述するカードが表示されています。

デフォルトでは、カード内にはリソースの内容を表す画像どリソースの名前が表示されます。productでは画像の情報は持っていないので、画像なしを示す画像が表示されています。

Bootstrapのグリッドは画面サイズを変えても適切にレイアウトが行われます。先ほどのWebページの横幅を小さく縮めたWebページが以下になります。

HTML

webページの表示のために出力されたHTMLは以下になります。

グリッド内のカードはclass属性にcardが設定されたdevタグで記述されています。

<?xml version="1.0"?>
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1" name="viewport"/>
    <link crossorigin="anonymous" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"/>
    <style>
      .card a {
      color: inherit;
      text-decoration: none;
      }
      .card:hover {
      background-color: #f8f9fa;
      border-color: #007bff;
      }
    </style>
  </head>
  <body>
    <script crossorigin="anonymous" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"/>
    <nav class="navbar navbar-light bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand mb-0 h1">Cozy Web</span>
      </div>
    </nav>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"/>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Features</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Pricing</a>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <div class="container">
      <div class="row">
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <a href="product/1.html" class="">
              <img src="assets/img/no-image-icon.png" class="card-img-top"/>
              <div class="card-header">
                <h4 class="card-title">Apple</h4>
              </div>
            </a>
          </div>
        </div>
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <a href="product/2.html" class="">
              <img src="assets/img/no-image-icon.png" class="card-img-top"/>
              <div class="card-header">
                <h4 class="card-title">Orange</h4>
              </div>
            </a>
          </div>
        </div>
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <a href="product/3.html" class="">
              <img src="assets/img/no-image-icon.png" class="card-img-top"/>
              <div class="card-header">
                <h4 class="card-title">Peach</h4>
              </div>
            </a>
          </div>
        </div>
        <div class="col-12 col-sm-6 col-md-3 col-lg-2 col-xl-1">
          <div class="card">
            <a href="product/4.html" class="">
              <img src="assets/img/no-image-icon.png" class="card-img-top"/>
              <div class="card-header">
                <h4 class="card-title">Berry</h4>
              </div>
            </a>
          </div>
        </div>
      </div>
    </div>
    <nav class="navbar navbar-light bg-secondary">
      <span>Cozy Web</span>
    </nav>
  </body>
</html>

まとめ

前回まではCozy Webでリソースの定義をすることで、Webアプリケーションが簡単に作成できることを説明しました。

今回は画面の表示方法のカスタマイズとして、グリッド表示を取り上げました。

テーマの設定のみで一覧表示をtableタグによる表からBootstrapならではのグリッド表示にすることができました。また、グリッド表示はBootstrapの機能により画面サイズに従って適切なレイアウトが行われることが確認できました。

モデルの定義以外はほとんどノープログラミングでWebアプリケーションの作成ができることが分かりました。

次回はグリッド表示のカスタマイズ方法について取り上げる予定です。

諸元

Cozy
0.0.15

2023年10月31日火曜日

Cozy Web上でエンティティのモデル定義によって設定されたリソースの一覧アクセスをノンプログラミングで行う方法についてみています。

前回はリソースの一覧表示の機能機能について説明します。

今回はページング機能について説明します。

HelloResource

リソースの作成・更新処理の題材としてHelloResourceを定義しました。

このHelloResourceを使用して、リソースの一覧表示の方法についてみています。

準備

cozyを起動するディレクトリのwebappsディレクトリに、アプリケーションのホームディレクトリとなるHelloResourceが作成されています。これをベースに開発を進めます。

モデル

WEB-INF/models/model.orgにアプリケーションで使用するモデルが定義されています。

* entity  
** product
*** features  
table=product
*** attributes  
| Name  | Type   | Multiplicity |
|-------+--------+--------------|
| id    | token  |            1 |
| name  | string |            1 |
| price | int    |            1 |

エンティティproductを定義しています。

これがWebのリソースproductとなります。

トップページ

トップページとしてホームディレクトリに以下のindex.htmlを配備しています。これは前回と同様です。

<html>
    <head>
	<title>HelloResource</title>
    </head>
    <body>
	<ul>
	    <li><a href="product">List</a></li>
	    <li><a href="product/_create_">Create</a></li>
	    <li><a href="product/_update_">Update</a></li>
	    <li><a href="product/_delete_">Delete</a></li>
	</ul>
    </body>
</html>

一覧表示

まず、前回紹介したパラメタなしの場合です。

以下のようにリソース名を指定してアクセスします。

$ curl http://localhost:8080/web/HelloResource/product

その結果、以下のページが表示されます。(この結果はxmllintで整形しています。他のHTML出力も同様です。)

<?xml version="1.0"?>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="keywords" content="TBD"/>
    <meta name="description" content="TBD"/>
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="author" content="TBD"/>
    <title>product</title>
  </head>
  <body>
    <div>
      <table class="">
        <caption class="">product</caption>
        <thead class="">
          <tr class="">
            <th scope="col" class="">Id</th>
            <th scope="col" class="">Name</th>
            <th scope="col" class="">Price</th>
          </tr>
        </thead>
        <tbody class="">
          <tr data-href="product/1.html">
            <td class="">1</td>
            <td class="">Apple</td>
            <td class="">300</td>
          </tr>
        </tbody>
      </table>
      <table>
        <tr>
          <td>Prev</td>
          <td>
            <a href="#?query.offset=0&amp;query.page.size=1">1</a>
          </td>
          <td>
            <a href="#?query.offset=1&amp;query.page.size=1">2</a>
          </td>
          <td>
            <a href="#?query.offset=2&amp;query.page.size=1">3</a>
          </td>
          <td>
            <a href="#?query.offset=3&amp;query.page.size=1">4</a>
          </td>
          <td>Next</td>
        </tr>
      </table>
    </div>
  </body>
</html>

ページング

次はページング機能を使用してみます。

ページング機能を使用する場合、query.page.sizeパラメタで1ページ数あたりの行数を指定します。

以下のようにリソース名のページを指定して、queyr.page.sizeに「1」を指定して実行してみます。

$ curl "http://localhost:8080/web/HelloResource/product?query.page.size=1"

この結果、以下のページが表示されました。

<?xml version="1.0"?>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="keywords" content="TBD"/>
    <meta name="description" content="TBD"/>
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="author" content="TBD"/>
    <title>product</title>
  </head>
  <body>
    <div>
      <table class="">
        <caption class="">product</caption>
        <thead class="">
          <tr class="">
            <th scope="col" class="">Id</th>
            <th scope="col" class="">Name</th>
            <th scope="col" class="">Price</th>
          </tr>
        </thead>
        <tbody class="">
          <tr data-href="product/1.html">
            <td class="">1</td>
            <td class="">Apple</td>
            <td class="">300</td>
          </tr>
        </tbody>
      </table>
      <table>
        <tr>
          <td>Prev</td>
          <td>
            <a href="#?query.offset=0&amp;query.page.size=1">1</a>
          </td>
          <td>
            <a href="#?query.offset=1&amp;query.page.size=1">2</a>
          </td>
          <td>
            <a href="#?query.offset=2&amp;query.page.size=1">3</a>
          </td>
          <td>
            <a href="#?query.offset=3&amp;query.page.size=1">4</a>
          </td>
          <td>Next</td>
        </tr>
      </table>
    </div>
  </body>
</html>

query.page.sizeに「1」を指定しているので1ページあたり1行表示のページングが行われています。データ表示のテーブルの下部にページング移動用のテーブルが表示されています。全レコード数が4なので、4ページの構成となっています。

まとめ

今回はリソースの一覧表示のページングについて説明しました。

次回はカスタム画面などの機能について説明する予定です。

諸元

Cozy
0.0.15

2023年9月30日土曜日

Cozy Web/一覧表示

Cozy Web上でエンティティのモデル定義によって設定されたリソースの一覧アクセスをノンプログラミングで行う方法についてみていきます。

今回はリソースの一覧表示の機能機能について説明します。

HelloResource

リソースの作成・更新処理の題材としてHelloResourceを定義しました。

今回からこのHelloResourceを使用して、リソースの一覧表示の方法についてみていきます。

準備

cozyを起動するディレクトリのwebappsディレクトリに、アプリケーションのホームディレクトリとなるHelloResourceが作成されています。これをベースに開発を進めます。

モデル

WEB-INF/models/model.orgにアプリケーションで使用するモデルが定義されています。

* entity  
** product
*** features  
table=product
*** attributes  
| Name  | Type   | Multiplicity |
|-------+--------+--------------|
| id    | token  |            1 |
| name  | string |            1 |
| price | int    |            1 |

エンティティproductを定義しています。

これがWebのリソースproductとなります。

トップページ

トップページとしてホームディレクトリに以下のindex.htmlを配備しています。

<html>
    <head>
	<title>HelloResource</title>
    </head>
    <body>
	<ul>
	    <li><a href="product">List</a></li>
	    <li><a href="product/_create_">Create</a></li>
	    <li><a href="product/_update_">Update</a></li>
	    <li><a href="product/_delete_">Delete</a></li>
	</ul>
    </body>
</html>

リソース一覧のシナリオ

リソースの作成・更新では入力フォームの入力から始まるシナリオによって、複数の画面によって作成・更新処理を実行しました。

一方、リソース一覧では一覧表示したいリソースの名前と表示方法を指定するパラメタを渡すことで、すぐに表示結果を得ることができます。

実行

それではリソース一覧の表示を行ってみましょう。

一覧表示

まず、パラメタなしの場合です。

以下のようにリソース名を指定してアクセスします。

$ curl http://localhost:8080/web/HelloResource/product

その結果、以下のページが表示されました。(この結果はxmllintで整形しています。他のHTML出力も同様です。)

<?xml version="1.0"?>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="keywords" content="TBD"/>
    <meta name="description" content="TBD"/>
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="author" content="TBD"/>
    <title>product</title>
  </head>
  <body>
    <table class="">
      <caption class="">product</caption>
      <thead class="">
        <tr class="">
          <th scope="col" class="">Id</th>
          <th scope="col" class="">Name</th>
          <th scope="col" class="">Price</th>
        </tr>
      </thead>
      <tbody class="">
        <tr data-href="product/1.html">
          <td class="">1</td>
          <td class="">Apple</td>
          <td class="">300</td>
        </tr>
        <tr data-href="product/2.html">
          <td class="">2</td>
          <td class="">Orange</td>
          <td class="">350</td>
        </tr>
        <tr data-href="product/3.html">
          <td class="">3</td>
          <td class="">Peach</td>
          <td class="">400</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

範囲指定

query.offsetパラメタで開始オフセットを、query.limitパラメタで表示の範囲を指定することができます。

以下の例ではquery.offsetに1、query.limitに1を指定しているので、2番目のレコードから1レコードを表示します。

$ curl "http://localhost:8080/web/HelloResource/product?query.offset=1&query.limit=1"

実行の結果、以下のようにレコード数1の表が表示されました。

<?xml version="1.0"?>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="keywords" content="TBD"/>
    <meta name="description" content="TBD"/>
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="author" content="TBD"/>
    <title>product</title>
  </head>
  <body>
    <table class="">
      <caption class="">product</caption>
      <thead class="">
        <tr class="">
          <th scope="col" class="">Id</th>
          <th scope="col" class="">Name</th>
          <th scope="col" class="">Price</th>
        </tr>
      </thead>
      <tbody class="">
        <tr data-href="product/2.html">
          <td class="">2</td>
          <td class="">Orange</td>
          <td class="">350</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

カラム指定

query.columnsパラメタで表示するカラムを指定することができます。

以下の例ではquery.columnsに「name,price」を指定しているのでnameとpriceの2カラムの表を表示します。

$ curl "http://localhost:8080/web/HelloResource/product?query.columns=name,price"

実行の結果、以下のように2カラムの表が表示されました。

<?xml version="1.0"?>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="keywords" content="TBD"/>
    <meta name="description" content="TBD"/>
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="author" content="TBD"/>
    <title>product</title>
  </head>
  <body>
    <table class="">
      <caption class="">product</caption>
      <thead class="">
        <tr class="">
          <th scope="col" class="">Name</th>
          <th scope="col" class="">Price</th>
        </tr>
      </thead>
      <tbody class="">
        <tr data-href="product/1.html">
          <td class="">Apple</td>
          <td class="">300</td>
        </tr>
        <tr data-href="product/2.html">
          <td class="">Orange</td>
          <td class="">350</td>
        </tr>
        <tr data-href="product/3.html">
          <td class="">Peach</td>
          <td class="">400</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

まとめ

今回はリソースの一覧表示の基本機能について説明しました。

次回はページングやカスタム画面などの機能について説明する予定です。

諸元

Cozy
0.0.14