Marty
Marty

Reputation: 2224

How to check the generated token doesn't exist yet?

I have a method that creates a token. This worked and now I want to also ensure uniqueness of the token. Therefore I added while self.class.exists?(api_digest: api_digest) (see below). However, now all sorts of test fail with the error:

ArgumentError: wrong number of arguments (0 for 2)

referring to def remember_api(type, user) which is in a sessions helper. Any ideas what I'm doing wrong?


User model:

def remember_api
  self.api_token = User.new_token  # Creates new api_token.
  update_attributes(api_digest: User.digest(api_token), api_sent_at: Time.zone.now)
end while self.class.exists?(api_digest: api_digest)

def User.new_token
  SecureRandom.urlsafe_base64
end

def User.digest(string)
  cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
  BCrypt::Password.create(string, cost: cost)
end

Sessions helper:

def remember_api(type, user)
  if type == "user"
    user.remember_api
  end
end

Update: So I tried the method below. But that doesn't seem to work either as it gets into an infinite loop.

def remember_api
  begin
    self.api_token = User.new_token
    update_attributes(api_digest: User.digest(api_token), api_sent_at: Time.zone.now)
  end while self.class.exists?(api_digest: api_digest)
end

Update2: I've got model validation for uniqueness on api_digest and tried the following:

def remember_api
  begin
    self.api_token = User.new_token
    api_digest = User.digest(api_token)
    debugger
  end until update_attributes(api_digest: api_digest, api_sent_at: Time.zone.now)
end

This goes wrong, again creating an infinite loop. It proofs that once I move update_attributes behind the end, debugger shows that api_digest and api_token are nil. And self returns: User(id: integer, email: string, username: string, fullname: string, .... If I place update_attributes inside the begin...end (below debugger), they are not nil and self returns: <Userid: 1001, email: "[email protected]", username: "mystring1", ....

Upvotes: 1

Views: 640

Answers (1)

Vincent
Vincent

Reputation: 872

The second api_digest should be self.api_digest. It is because it is looking for a local variable called api_digest, but you meant an attribute on this model

Like this:

def remember_api
  begin
    self.api_token = User.new_token
    update_attributes(api_digest: User.digest(api_token), api_sent_at: Time.zone.now)
  end while self.class.exists?(api_digest: self.api_digest)
end

Update

When you call update_attributes, it will return true if it updates and passes validation. Then it'll go ahead and check if it exists and it does because you just updated it in there, so it'll loop again and lead to infinite loop

If update_attributes returns false, that means it exists and meet the while statement and loop. This is the only time it does what you expected.

begin
  self.api_token = User.new_token
  self.api_digest = User.digest(api_token)
end while self.class.exists?(api_digest: self.api_digest)
self.api_sent_at = Time.zone.now
self.save!

Doc: update

Upvotes: 2

Related Questions