数日に渡る調査と試行錯誤の末、自分が期待する動作の実装方法がようやく分かったので、ここで共有します。
#課題
-
user
のequipment
を5個に制限したい。 -
equipment
を既に5個持っているuser
に対して、「equipment
を1個削除」と「equipment
を1個追加」を同時に行いたい。
app/models/equipment.rb
class Equipment < ActiveRecord::Base
belongs_to :user
end
app/models/user.rb
class User < ActiveRecord::Base
has_many :equipments
accepts_nested_attributes_for :equipments, allow_destroy: true
validates :equipments, length: { maximum: 5 }
end
validates
のlength
を使うとシンプルに書けるのですが、これだと課題に上げた処理をした場合にvalidation error
が出ます。
#実現方法
対象となるassociation
のlength
を呼ぶ前に
.reject(&:marked_for_destruction?)
で削除対象となるinstance
を除外します。
##具体例1: length
を再定義する
app/models/user.rb
class User < ActiveRecord::Base
has_many :equipments do
def length
reject(&:marked_for_destruction?).length
end
end
accepts_nested_attributes_for :equipments, allow_destroy: true
validates :equipments, length: { maximum: 5 }
end
##具体例2: validation
したい内容をmethod
として定義する
app/models/user.rb
class User < ActiveRecord::Base
has_many :equipments
accepts_nested_attributes_for :equipments, allow_destroy: true
EQUIPMENTS_MAX_LENGTH = 5
validate legth_of_equipments
def length_of_equipments
equipments_length = 0
if equipments.present?
# ↓ここのrejectが肝でした。
equipments_length = equipments.reject(&:marked_for_destruction?).length
end
errors.add(:equipments, 'too many') if equipments_length > EQUIPMENTS_MAX_LENGTH
end
end
以上です。
誰かのお役に立てれば幸いです。
もっとスマートな方法がありましたら、教えて頂けると嬉しいです。
#参考
http://homeonrails.com/2012/10/validating-nested-associations-in-rails/