mongoid_localized_slug 0.1.1

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