mongoid_slug 0.8.3 → 0.9.0

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/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