Skip to content

Commit

Permalink
Refactor #clean_old_tokens to reduce computational complexity.
Browse files Browse the repository at this point in the history
 * Previous version featured an `Enumerable#min_by` loop _inside_ a
   `while` loop, resulting in `O(n^2)` complexity.

 * Instead, break things into two separate loops, and skip altogether if
   they aren't even necessary.
  • Loading branch information
Evan-M committed Mar 22, 2018
1 parent 676c77a commit 04afcda
Showing 1 changed file with 11 additions and 3 deletions.
14 changes: 11 additions & 3 deletions app/models/devise_token_auth/concerns/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,17 @@ def max_client_tokens_exceeded?
end

def clean_old_tokens
while tokens.present? && max_client_tokens_exceeded?
oldest_client_id, _tk = tokens.min_by { |_cid, v| v[:expiry] || v["expiry"] }
tokens.delete(oldest_client_id)
if tokens.present? && max_client_tokens_exceeded?
# Using Enumerable#sort_by on a Hash will typecast it into an associative
# Array (i.e. an Array of key-value Array pairs). However, since Hashes
# have an internal order in Ruby 1.9+, the resulting sorted associative
# Array can be converted back into a Hash, while maintaining the sorted
# order.
self.tokens = tokens.sort_by { |_cid, v| v[:expiry] || v['expiry'] }.to_h

# Since the tokens are sorted by expiry, shift the oldest client token
# off the Hash until it no longer exceeds the maximum number of clients
tokens.shift while max_client_tokens_exceeded?
end
end
end

0 comments on commit 04afcda

Please sign in to comment.