1日1%成長するブログ

毎日成長するために仕事/プライベートで得た学びをアウトプットするブログです

ベイビーステップから学ぶメンタルコントロール (自覚編)

自分はベイビーステップというテニス漫画が大好きです。

少年マガジンで2007年〜2017年まで連載されていたので、 初連載時は今から13年前なのですが、未だによく読みます。

主人公のエーちゃんがノートでひたすらデータを貯めて改善して、 一歩ずつ成長していって強敵に立ち向かっていく姿がめちゃくちゃかっこいいんですよね。

面白いだけでなく、 人生レベルで役に立つエッセンスが色々詰まってるマンガだと思ってます。

特に自分みたいに不器用なタイプには、 ピッタリのロールモデルなので参考にしてます。

今回取り上げるのはメンタルコントロールです。 ベイビーステップの18巻でメンタルの状態には4つあるという話があります。

それが「無気力」・「怒り」・「重圧」・「挑戦」の4つです。

いい順番に並べ替えると、

「挑戦」←「重圧」←「怒り」←「無気力」

になります。

怒りは無気力よりはマシですが、勝利には直接結びつきません。 重圧は勝ちたいと思う心からくるものなので良いこと。

これを上手くコントロールして挑戦のメンタルに持っていくことが、 良いパフォーマンスを出す上でとても重要。

という話をしています。

このコントロールするのはまたスキルが必要になりますが、

まずは自分の今の状態を自覚して、 どの方向に向かっていくべきかを意識できるようになるだけでも、違います。

次回以降にはそれぞれのメンタル状態からいかにシフトさせていくか、について書いていきたいと思います。

Railsのコールバックをifで絞りつつ引数を指定する方法

before_xxx, after_xxxといったActiveRecordのコールバックを特定の条件で絞り込みしたい時は、 ifオプションを使うのが楽。

after_update :update_fuga, if: -> { saved_change_to_hoge_column? }

ただこれだとコールバックに引数を指定したい時に上手くいかない。 そういった場合は以下のようにラムダ式の中で自分でrubyのコードを書いてしまえばいい。

after_update -> { update_fuga("fuga") if saved_change_to_hoge_column? }

便利。

Railsで特定のカラムが更新された時だけバリデーションを実行する

Rails5.1からは特定のカラムが更新されたかどうかは、以下のメソッドで判定するようになった。

Save前

{カラム名}_changed?の代わりにwill_save_change_to_{カラム名}?
{カラム名}_was の代わりに {カラム名}_in_database

Save後

{カラム名}_changed?の代わりにsaved_change_to_{カラム名}?
{カラム名}_was の代わりに {カラム名} _before_last_save

今回はバリデーションなので、Save前のメソッドを使う。

validate :validate_update_hoge, if: :will_save_change_to_hoge?

def validate_update_hoge
  # hoge_in_databaseを使ってバリデーション書く
end

これでカラムが更新された場合のみ、変更前と後の値を使ってバリデーションを書ける。

Railsのupdateメソッドで特定の条件外でバリデーションをかける

特定の画面からの新規登録だけバリデーションをかけたいなど、 条件を絞ってバリデーションをかけたくなることはよくある。

そんな時は以下のようにsave時にcontextをオプションに渡してあげて、 with_optionsでその時に実行するバリデーションをまとめて設定すればいい。

with_options on: :create_context do
  validate :validate_create
end

user.save!(context: :create_context)

ただcontextオプションはupdateメソッドでは使えないので、 更新時で同じことをやりたい場合は、

以下のようにattributesにセットしてからsaveメソッドを使えば出来る。

user.attributes = update_params
user.save!(context: :update_context)

Rubyのアクセサメソッドを使ってEntityクラスを作ってAPIのレスポンスに使う

class HogePostsEntity
  attr_reader :author, :comments

  def initialize(post)
    @author = post.author
    @comments = post.comments
  end

  def self.collection(posts)
    posts.map { |post| new(post) }
  end
end

こんな感じでシンプルにEntity用のクラスを作れた。

render json: { status: 200, data: HogePostsEntity.collection(posts) }

APIのレスポンスの整形は全てEntityクラスに任せるようにすれば、 責務がハッキリするし、コントローラーがかなりスッキリしていい感じ。

RailsでFatControllerをリファクタリングしていく方針 (随時アップデート)

Viewで使うデータをどこで整形する?

  • DBから取得したデータを整形してハッシュの配列を用意するみたいな処理は置く場所に困る
  • Modelはvalidationやscope以外は対象テーブルの操作の処理以外は極力書くべきではないので適さない
  • Viewはslimを良く使うのでrubyシンタックスを使ってそこでだけ使うデータを整形したりもできるけど、どこで何を書いてるか後で追いづらい
  • コントローラーのprivateメソッドにするのは、分かりやすいけど、そうするとあっという間にFatControllerになっていく。。
  • そこでViewModel層を使うのが良さそう。Viewで使うデータを用意するのは全てViewModelに持っていく。
  • ViewModelの責務
    • ビューに渡すインスタンス変数を用意するクラス
    • before_actionでインスタンス変数をセットしていたようなコントローラーの処理を移行できる
    • ビューに渡すハッシュや配列といったデータはEntityで表現できると尚良さそう。
  • Entityの責務
    • Modelのデータを引数で渡すとビューで扱いたいデータの形式に変換するためのクラス
    • 一度共通で使えるEntityを作ってしまえば、至るところで同じような整形処理を書く手間がなくなる

複雑な検索クエリはどこで書く?

  • joinsしてwhereして..みたいな処理をコントローラーに書くと一気に行数が増えてしまう
  • 検索処理はscopeにしてModelに持たせる。
  • findする処理もコントローラーに書くのではなく対象のモデルに持たせるといい。

管理画面で必要とされるような複雑な検索機能はどこに書く?

  • 引数を受け取って存在する場合はwhereする (where if params[:hoge].present?)
  • のようなことを書いていると一気に膨れ上がる
  • DBに紐付かない検索用のモデルクラスを作ってそこに移行する (models/searchers/xxx_searcher.rb)

業務システム開発する上でのデータベース設計の勘所

データベース設計の手順

  • システム化分析

    • システム化対象範囲の業務プロセスを視覚化する
    • アウトプット: フロー図 (誰がどの手順で業務を行っているのか)
  • 論理データモデル化

    • ビジネス視点でビジネス活動をデータモデルとして視覚化する
    • アウトプット: ER図 (ビジネス視点の用語を使う、関連を洗い出すことを優先)
  • 物理データモデル化

    • 開発者視点のデータモデルを視覚化する
    • アウトプット: ER図 (開発者視点の用語を使う)、テーブル定義書

システム化分析と論理データモデル化は一度だけやれば終わりというよりかは、 何度かいったりきたりしながら詳細を詰めていくことになると思う。

開発当初は一部の業務をシステム化したいという話であっても、 ビジネス全体を通してデータを繋げたいというのはよくある要件なので、

まずは対象範囲を絞らずに一度ビジネス全体の論理データモデルをまとめたい。

開発者は物理データモデルに進みたくなりがちだが、

この論理データモデルまでの設計が固めてから進めないと、 開発してから大きな負債が残ってしまうのでここは関係者と時間をかけてすり合わせること。