Dashing Boy Dashing Boy - 25 days ago 7
Ruby Question

Ruby on Rails id column not generated after db:migration

I'm following a course on coursera and I'm on my first assignment details of which can be found on this and this link. WHen I ran rspec I found the test cases to be failing, turned out my schema didn't have an ID column in it. In the course it said that when I run migration the ID column is generated automatically just like created_at and updated_at. Anyone has any idea why the id column probably didn't get generated. I know I can overcome the problem by specifying it in a new migration but just wanted to know the reason.

Here's the Schema I currently have:

ActiveRecord::Schema.define(version: 20161108162529) do

create_table "profiles", force: :cascade do |t|
t.string "gender"
t.integer "birth_year"
t.string "first_name"
t.string "last_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

create_table "todo_items", force: :cascade do |t|
t.date "due_date"
t.string "title"
t.text "description"
t.boolean "completed", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

create_table "todo_lists", force: :cascade do |t|
t.string "list_name"
t.date "list_due_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

create_table "users", force: :cascade do |t|
t.string "username"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

end


This is the migrations for todolists:

class CreateTodoLists < ActiveRecord::Migration
def change
create_table :todo_lists do |t|
t.string :list_name
t.date :list_due_date

t.timestamps null: false
end
end
end


The model class it generated:

class TodoList < ActiveRecord::Base
end


My method that is inserting a record in it in the assignment.rb file as asked:

def create_todolist(params)
# accept a hash of todolist properties (`:name` and `:due_date`) as an input parameter. Note these are not 100% the same as Model class.
# use the TodoList Model class to create a new user in the DB
# return an instance of the class with primary key (`id`), and dates (`created_at` and `updated_at`) assigned
t1 = TodoList.new
t1.list_name = params["name"]
t1.list_due_date = params["due_date"]
t1.save
TodoList.first
end


rspec code that is failing:

context "rq03.2 assignment code has create_todolist method" do
it { is_expected.to respond_to(:create_todolist) }
it "should create_todolist with provided parameters" do
expect(TodoList.find_by list_name: "mylist").to be_nil
due_date=Date.today
assignment.create_todolist(:name=> 'mylist', :due_date=>due_date)
testList = TodoList.find_by list_name: 'mylist'
expect(testList.id).not_to be_nil
expect(testList.list_name).to eq "mylist"
expect(testList.list_due_date).to eq due_date
expect(testList.created_at).not_to be_nil
expect(testList.updated_at).not_to be_nil
end


Actual message that I get when it fails the test:

Failures:

1) Assignment rq03 rq03.2 assignment code has create_todolist method should create_todolist with provided parameters
Failure/Error: expect(testList.id).not_to be_nil

NoMethodError:
undefined method `id' for nil:NilClass
# ./spec/assignment_spec.rb:173:in `block (4 levels) in <top (required)>'
# ./spec/assignment_spec.rb:14:in `block (2 levels) in <top (required)>'

Answer

The schema won't show the ID column. I double checked with one of my rails apps:

db/schema.rb

create_table "users", force: :cascade do |t|
  t.string   "email",                  default: "",    null: false
  t.string   "first_name"
  t.string   "last_name"
end

And when I describe the table in Postgres with \d users, I see the id column:

         Column         |            Type             |                     Modifiers
------------------------+-----------------------------+----------------------------------------------------
 id                     | integer                     | not null default nextval('users_id_seq'::regclass)
 email                  | character varying           | not null default ''::character varying
 first_name             | character varying           |
 last_name              | character varying           |

If there is a reason you don't want the ID, you can omit it by passing id: false to create_table.

create_table :users, id: false do |t|
  #...
end

Probably a horrible idea on the users table :-)


Update

Looks like you are not actually creating a new TodoList.

The clue here is in the error: "undefined method 'id' for nil:NilClass". Your testList is nil.

One good tip is to always use the ActiveRecord's "bang" methods in tests. This will cause them to throw an exception if they fail. Which is super helpful when trying to track down errors -- you'll find the place with the actual error and not some side effect down the line.

I bet you can update your create_todolist to help track this down

def create_todolist(params)
  # Updated: To use ActiveRecord create! method. 
  # This will throw an exception if it fails.
  TodoList.create!(list_name: params[:name], due_date: params[:due_date])
end

I would also update your spec, by changing:

  testList = TodoList.find_by list_name: 'mylist'

To use the bang:

  testList = TodoList.find_by! list_name: 'mylist'