Ben
Ben

Reputation: 1342

rails model will not populate attribute on create, fails non-null db test

trying to create an api_key for my user. have setup so its non-null.

rails 4, not -null is triggered, but i am creating the access_token item before create so why? if i puts in def generate_access_token I can see access_token is populated its almost like it doesn't include access token (see db record at bottom) as part of the save command.

stumped! i must have missed something silly, surely?

test .log

      SQL (0.5ms)  INSERT INTO "api_keys" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2016-12-21 08:09:07.304051"], ["updated_at", "2016-12-21 08:09:07.304051"]]

* missing the access_token * why?

error

    $ rspec spec/models/api_key_spec.rb 
    F

    Failures:

      1) ApiKey populates token
         Failure/Error: let!(:a) { FactoryGirl.create(:api_key) }

         ActiveRecord::StatementInvalid:
           SQLite3::ConstraintException: NOT NULL constraint failed: api_keys.user_id: INSERT INTO "api_keys" ("created_at", "updated_at") VALUES (?, ?)
           .....
         # --- Caused by: ---
         # SQLite3::ConstraintException:
         #   NOT NULL constraint failed: api_keys.user_id
         #   /Users/ben.forrest/.rvm/gems/ruby-2.2.5@baseapi/gems/sqlite3-1.3.12/lib/sqlite3/statement.rb:108:in `step'

    Finished in 0.11903 seconds (files took 3.32 seconds to load)
    1 example, 1 failure

    Failed examples:

    rspec ./spec/models/api_key_spec.rb:5 # ApiKey populates token

api_key_spec.rb

    require 'rails_helper'
    RSpec.describe ApiKey, type: :model do
    let!(:a) { FactoryGirl.create(:api_key) }
        it "populates token" do
           expect(a.access_token).to match /[A-Za-z0-9]{32}/
        end 
    end

api_key.rb

    class ApiKey < ActiveRecord::Base  
      attr_accessor :access_token, :expires_at, :user_id, :active, :application
      before_create :generate_access_token
      before_create :set_expiration
      belongs_to :user

      def expired?
        DateTime.now >= self.expires_at
      end

      private
      def generate_access_token
        begin
          self.access_token = SecureRandom.hex
        end while self.class.exists?(access_token: access_token)
        puts "**** access_token: #{access_token}****" # this shows it is created
      end

      def set_expiration
        self.expires_at = DateTime.now+30
      end
    end  

migration

    class CreateApiKeys < ActiveRecord::Migration  
      def change
        create_table :api_keys do |t|
          t.string :access_token,      null: false
          t.integer :user_id,          null: false
          t.boolean :active,           null: false, default: true
          t.datetime :expires_at

          t.timestamps
        end

        add_index :api_keys, ["user_id"], name: "index_api_keys_on_user_id", unique: false
        add_index :api_keys, ["access_token"], name: "index_api_keys_on_access_token", unique: true
      end
    end 

Upvotes: 0

Views: 224

Answers (1)

31piy
31piy

Reputation: 23859

while self.class.exists?(access_token: access_token) becomes false for access_token = nil. Try assigning the token first, before the begin.

def generate_access_token
  return if access_token
  token = SecureRandom.hex
  token = SecureRandom.hex while self.class.exists?(access_token: access_token)
  self.access_token = token
end

Upvotes: 1

Related Questions