I'm trying to validate two models in rails.

Dismissal has a condition that says "if you have a dismissal type that is "bowled", then you must have a "BowlingRecord".

class Dismissal < ActiveRecord::Base

  include DismissalHelper

  belongs_to :bowling_record

  validate :dismissal_bowling_record_combo_is_appropriate

  def dismissal_bowling_record_combo_is_appropriate
    return if !dismissal_type.present?
    if bowling_record.present?
      return if DismissalHelper::BOWLING_DISMISSAL_TYPES.include?(dismissal_type)
      errors.add(:dismissal_type, "#{dismissal_type} can't have a bowler!")
    else
      return if !DismissalHelper::BOWLING_DISMISSAL_TYPES.include?(dismissal_type)
      errors.add(:dismissal_type, "#{dismissal_type} must have a bowler!")
    end
  end

end

BowlingRecord has a condition that says "if you have some dismissals, then they must be of a certain dismissal type" (of which bowled is one).

class BowlingRecord < ActiveRecord::Base

  include DismissalHelper

  has_many :dismissals

  validate :dismissals_go_against_bowler

  def dismissals_go_against_bowler
    dismissals.each do |dismissal|
      if !DismissalHelper::BOWLING_DISMISSAL_TYPES.include?(dismissal.dismissal_type)
        errors.add(:dismissal, "can't be #{dismissal.dismissal_type} if registered against a bowler") 
      end
    end
  end

end

I'm having trouble constructing this, realistically I want to do something like:

BowlingRecord.create!(
  extra_balls: 5,
  maidens: 3,
  runs: 56,
  dismissals: [ Dismissal.create!(dismissal_type: :bowled) ]
)

But this blows up, because when we create the dismissal, it quite rightly says "I need a bowling record, if you want your type to be bowled". But all the information is here, I want the Dismissal creating to be lazy, so that it has the context that it's being created belonging to a bowling record.

Of course I say create the bowling record up front, and then add the dismissals one-by-one. But this seems to be dirtier code and so I was wondering if there was any way to do it in this manner...

Thanks

有帮助吗?

解决方案

You could .build the associated dismissal:

record = BowlingRecord.new(
  extra_balls: 5,
  maidens: 3,
  runs: 56
)

record.dismissals.build dismissal_type: :bowled

record.save!

.build:

Returns a new object of the collection type that has been instantiated with attributes and linked to this object, but have not yet been saved.

See ActiveRecord::Associations::CollectionProxy for more info.

Through ActiveRecord::AutosaveAssociation, the dismissal is saved automatically when the parent BowlingRecord is saved. This happens within a transaction, so you don't end up in an inconsistent state if one of the two fails to save.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top