Search code examples
ruby-on-railsrspecentity-relationshiprelationshipmodels

This should not be nil


I have a simple test as such:

it "should create a post through a user for a blog." do
  @user.blogs.create(title: @blog.title)
  @user.blogs.find_by_title(@blog.title).posts.create(title: 'some title')
  post = Post.find_by_title('some title')
  post.title.should == 'some title'
end

This fails. Why? Because we don't have any blogs for a user. Ok lets throw a binding.pry in their:

it "should create a post through a user for a blog." do
  @user.blogs.create(title: @blog.title)
  binding.pry
  @user.blogs.find_by_title(@blog.title).posts.create(title: 'some title')
  post = Post.find_by_title('some title')
  post.title.should == 'some title'
end

Now that were in the console, lets see if @user.blogs gives us anything.

@user.blogs
=> [#<Blog id: nil, title: "user_blog_26">]

Ok .... But thats not the whole command. Lets see if @blogs has anything.

@blog
=> #<Blog id: 26, title: "user_blog_26">

Ok we are getting some where, we see that @user has the same blog associated to them. (although the id is missing in @user.blogs ... (Note: The relationship between users and blogs is: User has_and_belongs_to_many :blogs, join_table: 'blogs_users')

So lets do:

@user.blogs.find_by(title: @blog.title)
=> nil

Um .....

Whats going on?

  • One: My Blog association for a user doesn't have an id.
  • Two: @user.blogs, returns all blogs for that user, but @user.blogs.find_by(title: @blog.title) returns nil? oO

Whats going on?

Extra Info for the fun of it

Models

class User < ActiveRecord::Base
  include Promiscuous::Subscriber
  subscribe :first_name, :email, :user_name, :last_name

  has_and_belongs_to_many :blogs, join_table: 'blogs_users'
  has_many :posts, through: :blogs

  validates :first_name, presence: true
  validates :email, presence: true, uniqueness: true
  validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i

end


class Blog < ActiveRecord::Base
  has_many :posts

  validates :title, presence: true, uniqueness: true
end

Solution

  • @blog
    => #<Blog id: 26, title: "user_blog_26">
    

    @blog has an id assigned to it. So, that means its an existing record in blogs table with id = 26.

    @user.blogs.create(title: @blog.title)
    

    This line means that create a new record in blogs table with title same as that of @blog.title (i.e., user_blog_26)

    Now, in Blog model you have

    validates :title, presence: true, uniqueness: true ## title must be unique
    

    A uniqueness check on the title field. So, obviously @user.blogs.create(title: @blog.title) failed as a record with title user_blog_26 already exists in the blogs table (Recall @blog here). This is the reason why @user.blogs shows a blog with id = nil as the associated blog creation failed.

    @user.blogs.find_by(title: @blog.title)
    => nil
    

    As the associated blog with title user_blog_26 was not created for @user. This query returns nil.

    Possible Solution

    You already have a blog record in the database referred by @blog. To assign that blog to your @user, simply update the example as below:

    it "should create a post through a user for a blog." do
      @user.blogs << @blog ## This will set @blog.user_id = @user.id
      @user.blogs.find_by_title(@blog.title).posts.create(title: 'some title')
      post = Post.find_by_title('some title')
      post.title.should == 'some title'
    end