表参道.rb #25 ~ Rails アンチパターン ~
ダメ例
class AddBirthdayToUser < ActiveRecord::Migration[5.0]
def change
add_column :users, :birthday, :datetime
User.find_each do |user|
# 後から `age` が無くなったらエラーになる
user.update!(birthday: user.age.years.ago)
end
end
end
自己紹介
名前: sinsoku
会社: 株式会社grooves
github: sinsoku
twitter: @sinsoku_listy
ポートフォリオサービス Forkwell Portfolio
地雷メソッド
- TOP 3
- default_scope
- accepts_nested_attributes_for
- Active Record Callbacks
- おまけ
- belongs_to
- validates_uniqueness_of
default_scope
class Article
default_scope { where(published: true) }
end
Article.new.published # => true
Article.create.published # => true
Article.all
# => SELECT * FROM articles WHERE published = true
has_many
class User
# せめて :published_articles だったら...
has_many :articles, -> { where(published: true) }
end
問題点
- admin 画面など後で外すケースが意外と多い
- 一般的な名前を使われると直すのがツラい
-
has_many :_articles
が生まれる危険性
-
- joins, merge にも当然だけど影響する
- (昔は order にも影響した気がする)
User.joins(:articles).group(:xxx).count
Aritcle.merge(user.articles).count
accepts_nested_attributes_for
class Member < ActiveRecord::Base
has_one :avatar
accepts_nested_attributes_for :avatar
end
params = {
member: {
name: 'Jack',
avatar_attributes: { icon: 'smiling' }
}
}
member = Member.create(params[:member])
member.avatar.id # => 2
member.avatar.icon # => 'smiling'
Strong Parameters
def member_params
params.require(:member)
.permit(:name, avatar_attributes: [:icon, :_destroy])
end
fields_for
<%= form_for(@member) do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :avatar do |avatar_fields| %>
<%= avatar_fields.text_field :icon %>
<% end %>
<% end %>
問題点
- だいたい複雑になってツラくなる
- Callback やバリデーションの順序が複雑
- 解決方法は難しい...
- サービスクラスにする?
そのうち消える運命
Active Record Callbacks
class User
before_validation :set_age_from_birthday
before_create :build_user_profile
after_create :notify_signup_to_admins
end
問題点
- 1つの save で連鎖的に Callback が発火する
- factory_girl がたまに死ぬ
- テストで無駄にレコードが増える
- テストで無駄にメールが(ry
- callback が増えてきたら注意
- サービスクラスなど別クラスにした方が良い
解決例
def create
@user = User.new(user_params)
if @user.save
UserCreatedJob.perform_later(@user)
redirect_to user_path(@user)
else
render :new
end
end
class UserCreatedJob < ApplicationJob
def perform
# do something
end
end
おまけ: 暗黙的にSQLが使われるメソッド
- belongs_to
- belongs_to_required_by_default = true
- validates_uniqueness_of
大量の INSERT があるレコードの場合は問題になることもある。
技術書典3
日時: 10/22(日) 11:00〜17:00
場所: アキバ・スクエア