mongoid_slug 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -81,5 +81,17 @@ specify the `:inverse_of` option on the other side of the assocation.
81
81
 
82
82
  Embedded objects are automatically scoped by their parent.
83
83
 
84
+ If the value of `:scope` is not an association, it should be the name of a field within the model itself:
85
+
86
+ ```ruby
87
+ class Employee
88
+ include Mongoid::Document
89
+ include Mongoid::Slug
90
+ field :name
91
+ field :company_id
92
+ slug :name, :scope => :company_id
93
+ end
94
+ ```
95
+
84
96
  [1]: https://github.com/rsl/stringex/
85
- [2]: https://github.com/papercavalier/mongoid-slug/blob/master/lib/mongoid/slug.rb
97
+ [2]: https://github.com/hakanensari/mongoid-slug/blob/master/lib/mongoid/slug.rb
data/lib/mongoid/slug.rb CHANGED
@@ -3,8 +3,8 @@ require 'stringex'
3
3
 
4
4
  module Mongoid #:nodoc:
5
5
 
6
- # The slug module helps you generate a URL slug or permalink based on one or
7
- # more fields in a Mongoid model.
6
+ # The slug module helps you generate a URL slug or permalink based on
7
+ # one or more fields in a Mongoid model.
8
8
  #
9
9
  # class Person
10
10
  # include Mongoid::Document
@@ -18,7 +18,10 @@ module Mongoid #:nodoc:
18
18
  extend ActiveSupport::Concern
19
19
 
20
20
  included do
21
- cattr_accessor :slug_builder, :slugged_fields, :slug_name, :slug_scope
21
+ cattr_accessor :slug_builder,
22
+ :slugged_fields,
23
+ :slug_name,
24
+ :slug_scope
22
25
  end
23
26
 
24
27
  module ClassMethods
@@ -29,26 +32,28 @@ module Mongoid #:nodoc:
29
32
  #
30
33
  # The options hash respects the following members:
31
34
  #
32
- # * `:as`, which specifies name of the field that stores the slug.
33
- # Defaults to `slug`.
35
+ # * `:as`, which specifies name of the field that stores the
36
+ # slug. Defaults to `slug`.
34
37
  #
35
- # * `:scope`, which specifies a reference association to scope the slug
36
- # by. Embedded documents are by default scoped by their parent.
38
+ # * `:scope`, which specifies a reference association to scope
39
+ # the slug by. Embedded documents are by default scoped by their
40
+ # parent.
37
41
  #
38
- # * `:permanent`, which specifies whether the slug should be immutable
39
- # once created. Defaults to `false`.
42
+ # * `:permanent`, which specifies whether the slug should be
43
+ # immutable once created. Defaults to `false`.
40
44
  #
41
- # * `:index`, which specifies whether an index should be defined for the
42
- # slug. Defaults to `false` and has no effect if the document is em-
43
- # bedded. Make sure you have a unique index on the slug of root
44
- # documents to avoid the (very unlikely) race condition that would ensue
45
- # if two documents with identical slugs were to be saved simultaneously.
45
+ # * `:index`, which specifies whether an index should be defined
46
+ # for the slug. Defaults to `false` and has no effect if the
47
+ # document is embedded. Make sure you have a unique index on the
48
+ # slug of root documents to avoid the (very unlikely) race
49
+ # condition that would ensue if two documents with identical
50
+ # slugs were to be saved simultaneously.
46
51
  #
47
- # Alternatively, this method can be given a block to build a custom slug
48
- # out of the specified fields.
52
+ # Alternatively, this method can be given a block to build a
53
+ # custom slug out of the specified fields.
49
54
  #
50
- # The block takes a single argument, the document itself, and should
51
- # return a string that will serve as the base of the slug.
55
+ # The block takes a single argument, the document itself, and
56
+ # should return a string that will serve as the base of the slug.
52
57
  #
53
58
  # Here, for instance, we slug an array field.
54
59
  #
@@ -72,7 +77,8 @@ module Mongoid #:nodoc:
72
77
  block
73
78
  else
74
79
  lambda do |doc|
75
- slugged_fields.map { |f| doc.read_attribute(f) }.join(' ')
80
+ slugged_fields.map { |f| doc.read_attribute(f) }.
81
+ join(' ')
76
82
  end
77
83
  end
78
84
 
@@ -119,35 +125,54 @@ module Mongoid #:nodoc:
119
125
  # TODO: An epic method which calls for refactoring.
120
126
  slug = slug_builder.call(self).to_url
121
127
 
122
- # Regular expression that matches slug, slug-1, slug-2, ... slug-n
123
- # If slug_name field was indexed, MongoDB will utilize that index to
124
- # match /^.../ pattern
128
+ # Regular expression that matches slug, slug-1, ... slug-n
129
+ # If slug_name field was indexed, MongoDB will utilize that
130
+ # index to match /^.../ pattern.
125
131
  pattern = /^#{Regexp.escape(slug)}(?:-(\d+))?$/
126
132
 
127
- existing_slugs =
128
- uniqueness_scope.
129
- only(slug_name).
130
- where(slug_name => pattern, :_id.ne => _id).
131
- map { |obj| obj.try(:read_attribute, slug_name) }
133
+ if slug_scope &&
134
+ self.class.reflect_on_association(slug_scope).nil?
135
+ # scope is not an association, so it's scoped to a local field
136
+ # (e.g. an association id in a denormalized db design)
137
+ existing_slugs =
138
+ self.class.
139
+ only(slug_name).
140
+ where(slug_name => pattern,
141
+ :_id.ne => _id,
142
+ slug_scope => self[slug_scope])
143
+ else
144
+ existing_slugs =
145
+ uniqueness_scope.
146
+ only(slug_name).
147
+ where(slug_name => pattern, :_id.ne => _id)
148
+ end
149
+
150
+ existing_slugs = existing_slugs.map do |obj|
151
+ obj.try(:read_attribute, slug_name)
152
+ end
132
153
 
133
- if existing_slugs.count > 0
134
- # sort the existing_slugs in increasing order by comparing the suffix
135
- # numbers:
154
+ if existing_slugs.count > 0
155
+ # Sort the existing_slugs in increasing order by comparing the
156
+ # suffix numbers:
136
157
  # slug, slug-1, slug-2, ..., slug-n
137
158
  existing_slugs.sort! do |a, b|
138
- (pattern.match(a)[1] || -1).to_i <=> (pattern.match(b)[1] || -1).to_i
159
+ (pattern.match(a)[1] || -1).to_i <=>
160
+ (pattern.match(b)[1] || -1).to_i
139
161
  end
140
- max_counter = existing_slugs.last.match(/-(\d+)$/).try(:[], 1).to_i
162
+ max = existing_slugs.last.match(/-(\d+)$/).try(:[], 1).to_i
141
163
 
142
- # Use max_counter + 1 as unique counter
143
- slug += "-#{max_counter + 1}"
164
+ slug += "-#{max + 1}"
144
165
  end
145
166
 
146
167
  slug
147
168
  end
148
169
 
149
170
  def generate_slug
150
- if new_record? || slugged_fields_changed?
171
+ # Generate a slug for new records only if the slug was not set.
172
+ # If we're not a new record generate a slug if our slugged fields
173
+ # changed on us.
174
+ if (new_record? && !read_attribute(slug_name)) ||
175
+ (!new_record? && slugged_fields_changed?)
151
176
  generate_slug!
152
177
  end
153
178
  end
@@ -165,20 +190,20 @@ module Mongoid #:nodoc:
165
190
  metadata = self.class.reflect_on_association(slug_scope)
166
191
  parent = self.send(metadata.name)
167
192
 
168
- # Make sure doc is actually associated with something, and that some
169
- # referenced docs have been persisted to the parent
193
+ # Make sure doc is actually associated with something, and that
194
+ # some referenced docs have been persisted to the parent
170
195
  #
171
- # TODO: we need better reflection for reference associations, like
172
- # association_name instead of forcing collection_name here -- maybe
173
- # in the forthcoming Mongoid refactorings?
196
+ # TODO: we need better reflection for reference associations,
197
+ # like association_name instead of forcing collection_name here
198
+ # -- maybe in the forthcoming Mongoid refactorings?
174
199
  inverse = metadata.inverse_of || collection_name
175
200
  parent.respond_to?(inverse) ? parent.send(inverse) : self.class
176
201
  elsif embedded?
177
- parent_metadata = reflect_on_all_associations(:embedded_in).first
202
+ parent_metadata = reflect_on_all_associations(:embedded_in)[0]
178
203
  _parent.send(parent_metadata.inverse_of || self.metadata.name)
179
204
  else
180
205
  appropriate_class = self.class
181
- while (appropriate_class.superclass.include?(Mongoid::Document))
206
+ while appropriate_class.superclass.include?(Mongoid::Document)
182
207
  appropriate_class = appropriate_class.superclass
183
208
  end
184
209
  appropriate_class
@@ -1,5 +1,5 @@
1
1
  module Mongoid #:nodoc:
2
2
  module Slug
3
- VERSION = '0.8.3'
3
+ VERSION = '0.9.0'
4
4
  end
5
5
  end
@@ -0,0 +1,7 @@
1
+ class Magazine
2
+ include Mongoid::Document
3
+ include Mongoid::Slug
4
+ field :title
5
+ field :publisher_id
6
+ slug :title, :scope => :publisher_id
7
+ end
@@ -236,6 +236,23 @@ module Mongoid
236
236
  end
237
237
  end
238
238
  end
239
+
240
+ context "when slug is scoped by one of the class's own fields" do
241
+ let!(:magazine) do
242
+ Magazine.create(:title => "Big Weekly", :publisher_id => "abc123")
243
+ end
244
+
245
+ it "should scope by local field" do
246
+ magazine.to_param.should eql 'big-weekly'
247
+ magazine2 = Magazine.create(:title => "Big Weekly", :publisher_id => "def456")
248
+ magazine2.to_param.should eql magazine.to_param
249
+ end
250
+
251
+ it "should generate a unique slug by appending a counter to duplicate text" do
252
+ dup = Magazine.create(:title => "Big Weekly", :publisher_id => "abc123")
253
+ dup.to_param.should eql 'big-weekly-1'
254
+ end
255
+ end
239
256
 
240
257
  context "when :slug is given a block" do
241
258
  let(:caption) do
@@ -369,5 +386,12 @@ module Mongoid
369
386
  book.reload.slug.should eql "proust-and-signs"
370
387
  end
371
388
  end
389
+
390
+ context "when the slugged field is set upon creation" do
391
+ it "respects the provided slug and does not generate a new one" do
392
+ book = Book.create(:title => "A Thousand Plateaus", :slug => 'not-what-you-expected')
393
+ book.to_param.should eql "not-what-you-expected"
394
+ end
395
+ end
372
396
  end
373
397
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_slug
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-20 00:00:00.000000000Z
12
+ date: 2012-01-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongoid
16
- requirement: &70160200453060 !ruby/object:Gem::Requirement
16
+ requirement: &70354887937940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70160200453060
24
+ version_requirements: *70354887937940
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: stringex
27
- requirement: &70160200452160 !ruby/object:Gem::Requirement
27
+ requirement: &70354887953780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.3'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70160200452160
35
+ version_requirements: *70354887953780
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &70160200451400 !ruby/object:Gem::Requirement
38
+ requirement: &70354887953320 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0.9'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70160200451400
46
+ version_requirements: *70354887953320
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
- requirement: &70160200450620 !ruby/object:Gem::Requirement
49
+ requirement: &70354887952860 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '2.6'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70160200450620
57
+ version_requirements: *70354887952860
58
58
  description: Mongoid Slug generates a URL slug or permalink based on one or more fields
59
59
  in a Mongoid model.
60
60
  email:
@@ -72,6 +72,7 @@ files:
72
72
  - spec/models/author.rb
73
73
  - spec/models/book.rb
74
74
  - spec/models/caption.rb
75
+ - spec/models/magazine.rb
75
76
  - spec/models/page.rb
76
77
  - spec/models/partner.rb
77
78
  - spec/models/person.rb
@@ -91,21 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
91
92
  - - ! '>='
92
93
  - !ruby/object:Gem::Version
93
94
  version: '0'
94
- segments:
95
- - 0
96
- hash: -3230490500102343809
97
95
  required_rubygems_version: !ruby/object:Gem::Requirement
98
96
  none: false
99
97
  requirements:
100
98
  - - ! '>='
101
99
  - !ruby/object:Gem::Version
102
100
  version: '0'
103
- segments:
104
- - 0
105
- hash: -3230490500102343809
106
101
  requirements: []
107
102
  rubyforge_project: mongoid_slug
108
- rubygems_version: 1.8.6
103
+ rubygems_version: 1.8.11
109
104
  signing_key:
110
105
  specification_version: 3
111
106
  summary: Generates a URL slug
@@ -114,6 +109,7 @@ test_files:
114
109
  - spec/models/author.rb
115
110
  - spec/models/book.rb
116
111
  - spec/models/caption.rb
112
+ - spec/models/magazine.rb
117
113
  - spec/models/page.rb
118
114
  - spec/models/partner.rb
119
115
  - spec/models/person.rb