Skip to content

Narazaka/activehashie-validation.js

Repository files navigation

npm npm license npm download total npm download by month

Dependency Status devDependency Status Travis Build Status AppVeyor Build Status codecov.io Codacy Badge Greenkeeper badge

Rails等のマスターデータをJavaScript/TypeScriptでチェック可能にするツールです。

validateModel(Item, ({validates, validate}) => {
    validates("name", "presence", true);
    validates("name", "uniqueness", true);
    validates("type", "presence", true);
    validate((errors, items) => {
        const ids = items.where().not({type: ["a", "b"]}).pluck("id");
        if (ids.length) {
            errors.push({column: "type", ids, message: "typeは'a', 'b'のいずれかであるべきです"});
        }
    });
    validates("itemGroup", "presenceBelongsTo", true); // 参照先の存在をチェック
});

使用方法

rails_app/db/seeds.rbにてデータをテーブルごとにymlファイルから読み込む仕組みになっているとします。

下記のようにchecker/にチェック用の内容を書いて、checker/フォルダでahevコマンドを実行すると、エラー内容が記載されたreport.htmlが生成されます。

なおこの内容はexamples/の下にあります。

rails_app/db/seeds/items.yml:

data1:
  id: 1
  name: item1
  type: a
  item_group_id: 1
data2:
  id: 2
  name: item2
  type: a
  item_group_id: 1
data3:
  id: 3
  name: item3
  type: b
  item_group_id: 2
data4:
  id: 4
  name: item3
  type: c
  item_group_id: 2

rails_app/db/seeds/item_groups.yml:

data1:
  id: 1
  name: group1
data2:
  id: 2
  name: group2
data3:
  id: 3
  name: group2

checker/models/application_record.ts:

import {
    ActiveHashRecord,
    ActiveHashRecordBase,
    ActiveYaml,
} from "activehashie";

const rootPath = "../rails_app/db/seeds";

export class ApplicationRecord<Record extends ActiveHashRecord> extends ActiveYaml<Record> {
    constructor(
        name: string,
        recordClass: new (source: ActiveHashRecordBase) => Record,
        indexColumns?: Array<keyof (Record)>,
    ) {
        super(name, recordClass, {rootPath, indexColumns});
    }
}

// displayNameメソッドを定義しておくと、エラー表示の時にそのレコードを表すものとして使われます。
(<any> ActiveHashRecord.prototype).displayName = function () {
    return this.name;
};

checker/models/item.ts:

import {ActiveHashRecord} from "activehashie";
import {validate} from "activehashie-validation";
import {ApplicationRecord} from "./application_record";
import {ItemGroup, ItemGroupRecord} from "./item_group";

export class ItemRecord extends ActiveHashRecord {
    name: string;
    type: "a" | "b";
    item_group_id: number;
    get itemGroup(): ItemGroupRecord | undefined { return this.belongsTo(ItemGroup); }

    displayName() {
        const itemGroup = this.itemGroup;
        return `${itemGroup ? itemGroup.name : ""} - ${this.name} [${this.type}]`;
    }
}

export const Item = new ApplicationRecord("Item", ItemRecord);

validate(Item, ({validates, validate}) => {
    validates("name", "presence", true);
    validates("name", "uniqueness", true);
    validates("type", "presence", true);
    validate((errors, model) => {
        const ids = model.where().not({type: ["a", "b"]}).pluck("id");
        if (ids.length) {
            errors.push({column: "type", ids, message: "typeは'a', 'b'のいずれかであるべきです"});
        }
    });
    validates("itemGroup", "presenceBelongsTo", true); // 参照先の存在をチェックします
});

checker/models/item_group.ts:

import {ActiveHashRecord} from "activehashie";
import {validate} from "activehashie-validation";
import {ApplicationRecord} from "./application_record";
import {Item} from "./item";

export class ItemGroupRecord extends ActiveHashRecord {
    name: string;
    get items() { return this.hasMany(Item); }
}

export const ItemGroup = new ApplicationRecord("ItemGroup", ItemGroupRecord);

validate(ItemGroup, ({validates}) => {
    validates("name", "presence", true);
    validates("name", "uniqueness", true);
});

checker/spec/length.ts:

import {validate} from "activehashie-validation";
import "../models/item";
import {ItemGroup} from "../models/item_group";

validate(ItemGroup, ({validate}) => {
    validate((errors, model) => {
        if (model.length < 10) {
            errors.push({message: "ItemGroupは10個以上必要です"});
        }
    });
});

自動生成

db/schema.rbからTypeScriptコードを自動生成できます。

npx generate-model-ar-schema ./db/schema.rb ../checker

以下のファイルが生成されます

  • ApplicationTable.ts (同名ファイルがなければ生成 テーブルのベースクラスファイル)
  • Models.ts (テーブルの定義ファイル)
  • Extensions.ts (同名ファイルがなければ生成 テーブルの拡張メソッドを定義するためのファイル)
  • ModelAndExtensions.ts (テーブル拡張メソッドを便利に使うための定義ファイル)

生成されるExtensions.tsにテーブル名TableExt(ActiveRecordのscope等)、テーブル名RecordExt(ActiveRecordのインスタンスメソッド)を定義していくことができます。

See also

License

This is released under MIT License.