Module: Iron::Lexorank::Utils

Included in:
Rankable::ClassMethods, Rankable::InstanceMethods
Defined in:
lib/iron/lexorank/utils.rb

Constant Summary collapse

MIN_CHAR =
"0"
MAX_CHAR =
"z"
BASE =
MAX_CHAR.ord - MIN_CHAR.ord + 1

Instance Method Summary collapse

Instance Method Details

#get_char(string, index, default_char) ⇒ Object



54
55
56
# File 'lib/iron/lexorank/utils.rb', line 54

def get_char(string, index, default_char)
  index >= string.length ? default_char : string[index]
end

#mid(prev, after) ⇒ Object



49
50
51
52
# File 'lib/iron/lexorank/utils.rb', line 49

def mid(prev, after)
  middle_ascii = ((prev.ord + after.ord) / 2).round
  middle_ascii.chr
end

#optimal_rank_numeric_interval_for(items_count) ⇒ Object



70
71
72
73
74
75
76
# File 'lib/iron/lexorank/utils.rb', line 70

def optimal_rank_numeric_interval_for(items_count)
  i = 1
  while items_count >= BASE ** i
    i += 1
  end
  (BASE ** i) / (items_count + 1)
end

#to_rank(number) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/iron/lexorank/utils.rb', line 58

def to_rank(number)
  return MIN_CHAR if number.zero?

  result = ""
  while number > 0
    remainder = number % BASE
    number /= BASE
    result = (MIN_CHAR.ord + remainder).chr + result
  end
  result
end

#value_between(before_, after_) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/iron/lexorank/utils.rb', line 6

def value_between(before_, after_)
  before = before_ || MIN_CHAR
  after = after_ || MAX_CHAR

  rank = ""

  (before.length + after.length).times do |i|
    prev_char = get_char(before, i, MIN_CHAR)
    after_char = get_char(after, i, MAX_CHAR)

    if prev_char == after_char
      rank += prev_char
      next
    end

    mid_char = mid(prev_char, after_char)
    if mid_char == prev_char || mid_char == after_char
      rank += prev_char
      next
    end

    rank += mid_char
    break
  end

  # Problem: If we try to get a rank before the character '0' or after 'z' the algorithm would return the same char
  # This first of all breaks a possible unique constraint and of course makes no sense when ordering the items.
  #
  # Thoughts: I think this issue will never happen with the Lexorank::Rankable module
  # Why? Let's look at '0' as a rank:
  # Because the algorithm always chooses the char in between two other chars, '0' can only happen when before is nil and after is '1'
  # In this case the algorithm will return '0U' though. This means there will never be an item with rank '0' which is why this condition
  # should never equal to true.
  #
  # Please report if you have another opinion about that or if you reached the exception! (of course you can force it by using `value_between(nil, '0')`)
  if rank >= after
    raise Lexorank::Exceptions::InvalidRankError,
      "This rank should not be achievable using the Lexorank::Rankable module! " \
      "The supplied ranks were #{before_.inspect} and #{after_.inspect}."
  end
  rank
end