awesomeprogrammer.com

Sharing ideas

Solving problems

Gathering solutions

Exchanging thoughts

Ruby On Rails

PHP

Postgres

Debian & Ubuntu
jQuery & CSS

Rails After_commit Create Mindf*ck

Rails 3 observers can be tricky. You probably already heard a thousand times how to deal with undefined id issue and how transactions are handled, so let’s take a quick example that can be google’d in just a couple of seconds.

1
2
3
4
5
6
7
class UserObserver < ActiveRecord::Observer
  def after_commit(user)
    return unless user.send(:transaction_include_action?, :create)

    # some code that should be executed only on create, after transaction is committed
  end
end

So what’s wrong with this example? Nothing. But let say SuperUser has_many users. Some example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SuperUser < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  attr_accessible :name
  validates :name, presence: true, inclusion: { in: %(foo bar)}
end

class UserObserver < ActiveRecord::Observer
  def after_commit(user)
    return unless user.send(:transaction_include_action?, :create)

    raise "#{user.inspect} | Persisted: #{user.persisted?}"
  end
end

And try it out:

1
2
3
4
# obviously we need super user first
SuperUser.first.users.create(name: "boo") # this won't create record, due validation
#<RuntimeError: #<User id: nil, name: "boo", owner_id: 1, created_at: nil, updated_at: nil> | Persisted: false>
 => #<User id: nil, name: "boo", owner_id: 1, created_at: nil, updated_at: nil>

Hell yeah, observer code executed perfectly, even this is not what we would normally expect. I was quite surprised with this behavior, I recommend checking out ActiveRecord::Transactions internals, if I find some time I will write a follow-up to this, cheers.

Comments