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のインスタンスメソッド)を定義していくことができます。
This is released under MIT License.