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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f62313ab069384e3dea183cd680aae50318beb85
4
- data.tar.gz: 8881c0bef60727f9c57d8210b74f04bd48f545d6
3
+ metadata.gz: 2d38815d369307746d6c796167d08b99aae02f8d
4
+ data.tar.gz: e0f6c6ba7dc2431fe84de87a6b32dcc90ba1f2ba
5
5
  SHA512:
6
- metadata.gz: 0bbf0cc529c6c8da1955ab3f9d40dfddb5e081c740bee1f012b3c115ad1698ed00bd5c8b81e158ce3626678e99dc9337a565ea3a8c67107583e940d20eed78f7
7
- data.tar.gz: d9b4553651b7c2f588a8fe188974c1074283846dae8e5962d05471b216d50e7dc0db1ae10bad7ed46c932debf502ea28ebe7e1726fa1360866cbb9da802add49
6
+ metadata.gz: c4a7f3f5d7fdf36cb4d64b0ae5bb9f1e35bc6c2dbad3a7d1bbd586ff46b0a0da957bd3762bbe88182595b1aeb9b4e59c6a88bd143bbcdc3f17498c1a60ff82be
7
+ data.tar.gz: c91cb32733b81ec8b1ace9528bfced0c019aa1fc96d88cc4b6e11d803af0509cee3a0098260de8abcfd7702207deda9cd1c813b3f73f3a12f14930880d08c94c
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010-2015 Hakan Ensari & Contributors
1
+ Copyright (c) 2010-2016 Hakan Ensari & Contributors
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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, :history => true
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, :scope => :company
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, :scope => :company_id
146
+ slug :name, scope: :company_id
137
147
  end
138
148
  ```
139
149
 
140
- Optionally find and create slugs per model type
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, :history => true
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(:title => "Anti Oedipus")
160
- comic_book = ComicBook.create(:title => "Anti Oedipus")
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 :by_model_type => true.
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, :history => true, :by_model_type => true
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(:title => "Anti Oedipus")
181
- comic_book = ComicBook.create(:title => "Anti Oedipus")
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") #=> true
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, :history => true
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-2015 Hakan Ensari & Contributors, see [LICENSE](LICENSE) for details.
358
+ Copyright (c) 2010-2016 Hakan Ensari & Contributors, see [LICENSE](LICENSE) for details.
333
359
 
@@ -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
@@ -0,0 +1,9 @@
1
+ module Mongoid
2
+ module Slug
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ Dir[File.join(File.dirname(__FILE__), '../../tasks/*.rake')].each { |f| load f }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -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.
@@ -1,5 +1,5 @@
1
1
  module Mongoid #:nodoc:
2
2
  module Slug
3
- VERSION = '5.1.1'
3
+ VERSION = '5.2.0'
4
4
  end
5
5
  end
@@ -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
@@ -3,7 +3,7 @@ class Author
3
3
  include Mongoid::Slug
4
4
  field :first_name
5
5
  field :last_name
6
- slug :first_name, :last_name, scope: :book
6
+ slug :first_name, :last_name, scope: :book, history: false, max_length: 256
7
7
  belongs_to :book
8
8
  has_many :characters,
9
9
  class_name: 'Person',
@@ -3,7 +3,7 @@ class Book
3
3
  include Mongoid::Slug
4
4
  field :title
5
5
 
6
- slug :title, history: true
6
+ slug :title, history: true, max_length: nil
7
7
  embeds_many :subjects
8
8
  has_many :authors
9
9
  end
@@ -348,33 +348,50 @@ module Mongoid
348
348
  end
349
349
 
350
350
  context 'when :history is passed as an argument' do
351
- let(:book) do
352
- Book.create(title: 'Book Title')
353
- end
351
+ context 'true' do
352
+ let(:book) do
353
+ Book.create(title: 'Book Title')
354
+ end
354
355
 
355
- before(:each) do
356
- book.title = 'Other Book Title'
357
- book.save
358
- end
356
+ before(:each) do
357
+ book.title = 'Other Book Title'
358
+ book.save
359
+ end
359
360
 
360
- it "saves the old slug in the owner's history" do
361
- expect(book.slugs).to include('book-title')
362
- end
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
- it 'generates a unique slug by appending a counter to duplicate text' do
365
- dup = Book.create(title: 'Book Title')
366
- expect(dup.to_param).to eql 'book-title-1'
367
- end
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
- it 'does not allow a BSON::ObjectId as use for a slug' do
370
- bad = Book.create(title: '4ea0389f0364313d79104fb3')
371
- expect(bad.to_param).not_to eql '4ea0389f0364313d79104fb3'
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
- it 'ensures no duplicate values are stored in history' do
375
- book.update_attributes title: 'Book Title'
376
- book.update_attributes title: 'Foo'
377
- expect(book.slugs.find_all { |slug| slug == 'book-title' }.size).to eql 1
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.1.1
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: 2015-11-03 00:00:00.000000000 Z
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