mhenrixon
mhenrixon

Reputation: 6278

Rails association.create and association.create! returns nil

I'm just throwing this out there because I really can't figure this out. When I call for instance user.articles.create! { title: 'blah' } nil is returned but the object is created. I've not seen anything like this before and was wondering if someone else has?

I've tried rails 3.2.13 and 3.2.12 and they both do the same thing.

EDIT

In active record both create and create! ends up IN THIS METHOD that is supposed to return the record or throw an exception.

def create_record(attributes, options, raise = false, &block)
  unless owner.persisted?
    raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
  end

  if attributes.is_a?(Array)
    attributes.collect { |attr| create_record(attr, options, raise, &block) }
  else
    transaction do
      add_to_target(build_record(attributes, options)) do |record|
        yield(record) if block_given?
        insert_record(record, true, raise)
      end
    end
  end
end

Upvotes: 3

Views: 1623

Answers (4)

David Hempy
David Hempy

Reputation: 6237

I had the same symptom, and this question is the only relevant hit that I could find. I'll throw my solution into the mix in case it helps anyone else.

The code worked in real life, and only failed under rspec. All the troubleshooting I did made no sense, pointing to create! being broken, which I never believed.

As it turns out, I was mocking create! so it never got called. Adding .and_call_original to my mock solved the problem.

My model was something like this: (not really...but compatible with this answer)

class Flight < ApplicationRecord
  has_many :seats

  def create_seats(seat_count)
    seat_count.times { Seat.create!(flight: self) }
    seats.each(&:raise_seatback_and_lock_tray)
  end

And my test was:

it 'creates enough empty seats' do
  expect(Seat).to receive(:create!).twice
  flight.create_seats(2)
end

The expectation was met (confirmed manually), but an error was raised:

 NoMethodError:
   undefined method `raise_seatback_and_lock_tray=' for nil:NilClass

Changing my mock to allow create! to actually be called solved the problem:

it 'creates enough empty seats' do
  expect(Seat).to receive(:create!).twice.and_call_original
  flight.create_seats(2)
end

This now passed:

    creates enough empty seats
1 example, 0 failures

Upvotes: 2

Kevin Sj&#246;berg
Kevin Sj&#246;berg

Reputation: 2254

If I'm not mistaken Factory Girl mimic the actual object you're dealing with through your predefined factory. Therefor User#articles might not return what you think it is when called on a factory.

Changing

user.articles.create! { title: 'blah' }

to

create(:article, user: user, title: 'blah')

should enforce the association through Factory Girl's interface.

Upvotes: 2

gabrielhilal
gabrielhilal

Reputation: 10769

I believe there is something going on with your attr_accessible or attr_accessor in your Article class. I you might have not included the user_id or something else...

There is also a similar question here: rails Model.create(:attr=>"value") returns model with uninitialized fields

Upvotes: 1

HungryCoder
HungryCoder

Reputation: 7616

If you are expecting the object to be returned use

user.articles.create { title: 'blah' }

Why some methods have bang (!), you can read this topic Why are exclamation marks used in Ruby methods?

Upvotes: -1

Related Questions