バリデーションを自動化するgemを作った話

こんにちは。
マネーフォワードのわり算グループでインターンをしている小野直人です。

今回の記事ではタイトルにもある通り、rakeコマンドを打つことでRailsのActiveRecordのバリデーションを自動で書いてくれるgemについて紹介したいと思います。
https://github.com/ono-max/spicy_validation

gemを作った背景

僕の頂いたタスクで「与えられた仕様に沿ってモデルファイル・テーブル定義をする(マイグレーションファイルを書く)」というものがありました。この時に作るモデルファイルやマイグレーションファイルの量が多く、以下の課題感を持ちました。

  • 内向きな課題:単純作業なので途中で飽きてしまいそう
  • 外向きな課題:ケアレスミスを指摘する手間をレビュアーにかけたくない・効率的にミスなくやりたい

そこでこれらの解決策として自動化するgemを作ってみようと考えました。どこを自動化すべきか考えるために自分が普段モデルファイル・テーブル定義をする上でどのようなコマンドを打ちどのようにコードを書くのか考えてみることにしました。

  • 個別モデル・テーブル定義作成 -> modelファイル・migrationファイル作成
    • rails g model モデル名 hoge:string, foo:integer-> ここはコマンド1つでできるので問題なさそう
  • テーブル定義の更新(&ローカルへ反映) -> schema.rb更新
    • rails db:migrate-> ここもコマンド1つでできるので問題なさそう
  • 制約に合わせたバリデーション追加 -> modelファイルを編集
    • 手修正 -> modelファイルを編集するのはケアレスミスが出そう

このようにプロセスを考えてみるとmodelファイルを編集を自動化できると良さそうだと方針が立ちました。

設計コンセプト

設計コンセプトは以下の通りです。

  • いつでも消せるgem
    • チーム内で共通して使う必要がないようにしたいのでディレクトリを作り新規作成…ではなく、モデルファイルを書き換える形にする
      • validationメソッドを書いてくれるgemを探した時にapp/validatorsのようにディレクトリを作りvalidationメソッドを書くgemを見つけた。しかし、このようにディレクトリを作ると今後もチームでこのgemを使い続けなければならない or この書き方に合わせた形でvalidationメソッドを書き続けるという事態が起こってしまうと考えたので直接モデルファイルを書き換える形にした。
      • このgemを使うかどうか議論せずに気軽に使って欲しい
  • 既存のリポジトリでも使えるように
    • 自分が書き換えたいモデルファイルだけ書き換えられるようにテーブルを表示->ユーザーに選んでもらうようにした

gemについて

gemを使うまでの流れ

# modelファイルとmigrationファイルを作る(modelファイルはあとで作られるのでDBスキーマにmigrationを行うmigrationファイルさえあれば良い)
% rails g model User
    invoke  active_record
    create    db/migrate/20210405034238_create_users.rb
    create    app/models/user.rb
    invoke    test_unit
    create      test/models/user_test.rb
    create      test/fixtures/users.yml
# DBスキーマのMigrationを行う
% rails db:migrate
== 20210405034238 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0019s
== 20210405034238 CreateUsers: migrated (0.0019s) =============================
# ActiveRecord::Base.connectionで読み込んだDBスキーマのカラム情報を基にvalidationメソッドをモデルファイルに書く
% rails validation:generate
[warning] If you generate validation, model files will be overwritten.
{:"0"=>"users"}
Type a number you wanna generate validation > 

イメージ

以下のテーブル情報からvalidationメソッドを作った場合のイメージです。

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | NO   |     | NULL    |                |
| message    | varchar(255) | YES  |     | NULL    |                |
| age        | int(11)      | NO   |     | NULL    |                |
| score      | int(11)      | YES  |     | NULL    |                |
| premium    | tinyint(1)   | YES  |     | NULL    |                |
| created_at | datetime(6)  | NO   |     | NULL    |                |
| updated_at | datetime(6)  | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
# app/models/user.rb
class User < ApplicationRecord
  validates :name, presence: true
  validates :age, presence: true, numericality: true
  validates :score, numericality: true, allow_nil: true
end

フォルダ構成

フォルダ構成は以下の通りです。

|─ lib
|─ spicy_validation
|─ monkey
| └─ new_hash_syntax.rb ... hashへのメソッドアクセスをしている
|─ railtie.rb ... rakeタスクを読み込んでいる
|─ renderer.rb ... ファイルの読み書き・ユーザーからinputを受け取る
|─ schema.rb ... DBと接続している
|─ tasks
| └─ validation.rake ... rakeタスクが定義されている
|─ validation.rb ... validationメソッドが作られる
└─ spicy_validation.rb ... ファイルをrequireするところ

ファイル・DB間の繋がり

参考程度ですが、 ファイル・DB間は以下のような繋がりとなっています。(一部のファイルは省略しています)

今回のgemを作ってみて…

肌感ではありますが、意図せぬケアレスミスは相当減らせたのではないかと思っています。何より楽しいのでモチベーションUPにも繋がり内向き・外向きの課題も解決することができました。また、今回は偶然課題を発見できたので工夫して取り組むことができたのですが、発見までのプロセスをもっと再現性のあるものにしたいです。課題を解決するまでの過程はGoogleで調べたり社員の方に聞いたりすると大体の方針は立てられて解決に向かってトライアンドエラーを繰り返すだけなのでやるべきことはシンプルです。しかし、今の僕の場合課題発見までの過程はやるべきことが決まっておらず偶発的なものが多いです。普段行っているタスクやみんなの呟きから見つけられる問題は沢山あるはずなので、どのように見つけるのかを日々考え日常の習慣となるようにしていきたいです。

今後の課題

gemとして作ったからには皆さんにも使って頂きたいので、今後も改善していきます。使ってみて感じている課題は以下の通りです。

  • テーブルが多い時めっちゃ見にくい
  • validationを作りたいモデルファイルがいくつもある場合何回も走らせなければならない
  • モデルファイルを直接書き換えるのはやはり怖い

名前の由来(小ネタ)

今回gemを実装する上でpretty_validationを非常に参考にさせて頂きました。その敬意を込めspicy_validationという名前にしました。

 

マネーフォワードでは、エンジニアを募集しています。
ご応募お待ちしています。

【サイトのご案内】
マネーフォワード採用サイト
Wantedly
京都開発拠点

【プロダクトのご紹介】
お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android

ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』

おつり貯金アプリ 『しらたま』

お金の悩みを無料で相談 『マネーフォワード お金の相談』

だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』

金融商品の比較・申し込みサイト 『Money Forward Mall』

くらしの経済メディア 『MONEY PLUS』

Pocket