mongoid_localized_slug 0.1.1

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 ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010-2012 Hakan Ensari
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ Mongoid LocalizedSlug
2
+ ============
3
+
4
+ WARNING: This is a simplified version of the
5
+ [mongoid-slug gem](https://github.com/hakanensari/mongoid-slug) that supports
6
+ localized fields. It DOES NOT support nor embedded objects neither slug history.
7
+ If you need any of those features stick with the original gem.
8
+
9
+ Mongoid LocalizedSlug generates a URL slug or permalink based on one localized
10
+ fields in a Mongoid model. It sits idly on top of [stringex] [1], supporting
11
+ non-Latin characters.
12
+
13
+ Installation
14
+ ------------
15
+
16
+ Add to your Gemfile:
17
+
18
+ ```ruby
19
+ gem 'mongoid_localized_slug'
20
+ ```
21
+
22
+ Usage
23
+ -----
24
+
25
+ Set up a slug:
26
+
27
+ ```ruby
28
+ class Book
29
+ include Mongoid::Document
30
+ include Mongoid::LocalizedSlug
31
+
32
+ field :title, localize: true
33
+ slug :title, index: true
34
+ end
35
+ ```
36
+
37
+ Create a record with multiple translations:
38
+
39
+ ```ruby
40
+ I18n.locale = :en
41
+ book = Book.create(:title => "A Thousand Plateaus")
42
+ I18n.locale = :es
43
+ book.update_attribute :title, 'Mil Mesetas'
44
+ ```
45
+
46
+ Find a record by its slug:
47
+
48
+ ```ruby
49
+ # GET /books/a-thousand-plateaus
50
+ book = Book.find_by_slug params[:book_id]
51
+ # GET /books/mil-mesetas
52
+ book = Book.find_by_slug params[:book_id] # matches the same Book object
53
+ ```
54
+
55
+ Reserved Slugs
56
+ --------------
57
+
58
+ Pass words you do not want to be slugged using the `reserve` option:
59
+
60
+ ```ruby
61
+ class Friend
62
+ include Mongoid::Document
63
+
64
+ field :name
65
+ slug :name, reserve: ['admin', 'root']
66
+ end
67
+
68
+ friend = Friend.create name: 'admin'
69
+ Friend.find_by_slug('admin') # => nil
70
+ friend.slug # => 'admin-1'
71
+ ```
72
+
73
+ [1]: https://github.com/rsl/stringex/
@@ -0,0 +1,208 @@
1
+ module Mongoid
2
+ # The Slug module helps you generate a URL slug or permalink based on one or
3
+ # more fields in a Mongoid model.
4
+ module LocalizedSlug
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ cattr_accessor :slug_name,
9
+ :reserved_words_in_slug,
10
+ :slugged_attribute
11
+ end
12
+
13
+ module ClassMethods
14
+ # @overload slug(*fields)
15
+ # Sets one ore more fields as source of slug.
16
+ # @param [Symbol] The fields the slug should be based on.
17
+ #
18
+ # @overload slug(*field, options)
19
+ # Sets one ore more fields as source of slug.
20
+ # @param [Symbol] The the slug should be based on.
21
+ # @param [Hash] options
22
+ # @param options [String] :as The name of the field that stores the
23
+ # slug. Defaults to `slug`.
24
+ # @param options [Boolean] :index Whether an index should be defined
25
+ # on the slug field. Defaults to `false`.
26
+ # @param options [Boolean] :permanent Whether the slug should be
27
+ # immutable. Defaults to `false`.
28
+ # @param options [Array] :reserve` A list of reserved slugs
29
+ #
30
+ # @example A custom builder
31
+ # class Person
32
+ # include Mongoid::Document
33
+ # include Mongoid::LocalizedSlug
34
+ #
35
+ # field :name, localize: true
36
+ #
37
+ # slug :name, :index => true
38
+ # end
39
+ #
40
+ def slug(*field_and_options)
41
+ options = field_and_options.extract_options!
42
+
43
+ self.reserved_words_in_slug = options[:reserve] || []
44
+ self.slug_name = options[:as] || :slug
45
+ self.slugged_attribute = field_and_options.first.to_s
46
+
47
+ field slug_name, :type => Array, :default => []
48
+ field "#{slug_name}_translations", :type => Hash, :default => {}
49
+
50
+ unless slug_name == :slug
51
+ alias_attribute :slug, slug_name
52
+ alias_attribute :slug_translations, "#{slug_name}_translations"
53
+ end
54
+
55
+ if options[:index]
56
+ index slug_name, :unique => true
57
+ end
58
+
59
+ set_callback options[:permanent] ? :create : :save, :before do |doc|
60
+ doc.build_slug if doc.slug_should_be_rebuilt?
61
+ end
62
+
63
+ # Build a finder for slug.
64
+ #
65
+ # Defaults to `find_by_slug`.
66
+ instance_eval <<-CODE
67
+ def self.find_by_#{slug_name}(slug)
68
+ where(slug_name => slug).first
69
+ end
70
+
71
+ def self.find_by_#{slug_name}!(slug)
72
+ self.find_by_#{slug_name}(slug) ||
73
+ raise(Mongoid::Errors::DocumentNotFound.new self, slug)
74
+ end
75
+ CODE
76
+
77
+ # Build a scope based on the slug name.
78
+ #
79
+ # Defaults to `by_slug`.
80
+ scope "by_#{slug_name}".to_sym, lambda { |slug|
81
+ where(slug_name => slug)
82
+ }
83
+ end
84
+
85
+ # Finds a unique slug, were specified string used to generate a slug.
86
+ #
87
+ # Returned slug will the same as the specified string when there are no
88
+ # duplicates.
89
+ #
90
+ # @param [String] desired_slug
91
+ # @param [Hash] options
92
+ # @param options [Symbol] :scope The scope that should be used to
93
+ # generate the slug, if the class creates scoped slugs. Defaults to
94
+ # `nil`.
95
+ # @param options [Constant] :model The model that the slug should be
96
+ # generated for. This option overrides `:scope`, as the scope can now
97
+ # be extracted from the model. Defaults to `nil`.
98
+ # @return [String] A unique slug
99
+ def find_unique_slug_for(desired_slug, options = {})
100
+ excluded_id = options[:model]._id if options[:model]
101
+
102
+ slug = desired_slug.to_url
103
+
104
+ # Regular expression that matches slug, slug-1, ... slug-n
105
+ # If slug_name field was indexed, MongoDB will utilize that
106
+ # index to match /^.../ pattern.
107
+ pattern = /^#{Regexp.escape(slug)}(?:-(\d+))?$/
108
+
109
+ where_hash = {}
110
+ where_hash[slug_name] = pattern
111
+ where_hash[:_id.ne] = excluded_id if excluded_id
112
+
113
+ existing_slugs =
114
+ only(slug_name).
115
+ where(where_hash)
116
+
117
+ # p existing_slugs
118
+ existing_slugs = existing_slugs.map do |doc|
119
+ doc.slug
120
+ end.flatten
121
+ # p existing_slugs
122
+
123
+ # Do not allow BSON::ObjectIds as slugs
124
+ existing_slugs << slug if BSON::ObjectId.legal?(slug)
125
+
126
+ if reserved_words_in_slug.any? { |word| word === slug }
127
+ existing_slugs << slug
128
+ end
129
+
130
+ if existing_slugs.count > 0
131
+ # Sort the existing_slugs in increasing order by comparing the
132
+ # suffix numbers:
133
+ # slug, slug-1, slug-2, ..., slug-n
134
+ existing_slugs.sort! do |a, b|
135
+ (pattern.match(a)[1] || -1).to_i <=>
136
+ (pattern.match(b)[1] || -1).to_i
137
+ end
138
+ max = existing_slugs.last.match(/-(\d+)$/).try(:[], 1).to_i
139
+
140
+ slug += "-#{max + 1}"
141
+ end
142
+
143
+ slug
144
+ end
145
+
146
+ end
147
+
148
+ # Builds a new slug.
149
+ #
150
+ # @return [true]
151
+ def build_slug
152
+ translations = self.send("#{slugged_attribute}_translations")
153
+ slugs = []
154
+ trans_hash = {}
155
+ translations.each do |lang, text|
156
+ trans_hash[lang] = find_unique_slug_for(text)
157
+ slugs << trans_hash[lang]
158
+ end
159
+ self.send("#{slug_name}_translations=", trans_hash)
160
+ self.send("#{slug_name}=", slugs.uniq)
161
+ true
162
+ end
163
+
164
+ # Finds a unique slug, were specified string used to generate a slug.
165
+ #
166
+ # Returned slug will the same as the specified string when there are no
167
+ # duplicates.
168
+ #
169
+ # @param [String] Desired slug
170
+ # @return [String] A unique slug
171
+ def find_unique_slug_for(desired_slug)
172
+ self.class.find_unique_slug_for desired_slug, :model => self
173
+ end
174
+
175
+ # @return [Boolean] Whether the slug requires to be rebuilt
176
+ def slug_should_be_rebuilt?
177
+ new_record? or slug_changed? or slugged_attribute_changed?
178
+ end
179
+
180
+ def slugged_attribute_changed?
181
+ attribute_changed? slugged_attribute
182
+ end
183
+
184
+ # @return [String] A string which Action Pack uses for constructing an URL
185
+ # to this record.
186
+ def to_param
187
+ if slug.empty?
188
+ build_slug
189
+ save
190
+ end
191
+ locale = I18n.locale.to_s
192
+ slug_translations.each do |lang, slug|
193
+ return slug if lang == locale
194
+ end
195
+ slug.first
196
+ end
197
+
198
+ private
199
+
200
+ def find_unique_slug
201
+ find_unique_slug_for user_defined_slug
202
+ end
203
+
204
+ def user_defined_slug
205
+ slug if (new_record? and slug.present?) or (persisted? and slug_changed?)
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,25 @@
1
+ module Mongoid::LocalizedSlug::Criterion
2
+ # Override Mongoid's finder to use slug or id
3
+ def for_ids(*ids)
4
+ return super unless @klass.ancestors.include?(Mongoid::LocalizedSlug)
5
+
6
+ # We definitely don't want to rescue at the same level we call super above -
7
+ # that would risk applying our slug behavior to non-slug objects, in the case
8
+ # where their id conversion fails and super raises BSON::InvalidObjectId
9
+ begin
10
+ # note that there is a small possibility that a client could create a slug that
11
+ # resembles a BSON::ObjectId
12
+ ids.flatten!
13
+ BSON::ObjectId.from_string(ids.first) unless ids.first.is_a?(BSON::ObjectId)
14
+ super # Fallback to original Mongoid::Criterion::Optional
15
+ rescue BSON::InvalidObjectId
16
+ # slug
17
+ if ids.size > 1
18
+ where(@klass.slug_name.to_sym.in => ids)
19
+ else
20
+ where(@klass.slug_name => ids.first)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ Mongoid::Criteria.send :include, Mongoid::LocalizedSlug::Criterion
@@ -0,0 +1,5 @@
1
+ module Mongoid #:nodoc:
2
+ module LocalizedSlug
3
+ VERSION = '0.1.1'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ require 'mongoid'
2
+ require 'stringex'
3
+ require 'mongoid/localized_slug'
4
+ require 'mongoid/localized_slug/criterion'
@@ -0,0 +1,8 @@
1
+ class Animal
2
+ include Mongoid::Document
3
+ include Mongoid::LocalizedSlug
4
+ field :name, localize: true
5
+ field :nickname
6
+ key :name, :nickname
7
+ slug :name
8
+ end
@@ -0,0 +1,6 @@
1
+ class Book
2
+ include Mongoid::Document
3
+ include Mongoid::LocalizedSlug
4
+ field :title, localize: true
5
+ slug :title, :index => true
6
+ end
@@ -0,0 +1,6 @@
1
+ class Friend
2
+ include Mongoid::Document
3
+ include Mongoid::LocalizedSlug
4
+ field :name, localize: true
5
+ slug :name, :reserve => ['foo', 'bar', /^[a-z]{2}$/i]
6
+ end
@@ -0,0 +1,6 @@
1
+ class Person
2
+ include Mongoid::Document
3
+ include Mongoid::LocalizedSlug
4
+ field :name, localize: true
5
+ slug :name, :as => :permalink, :permanent => true, :scope => :author
6
+ end
@@ -0,0 +1,336 @@
1
+ #encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ module Mongoid
5
+ describe LocalizedSlug do
6
+ let(:book) do
7
+ Book.create(:title => "A Thousand Plateaus")
8
+ end
9
+
10
+ context "when the object is top-level" do
11
+ it "generates a slug" do
12
+ book.to_param.should eql "a-thousand-plateaus"
13
+ end
14
+
15
+ it "updates the slug" do
16
+ book.title = "Anti Oedipus"
17
+ book.save
18
+ book.to_param.should eql "anti-oedipus"
19
+ end
20
+
21
+ it "generates a unique slug by appending a counter to duplicate text" do
22
+ 15.times{ |x|
23
+ dup = Book.create(:title => book.title)
24
+ dup.to_param.should eql "a-thousand-plateaus-#{x+1}"
25
+ }
26
+ end
27
+
28
+ it "does not allow a BSON::ObjectId as use for a slug" do
29
+ bad = Book.create(:title => "4ea0389f0364313d79104fb3")
30
+ bad.to_param.should_not eql "4ea0389f0364313d79104fb3"
31
+ end
32
+
33
+ it "does not update slug if slugged fields have not changed" do
34
+ book.save
35
+ book.to_param.should eql "a-thousand-plateaus"
36
+ end
37
+
38
+ it "does not change slug if slugged fields have changed but generated slug is identical" do
39
+ book.title = "a thousand plateaus"
40
+ book.save
41
+ book.to_param.should eql "a-thousand-plateaus"
42
+ end
43
+
44
+ it "generate diffetent slugs for every traslation" do
45
+ I18n.locale = :en
46
+ book.to_param.should eql 'a-thousand-plateaus'
47
+ I18n.locale = :es
48
+ book.update_attribute :title, 'Platero y yo'
49
+ book.to_param.should eql 'platero-y-yo'
50
+ end
51
+
52
+ it "finds by slug" do
53
+ Book.find_by_slug(book.to_param).should eql book
54
+ end
55
+
56
+ it "find by any translated slug" do
57
+ I18n.locale = :en
58
+ book.to_param.should eql 'a-thousand-plateaus'
59
+ I18n.locale = :es
60
+ book.update_attribute :title, 'Platero y yo'
61
+ book.to_param.should eql 'platero-y-yo'
62
+ end
63
+
64
+ context "using find" do
65
+ it "finds by slug" do
66
+ Book.find(book.to_param).should eql book
67
+ end
68
+
69
+ it "finds by id as string" do
70
+ Book.find(book.id.to_s).should eql book
71
+ end
72
+
73
+ it "finds by id as array of strings" do
74
+ Book.find([book.id.to_s]).should eql [book]
75
+ end
76
+
77
+ it "finds by id as BSON::ObjectId" do
78
+ Book.find(book.id).should eql book
79
+ end
80
+
81
+ it "finds by id as an array of BSON::ObjectIds" do
82
+ Book.find([book.id]).should eql [book]
83
+ end
84
+
85
+ it "returns an empty array if given an empty array" do
86
+ Book.find([]).should eql []
87
+ end
88
+
89
+ it "find by any translated slug" do
90
+ book.save
91
+ Book.find('a-thousand-plateaus').should eql book
92
+ I18n.locale = :es
93
+ book.update_attribute :title, 'Platero y yo'
94
+ Book.find('platero-y-yo').should eql book
95
+ end
96
+ end
97
+ end
98
+
99
+ context "when :as is passed as an argument" do
100
+ let!(:person) do
101
+ Person.create(:name => "John Doe")
102
+ end
103
+
104
+ it "finds by slug" do
105
+ Person.find_by_permalink("john-doe").should eql person
106
+ end
107
+
108
+ it 'defines #slug' do
109
+ person.should respond_to :slug
110
+ end
111
+
112
+ it 'defines #slug_changed?' do
113
+ person.should respond_to :slug_changed?
114
+ end
115
+
116
+ it 'defines #slug_was' do
117
+ person.should respond_to :slug_was
118
+ end
119
+ end
120
+
121
+ context "when :permanent is passed as an argument" do
122
+ let(:person) do
123
+ Person.create(:name => "John Doe")
124
+ end
125
+
126
+ it "does not update the slug when the slugged fields change" do
127
+ person.name = "Jane Doe"
128
+ person.save
129
+ person.to_param.should eql "john-doe"
130
+ end
131
+ end
132
+
133
+ context "when slugged field contains non-ASCII characters" do
134
+ it "slugs Cyrillic characters" do
135
+ book.title = "Капитал"
136
+ book.save
137
+ book.to_param.should eql "kapital"
138
+ end
139
+
140
+ it "slugs Greek characters" do
141
+ book.title = "Ελλάδα"
142
+ book.save
143
+ book.to_param.should eql "ellada"
144
+ end
145
+
146
+ it "slugs Chinese characters" do
147
+ book.title = "中文"
148
+ book.save
149
+ book.to_param.should eql "zhong-wen"
150
+ end
151
+
152
+ it "slugs non-ASCII Latin characters" do
153
+ book.title = "Paul Cézanne"
154
+ book.save
155
+ book.to_param.should eql "paul-cezanne"
156
+ end
157
+ end
158
+
159
+ context "when :index is passed as an argument" do
160
+ before do
161
+ Book.collection.drop_indexes
162
+ end
163
+
164
+ context "when slug is not scoped by a reference association" do
165
+ it "defines an index on the slug" do
166
+ Book.create_indexes
167
+ Book.collection.index_information.should have_key "slug_1"
168
+ end
169
+
170
+ it "defines a unique index" do
171
+ Book.create_indexes
172
+ Book.index_information["slug_1"]["unique"].should be_true
173
+ end
174
+ end
175
+
176
+ end
177
+
178
+ context "when :index is not passed as an argument" do
179
+ it "does not define an index on the slug" do
180
+ Person.create_indexes
181
+ Person.collection.index_information.should_not have_key "permalink_1"
182
+ end
183
+ end
184
+
185
+ context "when :reserve is passed" do
186
+ it "does not use the the reserved slugs" do
187
+ friend1 = Friend.create(:name => "foo")
188
+ friend1.to_param.should_not eql("foo")
189
+ friend1.to_param.should eql("foo-1")
190
+
191
+ friend2 = Friend.create(:name => "bar")
192
+ friend2.to_param.should_not eql("bar")
193
+ friend2.to_param.should eql("bar-1")
194
+
195
+ friend3 = Friend.create(:name => "en")
196
+ friend3.to_param.should_not eql("en")
197
+ friend3.to_param.should eql("en-1")
198
+ end
199
+
200
+ it "should start with concatenation -1" do
201
+ friend1 = Friend.create(:name => "foo")
202
+ friend1.to_param.should eql("foo-1")
203
+ friend2 = Friend.create(:name => "foo")
204
+ friend2.to_param.should eql("foo-2")
205
+ end
206
+ end
207
+
208
+ describe ".find_by_slug" do
209
+ let!(:book) { Book.create(:title => "A Thousand Plateaus") }
210
+
211
+ it "returns nil if no document is found" do
212
+ Book.find_by_slug(:title => "Anti Oedipus").should be_nil
213
+ end
214
+
215
+ it "returns the document if it is found" do
216
+ Book.find_by_slug(book.to_param).should == book
217
+ end
218
+ end
219
+
220
+ describe ".find_by_slug!" do
221
+ let!(:book) { Book.create(:title => "A Thousand Plateaus") }
222
+
223
+ it "raises a Mongoid::Errors::DocumentNotFound error if no document is found" do
224
+ lambda {
225
+ Book.find_by_slug!(:title => "Anti Oedipus")
226
+ }.should raise_error(Mongoid::Errors::DocumentNotFound)
227
+ end
228
+
229
+ it "returns the document when it is found" do
230
+ Book.find_by_slug!(book.to_param).should == book
231
+ end
232
+ end
233
+
234
+ describe ".find" do
235
+ let!(:book) { Book.create(:title => "A Thousand Plateaus") }
236
+ let!(:book2) { Book.create(:title => "Difference and Repetition") }
237
+ let!(:friend) { Friend.create(:name => "Jim Bob") }
238
+ let!(:friend2) { Friend.create(:name => "Billy Bob") }
239
+ let!(:animal) { Animal.create(:name => "Cardu", :nickname => "Car") }
240
+
241
+ context "using slugs" do
242
+
243
+ it "raises a Mongoid::Errors::DocumentNotFound error if no document is found" do
244
+ lambda {
245
+ Book.find(:title => "Anti Oedipus")
246
+ }.should raise_error(Mongoid::Errors::DocumentNotFound)
247
+ end
248
+
249
+ it "raises a Mongoid::Errors::DocumentNotFound error if trying to find a slug that looks like an id" do
250
+ tricksy = "4f69cd6dfe75bd0cce000003"
251
+ friend.name = tricksy
252
+ friend.save!
253
+ lambda {
254
+ Friend.find(tricksy)
255
+ }.should raise_error(Mongoid::Errors::DocumentNotFound)
256
+ end
257
+
258
+ end
259
+
260
+ context "using ids" do
261
+
262
+ it "raises a Mongoid::Errors::DocumentNotFound error if no document is found" do
263
+ lambda {
264
+ Book.find(friend.id)
265
+ }.should raise_error(Mongoid::Errors::DocumentNotFound)
266
+ end
267
+
268
+ context "given a single document" do
269
+ it "returns the document" do
270
+ Friend.find(friend.id).should == friend
271
+ end
272
+ end
273
+
274
+ context "given multiple docuemnts" do
275
+ it "returns the documents" do
276
+ Book.find([book.id, book2.id]).should == [book, book2]
277
+ end
278
+ end
279
+ end
280
+ context "when a key is in use" do
281
+ it "raises a document not found since you should be finding on the slug" do
282
+ lambda {
283
+ Animal.find(animal.id)
284
+ }.should raise_error(Mongoid::Errors::DocumentNotFound)
285
+ end
286
+ it "is still ok with the use of slug" do
287
+ Animal.find(animal.to_param).should == animal
288
+ end
289
+ end
290
+ end
291
+
292
+ describe "#to_param" do
293
+ context "when called on an existing record with no slug" do
294
+ before do
295
+ Book.collection.insert(:title => {I18n.locale.to_s => "Proust and Signs"})
296
+ end
297
+
298
+ it "generates the missing slug" do
299
+ book = Book.first
300
+ book.to_param
301
+ book.reload.to_param.should eql "proust-and-signs"
302
+ end
303
+ end
304
+ end
305
+
306
+ describe ".for_unique_slug_for" do
307
+ it "returns the unique slug" do
308
+ Book.find_unique_slug_for("A Thousand Plateaus").should eq("a-thousand-plateaus")
309
+ end
310
+
311
+ it "returns the unique slug with a counter if necessary" do
312
+ Book.create(:title => "A Thousand Plateaus")
313
+ Book.find_unique_slug_for("A Thousand Plateaus").should eq("a-thousand-plateaus-1")
314
+ end
315
+
316
+ it "returns the unique slug as if it were the provided object" do
317
+ book = Book.create(:title => "A Thousand Plateaus")
318
+ Book.find_unique_slug_for("A Thousand Plateaus", :model => book).should eq("a-thousand-plateaus")
319
+ end
320
+ end
321
+
322
+ describe "#find_unique_slug_for" do
323
+ let!(:book) { Book.create(:title => "A Thousand Plateaus") }
324
+
325
+ it "returns the unique slug" do
326
+ book.find_unique_slug_for("Anti Oedipus").should eq("anti-oedipus")
327
+ end
328
+
329
+ it "returns the unique slug with a counter if necessary" do
330
+ Book.create(:title => "Anti Oedipus")
331
+ book.find_unique_slug_for("Anti Oedipus").should eq("anti-oedipus-1")
332
+ end
333
+
334
+ end
335
+ end
336
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'pry'
5
+ require 'rspec'
6
+
7
+ require File.expand_path('../../lib/mongoid_localized_slug', __FILE__)
8
+
9
+ Mongoid.configure do |config|
10
+ name = 'mongoid_slug_test'
11
+ config.master = Mongo::Connection.new.db(name)
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |f| require f }
15
+
16
+ RSpec.configure do |c|
17
+ c.before(:each) do
18
+ Mongoid.master.collections.select {|c| c.name !~ /system/ }.each(&:remove)
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_localized_slug
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Juan Schwindt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: &70295941070740 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70295941070740
25
+ - !ruby/object:Gem::Dependency
26
+ name: stringex
27
+ requirement: &70295941070240 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70295941070240
36
+ - !ruby/object:Gem::Dependency
37
+ name: bson_ext
38
+ requirement: &70295941069760 !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: *70295941069760
47
+ - !ruby/object:Gem::Dependency
48
+ name: pry
49
+ requirement: &70295941069280 !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: *70295941069280
58
+ - !ruby/object:Gem::Dependency
59
+ name: rake
60
+ requirement: &70295941068760 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '0.9'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70295941068760
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: &70295941068220 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '2.8'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70295941068220
80
+ description: ! " a \n a ."
81
+ email:
82
+ - js@comenta-tv
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - lib/mongoid/localized_slug/criterion.rb
88
+ - lib/mongoid/localized_slug/version.rb
89
+ - lib/mongoid/localized_slug.rb
90
+ - lib/mongoid_localized_slug.rb
91
+ - LICENSE
92
+ - README.md
93
+ - spec/models/animal.rb
94
+ - spec/models/book.rb
95
+ - spec/models/friend.rb
96
+ - spec/models/person.rb
97
+ - spec/mongoid/slug_spec.rb
98
+ - spec/spec_helper.rb
99
+ homepage: http://github.com/hakanensari/mongoid-localized-slug
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ segments:
112
+ - 0
113
+ hash: -1768269109729011774
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ segments:
121
+ - 0
122
+ hash: -1768269109729011774
123
+ requirements: []
124
+ rubyforge_project: mongoid_localized_slug
125
+ rubygems_version: 1.8.11
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: Generates a URL slug in a Mongoid model for localized fields
129
+ test_files:
130
+ - spec/models/animal.rb
131
+ - spec/models/book.rb
132
+ - spec/models/friend.rb
133
+ - spec/models/person.rb
134
+ - spec/mongoid/slug_spec.rb
135
+ - spec/spec_helper.rb