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

- (SlugGenerator) initialize(sluggable, normalized)

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

- (Object) normalized (readonly)

Returns the value of attribute normalized



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

def normalized
  @normalized
end

- (Object) sluggable (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

- (Object) column (private)



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

def column
  sluggable.connection.quote_column_name friendly_id_config.slug_column
end

- (Object) conflict (private)



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

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

- (Boolean) conflict? (private)

Returns:

  • (Boolean)


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

def conflict?
  !! conflict
end

- (Object) conflicts (private)



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

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.where(base, normalized, wildcard)
  scope = scope.where("#{pkey} <> ?", value) unless sluggable.new_record?
  
  length_command = "LENGTH"
  length_command = "LEN" if sluggable.connection.adapter_name =~ /sqlserver/i
  scope = scope.order("#{length_command}(#{column}) DESC, #{column} DESC")
end

- (Object) extract_sequence_from_slug(slug) (private)



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

def extract_sequence_from_slug(slug)
  split_slug = slug.split("#{normalized}#{separator}")
  split_slug.length > 1 ? split_slug.last.to_i : 1
end

- (Object) friendly_id_config (private)



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

def friendly_id_config
  sluggable.friendly_id_config
end

- (Object) generate

Generate a new sequenced slug.



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

def generate
  conflict? ? self.next : normalized
end

- (Object) last_in_sequence (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

- (Object) next

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

- (Object) next_in_sequence (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

- (Object) separator (private)



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

def separator
  friendly_id_config.sequence_separator
end

- (Object) wildcard (private)



77
78
79
80
81
82
# File 'lib/friendly_id/slug_generator.rb', line 77

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