Class: FriendlyId::SlugGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/friendly_id/slug_generator.rb

Overview

The default slug generator offers functionality to check slug strings for uniqueness and, if necessary, appends a sequence to guarantee it.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sluggable, normalized) ⇒ SlugGenerator

Create a new slug generator.



8
9
10
11
# File 'lib/friendly_id/slug_generator.rb', line 8

def initialize(sluggable, normalized)
  @sluggable  = sluggable
  @normalized = normalized
end

Instance Attribute Details

#normalizedObject (readonly)

Returns the value of attribute normalized.



5
6
7
# File 'lib/friendly_id/slug_generator.rb', line 5

def normalized
  @normalized
end

#sluggableObject (readonly)

Returns the value of attribute sluggable.



5
6
7
# File 'lib/friendly_id/slug_generator.rb', line 5

def sluggable
  @sluggable
end

Instance Method Details

#columnObject (private)



37
38
39
# File 'lib/friendly_id/slug_generator.rb', line 37

def column
  sluggable.connection.quote_column_name friendly_id_config.slug_column
end

#conflictObject (private)



45
46
47
48
49
50
# File 'lib/friendly_id/slug_generator.rb', line 45

def conflict
  unless defined? @conflict
    @conflict = conflicts.first
  end
  @conflict
end

#conflict?Boolean (private)

Returns:

  • (Boolean)


41
42
43
# File 'lib/friendly_id/slug_generator.rb', line 41

def conflict?
  !! conflict
end

#conflictsObject (private)



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/friendly_id/slug_generator.rb', line 52

def conflicts
  sluggable_class = friendly_id_config.model_class.base_class

  pkey  = sluggable_class.primary_key
  value = sluggable.send pkey
  base = "#{column} = ? OR #{column} LIKE ?"
  # Awful hack for SQLite3, which does not pick up '\' as the escape character without this.
  base << "ESCAPE '\\'" if sluggable.connection.adapter_name =~ /sqlite/i
  scope = sluggable_class.unscoped.with_deleted.where(base, normalized, wildcard)
  scope = scope.where("#{pkey} <> ?", value) unless sluggable.new_record?
  scope = scope.order("LENGTH(#{column}) DESC, #{column} DESC")
end

#extract_sequence_from_slug(slug) ⇒ Object (private)



33
34
35
# File 'lib/friendly_id/slug_generator.rb', line 33

def extract_sequence_from_slug(slug)
  slug.split("#{normalized}#{separator}").last.to_i
end

#friendly_id_configObject (private)



65
66
67
# File 'lib/friendly_id/slug_generator.rb', line 65

def friendly_id_config
  sluggable.friendly_id_config
end

#generateObject

Generate a new sequenced slug.



19
20
21
# File 'lib/friendly_id/slug_generator.rb', line 19

def generate
  conflict? ? self.next : normalized
end

#last_in_sequenceObject (private)



29
30
31
# File 'lib/friendly_id/slug_generator.rb', line 29

def last_in_sequence
  @_last_in_sequence ||= extract_sequence_from_slug(conflict.to_param)
end

#nextObject

Given a slug, get the next available slug in the sequence.



14
15
16
# File 'lib/friendly_id/slug_generator.rb', line 14

def next
  "#{normalized}#{separator}#{next_in_sequence}"
end

#next_in_sequenceObject (private)



25
26
27
# File 'lib/friendly_id/slug_generator.rb', line 25

def next_in_sequence
  last_in_sequence == 0 ? 2 : last_in_sequence.next
end

#separatorObject (private)



69
70
71
# File 'lib/friendly_id/slug_generator.rb', line 69

def separator
  friendly_id_config.sequence_separator
end

#wildcardObject (private)



73
74
75
76
77
78
# File 'lib/friendly_id/slug_generator.rb', line 73

def wildcard
  # Underscores (matching a single character) and percent signs (matching
  # any number of characters) need to be escaped
  # (While this seems like an excessive number of backslashes, it is correct)
  "#{normalized}#{separator}".gsub(/[_%]/, '\\\\\&') + '%'
end