class FriendlyId::SequentiallySlugged::SequentialSlugCalculator

Attributes

scope[RW]
sequence_separator[RW]
slug[RW]
slug_column[RW]

Public Class Methods

new(scope, slug, slug_column, sequence_separator, base_class) click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 20
def initialize(scope, slug, slug_column, sequence_separator, base_class)
  @scope = scope
  @slug = slug
  table_name = scope.connection.quote_table_name(base_class.arel_table.name)
  @slug_column = "#{table_name}.#{scope.connection.quote_column_name(slug_column)}"
  @sequence_separator = sequence_separator
end

Public Instance Methods

next_slug() click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 28
def next_slug
  slug + sequence_separator + next_sequence_number.to_s
end

Private Instance Methods

conflict_query() click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 53
def conflict_query
  base = "#{slug_column} = ? OR #{slug_column} LIKE ?"
  # Awful hack for SQLite3, which does not pick up '\' as the escape character
  # without this.
  base << " ESCAPE '\\'" if scope.connection.adapter_name =~ /sqlite/i
  base
end
last_sequence_number() click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 38
def last_sequence_number
  regexp = /#{slug}#{sequence_separator}(\d+)\z/
  # Reject slug_conflicts that doesn't come from the first_candidate
  # Map all sequence numbers and take the maximum
  slug_conflicts.reject{ |slug_conflict| !regexp.match(slug_conflict) }.map do |slug_conflict|
    regexp.match(slug_conflict)[1].to_i
  end.max
end
next_sequence_number() click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 34
def next_sequence_number
  last_sequence_number ? last_sequence_number + 1 : 2
end
ordering_query() click to toggle source

Return the unnumbered (shortest) slug first, followed by the numbered ones in ascending order.

# File lib/friendly_id/sequentially_slugged.rb, line 70
def ordering_query
  length_command = "LENGTH"
  length_command = "LEN" if scope.connection.adapter_name =~ /sqlserver/i
  "#{length_command}(#{slug_column}) ASC, #{slug_column} ASC"
end
sequential_slug_matcher() click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 61
def sequential_slug_matcher
  # Underscores (matching a single character) and percent signs (matching
  # any number of characters) need to be escaped. While this looks like
  # an excessive number of backslashes, it is correct.
  "#{slug}#{sequence_separator}".gsub(/[_%]/, '\\\\\&') + '%'
end
slug_conflicts() click to toggle source
# File lib/friendly_id/sequentially_slugged.rb, line 47
def slug_conflicts
  scope.
    where(conflict_query, slug, sequential_slug_matcher).
    order(Arel.sql(ordering_query)).pluck(Arel.sql(slug_column))
end