ManifestForm @ AboutManifest

記述例

  forms: {
    base: {
      form_name1: {
        fields: {
        }
        field_names: [
        ]
      }
    }
  },
  extend: {
    extend_form_name1: {
      fields: {
      },
      field_names: [
      ]
    },
  }
}

すべてのアイテムのカラムに対して、表示するべき入力フィールドの表示形態を定義する。ただし、拡張可能なエレメントの場合、拡張後の入力フィールドも含めて定義する。

拡張できないアイテムについてはbaseに。拡張可能なアイテムについてはextend以下に記述する。

フォームの処理はファイラーなどの処理と大きく違う部分がある。それは拡張エレメントはベースエレメントを継承して扱うことである。つまりベースエレメントの読み込みが完了していない限り、拡張エレメントを読み込むことができないのである。フォームから見て各フィールドは対等であるものの、同じように扱うことができないある。そこで、マニフェストをベースと拡張に分けることにした。ベースを先に読んで、後から拡張を読む二段階構成である。拡張フィールドはフォームのマニフェスト(ベース)を参照しながらデフォルト値を埋めていく。

記述

fields

このフォームの入力フィールドに関してhashで設定する。省略したときは空のhashが補充される。

形式はname > type > argsである 入力フィールドにはいくつかの階層がある。自分自身の階層、子の階層、孫の階層

type

入力フィールドの階層タイプを次の中から文字列で設定する。省略したときは'root'が補充される。

  • 'root'
  • 'element'
  • 'part'

args

階層に必要なオプションを与えるための設定。記述するべき内容はタイプによって違ってくる。

ElementField

typeでelementを選択したときに利用される。

model_name

この入力フィールドのモデル名に関して文字列で設定する。省略したときはフォーム名が補充される。ただし、フィールド名が.を含むとき、ピリオドより左の文字列が補充される。

column_name

この入力フィールドのカラム名に関して文字列で設定する。省略したときはフィールド名が補充される。ただし、フィールド名が.を含むとき、ピリオドより右の文字列が補充される。

label

この入力フィールドのラベルに関してhashで設定する。省略したときは空のhashが補充される。

形式はtype > argsである

type

入力フィールドのラベルタイプを次の中から文字列で設定する。省略したときは'default'が補充される。

  • 'default'
  • 'none'

args

一覧の処理に必要なオプションを与えるための設定。記述するべき内容はタイプによって違ってくる。

Default

label typeでdefaultを選択したときに利用される。

None

label typeでnoneを選択したときに利用される。

tag

この入力フィールドのタグに関してhashで設定する。省略したときは空のhashが補充される。

形式はtype > argsである

type

入力フィールドのタグタイプを次の中から文字列で設定する。省略したときは'none'が補充される。

  • text
    • 文字列を入力するためのフィールド
  • number
    • 数値を入力するためのフィールド
  • text_area
    • 長い文字列を入力するためのフィールド
  • select
    • セレクトボックスのフィールド
  • hidden
    • 隠しフィールド

args

ヘルパーの処理に必要なオプションを与えるための設定。記述するべき内容はタイプによって違ってくる。

Default

helper typeでdefaultを選択したときに利用される。

helper

この入力フィールドのヘルパーに関してhashで設定する。省略したときは空のhashが補充される。

形式はname > type > argsである

type

入力フィールドのヘルパータイプを次の中から文字列で設定する。省略したときは'none'が補充される。

  • size
  • tail_angle
  • color

args

ヘルパーの処理に必要なオプションを与えるための設定。記述するべき内容はタイプによって違ってくる。

Default

helper typeでdefaultを選択したときに利用される。

path

ヘルパーを表示するためのテンプレートのファイル名を文字列で指定する。

wrapper

ヘルパーのブロックを囲むdivタグが必要な場合、そのクラス名を文字列で設定する。デフォルトでは指定なし。

JavaScriptのコードからヘルパーの部品を操作するとき、必要な情報がヘルパーから得られなかったり、ヘルパーのブロックがどこにあるかを jqueryから検索できないようなことがある。このような場合、この設定を有効にすることでヘルパーをこのクラス名を持つdivタグで囲むことができるようになる。

現状では色を選択するヘルパーで利用している。コードの方もう少し整理すれば必要なくなるかもしれない。

options
class

画像のサイズ調整ヘルパーで利用する。ヘルパーのテンプレートからは幅サイズの調整をするためのヘルパーなのか、高さサイズのそれをするものなのかを判別することはできない。そこで、このオプションを使ってクラス名を埋め込むわけだが…もう少しうまい方法はないだろうか? 。

None

helper typeでnoneを選択したときに利用される。

row_break

この入力フィールドのに関してboolで設定する。省略したときはtrueが補充される。

  • true
  • false

field_names

このフォームの入力フィールドに関してで設定する。省略したときは空のが補充される。

入力フォームを表示するための設定。

formsはhash型である。 キーと値のペアになる。基本的には次のように各フォームの構成を個別に設定していく。

  forms: {
    element_name1: {
      form_conf...
    },
    element_name2: {
      form_conf...
    },
    element_name3: {
      form_conf...
    },
    element_name4: {
      form_conf...
    },
  },

modelsと同じようにキーの表記揺れを解消できる。フォームごとの定義をform_confと呼ぶことにする。

form_conf

大雑把に言うと、宣言のためのattributesとタグ出力するためのfieldsの二つに分かれている。 attributesで各フィールドがどのような振る舞いを期待されているかを記述し、fieldsでそれらをどのような順番で表示するかを記述する。

attributesはハッシュで記述し、キーにはフィールドの名前、値にはハッシュでフィールドの振る舞いを記述する。fieldsは配列で、attributesでキーにした名前の文字列を記述する。

サンプル

  forms: {
    element_name1: {
      attributes: {
        field_name1: {
        },
        field_name2: {
        },
        field_name3: {
        },
        field_name4: {
        },
      },
      fields: [
        'field_name3',
        'field_name1',
        'field_name4',
        'field_name2',
      ]
    },
  },

element_name1というフォームには、四つのフィールドfield_name1,field_name2, field_name3,field_name4が存在し、ページ上にはfield_name3,field_name1,field_name4,field_name2の順序で表示される。

ベーシックな入力フォームを表示するだけなら、これだけで要件を満たせるのだが、現実は相当複雑な問題を抱えているので、膨大かつ難解なオプションを用意してある。ここからは、その難解さを説明してみる。

二段階構成の謎

フィールドをページに表示する時の順序を保証するだけでよければ、attributesをハッシュではなく配列で記述すれば、わざわざ二段階に分離する必要は無い。

では、なぜこのようなニ段階構成になっているかというと、クラスを拡張できるモデルでは、ベースとなるモデルのフィールドを何度も定義する羽目になるからである。例えば、フキダシが拡張可能なエレメントである。フキダシはSpeechBalloonモデルをベースとして、拡張によってCircleSpeechBalloonPlainSpeechBalloonSquareSpeechBalloonの三つ(現時点で)に派生する。フキダシはこの分岐によって、入力フォームに必要なフィールドが変わってくる。最も分かりやすい例は「尻尾の角度」で、四角形、透明のフキダシにはそれを入力する必要がない(表示しなくて良い) 。それをサンプルにして、記述してみよう。

    circle_speech_balloon: {
        r: {
          column: 'r',
          type: 'number',
        },
        'caption': {
          column: 'caption',
          type: 'text',
        },
     },
    plain_speech_balloon: {
        r: {
          column: 'r',
          type: 'hidden',
        },
        'caption': {
          column: 'caption',
          type: 'text',
        },
     },
    square_speech_balloon: {
        r: {
          column: 'r',
          type: 'hidden',
        },
        'caption': {
          column: 'caption',
          type: 'text',
        },
     },

このような具合に違いが出る。ここではわかりやすくするために「尻尾の角度」「見出し」だけに絞って記述しているが、フキダシには(現状で)三十個ものフィールドがあるので、実際にはその全てのフィールドを記述しなければならない。これではフキダシが追加されるたびに、膨大な量の設定を記述せねばならず、メンテナンスにかかる労力が莫大となる。

これを回避するためにbaseが用意してある。

テーブルをまたぐフォーム

入力フォームはHTML上ではデータベースのテーブルと結合しているわけではない。テーブルとの結合はフレームワークレベルで行っている。つまり、一つの入力フォームに複数のテーブルの値が入力されてpostメソッドでサーバに送信される恐れがあるということである。そして、ぺったんRにはそれが実際に存在する。このような場合、Railsではテーブルの構造を厳格に区別するためにフィールドタグのname属性を[]で区切ることで対処している。

次の例は、通常のフィールドをタグにするケースで、エレメント名+カラム名で出力されるケース。

element_name[column_name]
  <input name="panel_picture[y]" size="5" type="number" value="121" />

問題が登場するのは「フキダシ」である。フキダシはエレメントパーツを持つ複合体のエレメントである。フキダシの下にバルーンとセリフが従属している。次の例は、エレメント名+エレメントパート名_attributes +カラム名で出力されるケース。

element_name[element_part_name_attributes][column_name]
  <textarea name="speech_balloon[speech_attributes][content]" cols="45" rows="5">バレンタイン</textarea>

どのモデル(入力フォーム)がどのタイプのエレメントなのかを判断することは、プログラム側からでは不可能なので、判断材料を何らかの形で記述しなければならない。これを回避するためにpartが用意してある。

ほかにもある例外的なケース

baseとパート、この二つの配慮で大抵の表現に対応できると思う。しかしながら、入力フォームの表示の際に可変的な処理をする場合、何らかの形でパラメータを与える必要がある。細かいパラメータは必要な分だけ追加して行くよりない。ここまでに登場していない仕様をピックアップしておく。

  • フィールドにリンクしたカラム名を知りたいのだけど、どうすればいい?
    • フィールドからは入力された値をどのテーブルのカラムにひき渡せばよいかがわからない。
    • columnを使う。
  • 値の入力方式を変えたいのだけど、どうすればいい?
    • ユーザに入力してほしい値がどんな形状なのかがわからない。
    • 文字・数字・改行を含む文字・リストから選択・システムが管理などが様々な入力形式が想定される。
    • typeを使う。
  • 入力をサポートするためのヘルパーを表示したいのだけど、どうすればいい?
    • フィールドがヘルパーを持つか、何を持つかが分からない。
    • サイズの調整、カラーコードのイコライザーなどが想定される。
    • helpersを使う。
  • セレクトボックスの選択肢を設定したいのだけど、どうすればいい?
    • リストアイテムをどこから取得すればよいかがわからない。
    • リストはデータベースのマスターテーブルに登録されているケースと、システムの定数に登録されているケースがある。
    • sourceを使う。
  • 新規作成の時、プライマリーキーとなるフィールドを出力してもらっては困るのだけど、どうすればいい?
    • 新規作成時はIDは空なので出力してはならない。
    • しかし、更新時は必ずIDを出力しなければならない。
    • primary_keyを使う。
  • フィールドのリキットレイアウト表示を有効/解除したいのだけど、どうすればいい?
    • フィールドのブロックを改行してよいかがわからない。
    • 画面の利用効率を上げるためにブロックを横並びにするケースと、縦並びにするケースがある。
    • row_breakを使う。
  • ラベル表示の有無を設定したいのだけど、どうすればいい?
    • ラベルを表示したくないケースがあるが、状況が分からない。
    • カラーコードのようにユーザには値の意味を理解しがたく、ヘルパーで入力させるような状況では、フィールドを表示しないが、ラベルは表示する、といった処理となる。
    • labelを使う。
  • ラベルのリキド表示を有効/解除したいのだけど、どうすればいい?
    • row_breakを使う。
  • 入力フィールドの表示サイズを設定したいのだけど、どうすればいい?
    • フィールドによって、最適なサイズは違ってくる。
    • sizeを使う。

ここからはパラメータの解説。

model

入力フォームにリンクしたモデルの名前を文字列で設定する。省略時はbaseが設定される。baseが設定されていなければ操作中のエレメント名が設定される。

このオプションは一つの入力フォームに複数のモデルのカラムを使う時を考慮して用意した。通常はエレメント名=モデル名なので設定する必要は無い。

base

baseが記述されている場合、ベースとなるエレメントのフィールドの設定も加えることができる。

入力フォームにリンクしたエレメントが拡張モデルの時にベースとなるエレメントのエレメント名を文字列で設定する。省略したときは設定なし。 通常はエレメントとモデルが一致しているのでmodelの設定と混乱しがちだが、この設定はあくまで入力フォームのフィールドをどう扱うかを決めるものである。この文字列を使ってモデルを検索してはいけない。

サンプル

    speech_balloon: {
      attributes: {
        'speech_balloon.caption': {
          column: 'caption',
          type: 'text',
        },
    },
    circle_speech_balloon: {
      base: 'speech_balloon',
      attributes: {
      },
    },

circle_speech_balloonはspeech_balloonを拡張したモデル。ベースのattributesでspeech_balloon.captionを設定しているので、拡張後のcircle_speech_balloonは、これといった記述がないにもかかわらず、speech_balloon.captionを利用できる。

ベースから拡張クラスを上書きするので設定を変えることもできる。

サンプル

    speech_balloon: {
      attributes: {
        'balloon.r': {
          part: 'balloon',
          column: 'r',
          type: 'number',
        },
    },
    square_speech_balloon: {
      base: 'speech_balloon',
      attributes: {
        'balloon.r': {
          part: 'balloon',
          column: 'r',
          type: 'hidden',
        },
      },
    },

フキダシの尻尾の角度はベースのspeech_balloonでは表示することになっているが、四角形や透明フキダシにはしっぽがないので表示しない方が見栄えが良い。そこで、これらのフキダシについては、後から表示を隠す設定に上書きしている。

attributes

入力フォームに登場するすべてのフィールドをハッシュで設定する。省略には空のハッシュ。

model

このフィールドにリンクしているモデルを文字列で指定する。省略時はデフォルトモデルが設定される。

一つの入力フォームに複数のモデルのフィールドが収まっているときにモデルを区別するために設定する。通常はエレメント名=モデル名なので設定する必要は無い

part

フィールドがelement_partの時に文字列で指定する。デフォルトでは nil

エレメントからエレメントパートを取得するためのメソッド名のこと。

サンプル

    speech_balloon: {
      attributes: {
        'speech.content': {
          part: 'speech',
          column: 'content',
          type: 'text_area',
        },
      },
    },

セリフはエレメントパートである。その中のカラム「台詞」を記述している。

attributesのキーで'speech.content':といった具合にモデル名.カラム名のフォーマットで記述していることについて補足。エレメントのパートのように複数のモデルを一つのフォームで扱うとカラム名が重複することがある。例えば、フキダシとセリフともにXY幅高さのカラムがある。単にカラム名をキーにしてしまうと両者が衝突してしまう。そこで(冗長にはなるものの)モデル名を付加して対処している。

column

フィールドにリンクしているカラム名を文字列で指定する。省略はできない。

サンプル

    panel_picture: {
      attributes: {
        link: {
          column: 'link',
        },
      },
    },

コマ絵を入力するフォームのlinkをpanel_pictureモデルのlinkカラムに関連付けている。

type

入力インターフェイスの形式を次の中から文字列で指定する。省略はできない。

サンプル

    panel_picture: {
      attributes: {
        caption: {
          column: 'caption',
          type: 'text',
        },
      },
    },

コマ絵の入力フォームのcaptionは文字を入力するためのフィールドである。

label

入力フィールドの横にラベルを表示するための設定。

type

ラベルの表示形式を文字列で指定する。デフォルトではラベルを表示する。

  • hidden
    • ラベルを表示しない

サンプル

    ground_color: {
        panel_id: {
          column: 'panel_id',
          type: 'hidden',
          label: {
            type: 'hidden'
          },
        },
      },
    },
    ground_color: {
      attributes: {
        code: {
          column: 'code',
          type: 'hidden',
          label: {
            row_break: true
          },
        },
      },
    },

背景色のパネルidはシステムが管理する値なので、フィールドと共にラベルも表示しない。しかし、カラーコードはフィールドを表示しないものの、ユーザがヘルパーを使って入力する値なので、ラベルは表示する。

row_break

ラベルの改行フラグをtrue/falseで指定する。デフォルトはfalse

表示スペースを縦方向に圧縮したい場合、ラベルとフィールドを左右に並べて表示する。ほとんどのフィールドは縦方向に圧縮したいのでこのパターンで良い。しかし、横方向に圧縮したいフィールドもある。この場合、この設定を無効にすることで改行タグを入れて上下に並べて表示する。

サンプル

    speech_balloon: {
      attributes: {
        'speech.content': {
          part: 'speech',
          column: 'content',
          type: 'text_area',
          label: {
            row_break: true
          },
        },
      },
    },

セリフのcontentのフィールドは長い文章を入力する必要があるため、サイズを最大限に大きくとっている。横幅を確保するためにラベルを改行してから表示する。

options

row_break

リキッドレイアウトにされているフィールドのブロックを改行するフラグをtrue/falseで指定する。デフォルトはfalse

size

フィールドのサイズを数値で設定する。テキストエリアでは幅×高さ(この場合は文字列)でも指定できる。デフォルトでは指定なし。

サンプル

    panel_picture: {
      attributes: {
        x: {
          column: 'x',
          type: 'number',
          label: {
          },
          options: {
            size: 5, 
          }
        },
        y: {
          column: 'y',
          type: 'number',
          label: {
          },
          options: {
            size: 5, 
            row_break: true
          }
        },
      },
    },

helpers

サンプル

    ground_color: {
      attributes: {
        code: {
          column: 'code',
          type: 'hidden',
          label: {
            row_break: true
          },
          options: {
            row_break: true
          }
          helpers: {
            color: {
              path: 'panels/color_helper',
              options: {
              },
              wrapper: 'ground_color-code-wrap'
            }
          },
        },
      },
    },

fields

算出しなければらないパラメータ

モデルの設定e

各フィールドがどのモデルとリンクしているかを取得する。フィールド出力はエレメント毎に処理されるので、モデル名はエレメントから求められるのではないか、と思いがちだが、そう簡単にはいかない。 一枚の入力フォームに複数のモデルをまたいだフィールドを表示することがあるからだ。例えば、吹き出しなどはエレメントとパートに分かれているので、フキダシとセリフとバルーンの三つのモデルのフィールドがチャンポンになっている。このためフィールドはエレメントなのかパートなのかを判別して、パートであれば該当するモデルをモデルの設計から取得してくることになる。もちろんフィールドがエレメントであればそこから求めればよい。

primary_key

IDのフィールドは更新する時は表示しなければならないが、新規作成の時は表示してはならない。この判定のためにフィールドにリンクしたカラムがテーブルのprimary keyで無いかを所得しなければならない。これにはモデルの設定から拾ってくる必要がある。

フォームにリンクしているモデルの名前 form_model_name

入力フォームがどのモデルとリンクしているかを判別するためのパラメータ。

フィールドタグの名前name

フィールドタグにはname属性を文字列で指定しなければならないが、 railsにはこの文字列を作成するための作法があるので、それにならって生成する必要がある。

命名規則の基本は

モデル名カラム名

だが、エレメントパートのように階層構造になっているモデルは特別に

モデル名partモデル名_attributesカラム名

となる