1日1%成長するブログ

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

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図 (開発者視点の用語を使う)、テーブル定義書

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

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

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

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

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

ハッシュと暗号化の違いについて

ハッシュ化とは

SHA256などのハッシュアルゴリズムを使って復元不可能な別の値へ変換すること。

「パスワード」のように元が復元できないようにしたい時に使う。

暗号化とは

RSA, DES, AESなどの暗号アルゴリズムを使って復元可能な別の値へ変換すること。

「機密情報」のように特定の人は復元できるようにしたい場合に使う。

Railsでページ毎にJSとCSSファイルを読み込む

- content_for :css do
  = stylesheet_link_tag 'posts/show'

- content_for :js do
  = javascript_include_tag 'posts/show'
Rails.application.config.assets.precompile += %w(
  posts/*
)
  • ページ毎にcontent_forでレイアウトファイルに読み込ませる
  • assets.rbにファイルを追加してasset_pipelineの対象に追加する

RailsにおけるCSS構成のプラクティス

  body class="#{controller.controller_name}-#{controller.action_name}"
  • コントローラー名とアクション名
.articles {
  &-show {
    .inputContainer {
      margin: 20px
    }
  }
  &-edit {
    .inputContainer {
      margin: 10px;
    }
  }
}
@import "user/articles"
  • ユーザー画面でのみ呼ばれるuser.htmlでコントローラーとアクション名を親要素で囲みその下にすれば名前空間は守られるはず

参考記事