Learning Ruby

Reference, Mnemonic & Ramblings

A Note About Creating an Associated Model

For a Review model which belongs_to a Video (Video has_many :reviews), there are a few ways to create a new Review associated to a Video. Two of which are:

Given test_video = Video.create(...) # attributes for video omitted

  1. Review.create(video: test_video, ...) # other attributes for review omitted, or
  2. test_video.reviews.create(...) # attributes for review omitted

Here are the Review and Video models for reference:

1
2
3
4
5
6
7
8
9
10
11
12
class Video < ActiveRecord::Base
  # irrelevant stuff omitted
  before_validation :update_rating!

  has_many :reviews

  private

  def update_rating!
    # update the video's average rating based on it's reviews' ratings
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
class Review < ActiveRecord::Base
  after_save :update_video_rating!

  # irrelevant stuff omitted
  belongs_to :video

  private

  def update_video_rating!
    video.save if video
  end
end

The difference between the two methods to create the Review became obvious because i had the following in the test for the Review model:

1
2
expect(test_video).to receive(:update_rating!).with(no_args)
Review.create( ..., video: test_video)

The above test wouldn't pass because when the Review instance is created this way, the test_video instance in the test has a different memory location from the one which received the update_rating! call through the Review model after_save :update_video_rating callback.

When the Review instance is created using the 2nd way shown above, the same test passes because now the Video instance that receives the update_rating! call doesn't change.

However, i think i would still rewrite the expectation to accommodate for both calls this way: expect_any_instance_of(Video).to receive(:update_rating!).with(no_args)

comments powered by Disqus