mongoid-slug 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +333 -0
- data/lib/mongoid/slug.rb +333 -0
- data/lib/mongoid/slug/criteria.rb +110 -0
- data/lib/mongoid/slug/index.rb +27 -0
- data/lib/mongoid/slug/paranoia.rb +22 -0
- data/lib/mongoid/slug/slug_id_strategy.rb +3 -0
- data/lib/mongoid/slug/unique_slug.rb +153 -0
- data/lib/mongoid/slug/version.rb +5 -0
- data/lib/mongoid_slug.rb +2 -0
- data/spec/models/alias.rb +6 -0
- data/spec/models/article.rb +9 -0
- data/spec/models/author.rb +11 -0
- data/spec/models/author_polymorphic.rb +11 -0
- data/spec/models/book.rb +12 -0
- data/spec/models/book_polymorphic.rb +12 -0
- data/spec/models/caption.rb +17 -0
- data/spec/models/entity.rb +12 -0
- data/spec/models/friend.rb +7 -0
- data/spec/models/incorrect_slug_persistence.rb +9 -0
- data/spec/models/integer_id.rb +9 -0
- data/spec/models/magazine.rb +7 -0
- data/spec/models/page.rb +9 -0
- data/spec/models/page_localize.rb +9 -0
- data/spec/models/page_slug_localized.rb +9 -0
- data/spec/models/page_slug_localized_custom.rb +11 -0
- data/spec/models/page_slug_localized_history.rb +9 -0
- data/spec/models/paranoid_document.rb +8 -0
- data/spec/models/paranoid_permanent.rb +8 -0
- data/spec/models/partner.rb +7 -0
- data/spec/models/person.rb +8 -0
- data/spec/models/relationship.rb +8 -0
- data/spec/models/string_id.rb +9 -0
- data/spec/models/subject.rb +7 -0
- data/spec/models/without_slug.rb +5 -0
- data/spec/mongoid/criteria_spec.rb +190 -0
- data/spec/mongoid/index_spec.rb +34 -0
- data/spec/mongoid/paranoia_spec.rb +169 -0
- data/spec/mongoid/slug_spec.rb +1022 -0
- data/spec/mongoid/slug_spec.rb.b00 +1101 -0
- data/spec/shared/indexes.rb +27 -0
- data/spec/spec_helper.rb +47 -0
- metadata +245 -0
data/spec/models/page.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Mongoid::Slug::Criteria do
|
5
|
+
describe ".find" do
|
6
|
+
let!(:book) { Book.create(:title => "A Working Title").tap { |d| d.update_attribute(:title, "A Thousand Plateaus") } }
|
7
|
+
let!(:book2) { Book.create(:title => "Difference and Repetition") }
|
8
|
+
let!(:friend) { Friend.create(:name => "Jim Bob") }
|
9
|
+
let!(:friend2) { Friend.create(:name => friend.id.to_s) }
|
10
|
+
let!(:integer_id) { IntegerId.new(:name => "I have integer ids").tap { |d| d.id = 123; d.save } }
|
11
|
+
let!(:integer_id2) { IntegerId.new(:name => integer_id.id.to_s).tap { |d| d.id = 456; d.save } }
|
12
|
+
let!(:string_id) { StringId.new(:name => "I have string ids").tap { |d| d.id = 'abc'; d.save } }
|
13
|
+
let!(:string_id2) { StringId.new(:name => string_id.id.to_s).tap { |d| d.id = 'def'; d.save } }
|
14
|
+
let!(:subject) { Subject.create(:name => "A Subject", :book => book) }
|
15
|
+
let!(:subject2) { Subject.create(:name => "A Subject", :book => book2) }
|
16
|
+
let!(:without_slug) { WithoutSlug.new().tap { |d| d.id = 456; d.save } }
|
17
|
+
|
18
|
+
context "when the model does not use mongoid slugs" do
|
19
|
+
it "should not use mongoid slug's custom find methods" do
|
20
|
+
Mongoid::Slug::Criteria.any_instance.should_not_receive(:find)
|
21
|
+
WithoutSlug.find(without_slug.id.to_s).should == without_slug
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "using slugs" do
|
26
|
+
context "(single)" do
|
27
|
+
context "and a document is found" do
|
28
|
+
it "returns the document as an object" do
|
29
|
+
Book.find(book.slugs.first).should == book
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "but no document is found" do
|
34
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
35
|
+
lambda {
|
36
|
+
Book.find("Anti Oedipus")
|
37
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "(multiple)" do
|
43
|
+
context "and all documents are found" do
|
44
|
+
it "returns the documents as an array without duplication" do
|
45
|
+
Book.find(book.slugs + book2.slugs).should =~ [book, book2]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "but not all documents are found" do
|
50
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
51
|
+
lambda {
|
52
|
+
Book.find(book.slugs + ['something-nonexistent'])
|
53
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when no documents match" do
|
59
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
60
|
+
lambda {
|
61
|
+
Book.find("Anti Oedipus")
|
62
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when ids are BSON::ObjectIds and the supplied argument looks like a BSON::ObjectId" do
|
67
|
+
it "it should find based on ids not slugs" do # i.e. it should type cast the argument
|
68
|
+
Friend.find(friend.id.to_s).should == friend
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when ids are Strings" do
|
73
|
+
it "it should find based on ids not slugs" do # i.e. string ids should take precedence over string slugs
|
74
|
+
StringId.find(string_id.id.to_s).should == string_id
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when ids are Integers and the supplied arguments looks like an Integer" do
|
79
|
+
it "it should find based on slugs not ids" do # i.e. it should not type cast the argument
|
80
|
+
IntegerId.find(integer_id.id.to_s).should == integer_id2
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "models that does not use slugs, should find using the original find" do
|
85
|
+
it "it should find based on ids" do # i.e. it should not type cast the argument
|
86
|
+
WithoutSlug.find(without_slug.id.to_s).should == without_slug
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when scoped" do
|
91
|
+
context "and a document is found" do
|
92
|
+
it "returns the document as an object" do
|
93
|
+
book.subjects.find(subject.slugs.first).should == subject
|
94
|
+
book2.subjects.find(subject.slugs.first).should == subject2
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "but no document is found" do
|
99
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
100
|
+
lambda {
|
101
|
+
book.subjects.find('Another Subject')
|
102
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "using ids" do
|
109
|
+
it "raises a Mongoid::Errors::DocumentNotFound error if no document is found" do
|
110
|
+
lambda {
|
111
|
+
Book.find(friend.id)
|
112
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
113
|
+
end
|
114
|
+
|
115
|
+
context "given a single document" do
|
116
|
+
it "returns the document" do
|
117
|
+
Friend.find(friend.id).should == friend
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "given multiple documents" do
|
122
|
+
it "returns the documents" do
|
123
|
+
Book.find([book.id, book2.id]).should =~ [book, book2]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe ".find_by_slug!" do
|
130
|
+
let!(:book) { Book.create(:title => "A Working Title").tap { |d| d.update_attribute(:title, "A Thousand Plateaus") } }
|
131
|
+
let!(:book2) { Book.create(:title => "Difference and Repetition") }
|
132
|
+
let!(:friend) { Friend.create(:name => "Jim Bob") }
|
133
|
+
let!(:friend2) { Friend.create(:name => friend.id.to_s) }
|
134
|
+
let!(:integer_id) { IntegerId.new(:name => "I have integer ids").tap { |d| d.id = 123; d.save } }
|
135
|
+
let!(:integer_id2) { IntegerId.new(:name => integer_id.id.to_s).tap { |d| d.id = 456; d.save } }
|
136
|
+
let!(:string_id) { StringId.new(:name => "I have string ids").tap { |d| d.id = 'abc'; d.save } }
|
137
|
+
let!(:string_id2) { StringId.new(:name => string_id.id.to_s).tap { |d| d.id = 'def'; d.save } }
|
138
|
+
let!(:subject) { Subject.create(:name => "A Subject", :book => book) }
|
139
|
+
let!(:subject2) { Subject.create(:name => "A Subject", :book => book2) }
|
140
|
+
|
141
|
+
context "(single)" do
|
142
|
+
context "and a document is found" do
|
143
|
+
it "returns the document as an object" do
|
144
|
+
Book.find_by_slug!(book.slugs.first).should == book
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "but no document is found" do
|
149
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
150
|
+
lambda {
|
151
|
+
Book.find_by_slug!("Anti Oedipus")
|
152
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "(multiple)" do
|
158
|
+
context "and all documents are found" do
|
159
|
+
it "returns the documents as an array without duplication" do
|
160
|
+
Book.find_by_slug!(book.slugs + book2.slugs).should =~ [book, book2]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "but not all documents are found" do
|
165
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
166
|
+
lambda {
|
167
|
+
Book.find_by_slug!(book.slugs + ['something-nonexistent'])
|
168
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context "when scoped" do
|
174
|
+
context "and a document is found" do
|
175
|
+
it "returns the document as an object" do
|
176
|
+
book.subjects.find_by_slug!(subject.slugs.first).should == subject
|
177
|
+
book2.subjects.find_by_slug!(subject.slugs.first).should == subject2
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "but no document is found" do
|
182
|
+
it "raises a Mongoid::Errors::DocumentNotFound error" do
|
183
|
+
lambda {
|
184
|
+
book.subjects.find_by_slug!('Another Subject')
|
185
|
+
}.should raise_error(Mongoid::Errors::DocumentNotFound)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Mongoid::Slug::Index do
|
5
|
+
|
6
|
+
let(:scope_key) { nil }
|
7
|
+
let(:by_model_type) { false }
|
8
|
+
subject { Mongoid::Slug::Index.build_index(scope_key, by_model_type) }
|
9
|
+
|
10
|
+
context "when scope_key is set" do
|
11
|
+
let(:scope_key) { :foo }
|
12
|
+
|
13
|
+
context "when by_model_type is true" do
|
14
|
+
let(:by_model_type) { true }
|
15
|
+
it { should eq [{:_slugs=>1, :foo=>1, :_type=>1}, {}] }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when by_model_type is false" do
|
19
|
+
it { should eq [{:_slugs=>1, :foo=>1}, {:unique=>true, :sparse=>true}] }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when scope_key is not set" do
|
24
|
+
|
25
|
+
context "when by_model_type is true" do
|
26
|
+
let(:by_model_type) { true }
|
27
|
+
it { should eq [{:_slugs=>1, :_type=>1}, {}] }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when by_model_type is false" do
|
31
|
+
it { should eq [{:_slugs=>1}, {:unique=>true, :sparse=>true}] }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe "Mongoid::Paranoia with Mongoid::Slug" do
|
5
|
+
|
6
|
+
let(:paranoid_doc) { ParanoidDocument.create!(:title => "slug") }
|
7
|
+
let(:paranoid_doc_2) { ParanoidDocument.create!(:title => "slug") }
|
8
|
+
let(:paranoid_perm) { ParanoidPermanent.create!(:title => "slug") }
|
9
|
+
let(:paranoid_perm_2) { ParanoidPermanent.create!(:title => "slug") }
|
10
|
+
let(:non_paranoid_doc){ Article.create!(:title => "slug") }
|
11
|
+
subject{ paranoid_doc }
|
12
|
+
|
13
|
+
describe ".paranoid?" do
|
14
|
+
|
15
|
+
context "when Mongoid::Paranoia is included" do
|
16
|
+
subject { paranoid_doc.class }
|
17
|
+
its(:is_paranoid_doc?){ should be_truthy }
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when Mongoid::Paranoia not included" do
|
21
|
+
subject { non_paranoid_doc.class }
|
22
|
+
its(:is_paranoid_doc?){ should be_falsey }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#paranoid_deleted?" do
|
27
|
+
|
28
|
+
context "when Mongoid::Paranoia is included" do
|
29
|
+
|
30
|
+
context "when not destroyed" do
|
31
|
+
its(:paranoid_deleted?){ should be_falsey }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when destroyed" do
|
35
|
+
before { subject.destroy }
|
36
|
+
its(:paranoid_deleted?){ should be_truthy }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "when Mongoid::Paranoia not included" do
|
41
|
+
subject { non_paranoid_doc }
|
42
|
+
its(:paranoid_deleted?){ should be_falsey }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "restore callbacks" do
|
47
|
+
|
48
|
+
context "when Mongoid::Paranoia is included" do
|
49
|
+
subject { paranoid_doc.class }
|
50
|
+
it { should respond_to(:before_restore) }
|
51
|
+
it { should respond_to(:after_restore) }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when Mongoid::Paranoia not included" do
|
55
|
+
it { should_not respond_to(:before_restore) }
|
56
|
+
it { should_not respond_to(:after_restore) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "index" do
|
61
|
+
before { ParanoidDocument.create_indexes }
|
62
|
+
after { ParanoidDocument.remove_indexes }
|
63
|
+
subject { ParanoidDocument }
|
64
|
+
|
65
|
+
it_should_behave_like "has an index", { _slugs: 1 }, { unique: true, sparse: true }
|
66
|
+
end
|
67
|
+
|
68
|
+
shared_examples_for "paranoid slugs" do
|
69
|
+
|
70
|
+
context "querying" do
|
71
|
+
|
72
|
+
it "returns paranoid_doc for correct slug" do
|
73
|
+
subject.class.find(subject.slug).should eq(subject)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "delete (callbacks not fired)" do
|
78
|
+
|
79
|
+
before { subject.delete }
|
80
|
+
|
81
|
+
it "retains slug value" do
|
82
|
+
subject.slug.should eq "slug"
|
83
|
+
subject.class.unscoped.find("slug").should eq subject
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "destroy" do
|
88
|
+
|
89
|
+
before { subject.destroy }
|
90
|
+
|
91
|
+
it "unsets slug value when destroyed" do
|
92
|
+
subject._slugs.should eq []
|
93
|
+
subject.slug.should be_nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it "persists the removed slug" do
|
97
|
+
subject.reload._slugs.should eq []
|
98
|
+
subject.reload.slug.should be_nil
|
99
|
+
end
|
100
|
+
|
101
|
+
it "persists the removed slug in the database" do
|
102
|
+
subject.class.unscoped.exists(_slugs: false).first.should eq subject
|
103
|
+
expect{subject.class.unscoped.find("slug")}.to raise_error(Mongoid::Errors::DocumentNotFound)
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when saving the doc again" do
|
107
|
+
|
108
|
+
before { subject.save }
|
109
|
+
|
110
|
+
it "should have the default slug value" do
|
111
|
+
subject._slugs.should eq []
|
112
|
+
subject.slug.should be_nil
|
113
|
+
end
|
114
|
+
|
115
|
+
it "the slug remains unset in the database" do
|
116
|
+
subject.class.unscoped.exists(_slugs: false).first.should eq subject
|
117
|
+
expect{subject.class.unscoped.find("slug")}.to raise_error(Mongoid::Errors::DocumentNotFound)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "restore" do
|
123
|
+
|
124
|
+
before do
|
125
|
+
subject.destroy
|
126
|
+
subject.restore
|
127
|
+
end
|
128
|
+
|
129
|
+
it "resets slug value when restored" do
|
130
|
+
subject.slug.should eq "slug"
|
131
|
+
subject.reload.slug.should eq "slug"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "multiple documents" do
|
136
|
+
|
137
|
+
it "new documents should be able to use the slug of destroyed documents" do
|
138
|
+
subject.slug.should eq "slug"
|
139
|
+
subject.destroy
|
140
|
+
subject.reload.slug.should be_nil
|
141
|
+
other_doc.slug.should eq "slug"
|
142
|
+
subject.restore
|
143
|
+
subject.slug.should eq "slug-1"
|
144
|
+
subject.reload.slug.should eq "slug-1"
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should allow multiple documents to be destroyed without index conflict" do
|
148
|
+
subject.slug.should eq "slug"
|
149
|
+
subject.destroy
|
150
|
+
subject.reload.slug.should be_nil
|
151
|
+
other_doc.slug.should eq "slug"
|
152
|
+
other_doc.destroy
|
153
|
+
other_doc.reload.slug.should be_nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "non-permanent slug" do
|
159
|
+
subject { paranoid_doc }
|
160
|
+
let(:other_doc) { paranoid_doc_2 }
|
161
|
+
it_behaves_like "paranoid slugs"
|
162
|
+
end
|
163
|
+
|
164
|
+
context "permanent slug" do
|
165
|
+
subject { paranoid_perm }
|
166
|
+
let(:other_doc) { paranoid_perm_2 }
|
167
|
+
it_behaves_like "paranoid slugs"
|
168
|
+
end
|
169
|
+
end
|