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の責務
- Entityの責務
- Modelのデータを引数で渡すとビューで扱いたいデータの形式に変換するためのクラス
- 一度共通で使えるEntityを作ってしまえば、至るところで同じような整形処理を書く手間がなくなる
複雑な検索クエリはどこで書く?
- joinsしてwhereして..みたいな処理をコントローラーに書くと一気に行数が増えてしまう
- 検索処理はscopeにしてModelに持たせる。
- findする処理もコントローラーに書くのではなく対象のモデルに持たせるといい。
管理画面で必要とされるような複雑な検索機能はどこに書く?
- 引数を受け取って存在する場合はwhereする (where if params[:hoge].present?)
- のようなことを書いていると一気に膨れ上がる
- DBに紐付かない検索用のモデルクラスを作ってそこに移行する (models/searchers/xxx_searcher.rb)
業務システム開発する上でのデータベース設計の勘所
データベース設計の手順
システム化分析
- システム化対象範囲の業務プロセスを視覚化する
- アウトプット: フロー図 (誰がどの手順で業務を行っているのか)
論理データモデル化
- ビジネス視点でビジネス活動をデータモデルとして視覚化する
- アウトプット: ER図 (ビジネス視点の用語を使う、関連を洗い出すことを優先)
物理データモデル化
- 開発者視点のデータモデルを視覚化する
- アウトプット: ER図 (開発者視点の用語を使う)、テーブル定義書
システム化分析と論理データモデル化は一度だけやれば終わりというよりかは、 何度かいったりきたりしながら詳細を詰めていくことになると思う。
開発当初は一部の業務をシステム化したいという話であっても、 ビジネス全体を通してデータを繋げたいというのはよくある要件なので、
まずは対象範囲を絞らずに一度ビジネス全体の論理データモデルをまとめたい。
開発者は物理データモデルに進みたくなりがちだが、
この論理データモデルまでの設計が固めてから進めないと、 開発してから大きな負債が残ってしまうのでここは関係者と時間をかけてすり合わせること。
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でコントローラーとアクション名を親要素で囲みその下にすれば名前空間は守られるはず
参考記事
- RailsでCSSスタイリングをコントローラー単位で分けるTips - Rails Webook http://ruby-rails.hatenadiary.com/entry/20141220/1419058040