mongoid_slug 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
- data/README.md +83 -44
- data/lib/mongoid/slug.rb +295 -155
- data/lib/mongoid/slug/version.rb +1 -1
- data/spec/models/alias.rb +6 -0
- data/spec/models/book.rb +1 -1
- data/spec/models/friend.rb +6 -0
- data/spec/models/subject.rb +1 -1
- data/spec/mongoid/slug_spec.rb +175 -25
- data/spec/spec_helper.rb +6 -5
- metadata +42 -17
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
Mongoid Slug
|
|
2
2
|
============
|
|
3
3
|
|
|
4
|
-
Mongoid Slug generates a URL slug or permalink based on one or more
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Mongoid Slug generates a URL slug or permalink based on one or more fields in a
|
|
5
|
+
Mongoid model. It sits idly on top of [stringex] [1], supporting non-Latin
|
|
6
|
+
characters.
|
|
7
7
|
|
|
8
|
-
[![travis]
|
|
8
|
+
[![travis] [2]] [3]
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Installation
|
|
11
|
+
------------
|
|
12
12
|
|
|
13
|
-
Add
|
|
13
|
+
Add to your Gemfile:
|
|
14
14
|
|
|
15
15
|
```ruby
|
|
16
16
|
gem 'mongoid_slug'
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Usage
|
|
20
|
+
-----
|
|
21
|
+
|
|
22
|
+
Set up a slug:
|
|
20
23
|
|
|
21
24
|
```ruby
|
|
22
25
|
class Book
|
|
@@ -24,34 +27,18 @@ class Book
|
|
|
24
27
|
include Mongoid::Slug
|
|
25
28
|
|
|
26
29
|
field :title
|
|
27
|
-
embeds_many :authors
|
|
28
|
-
|
|
29
30
|
slug :title
|
|
30
31
|
end
|
|
31
|
-
|
|
32
|
-
class Author
|
|
33
|
-
include Mongoid::Document
|
|
34
|
-
include Mongoid::Slug
|
|
35
|
-
|
|
36
|
-
field :first
|
|
37
|
-
field :last
|
|
38
|
-
embedded_in :book, :inverse_of => :authors
|
|
39
|
-
|
|
40
|
-
slug :first, :last, :as => :name
|
|
41
|
-
end
|
|
42
32
|
```
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+
Find a record by its slug:
|
|
45
35
|
|
|
46
36
|
```ruby
|
|
47
|
-
# GET /books/a-thousand-plateaus
|
|
48
|
-
|
|
49
|
-
authors.
|
|
50
|
-
find_by_name(params[:id])
|
|
37
|
+
# GET /books/a-thousand-plateaus
|
|
38
|
+
book = Book.find_by_slug params[:book_id]
|
|
51
39
|
```
|
|
52
40
|
|
|
53
|
-
[Read here] [
|
|
54
|
-
for all available options.
|
|
41
|
+
[Read here] [3] for all available options.
|
|
55
42
|
|
|
56
43
|
Scoping
|
|
57
44
|
-------
|
|
@@ -61,37 +48,89 @@ To scope a slug by a reference association, pass `:scope`:
|
|
|
61
48
|
```ruby
|
|
62
49
|
class Company
|
|
63
50
|
include Mongoid::Document
|
|
51
|
+
|
|
64
52
|
references_many :employees
|
|
65
53
|
end
|
|
66
54
|
|
|
67
55
|
class Employee
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
include Mongoid::Document
|
|
57
|
+
include Mongoid::Slug
|
|
58
|
+
|
|
59
|
+
field :name
|
|
60
|
+
referenced_in :company
|
|
61
|
+
|
|
62
|
+
slug :name, :scope => :company
|
|
73
63
|
end
|
|
74
64
|
```
|
|
75
65
|
|
|
76
|
-
In this example, if you create an employee without associating it with
|
|
77
|
-
|
|
66
|
+
In this example, if you create an employee without associating it with any
|
|
67
|
+
company, the scope will fall back to the root employees collection.
|
|
78
68
|
|
|
79
|
-
Currently, if you have an irregular association name, you **must**
|
|
80
|
-
|
|
69
|
+
Currently, if you have an irregular association name, you **must** specify the
|
|
70
|
+
`:inverse_of` option on the other side of the assocation.
|
|
81
71
|
|
|
82
72
|
Embedded objects are automatically scoped by their parent.
|
|
83
73
|
|
|
84
|
-
|
|
74
|
+
The value of `:scope` can alternatively be a field within the model itself:
|
|
85
75
|
|
|
86
76
|
```ruby
|
|
87
77
|
class Employee
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
include Mongoid::Document
|
|
79
|
+
include Mongoid::Slug
|
|
80
|
+
|
|
81
|
+
field :name
|
|
82
|
+
field :company_id
|
|
83
|
+
|
|
84
|
+
slug :name, :scope => :company_id
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
History
|
|
89
|
+
-------
|
|
90
|
+
|
|
91
|
+
To specify that the history of a document should be kept track of, pass
|
|
92
|
+
`:history` with a value of `true`.
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
class Page
|
|
96
|
+
include Mongoid::Document
|
|
97
|
+
include Mongoid::Slug
|
|
98
|
+
|
|
99
|
+
field :title
|
|
100
|
+
|
|
101
|
+
slug :title, history: true
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The document will then be returned for any of the saved slugs:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
page = Page.new title: "Home"
|
|
109
|
+
page.save
|
|
110
|
+
page.update_attributes title: "Welcome"
|
|
111
|
+
|
|
112
|
+
Page.find_by_slug("welcome") == Page.find_by_slug("home") #=> true
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Reserved Slugs
|
|
116
|
+
--------------
|
|
117
|
+
|
|
118
|
+
Pass words you do not want to be slugged using the `reserve` option:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
class Friend
|
|
122
|
+
include Mongoid::Document
|
|
123
|
+
|
|
124
|
+
field :name
|
|
125
|
+
slug :name, reserve: ['admin', 'root']
|
|
93
126
|
end
|
|
127
|
+
|
|
128
|
+
friend = Friend.create name: 'admin'
|
|
129
|
+
Friend.find_by_slug('admin') # => nil
|
|
130
|
+
friend.slug # => 'admin-1'
|
|
94
131
|
```
|
|
95
132
|
|
|
96
133
|
[1]: https://github.com/rsl/stringex/
|
|
97
|
-
[2]: https://
|
|
134
|
+
[2]: https://secure.travis-ci.org/hakanensari/mongoid-slug.png
|
|
135
|
+
[3]: http://travis-ci.org/hakanensari/mongoid-slug
|
|
136
|
+
[4]: https://github.com/hakanensari/mongoid-slug/blob/master/lib/mongoid/slug.rb
|
data/lib/mongoid/slug.rb
CHANGED
|
@@ -1,213 +1,353 @@
|
|
|
1
1
|
require 'mongoid'
|
|
2
2
|
require 'stringex'
|
|
3
3
|
|
|
4
|
-
module Mongoid
|
|
5
|
-
|
|
6
|
-
#
|
|
7
|
-
# one or more fields in a Mongoid model.
|
|
8
|
-
#
|
|
9
|
-
# class Person
|
|
10
|
-
# include Mongoid::Document
|
|
11
|
-
# include Mongoid::Slug
|
|
12
|
-
#
|
|
13
|
-
# field :name
|
|
14
|
-
# slug :name
|
|
15
|
-
# end
|
|
16
|
-
#
|
|
4
|
+
module Mongoid
|
|
5
|
+
# The Slug module helps you generate a URL slug or permalink based on one or
|
|
6
|
+
# more fields in a Mongoid model.
|
|
17
7
|
module Slug
|
|
18
8
|
extend ActiveSupport::Concern
|
|
19
9
|
|
|
20
10
|
included do
|
|
21
11
|
cattr_accessor :slug_builder,
|
|
22
|
-
:slugged_fields,
|
|
23
12
|
:slug_name,
|
|
24
|
-
:
|
|
13
|
+
:slug_history_name,
|
|
14
|
+
:slug_scope,
|
|
15
|
+
:reserved_words_in_slug,
|
|
16
|
+
:slugged_attributes
|
|
25
17
|
end
|
|
26
18
|
|
|
27
19
|
module ClassMethods
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# The options hash respects the following members:
|
|
34
|
-
#
|
|
35
|
-
# * `:as`, which specifies name of the field that stores the
|
|
36
|
-
# slug. Defaults to `slug`.
|
|
37
|
-
#
|
|
38
|
-
# * `:scope`, which specifies a reference association to scope
|
|
39
|
-
# the slug by. Embedded documents are by default scoped by their
|
|
40
|
-
# parent.
|
|
41
|
-
#
|
|
42
|
-
# * `:permanent`, which specifies whether the slug should be
|
|
43
|
-
# immutable once created. Defaults to `false`.
|
|
20
|
+
# @overload slug(*fields)
|
|
21
|
+
# Sets one ore more fields as source of slug.
|
|
22
|
+
# @param [Array] fields One or more fields the slug should be based on.
|
|
23
|
+
# @yield If given, the block is used to build a custom slug.
|
|
44
24
|
#
|
|
45
|
-
# *
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
25
|
+
# @overload slug(*fields, options)
|
|
26
|
+
# Sets one ore more fields as source of slug.
|
|
27
|
+
# @param [Array] fields One or more fields the slug should be based on.
|
|
28
|
+
# @param [Hash] options
|
|
29
|
+
# @param options [String] :as The name of the field that stores the
|
|
30
|
+
# slug. Defaults to `slug`.
|
|
31
|
+
# @param options [Boolean] :history Whether a history of changes to
|
|
32
|
+
# the slug should be retained. When searched by slug, the document now
|
|
33
|
+
# matches both past and present slugs.
|
|
34
|
+
# @param options [Boolean] :index Whether an index should be defined
|
|
35
|
+
# on the slug field. Defaults to `false` and has no effect if the
|
|
36
|
+
# document is embedded.
|
|
37
|
+
# Make sure you have a unique index on the slugs of root documents to
|
|
38
|
+
# avoid race conditions.
|
|
39
|
+
# @param options [Boolean] :permanent Whether the slug should be
|
|
40
|
+
# immutable. Defaults to `false`.
|
|
41
|
+
# @param options [Array] :reserve` A list of reserved slugs
|
|
42
|
+
# @param options :scope [Symbol] a reference association or field to
|
|
43
|
+
# scope the slug by. Embedded documents are, by default, scoped by
|
|
44
|
+
# their parent.
|
|
45
|
+
# @yield If given, a block is used to build a slug.
|
|
51
46
|
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
47
|
+
# @example A custom builder
|
|
48
|
+
# class Person
|
|
49
|
+
# include Mongoid::Document
|
|
50
|
+
# include Mongoid::Slug
|
|
54
51
|
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# class Person
|
|
61
|
-
# include Mongoid::Document
|
|
62
|
-
# include Mongoid::Slug
|
|
63
|
-
#
|
|
64
|
-
# field :names, :type => Array
|
|
65
|
-
# slug :names do |doc|
|
|
66
|
-
# doc.names.join(' ')
|
|
67
|
-
# end
|
|
52
|
+
# field :names, :type => Array
|
|
53
|
+
# slug :names do |doc|
|
|
54
|
+
# doc.names.join(' ')
|
|
55
|
+
# end
|
|
56
|
+
# end
|
|
68
57
|
#
|
|
69
58
|
def slug(*fields, &block)
|
|
70
|
-
options
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
self.
|
|
74
|
-
|
|
75
|
-
self.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
59
|
+
options = fields.extract_options!
|
|
60
|
+
options[:history] = false if options[:permanent]
|
|
61
|
+
|
|
62
|
+
self.slug_scope = options[:scope]
|
|
63
|
+
self.reserved_words_in_slug = options[:reserve] || []
|
|
64
|
+
self.slug_name = options[:as] || :slug
|
|
65
|
+
self.slugged_attributes = fields.map(&:to_s)
|
|
66
|
+
if options[:history] && !options[:permanent]
|
|
67
|
+
self.slug_history_name = "#{self.slug_name}_history".to_sym
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
default_builder = lambda do |doc|
|
|
71
|
+
slugged_attributes.map { |f| doc.send f }.join ' '
|
|
72
|
+
end
|
|
73
|
+
self.slug_builder = block_given? ? block : default_builder
|
|
84
74
|
|
|
85
75
|
field slug_name
|
|
86
76
|
|
|
77
|
+
if slug_history_name
|
|
78
|
+
field slug_history_name, :type => Array, :default => []
|
|
79
|
+
end
|
|
80
|
+
|
|
87
81
|
if options[:index]
|
|
88
|
-
index
|
|
82
|
+
index slug_name, :unique => !slug_scope
|
|
83
|
+
index slug_history_name if slug_history_name
|
|
89
84
|
end
|
|
90
85
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
else
|
|
94
|
-
before_save :generate_slug
|
|
86
|
+
set_callback options[:permanent] ? :create : :save, :before do |doc|
|
|
87
|
+
doc.build_slug if doc.slug_should_be_rebuilt?
|
|
95
88
|
end
|
|
96
89
|
|
|
97
|
-
# Build a finder
|
|
90
|
+
# Build a finder for slug.
|
|
98
91
|
#
|
|
99
92
|
# Defaults to `find_by_slug`.
|
|
100
93
|
instance_eval <<-CODE
|
|
101
94
|
def self.find_by_#{slug_name}(slug)
|
|
102
|
-
|
|
95
|
+
if slug_history_name
|
|
96
|
+
any_of({ slug_name => slug }, { slug_history_name => slug })
|
|
97
|
+
else
|
|
98
|
+
where(slug_name => slug)
|
|
99
|
+
end.first
|
|
103
100
|
end
|
|
104
101
|
|
|
105
102
|
def self.find_by_#{slug_name}!(slug)
|
|
106
|
-
|
|
107
|
-
raise(Mongoid::Errors::DocumentNotFound.new
|
|
103
|
+
self.find_by_#{slug_name}(slug) ||
|
|
104
|
+
raise(Mongoid::Errors::DocumentNotFound.new self, slug)
|
|
108
105
|
end
|
|
109
106
|
CODE
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
# Build a scope based on the slug name.
|
|
109
|
+
#
|
|
110
|
+
# Defaults to `by_slug`.
|
|
111
|
+
scope "by_#{slug_name}".to_sym, lambda { |slug|
|
|
112
|
+
if slug_history_name
|
|
113
|
+
any_of({ slug_name => slug }, { slug_history_name => slug })
|
|
114
|
+
else
|
|
115
|
+
where(slug_name => slug)
|
|
116
|
+
end
|
|
117
|
+
}
|
|
119
118
|
end
|
|
120
|
-
|
|
119
|
+
|
|
120
|
+
# Finds a unique slug, were specified string used to generate a slug.
|
|
121
|
+
#
|
|
122
|
+
# Returned slug will the same as the specified string when there are no
|
|
123
|
+
# duplicates.
|
|
124
|
+
#
|
|
125
|
+
# @param [String] desired_slug
|
|
126
|
+
# @param [Hash] options
|
|
127
|
+
# @param options [Symbol] :scope The scope that should be used to
|
|
128
|
+
# generate the slug, if the class creates scoped slugs. Defaults to
|
|
129
|
+
# `nil`.
|
|
130
|
+
# @param options [Constant] :model The model that the slug should be
|
|
131
|
+
# generated for. This option overrides `:scope`, as the scope can now
|
|
132
|
+
# be extracted from the model. Defaults to `nil`.
|
|
133
|
+
# @return [String] A unique slug
|
|
134
|
+
def find_unique_slug_for(desired_slug, options = {})
|
|
135
|
+
if slug_scope && self.reflect_on_association(slug_scope).nil?
|
|
136
|
+
scope_object = uniqueness_scope(options[:model])
|
|
137
|
+
scope_attribute = options[:scope] || options[:model].try(:read_attribute, slug_scope)
|
|
138
|
+
else
|
|
139
|
+
scope_object = options[:scope] || uniqueness_scope(options[:model])
|
|
140
|
+
scope_attribute = nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
excluded_id = options[:model]._id if options[:model]
|
|
144
|
+
|
|
145
|
+
slug = desired_slug.to_url
|
|
146
|
+
|
|
147
|
+
# Regular expression that matches slug, slug-1, ... slug-n
|
|
148
|
+
# If slug_name field was indexed, MongoDB will utilize that
|
|
149
|
+
# index to match /^.../ pattern.
|
|
150
|
+
pattern = /^#{Regexp.escape(slug)}(?:-(\d+))?$/
|
|
121
151
|
|
|
122
|
-
|
|
152
|
+
if slug_scope &&
|
|
153
|
+
self.reflect_on_association(slug_scope).nil?
|
|
154
|
+
# scope is not an association, so it's scoped to a local field
|
|
155
|
+
# (e.g. an association id in a denormalized db design)
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# Regular expression that matches slug, slug-1, ... slug-n
|
|
129
|
-
# If slug_name field was indexed, MongoDB will utilize that
|
|
130
|
-
# index to match /^.../ pattern.
|
|
131
|
-
pattern = /^#{Regexp.escape(slug)}(?:-(\d+))?$/
|
|
132
|
-
|
|
133
|
-
if slug_scope &&
|
|
134
|
-
self.class.reflect_on_association(slug_scope).nil?
|
|
135
|
-
# scope is not an association, so it's scoped to a local field
|
|
136
|
-
# (e.g. an association id in a denormalized db design)
|
|
137
|
-
existing_slugs =
|
|
138
|
-
self.class.
|
|
139
|
-
only(slug_name).
|
|
140
|
-
where(slug_name => pattern,
|
|
141
|
-
:_id.ne => _id,
|
|
142
|
-
slug_scope => self[slug_scope])
|
|
143
|
-
else
|
|
144
|
-
existing_slugs =
|
|
145
|
-
uniqueness_scope.
|
|
146
|
-
only(slug_name).
|
|
147
|
-
where(slug_name => pattern, :_id.ne => _id)
|
|
148
|
-
end
|
|
157
|
+
where_hash = {}
|
|
158
|
+
where_hash[slug_name] = pattern
|
|
159
|
+
where_hash[:_id.ne] = excluded_id if excluded_id
|
|
160
|
+
where_hash[slug_scope] = scope_attribute
|
|
149
161
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
existing_slugs =
|
|
163
|
+
deepest_document_superclass.
|
|
164
|
+
only(slug_name).
|
|
165
|
+
where(where_hash)
|
|
166
|
+
else
|
|
167
|
+
where_hash = {}
|
|
168
|
+
where_hash[slug_name] = pattern
|
|
169
|
+
where_hash[:_id.ne] = excluded_id if excluded_id
|
|
170
|
+
|
|
171
|
+
existing_slugs =
|
|
172
|
+
scope_object.
|
|
173
|
+
only(slug_name).
|
|
174
|
+
where(where_hash)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
existing_slugs = existing_slugs.map do |doc|
|
|
178
|
+
doc.slug
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
if slug_history_name
|
|
182
|
+
if slug_scope &&
|
|
183
|
+
self.reflect_on_association(slug_scope).nil?
|
|
184
|
+
# scope is not an association, so it's scoped to a local field
|
|
185
|
+
# (e.g. an association id in a denormalized db design)
|
|
186
|
+
|
|
187
|
+
where_hash = {}
|
|
188
|
+
where_hash[slug_history_name.all] = [pattern]
|
|
189
|
+
where_hash[:_id.ne] = excluded_id if excluded_id
|
|
190
|
+
where_hash[slug_scope] = scope_attribute
|
|
191
|
+
|
|
192
|
+
history_slugged_documents =
|
|
193
|
+
deepest_document_superclass.
|
|
194
|
+
where(where_hash)
|
|
195
|
+
else
|
|
196
|
+
where_hash = {}
|
|
197
|
+
where_hash[slug_history_name.all] = [pattern]
|
|
198
|
+
where_hash[:_id.ne] = excluded_id if excluded_id
|
|
199
|
+
|
|
200
|
+
history_slugged_documents =
|
|
201
|
+
scope_object.
|
|
202
|
+
where(where_hash)
|
|
203
|
+
end
|
|
153
204
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
205
|
+
existing_history_slugs = []
|
|
206
|
+
history_slugged_documents.each do |doc|
|
|
207
|
+
history_slugs = doc.read_attribute(slug_history_name)
|
|
208
|
+
next if history_slugs.nil?
|
|
209
|
+
existing_history_slugs.push(*history_slugs.find_all { |slug| slug =~ pattern })
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# If the only conflict is in the history of a document in the same scope,
|
|
213
|
+
# transfer the slug
|
|
214
|
+
if slug_scope && existing_slugs.count == 0 && existing_history_slugs.count > 0
|
|
215
|
+
history_slugged_documents.each do |doc|
|
|
216
|
+
doc_history_slugs = doc.read_attribute(slug_history_name)
|
|
217
|
+
next if doc_history_slugs.nil?
|
|
218
|
+
doc_history_slugs -= existing_history_slugs
|
|
219
|
+
doc.write_attribute(slug_history_name, doc_history_slugs)
|
|
220
|
+
doc.save
|
|
221
|
+
end
|
|
222
|
+
existing_history_slugs = []
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
existing_slugs += existing_history_slugs
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
if reserved_words_in_slug.any? { |word| word === slug }
|
|
229
|
+
existing_slugs << slug
|
|
161
230
|
end
|
|
162
|
-
max = existing_slugs.last.match(/-(\d+)$/).try(:[], 1).to_i
|
|
163
231
|
|
|
164
|
-
|
|
232
|
+
if existing_slugs.count > 0
|
|
233
|
+
# Sort the existing_slugs in increasing order by comparing the
|
|
234
|
+
# suffix numbers:
|
|
235
|
+
# slug, slug-1, slug-2, ..., slug-n
|
|
236
|
+
existing_slugs.sort! do |a, b|
|
|
237
|
+
(pattern.match(a)[1] || -1).to_i <=>
|
|
238
|
+
(pattern.match(b)[1] || -1).to_i
|
|
239
|
+
end
|
|
240
|
+
max = existing_slugs.last.match(/-(\d+)$/).try(:[], 1).to_i
|
|
241
|
+
|
|
242
|
+
slug += "-#{max + 1}"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
slug
|
|
165
246
|
end
|
|
247
|
+
|
|
248
|
+
private
|
|
249
|
+
|
|
250
|
+
def uniqueness_scope(model = nil)
|
|
251
|
+
if model
|
|
252
|
+
if slug_scope && (metadata = self.reflect_on_association(slug_scope))
|
|
253
|
+
parent = model.send(metadata.name)
|
|
166
254
|
|
|
167
|
-
|
|
255
|
+
# Make sure doc is actually associated with something, and that
|
|
256
|
+
# some referenced docs have been persisted to the parent
|
|
257
|
+
#
|
|
258
|
+
# TODO: we need better reflection for reference associations,
|
|
259
|
+
# like association_name instead of forcing collection_name here
|
|
260
|
+
# -- maybe in the forthcoming Mongoid refactorings?
|
|
261
|
+
inverse = metadata.inverse_of || collection_name
|
|
262
|
+
return parent.respond_to?(inverse) ? parent.send(inverse) : self
|
|
263
|
+
end
|
|
264
|
+
if embedded?
|
|
265
|
+
parent_metadata = reflect_on_all_associations(:embedded_in)[0]
|
|
266
|
+
return model._parent.send(parent_metadata.inverse_of || model.metadata.name)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
deepest_document_superclass
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def deepest_document_superclass
|
|
273
|
+
appropriate_class = self
|
|
274
|
+
while appropriate_class.superclass.include?(Mongoid::Document)
|
|
275
|
+
appropriate_class = appropriate_class.superclass
|
|
276
|
+
end
|
|
277
|
+
appropriate_class
|
|
278
|
+
end
|
|
168
279
|
end
|
|
169
280
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
281
|
+
# Builds a new slug.
|
|
282
|
+
#
|
|
283
|
+
# @return [true]
|
|
284
|
+
def build_slug
|
|
285
|
+
old = slug
|
|
286
|
+
write_attribute slug_name, find_unique_slug
|
|
287
|
+
|
|
288
|
+
# @note I find it odd that we can't use `slug_was`, `slug_changed?`, or
|
|
289
|
+
# `read_attribute (slug_history_name)` here.
|
|
290
|
+
|
|
291
|
+
if slug_history_name && old && old != slug
|
|
292
|
+
self.send(slug_history_name).<<(old).uniq!
|
|
177
293
|
end
|
|
294
|
+
|
|
295
|
+
true
|
|
178
296
|
end
|
|
179
297
|
|
|
180
|
-
|
|
181
|
-
|
|
298
|
+
# Finds a unique slug, were specified string used to generate a slug.
|
|
299
|
+
#
|
|
300
|
+
# Returned slug will the same as the specified string when there are no
|
|
301
|
+
# duplicates.
|
|
302
|
+
#
|
|
303
|
+
# @param [String] Desired slug
|
|
304
|
+
# @return [String] A unique slug
|
|
305
|
+
def find_unique_slug_for(desired_slug)
|
|
306
|
+
self.class.find_unique_slug_for desired_slug, :model => self
|
|
182
307
|
end
|
|
183
308
|
|
|
184
|
-
|
|
185
|
-
|
|
309
|
+
# @return [Boolean] Whether the slug requires to be rebuilt
|
|
310
|
+
def slug_should_be_rebuilt?
|
|
311
|
+
new_record? or slug_changed? or slugged_attributes_changed?
|
|
186
312
|
end
|
|
187
313
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
314
|
+
unless self.respond_to? :slug
|
|
315
|
+
def slug
|
|
316
|
+
read_attribute slug_name
|
|
317
|
+
end
|
|
192
318
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
319
|
+
def slug_changed?
|
|
320
|
+
attribute_changed? slug_name
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def slug_was
|
|
324
|
+
attribute_was slug_name
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def slugged_attributes_changed?
|
|
329
|
+
slugged_attributes.any? { |f| attribute_changed? f }
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# @return [String] A string which Action Pack uses for constructing an URL
|
|
333
|
+
# to this record.
|
|
334
|
+
def to_param
|
|
335
|
+
unless slug
|
|
336
|
+
build_slug
|
|
337
|
+
save
|
|
210
338
|
end
|
|
339
|
+
|
|
340
|
+
slug
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
private
|
|
344
|
+
|
|
345
|
+
def find_unique_slug
|
|
346
|
+
find_unique_slug_for user_defined_slug || slug_builder.call(self)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def user_defined_slug
|
|
350
|
+
slug if new_record? and slug.present? or slug_changed?
|
|
211
351
|
end
|
|
212
352
|
end
|
|
213
353
|
end
|
data/lib/mongoid/slug/version.rb
CHANGED
data/spec/models/book.rb
CHANGED
data/spec/models/subject.rb
CHANGED
data/spec/mongoid/slug_spec.rb
CHANGED
|
@@ -58,7 +58,7 @@ module Mongoid
|
|
|
58
58
|
|
|
59
59
|
it "generates a unique slug by appending a counter to duplicate text" do
|
|
60
60
|
dup = book.subjects.create(:name => subject.name)
|
|
61
|
-
dup.to_param.should eql
|
|
61
|
+
dup.to_param.should eql "psychoanalysis-1"
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
it "does not update slug if slugged fields have not changed" do
|
|
@@ -147,26 +147,25 @@ module Mongoid
|
|
|
147
147
|
dup = Author.create(
|
|
148
148
|
:first_name => author.first_name,
|
|
149
149
|
:last_name => author.last_name)
|
|
150
|
-
dup.to_param.should eql
|
|
150
|
+
dup.to_param.should eql "gilles-deleuze-1"
|
|
151
151
|
|
|
152
152
|
dup2 = Author.create(
|
|
153
153
|
:first_name => author.first_name,
|
|
154
154
|
:last_name => author.last_name)
|
|
155
155
|
|
|
156
156
|
dup.save
|
|
157
|
-
dup2.to_param.should eql
|
|
157
|
+
dup2.to_param.should eql "gilles-deleuze-2"
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
it "does not update slug if slugged fields have changed but generated slug is identical" do
|
|
161
161
|
author.last_name = "DELEUZE"
|
|
162
162
|
author.save
|
|
163
|
-
author.to_param.should eql
|
|
163
|
+
author.to_param.should eql "gilles-deleuze"
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
it "finds by slug" do
|
|
167
167
|
Author.find_by_slug("gilles-deleuze").should eql author
|
|
168
168
|
end
|
|
169
|
-
|
|
170
169
|
end
|
|
171
170
|
|
|
172
171
|
context "when :as is passed as an argument" do
|
|
@@ -196,6 +195,40 @@ module Mongoid
|
|
|
196
195
|
end
|
|
197
196
|
end
|
|
198
197
|
|
|
198
|
+
context "when :history is passed as an argument" do
|
|
199
|
+
let(:book) do
|
|
200
|
+
Book.create(:title => "Book Title")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
before(:each) do
|
|
204
|
+
book.title = "Other Book Title"
|
|
205
|
+
book.save
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "saves the old slug in the owner's history" do
|
|
209
|
+
book.slug_history.should include("book-title")
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it "returns the document for the old slug" do
|
|
213
|
+
Book.find_by_slug("book-title").should == book
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it "returns the document for the new slug" do
|
|
217
|
+
Book.find_by_slug("other-book-title").should == book
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it "generates a unique slug by appending a counter to duplicate text" do
|
|
221
|
+
dup = Book.create(:title => "Book Title")
|
|
222
|
+
dup.to_param.should eql "book-title-1"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "ensures no duplicate values are stored in history" do
|
|
226
|
+
book.update_attributes :title => 'Book Title'
|
|
227
|
+
book.update_attributes :title => 'Foo'
|
|
228
|
+
book.slug_history.find_all { |slug| slug == 'book-title' }.size.should eql 1
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
199
232
|
context "when slug is scoped by a reference association" do
|
|
200
233
|
let(:author) do
|
|
201
234
|
book.authors.create(:first_name => "Gilles", :last_name => "Deleuze")
|
|
@@ -214,7 +247,7 @@ module Mongoid
|
|
|
214
247
|
dup = book.authors.create(
|
|
215
248
|
:first_name => author.first_name,
|
|
216
249
|
:last_name => author.last_name)
|
|
217
|
-
dup.to_param.should eql
|
|
250
|
+
dup.to_param.should eql "gilles-deleuze-1"
|
|
218
251
|
end
|
|
219
252
|
|
|
220
253
|
context "with an irregular association name" do
|
|
@@ -235,46 +268,70 @@ module Mongoid
|
|
|
235
268
|
dup.to_param.should eql character.to_param
|
|
236
269
|
end
|
|
237
270
|
end
|
|
271
|
+
|
|
272
|
+
context "when using history and reusing a slug within the scope" do
|
|
273
|
+
let!(:subject1) do
|
|
274
|
+
book.subjects.create(:name => "A Subject")
|
|
275
|
+
end
|
|
276
|
+
let!(:subject2) do
|
|
277
|
+
book.subjects.create(:name => "Another Subject")
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
before(:each) do
|
|
281
|
+
subject1.name = "Something Else Entirely"
|
|
282
|
+
subject1.save
|
|
283
|
+
subject2.name = "A Subject"
|
|
284
|
+
subject2.save
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it "allows using the slug" do
|
|
288
|
+
subject2.slug.should == "a-subject"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it "removes the slug from the old owner's history" do
|
|
292
|
+
subject1.slug_history.should_not include("a-subject")
|
|
293
|
+
end
|
|
294
|
+
end
|
|
238
295
|
end
|
|
239
|
-
|
|
296
|
+
|
|
240
297
|
context "when slug is scoped by one of the class's own fields" do
|
|
241
298
|
let!(:magazine) do
|
|
242
299
|
Magazine.create(:title => "Big Weekly", :publisher_id => "abc123")
|
|
243
300
|
end
|
|
244
301
|
|
|
245
302
|
it "should scope by local field" do
|
|
246
|
-
magazine.to_param.should eql
|
|
303
|
+
magazine.to_param.should eql "big-weekly"
|
|
247
304
|
magazine2 = Magazine.create(:title => "Big Weekly", :publisher_id => "def456")
|
|
248
305
|
magazine2.to_param.should eql magazine.to_param
|
|
249
306
|
end
|
|
250
307
|
|
|
251
308
|
it "should generate a unique slug by appending a counter to duplicate text" do
|
|
252
309
|
dup = Magazine.create(:title => "Big Weekly", :publisher_id => "abc123")
|
|
253
|
-
dup.to_param.should eql
|
|
310
|
+
dup.to_param.should eql "big-weekly-1"
|
|
254
311
|
end
|
|
255
312
|
end
|
|
256
313
|
|
|
257
|
-
context "when
|
|
314
|
+
context "when #slug is given a block" do
|
|
258
315
|
let(:caption) do
|
|
259
|
-
Caption.create(:identity =>
|
|
260
|
-
:title =>
|
|
261
|
-
:medium =>
|
|
316
|
+
Caption.create(:identity => "Edward Hopper (American, 1882-1967)",
|
|
317
|
+
:title => "Soir Bleu, 1914",
|
|
318
|
+
:medium => "Oil on Canvas")
|
|
262
319
|
end
|
|
263
320
|
|
|
264
321
|
it "generates a slug" do
|
|
265
|
-
caption.to_param.should eql
|
|
322
|
+
caption.to_param.should eql "edward-hopper-soir-bleu-1914"
|
|
266
323
|
end
|
|
267
324
|
|
|
268
325
|
it "updates the slug" do
|
|
269
|
-
caption.title =
|
|
326
|
+
caption.title = "Road in Maine, 1914"
|
|
270
327
|
caption.save
|
|
271
328
|
caption.to_param.should eql "edward-hopper-road-in-maine-1914"
|
|
272
329
|
end
|
|
273
330
|
|
|
274
331
|
it "does not change slug if slugged fields have changed but generated slug is identical" do
|
|
275
|
-
caption.identity =
|
|
332
|
+
caption.identity = "Edward Hopper"
|
|
276
333
|
caption.save
|
|
277
|
-
caption.to_param.should eql
|
|
334
|
+
caption.to_param.should eql "edward-hopper-soir-bleu-1914"
|
|
278
335
|
end
|
|
279
336
|
|
|
280
337
|
it "finds by slug" do
|
|
@@ -298,13 +355,13 @@ module Mongoid
|
|
|
298
355
|
it "slugs Chinese characters" do
|
|
299
356
|
book.title = "中文"
|
|
300
357
|
book.save
|
|
301
|
-
book.to_param.should eql
|
|
358
|
+
book.to_param.should eql "zhong-wen"
|
|
302
359
|
end
|
|
303
360
|
|
|
304
361
|
it "slugs non-ASCII Latin characters" do
|
|
305
|
-
book.title =
|
|
362
|
+
book.title = "Paul Cézanne"
|
|
306
363
|
book.save
|
|
307
|
-
book.to_param.should eql
|
|
364
|
+
book.to_param.should eql "paul-cezanne"
|
|
308
365
|
end
|
|
309
366
|
end
|
|
310
367
|
|
|
@@ -340,6 +397,29 @@ module Mongoid
|
|
|
340
397
|
Person.collection.index_information.should_not have_key "permalink_1"
|
|
341
398
|
end
|
|
342
399
|
end
|
|
400
|
+
|
|
401
|
+
context "when :reserve is passed" do
|
|
402
|
+
it "does not use the the reserved slugs" do
|
|
403
|
+
friend1 = Friend.create(:name => "foo")
|
|
404
|
+
friend1.slug.should_not eql("foo")
|
|
405
|
+
friend1.slug.should eql("foo-1")
|
|
406
|
+
|
|
407
|
+
friend2 = Friend.create(:name => "bar")
|
|
408
|
+
friend2.slug.should_not eql("bar")
|
|
409
|
+
friend2.slug.should eql("bar-1")
|
|
410
|
+
|
|
411
|
+
friend3 = Friend.create(:name => "en")
|
|
412
|
+
friend3.slug.should_not eql("en")
|
|
413
|
+
friend3.slug.should eql("en-1")
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
it "should start with concatenation -1" do
|
|
417
|
+
friend1 = Friend.create(:name => "foo")
|
|
418
|
+
friend1.slug.should eql("foo-1")
|
|
419
|
+
friend2 = Friend.create(:name => "foo")
|
|
420
|
+
friend2.slug.should eql("foo-2")
|
|
421
|
+
end
|
|
422
|
+
end
|
|
343
423
|
|
|
344
424
|
context "when the object has STI" do
|
|
345
425
|
it "scopes by the superclass" do
|
|
@@ -349,6 +429,25 @@ module Mongoid
|
|
|
349
429
|
end
|
|
350
430
|
end
|
|
351
431
|
|
|
432
|
+
context "when slug defined on alias of field" do
|
|
433
|
+
it "should use accessor, not alias" do
|
|
434
|
+
pseudonim = Alias.create(:author_name => "Max Stirner")
|
|
435
|
+
pseudonim.slug.should eql("max-stirner")
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
describe ".by_slug scope" do
|
|
440
|
+
let!(:author) { book.authors.create(:first_name => "Gilles", :last_name => "Deleuze") }
|
|
441
|
+
|
|
442
|
+
it "returns an empty array if no document is found" do
|
|
443
|
+
book.authors.by_slug("never-heard-of").should == []
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
it "returns an array containing the document if it is found" do
|
|
447
|
+
book.authors.by_slug(author.slug).should == [author]
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
352
451
|
describe ".find_by_slug" do
|
|
353
452
|
let!(:book) { Book.create(:title => "A Thousand Plateaus") }
|
|
354
453
|
|
|
@@ -386,11 +485,62 @@ module Mongoid
|
|
|
386
485
|
book.reload.slug.should eql "proust-and-signs"
|
|
387
486
|
end
|
|
388
487
|
end
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
it "
|
|
392
|
-
|
|
393
|
-
|
|
488
|
+
|
|
489
|
+
describe ".for_unique_slug_for" do
|
|
490
|
+
it "returns the unique slug" do
|
|
491
|
+
Book.find_unique_slug_for("A Thousand Plateaus").should eq("a-thousand-plateaus")
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
it "returns the unique slug with a counter if necessary" do
|
|
495
|
+
Book.create(:title => "A Thousand Plateaus")
|
|
496
|
+
Book.find_unique_slug_for("A Thousand Plateaus").should eq("a-thousand-plateaus-1")
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
it "returns the unique slug as if it were the provided object" do
|
|
500
|
+
book = Book.create(:title => "A Thousand Plateaus")
|
|
501
|
+
Book.find_unique_slug_for("A Thousand Plateaus", :model => book).should eq("a-thousand-plateaus")
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
describe "#find_unique_slug_for" do
|
|
506
|
+
let!(:book) { Book.create(:title => "A Thousand Plateaus") }
|
|
507
|
+
|
|
508
|
+
it "returns the unique slug" do
|
|
509
|
+
book.find_unique_slug_for("Anti Oedipus").should eq("anti-oedipus")
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
it "returns the unique slug with a counter if necessary" do
|
|
513
|
+
Book.create(:title => "Anti Oedipus")
|
|
514
|
+
book.find_unique_slug_for("Anti Oedipus").should eq("anti-oedipus-1")
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
context "when the slugged field is set manually" do
|
|
519
|
+
context "when it set to a non-empty string" do
|
|
520
|
+
it "respects the provided slug" do
|
|
521
|
+
book = Book.create(:title => "A Thousand Plateaus", :slug => "not-what-you-expected")
|
|
522
|
+
book.to_param.should eql "not-what-you-expected"
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it "ensures uniqueness" do
|
|
526
|
+
book1 = Book.create(:title => "A Thousand Plateaus", :slug => "not-what-you-expected")
|
|
527
|
+
book2 = Book.create(:title => "A Thousand Plateaus", :slug => "not-what-you-expected")
|
|
528
|
+
book2.to_param.should eql "not-what-you-expected-1"
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
it "updates the slug when a new one is passed in" do
|
|
532
|
+
book = Book.create(:title => "A Thousand Plateaus", :slug => "not-what-you-expected")
|
|
533
|
+
book.slug = "not-it-either"
|
|
534
|
+
book.save
|
|
535
|
+
book.to_param.should eql "not-it-either"
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
context "when it is set to an empty string" do
|
|
540
|
+
it "generate a new one" do
|
|
541
|
+
book = Book.create(:title => "A Thousand Plateaus", :slug => "")
|
|
542
|
+
book.to_param.should eql "a-thousand-plateaus"
|
|
543
|
+
end
|
|
394
544
|
end
|
|
395
545
|
end
|
|
396
546
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler/setup'
|
|
3
3
|
|
|
4
|
-
require
|
|
4
|
+
require 'pry'
|
|
5
|
+
require 'rspec'
|
|
5
6
|
|
|
6
|
-
require File.expand_path(
|
|
7
|
+
require File.expand_path('../../lib/mongoid/slug', __FILE__)
|
|
7
8
|
|
|
8
9
|
Mongoid.configure do |config|
|
|
9
|
-
name =
|
|
10
|
+
name = 'mongoid_slug_test'
|
|
10
11
|
config.master = Mongo::Connection.new.db(name)
|
|
11
12
|
end
|
|
12
13
|
|
metadata
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mongoid_slug
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
8
|
-
-
|
|
8
|
+
- Hakan Ensari
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-03-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: mongoid
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &70314214792060 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ~>
|
|
@@ -21,10 +21,10 @@ dependencies:
|
|
|
21
21
|
version: '2.0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *70314214792060
|
|
25
25
|
- !ruby/object:Gem::Dependency
|
|
26
26
|
name: stringex
|
|
27
|
-
requirement: &
|
|
27
|
+
requirement: &70314214791560 !ruby/object:Gem::Requirement
|
|
28
28
|
none: false
|
|
29
29
|
requirements:
|
|
30
30
|
- - ~>
|
|
@@ -32,10 +32,32 @@ dependencies:
|
|
|
32
32
|
version: '1.3'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *70314214791560
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: bson_ext
|
|
38
|
+
requirement: &70314214791100 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '1.6'
|
|
44
|
+
type: :development
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *70314214791100
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: pry
|
|
49
|
+
requirement: &70314214790640 !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.9'
|
|
55
|
+
type: :development
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: *70314214790640
|
|
36
58
|
- !ruby/object:Gem::Dependency
|
|
37
59
|
name: rake
|
|
38
|
-
requirement: &
|
|
60
|
+
requirement: &70314214790180 !ruby/object:Gem::Requirement
|
|
39
61
|
none: false
|
|
40
62
|
requirements:
|
|
41
63
|
- - ~>
|
|
@@ -43,22 +65,21 @@ dependencies:
|
|
|
43
65
|
version: '0.9'
|
|
44
66
|
type: :development
|
|
45
67
|
prerelease: false
|
|
46
|
-
version_requirements: *
|
|
68
|
+
version_requirements: *70314214790180
|
|
47
69
|
- !ruby/object:Gem::Dependency
|
|
48
70
|
name: rspec
|
|
49
|
-
requirement: &
|
|
71
|
+
requirement: &70314214789720 !ruby/object:Gem::Requirement
|
|
50
72
|
none: false
|
|
51
73
|
requirements:
|
|
52
74
|
- - ~>
|
|
53
75
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '2.
|
|
76
|
+
version: '2.8'
|
|
55
77
|
type: :development
|
|
56
78
|
prerelease: false
|
|
57
|
-
version_requirements: *
|
|
58
|
-
description:
|
|
59
|
-
in a Mongoid model.
|
|
79
|
+
version_requirements: *70314214789720
|
|
80
|
+
description: ! " a \n a ."
|
|
60
81
|
email:
|
|
61
|
-
-
|
|
82
|
+
- hakan.ensari@papercavalier.com
|
|
62
83
|
executables: []
|
|
63
84
|
extensions: []
|
|
64
85
|
extra_rdoc_files: []
|
|
@@ -68,10 +89,12 @@ files:
|
|
|
68
89
|
- lib/mongoid_slug.rb
|
|
69
90
|
- LICENSE
|
|
70
91
|
- README.md
|
|
92
|
+
- spec/models/alias.rb
|
|
71
93
|
- spec/models/article.rb
|
|
72
94
|
- spec/models/author.rb
|
|
73
95
|
- spec/models/book.rb
|
|
74
96
|
- spec/models/caption.rb
|
|
97
|
+
- spec/models/friend.rb
|
|
75
98
|
- spec/models/magazine.rb
|
|
76
99
|
- spec/models/page.rb
|
|
77
100
|
- spec/models/partner.rb
|
|
@@ -80,7 +103,7 @@ files:
|
|
|
80
103
|
- spec/models/subject.rb
|
|
81
104
|
- spec/mongoid/slug_spec.rb
|
|
82
105
|
- spec/spec_helper.rb
|
|
83
|
-
homepage: http://github.com/
|
|
106
|
+
homepage: http://github.com/hakanensari/mongoid-slug
|
|
84
107
|
licenses: []
|
|
85
108
|
post_install_message:
|
|
86
109
|
rdoc_options: []
|
|
@@ -103,12 +126,14 @@ rubyforge_project: mongoid_slug
|
|
|
103
126
|
rubygems_version: 1.8.11
|
|
104
127
|
signing_key:
|
|
105
128
|
specification_version: 3
|
|
106
|
-
summary: Generates a URL slug
|
|
129
|
+
summary: Generates a URL slug in a Mongoid model
|
|
107
130
|
test_files:
|
|
131
|
+
- spec/models/alias.rb
|
|
108
132
|
- spec/models/article.rb
|
|
109
133
|
- spec/models/author.rb
|
|
110
134
|
- spec/models/book.rb
|
|
111
135
|
- spec/models/caption.rb
|
|
136
|
+
- spec/models/friend.rb
|
|
112
137
|
- spec/models/magazine.rb
|
|
113
138
|
- spec/models/page.rb
|
|
114
139
|
- spec/models/partner.rb
|