Friday, January 1, 2016

How to debug a seeding error



This happened to me right now: http://stackoverflow.com/questions/34554968/type-mismatch-when-seeding-database

I was seeding a database for a web application when I started getting this error: ActiveRecord::AssociationTypeMismatch: Role(#97332160) expected,
got Fixnum(#76482890)

This means I was tying to force a Fixnum into a Role object. But this wasn't the case. Or at least so I thought...

Here are the definitions of the tables I was trying to seed.

  create_table "roles", force: :cascade do |t|
    t.string   "role",       limit: 50, null: false
    t.datetime "created_at",            null: false
    t.datetime "updated_at",            null: false
  end

  create_table "users", force: :cascade do |t|
    t.integer  "role_id",    limit: 11
    t.string   "first_name", limit: 100, null: false
    t.string   "surname",    limit: 100, null: false
    t.string   "email",      limit: 255
    t.string   "password",   limit: 150
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
  end

  add_index "users", ["first_name"], name: "index_users_on_first_name", using: :btree
  add_index "users", ["role_id"], name: "index_users_on_role_id", using: :btree
  add_index "users", ["surname"], name: "index_users_on_surname", using: :btree

  add_foreign_key "users", "roles"

And here are my seeds.rb commands:

rl = Role.create(role: "root")
User.create(first_name: "Edvaldo", surname: "Silva de Almeida Júnior", email: "xxxxxxxxxxxxx", role_id: rl.id, password: "xxxxxxxxxxxxx")

rl = Role.create(role: "admin")
User.create(first_name: "Daiely", surname: "Fanchin", email: "xxxxxxxxxxxxx", role_id: rl.id, password: "xxxxxxxxxxxxx")

rl = Role.create(role: "user")
User.create(first_name: "César", surname: "Silva", email: "xxxxxxxxxxxxx", role_id: rl.id, password: "xxxxxxxxxxxxx")

As you may see, I was doing no wrong, at least at first sight. The only point in my seeding which could go wrong was role_id: rl.id and this wasn't wrong ar all. I wasn't forcing a Fixnum into a Role. I was forcing a Fixnum into a numeric field.

This is the kind of error which may be confusing to debug. You keep looking to what you did right and forget to look what you did wrong, which may be masked somewhere else. Exactly what I did and it took me two hours and a question posted ad StackOverflow to remember a very important truth.

Seeding is not just forcing data inside a database. Seeding makes ActiveRecord evaluate completely the models involved. Then, your error may be anywhere inside your models and then checking your methods is necessary. 

Exactly what happened to me in this particular case.

This was my User model:

class User < ActiveRecord::Base

  has_one :role
  after_initialize :set_default_role, :if => :new_record?

  def set_default_role
    if User.count == 0
      self.role ||= 1
    else
      self.role ||= 2
    end
  end

end

As you may see, I was trying to initialize sel.role with numerial values, indeed! Exactly what ActiveRecord told I was doing!

As soon as I rewrote this set_default_role method to

  def set_default_role
    if User.count == 0
      self.role_id ||= 1
    else
      self.role_id ||= 2
    end
  end

everything worked fine.

The suggestion, then, is: Whenever you have a seeding error, check the models involved, not just the values you are seeding.