mongoid-slug 5.1.1 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|