2

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
mhenrixon
  • 6,179
  • 4
  • 40
  • 64

4 Answers4

2

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.

Kevin Sjöberg
  • 2,261
  • 14
  • 20
1

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

Community
  • 1
  • 1
gabrielhilal
  • 10,660
  • 6
  • 54
  • 81
1

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(LicenseFile).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 a LicenseFile for each destination rule' do
  expect(LicenseFile).to receive(:create!).twice.and_call_original
  flight.create_seats(2)
end

This now passed:

    creates enough empty seats
1 example, 0 failures
David Hempy
  • 5,373
  • 2
  • 40
  • 68
-1

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?

Community
  • 1
  • 1
HungryCoder
  • 7,506
  • 1
  • 38
  • 51
  • The create! method creates the object or raises an exception if it fails it never returns nil. create without bang returns the object with id nil if it fails (doesn't throw exception) – mhenrixon Mar 21 '13 at 09:45
  • thanks for downvote. but nil/false is better than exception. you can handle like `if user.articles.create { title: 'blah' } ...`. – HungryCoder Mar 21 '13 at 09:47