Class: Mongoid::Slug::UniqueSlug

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/mongoid/slug/unique_slug.rb

Defined Under Namespace

Classes: SlugState

Constant Summary collapse

MUTEX_FOR_SLUG =
Mutex.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model) ⇒ UniqueSlug

Returns a new instance of UniqueSlug.



68
69
70
71
72
# File 'lib/mongoid/slug/unique_slug.rb', line 68

def initialize(model)
  @model = model
  @_slug = ''
  @state = nil
end

Instance Attribute Details

#_slugObject (readonly)

Returns the value of attribute _slug.



62
63
64
# File 'lib/mongoid/slug/unique_slug.rb', line 62

def _slug
  @_slug
end

#modelObject (readonly)

Returns the value of attribute model.



62
63
64
# File 'lib/mongoid/slug/unique_slug.rb', line 62

def model
  @model
end

Instance Method Details

#find_unique(attempt = nil) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/mongoid/slug/unique_slug.rb', line 78

def find_unique(attempt = nil)
  MUTEX_FOR_SLUG.synchronize do
    @_slug = if attempt
               attempt.to_url
             else
               slug_url_builder.call(model)
             end

    @_slug = @_slug[0...slug_max_length] if slug_max_length

    # Regular expression that matches slug, slug-1, ... slug-n
    # If slug_name field was indexed, MongoDB will utilize that
    # index to match /^.../ pattern.
    pattern = /^#{Regexp.escape(@_slug)}(?:-(\d+))?$/

    where_hash = {}
    where_hash[:_slugs.all] = [pattern]
    where_hash[:_id.ne]     = model._id

    if (scope = slug_scope) && reflect_on_association(scope).nil?
      # scope is not an association, so it's scoped to a local field
      # (e.g. an association id in a denormalized db design)
      where_hash[scope] = model.try(:read_attribute, scope)
    end

    if slug_by_model_type == true
      where_hash[:_type] = model.try(:read_attribute, :_type)
    end

    @state = SlugState.new @_slug, uniqueness_scope.unscoped.where(where_hash), pattern

    # do not allow a slug that can be interpreted as the current document id
    @state.include_slug unless model.class.look_like_slugs?([@_slug])

    # make sure that the slug is not equal to a reserved word
    @state.include_slug if slug_reserved_words.any? { |word| word === @_slug }

    # only look for a new unique slug if the existing slugs contains the current slug
    # - e.g if the slug 'foo-2' is taken, but 'foo' is available, the user can use 'foo'.
    if @state.slug_included?
      highest = @state.highest_existing_counter
      @_slug += "-#{highest.succ}"
    end
    @_slug
  end
end

#metadataObject



74
75
76
# File 'lib/mongoid/slug/unique_slug.rb', line 74

def 
  @model.respond_to?(:relation_metadata) ? @model. : @model.
end

#uniqueness_scopeObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/mongoid/slug/unique_slug.rb', line 125

def uniqueness_scope
  if slug_scope && ( = reflect_on_association(slug_scope))

    parent = model.send(.name)

    # Make sure doc is actually associated with something, and that
    # some referenced docs have been persisted to the parent
    #
    # TODO: we need better reflection for reference associations,
    # like association_name instead of forcing collection_name here
    # -- maybe in the forthcoming Mongoid refactorings?
    inverse = .inverse_of || collection_name
    return parent.respond_to?(inverse) ? parent.send(inverse) : model.class
  end

  if embedded?
     = reflect_on_all_associations(:embedded_in)[0]
    return model._parent.send(.inverse_of || self..name)
  end

  # unless embedded or slug scope, return the deepest document superclass
  appropriate_class = model.class
  while appropriate_class.superclass.include?(Mongoid::Document)
    appropriate_class = appropriate_class.superclass
  end
  appropriate_class
end