mongoid-slug 5.1.1 → 5.2.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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +41 -15
- data/lib/mongoid/slug.rb +7 -1
- data/lib/mongoid/slug/railtie.rb +9 -0
- data/lib/mongoid/slug/unique_slug.rb +4 -1
- data/lib/mongoid/slug/version.rb +1 -1
- data/lib/tasks/mongoid_slug.rake +18 -0
- data/spec/models/author.rb +1 -1
- data/spec/models/book.rb +1 -1
- data/spec/mongoid/slug_spec.rb +89 -21
- data/spec/tasks/mongoid_slug_rake_spec.rb +73 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d38815d369307746d6c796167d08b99aae02f8d
|
4
|
+
data.tar.gz: e0f6c6ba7dc2431fe84de87a6b32dcc90ba1f2ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4a7f3f5d7fdf36cb4d64b0ae5bb9f1e35bc6c2dbad3a7d1bbd586ff46b0a0da957bd3762bbe88182595b1aeb9b4e59c6a88bd143bbcdc3f17498c1a60ff82be
|
7
|
+
data.tar.gz: c91cb32733b81ec8b1ace9528bfced0c019aa1fc96d88cc4b6e11d803af0509cee3a0098260de8abcfd7702207deda9cd1c813b3f73f3a12f14930880d08c94c
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -60,7 +60,7 @@ class Post
|
|
60
60
|
field :_id, type: String, slug_id_strategy: lambda {|id| id.start_with?('....')}
|
61
61
|
|
62
62
|
field :name
|
63
|
-
slug :name, :
|
63
|
+
slug :name, history: true
|
64
64
|
end
|
65
65
|
|
66
66
|
Post.fields['_id'].type
|
@@ -72,6 +72,16 @@ post = Post.find '50b1386a0482939864000001' # Finds by bson ids
|
|
72
72
|
```
|
73
73
|
[Examine slug.rb](lib/mongoid/slug.rb) for all available options.
|
74
74
|
|
75
|
+
To set slugs for existing records run following rake task:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
rake mongoid_slug:set
|
79
|
+
```
|
80
|
+
You can pass model names as an option for which you want to set slugs:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
rake mongoid_slug:set[Model1,Model2]
|
84
|
+
```
|
75
85
|
Custom Slug Generation
|
76
86
|
-------
|
77
87
|
|
@@ -111,7 +121,7 @@ class Employee
|
|
111
121
|
field :name
|
112
122
|
referenced_in :company
|
113
123
|
|
114
|
-
slug :name, :
|
124
|
+
slug :name, scope: :company
|
115
125
|
end
|
116
126
|
```
|
117
127
|
|
@@ -133,12 +143,28 @@ class Employee
|
|
133
143
|
field :name
|
134
144
|
field :company_id
|
135
145
|
|
136
|
-
slug :name, :
|
146
|
+
slug :name, scope: :company_id
|
137
147
|
end
|
138
148
|
```
|
139
149
|
|
140
|
-
|
141
|
-
|
150
|
+
Limit Slug Length
|
151
|
+
-----------------
|
152
|
+
|
153
|
+
MongoDB has a default limit around 1KB to the size of the index keys and will raise error 17280, `key too large to index` when trying to create a record that causes an index key to exceed that limit. By default slugs are of the form `text[-number]` and the text portion is limited in size to `Mongoid::Slug::MONGO_INDEX_KEY_LIMIT_BYTES - 32` bytes. You can change this limit with `max_length` or set it to `nil` if you're running MongoDB with [failIndexKeyTooLong](https://docs.mongodb.org/manual/reference/parameters/#param.failIndexKeyTooLong) set to `false`.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
class Company
|
157
|
+
include Mongoid::Document
|
158
|
+
include Mongoid::Slug
|
159
|
+
|
160
|
+
field :name
|
161
|
+
|
162
|
+
slug :name, max_length: 24
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
Optionally Find and Create Slugs per Model Type
|
167
|
+
-----------------------------------------------
|
142
168
|
|
143
169
|
By default when using STI, the scope will be around the super-class.
|
144
170
|
|
@@ -148,7 +174,7 @@ class Book
|
|
148
174
|
include Mongoid::Slug
|
149
175
|
field :title
|
150
176
|
|
151
|
-
slug :title, :
|
177
|
+
slug :title, history: true
|
152
178
|
embeds_many :subjects
|
153
179
|
has_many :authors
|
154
180
|
end
|
@@ -156,12 +182,12 @@ end
|
|
156
182
|
class ComicBook < Book
|
157
183
|
end
|
158
184
|
|
159
|
-
book = Book.create(:
|
160
|
-
comic_book = ComicBook.create(:
|
185
|
+
book = Book.create(title: 'Anti Oedipus')
|
186
|
+
comic_book = ComicBook.create(title: 'Anti Oedipus')
|
161
187
|
comic_book.slugs.should_not eql(book.slugs)
|
162
188
|
```
|
163
189
|
|
164
|
-
If you want the scope to be around the subclass, then set the option :
|
190
|
+
If you want the scope to be around the subclass, then set the option `by_model_type: true`.
|
165
191
|
|
166
192
|
```ruby
|
167
193
|
class Book
|
@@ -169,7 +195,7 @@ class Book
|
|
169
195
|
include Mongoid::Slug
|
170
196
|
field :title
|
171
197
|
|
172
|
-
slug :title, :
|
198
|
+
slug :title, history: true, by_model_type: true
|
173
199
|
embeds_many :subjects
|
174
200
|
has_many :authors
|
175
201
|
end
|
@@ -177,8 +203,8 @@ end
|
|
177
203
|
class ComicBook < Book
|
178
204
|
end
|
179
205
|
|
180
|
-
book = Book.create(:
|
181
|
-
comic_book = ComicBook.create(:
|
206
|
+
book = Book.create(title: 'Anti Oedipus')
|
207
|
+
comic_book = ComicBook.create(title: 'Anti Oedipus')
|
182
208
|
comic_book.slugs.should eql(book.slugs)
|
183
209
|
```
|
184
210
|
|
@@ -206,7 +232,7 @@ page = Page.new title: "Home"
|
|
206
232
|
page.save
|
207
233
|
page.update_attributes title: "Welcome"
|
208
234
|
|
209
|
-
Page.find("welcome") == Page.find("home")
|
235
|
+
Page.find("welcome") == Page.find("home") # => true
|
210
236
|
```
|
211
237
|
|
212
238
|
Reserved Slugs
|
@@ -276,7 +302,7 @@ class Entity
|
|
276
302
|
field :_id, type: String, slug_id_strategy: UuidIdStrategy
|
277
303
|
|
278
304
|
field :user_edited_variation
|
279
|
-
slug :user_edited_variation, :
|
305
|
+
slug :user_edited_variation, history: true
|
280
306
|
end
|
281
307
|
```
|
282
308
|
|
@@ -329,5 +355,5 @@ Mongoid-slug is work of [many of contributors](https://github.com/digitalplaywri
|
|
329
355
|
Copyright & License
|
330
356
|
-------------------
|
331
357
|
|
332
|
-
Copyright (c) 2010-
|
358
|
+
Copyright (c) 2010-2016 Hakan Ensari & Contributors, see [LICENSE](LICENSE) for details.
|
333
359
|
|
data/lib/mongoid/slug.rb
CHANGED
@@ -6,19 +6,23 @@ require 'mongoid/slug/paranoia'
|
|
6
6
|
require 'mongoid/slug/unique_slug'
|
7
7
|
require 'mongoid/slug/slug_id_strategy'
|
8
8
|
require 'mongoid-compatibility'
|
9
|
+
require 'mongoid/slug/railtie' if defined?(Rails)
|
9
10
|
|
10
11
|
module Mongoid
|
11
12
|
# Slugs your Mongoid model.
|
12
13
|
module Slug
|
13
14
|
extend ActiveSupport::Concern
|
14
15
|
|
16
|
+
MONGO_INDEX_KEY_LIMIT_BYTES = 1024
|
17
|
+
|
15
18
|
included do
|
16
19
|
cattr_accessor :reserved_words,
|
17
20
|
:slug_scope,
|
18
21
|
:slugged_attributes,
|
19
22
|
:url_builder,
|
20
23
|
:history,
|
21
|
-
:by_model_type
|
24
|
+
:by_model_type,
|
25
|
+
:slug_max_length
|
22
26
|
|
23
27
|
# field :_slugs, type: Array, default: [], localize: false
|
24
28
|
# alias_attribute :slugs, :_slugs
|
@@ -43,6 +47,7 @@ module Mongoid
|
|
43
47
|
# @param options :scope [Symbol] a reference association or field to
|
44
48
|
# scope the slug by. Embedded documents are, by default, scoped by
|
45
49
|
# their parent.
|
50
|
+
# @param options :max_length [Integer] the maximum length of the text portion of the slug
|
46
51
|
# @yield If given, a block is used to build a slug.
|
47
52
|
#
|
48
53
|
# @example A custom builder
|
@@ -64,6 +69,7 @@ module Mongoid
|
|
64
69
|
self.slugged_attributes = fields.map(&:to_s)
|
65
70
|
self.history = options[:history]
|
66
71
|
self.by_model_type = options[:by_model_type]
|
72
|
+
self.slug_max_length = options.key?(:max_length) ? options[:max_length] : MONGO_INDEX_KEY_LIMIT_BYTES - 32
|
67
73
|
|
68
74
|
field :_slugs, type: Array, default: [], localize: options[:localize]
|
69
75
|
alias_attribute :slugs, :_slugs
|
@@ -63,7 +63,7 @@ module Mongoid
|
|
63
63
|
|
64
64
|
def_delegators :@model, :slug_scope, :reflect_on_association, :read_attribute,
|
65
65
|
:check_against_id, :reserved_words, :url_builder, :collection_name,
|
66
|
-
:embedded?, :reflect_on_all_associations, :by_model_type
|
66
|
+
:embedded?, :reflect_on_all_associations, :by_model_type, :slug_max_length
|
67
67
|
|
68
68
|
def initialize(model)
|
69
69
|
@model = model
|
@@ -82,6 +82,9 @@ module Mongoid
|
|
82
82
|
else
|
83
83
|
url_builder.call(model)
|
84
84
|
end
|
85
|
+
|
86
|
+
@_slug = @_slug[0...slug_max_length] if slug_max_length
|
87
|
+
|
85
88
|
# Regular expression that matches slug, slug-1, ... slug-n
|
86
89
|
# If slug_name field was indexed, MongoDB will utilize that
|
87
90
|
# index to match /^.../ pattern.
|
data/lib/mongoid/slug/version.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
namespace :mongoid_slug do
|
2
|
+
desc 'Goes though all documents and sets slug if not already set'
|
3
|
+
task set: :environment do |_, args|
|
4
|
+
::Rails.application.eager_load! if defined?(Rails)
|
5
|
+
klasses = Module.constants.find_all do |const|
|
6
|
+
const != const.upcase ? Mongoid::Slug > (Object.const_get const) : nil
|
7
|
+
end
|
8
|
+
klasses.map! { |klass| klass.to_s.constantize }
|
9
|
+
unless klasses.blank?
|
10
|
+
models = args.extras
|
11
|
+
klasses = (klasses.map(&:to_s) & models.map(&:classify)).map(&:constantize) if models.any?
|
12
|
+
klasses.each do |klass|
|
13
|
+
# set slug for objects having blank slug
|
14
|
+
klass.each { |object| object.set_slug! unless object.slugs? }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/models/author.rb
CHANGED
data/spec/models/book.rb
CHANGED
data/spec/mongoid/slug_spec.rb
CHANGED
@@ -348,33 +348,50 @@ module Mongoid
|
|
348
348
|
end
|
349
349
|
|
350
350
|
context 'when :history is passed as an argument' do
|
351
|
-
|
352
|
-
|
353
|
-
|
351
|
+
context 'true' do
|
352
|
+
let(:book) do
|
353
|
+
Book.create(title: 'Book Title')
|
354
|
+
end
|
354
355
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
356
|
+
before(:each) do
|
357
|
+
book.title = 'Other Book Title'
|
358
|
+
book.save
|
359
|
+
end
|
359
360
|
|
360
|
-
|
361
|
-
|
362
|
-
|
361
|
+
it "saves the old slug in the owner's history" do
|
362
|
+
expect(book.slugs).to include('book-title')
|
363
|
+
end
|
363
364
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
365
|
+
it 'generates a unique slug by appending a counter to duplicate text' do
|
366
|
+
dup = Book.create(title: 'Book Title')
|
367
|
+
expect(dup.to_param).to eql 'book-title-1'
|
368
|
+
end
|
368
369
|
|
369
|
-
|
370
|
-
|
371
|
-
|
370
|
+
it 'does not allow a BSON::ObjectId as use for a slug' do
|
371
|
+
bad = Book.create(title: '4ea0389f0364313d79104fb3')
|
372
|
+
expect(bad.to_param).not_to eql '4ea0389f0364313d79104fb3'
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'ensures no duplicate values are stored in history' do
|
376
|
+
book.update_attributes title: 'Book Title'
|
377
|
+
book.update_attributes title: 'Foo'
|
378
|
+
expect(book.slugs.find_all { |slug| slug == 'book-title' }.size).to eql 1
|
379
|
+
end
|
372
380
|
end
|
381
|
+
context 'false' do
|
382
|
+
let(:author) do
|
383
|
+
Author.create(first_name: 'Gilles', last_name: 'Deleuze')
|
384
|
+
end
|
373
385
|
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
386
|
+
before(:each) do
|
387
|
+
author.first_name = 'John'
|
388
|
+
author.save
|
389
|
+
end
|
390
|
+
|
391
|
+
it "does not save the old slug in the owner's history" do
|
392
|
+
expect(author.slugs.count).to eq 1
|
393
|
+
expect(author.slugs).to_not include('gilles-deleuze')
|
394
|
+
end
|
378
395
|
end
|
379
396
|
end
|
380
397
|
|
@@ -519,6 +536,28 @@ module Mongoid
|
|
519
536
|
it_should_behave_like 'has an index', { _slugs: 1 }, unique: true, sparse: true
|
520
537
|
end
|
521
538
|
|
539
|
+
context 'with a value exceeding mongodb max index key' do
|
540
|
+
if Mongoid::Compatibility::Version.mongoid5?
|
541
|
+
it 'errors with a model without a max length' do
|
542
|
+
expect do
|
543
|
+
Book.create!(title: 't' * 1025)
|
544
|
+
end.to raise_error Mongo::Error::OperationFailure, /key too large to index/
|
545
|
+
end
|
546
|
+
elsif Mongoid::Compatibility::Version.mongoid4?
|
547
|
+
it 'errors with a model without a max length' do
|
548
|
+
expect do
|
549
|
+
Book.create!(title: 't' * 1025)
|
550
|
+
end.to raise_error Moped::Errors::OperationFailure, /key too large to index/
|
551
|
+
end
|
552
|
+
end
|
553
|
+
it 'succeeds with a model with a max length' do
|
554
|
+
expect do
|
555
|
+
author = Author.create!(last_name: 't' * 1025)
|
556
|
+
expect(author.slug.length).to eq 256
|
557
|
+
end.to_not raise_error
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
522
561
|
context 'when slug is scoped by a reference association' do
|
523
562
|
subject { Author }
|
524
563
|
it_should_behave_like 'does not have an index', _slugs: 1
|
@@ -1019,5 +1058,34 @@ module Mongoid
|
|
1019
1058
|
'nl' => ['title-on-netherlands'])
|
1020
1059
|
end
|
1021
1060
|
end
|
1061
|
+
|
1062
|
+
describe 'slug_max_length' do
|
1063
|
+
before do
|
1064
|
+
Author.create_indexes
|
1065
|
+
end
|
1066
|
+
after do
|
1067
|
+
Author.remove_indexes
|
1068
|
+
end
|
1069
|
+
it 'can be assigned to nil' do
|
1070
|
+
expect(Book.slug_max_length).to be nil
|
1071
|
+
end
|
1072
|
+
it 'defaults to MONGO_INDEX_KEY_LIMIT_BYTES - 32' do
|
1073
|
+
expect(Article.slug_max_length).to eq Mongoid::Slug::MONGO_INDEX_KEY_LIMIT_BYTES - 32
|
1074
|
+
end
|
1075
|
+
it 'is assigned via max_length' do
|
1076
|
+
expect(Author.slug_max_length).to eq 256
|
1077
|
+
end
|
1078
|
+
it 'enforces max length of slug' do
|
1079
|
+
author1 = Author.create!(last_name: 't' * 1024)
|
1080
|
+
expect(author1.slug.length).to eq 256
|
1081
|
+
expect(author1.slug.ends_with?('ttt')).to be true
|
1082
|
+
author2 = Author.create!(last_name: 't' * 1024)
|
1083
|
+
expect(author2.slug.length).to eq 258
|
1084
|
+
expect(author2.slug.ends_with?('tt-1')).to be true
|
1085
|
+
author3 = Author.create!(last_name: 't' * 1024)
|
1086
|
+
expect(author3.slug.length).to eq 258
|
1087
|
+
expect(author3.slug.ends_with?('tt-2')).to be true
|
1088
|
+
end
|
1089
|
+
end
|
1022
1090
|
end
|
1023
1091
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
describe 'mongoid_slug:set' do
|
5
|
+
before :all do
|
6
|
+
load File.expand_path('../../../lib/tasks/mongoid_slug.rake', __FILE__)
|
7
|
+
Rake::Task.define_task(:environment)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'when models parameter is passed' do
|
11
|
+
before :all do
|
12
|
+
UninitalizedSlugFirst = Class.new do
|
13
|
+
include Mongoid::Document
|
14
|
+
field :name, type: String
|
15
|
+
store_in collection: 'uninitalized_slug_first'
|
16
|
+
end
|
17
|
+
UninitalizedSlugSecond = Class.new do
|
18
|
+
include Mongoid::Document
|
19
|
+
field :name, type: String
|
20
|
+
store_in collection: 'uninitalized_slug_second'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'goes though all documents of passed models and sets slug if not already set' do
|
25
|
+
uninitalized_slug1 = UninitalizedSlugFirst.create(name: 'uninitalized-slug1')
|
26
|
+
uninitalized_slug2 = UninitalizedSlugSecond.create(name: 'uninitalized-slug2')
|
27
|
+
|
28
|
+
UninitalizedSlugFirst.class_eval do
|
29
|
+
include Mongoid::Slug
|
30
|
+
slug :name
|
31
|
+
end
|
32
|
+
UninitalizedSlugSecond.class_eval do
|
33
|
+
include Mongoid::Slug
|
34
|
+
slug :name
|
35
|
+
end
|
36
|
+
|
37
|
+
expect(uninitalized_slug1.slugs).to be_nil
|
38
|
+
expect(uninitalized_slug2.slugs).to be_nil
|
39
|
+
|
40
|
+
Rake::Task['mongoid_slug:set'].reenable
|
41
|
+
Rake::Task['mongoid_slug:set'].invoke('UninitalizedSlugFirst')
|
42
|
+
|
43
|
+
expect(uninitalized_slug1.reload.slugs).to eq(['uninitalized-slug1'])
|
44
|
+
expect(uninitalized_slug2.reload.slugs).to eq []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when models parameter is not passed' do
|
49
|
+
before :all do
|
50
|
+
UninitalizedSlugThird = Class.new do
|
51
|
+
include Mongoid::Document
|
52
|
+
field :name, type: String
|
53
|
+
store_in collection: 'uninitalized_slug_third'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'goes though all documents and sets slug if not already set' do
|
58
|
+
uninitalized_slug3 = UninitalizedSlugThird.create(name: 'uninitalized-slug3')
|
59
|
+
|
60
|
+
UninitalizedSlugThird.class_eval do
|
61
|
+
include Mongoid::Slug
|
62
|
+
slug :name
|
63
|
+
end
|
64
|
+
|
65
|
+
expect(uninitalized_slug3.slugs).to be_nil
|
66
|
+
|
67
|
+
Rake::Task['mongoid_slug:set'].reenable
|
68
|
+
Rake::Task['mongoid_slug:set'].invoke
|
69
|
+
|
70
|
+
expect(uninitalized_slug3.reload.slugs).to eq(['uninitalized-slug3'])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-slug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Saebjoernsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mongoid
|
@@ -177,10 +177,12 @@ files:
|
|
177
177
|
- lib/mongoid/slug/criteria.rb
|
178
178
|
- lib/mongoid/slug/index.rb
|
179
179
|
- lib/mongoid/slug/paranoia.rb
|
180
|
+
- lib/mongoid/slug/railtie.rb
|
180
181
|
- lib/mongoid/slug/slug_id_strategy.rb
|
181
182
|
- lib/mongoid/slug/unique_slug.rb
|
182
183
|
- lib/mongoid/slug/version.rb
|
183
184
|
- lib/mongoid_slug.rb
|
185
|
+
- lib/tasks/mongoid_slug.rake
|
184
186
|
- spec/models/alias.rb
|
185
187
|
- spec/models/article.rb
|
186
188
|
- spec/models/artist.rb
|
@@ -215,6 +217,7 @@ files:
|
|
215
217
|
- spec/mongoid/slug_spec.rb
|
216
218
|
- spec/shared/indexes.rb
|
217
219
|
- spec/spec_helper.rb
|
220
|
+
- spec/tasks/mongoid_slug_rake_spec.rb
|
218
221
|
homepage: https://github.com/digitalplaywright/mongoid-slug
|
219
222
|
licenses:
|
220
223
|
- MIT
|
@@ -274,3 +277,4 @@ test_files:
|
|
274
277
|
- spec/mongoid/slug_spec.rb
|
275
278
|
- spec/shared/indexes.rb
|
276
279
|
- spec/spec_helper.rb
|
280
|
+
- spec/tasks/mongoid_slug_rake_spec.rb
|