require 'set'
module UsernameSuggester
class Suggester
attr_reader :first_name
attr_reader :last_name
# Creates a UsernameSuggester to suggest usernames
#
# ==== Parameters
#
# * :first_name - Required.
# * :last_name - Required.
# * :options - There is one option -- :validate. You can pass in a Proc object and the suggestion will
# be returned if involving the Proc with the suggestion returns true
#
def initialize(first_name, last_name, options = {})
@first_name = (first_name || "").downcase
@last_name = (last_name || "").downcase
@options = options
end
# Generates the combinations without the knowledge of what names are available
def name_combinations
@name_combinations ||= [
"#{@first_name}",
"#{@last_name}",
"#{@first_name.first}#{@last_name}",
"#{@first_name}#{@last_name.first}",
"#{@first_name}#{@last_name}",
"#{@last_name.first}#{@first_name}",
"#{@last_name}#{@first_name.first}",
"#{@last_name}#{@first_name}"
].uniq.reject { |s| s.blank? }
end
# Generates suggestions and making sure they are not in unavailable_suggestions
def suggest(max_num_suggestion, unavailable_suggestions = [])
unavailable_set = unavailable_suggestions.to_set
results = []
candidates = name_combinations.clone
while results.size < max_num_suggestion and !candidates.blank?
candidate = candidates.shift
if @options[:validate] and !@options[:validate].call(candidate)
# Do nothing
elsif unavailable_set.include? candidate
candidates << find_extended_candidate(candidate, unavailable_set)
else
results << candidate
end
end
results
end
private
# Generates a candidate with "candidate" which is not included in unavailable_set
def find_extended_candidate(candidate, unavailable_set)
i = 1
i+=1 while unavailable_set.include? "#{candidate}#{i}"
"#{candidate}#{i}"
end
end
end