mongoid_slug 0.6.4 → 0.7.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
@@ -2,10 +2,9 @@ Mongoid Slug
2
2
  ============
3
3
 
4
4
  Mongoid Slug generates a URL slug or permalink based on one or more
5
- fields in a Mongoid model.
5
+ fields in a Mongoid model. It sits idly on top of [stringex](https://github.com/rsl/stringex) and works with non-Latin characters.
6
6
 
7
- It sits idly on top of [stringex](https://github.com/rsl/stringex) and
8
- works with non-Latin characters.
7
+ ![lacan](http://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Nus_borromeu_1.jpg/360px-Nus_borromeu_1.jpg)
9
8
 
10
9
  Quick Start
11
10
  -----------
@@ -14,7 +13,7 @@ Add mongoid_slug to your Gemfile:
14
13
 
15
14
  gem 'mongoid_slug', :require => 'mongoid/slug'
16
15
 
17
- Set up slugs in models like this:
16
+ Set up some slugs:
18
17
 
19
18
  class Book
20
19
  include Mongoid::Document
@@ -37,36 +36,20 @@ Set up slugs in models like this:
37
36
  slug :first, :last, :as => :name
38
37
  end
39
38
 
40
- Finder
41
- ------
42
-
43
- In your controller, throw in some minimal magic:
39
+ In your controller, use available finders:
44
40
 
45
41
  # GET /books/a-thousand-plateaus/authors/gilles-deleuze
46
42
  author = Book.find_by_slug(params[:book_id]).
47
43
  authors.
48
44
  find_by_name(params[:id])
49
45
 
50
- Permanence
51
- ----------
52
-
53
- By default, slugs are not permanent:
54
-
55
- >> book = Book.create(:title => "A Thousand Plateaus")
56
- >> book.to_param
57
- "a-thousand-plateaus"
58
- >> book.title = "Anti Oedipus"
59
- >> book.save
60
- >> book.to_param
61
- "anti-oedipus"
62
-
63
- If you require permanent slugs, pass the `:permanent` option when
64
- defining the slug.
46
+ [Read here](https://github.com/papercavalier/mongoid-slug/blob/master/lib/mongoid/slug.rb)
47
+ for all available options.
65
48
 
66
- Scope
67
- -----
49
+ Scoping
50
+ -------
68
51
 
69
- To scope an object by a reference association, pass `:scope`:
52
+ To scope a slug by a reference association, pass `:scope`:
70
53
 
71
54
  class Company
72
55
  include Mongoid::Document
@@ -88,18 +71,3 @@ Currently, if you have an irregular association name, you **must**
88
71
  specify the `:inverse_of` option on the other side of the assocation.
89
72
 
90
73
  Embedded objects are automatically scoped by their parent.
91
-
92
- Indexes
93
- -------
94
-
95
- You may optionally pass an `:index` option to define an index on top-level
96
- slugs.
97
-
98
- class Book
99
- field :title
100
- slug :title, :index => true
101
- end
102
-
103
- Indexes on unscoped slugs will be unique.
104
-
105
- This option has no effect if the object is embedded.
data/lib/mongoid/slug.rb CHANGED
@@ -2,52 +2,82 @@ require 'stringex'
2
2
 
3
3
  module Mongoid #:nodoc:
4
4
 
5
- # Generates a URL slug or permalink based on one or more fields in a Mongoid
6
- # model.
5
+ # The slug module helps you generate a URL slug or permalink based on one or
6
+ # more fields in a Mongoid model.
7
+ #
8
+ # class Person
9
+ # include Mongoid::Document
10
+ # include Mongoid::Slug
11
+ #
12
+ # field :name
13
+ # slug :name
14
+ # end
15
+ #
7
16
  module Slug
8
17
  extend ActiveSupport::Concern
9
18
 
10
19
  included do
11
- cattr_accessor :slug_name, :slugged_fields, :slug_scope
20
+ cattr_accessor :slug_builder, :slugged_fields, :slug_name, :slug_scope
12
21
  end
13
22
 
14
23
  module ClassMethods
15
24
 
16
25
  # Sets one ore more fields as source of slug.
17
26
  #
18
- # By default, the name of the field that stores the slug is "slug". Pass an
19
- # alternative name with the :as option.
27
+ # Takes a list of one or more fields to slug and an optional options
28
+ # hash.
20
29
  #
21
- # If you wish the slug to be permanent once created, set :permanent to true.
30
+ # The options hash respects the following members:
22
31
  #
23
- # To index slug in a top-level object, set :index to true.
24
- def slug(*fields)
32
+ # * `:as`, which specifies name of the field that stores the slug.
33
+ # Defaults to `slug`.
34
+ #
35
+ # * `:scope`, which specifies a reference association to scope the slug
36
+ # by. Embedded documents are by default scoped by their parent.
37
+ #
38
+ # * `:permanent`, which specifies whether the slug should be immutable
39
+ # once created. Defaults to `false`.
40
+ #
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.
44
+ #
45
+ # Alternatively, this method can be given a block to build a custom slug
46
+ # out of the specified fields.
47
+ #
48
+ # The block takes a single argument, the document itself, and should
49
+ # return a string that will serve as the base of the slug.
50
+ #
51
+ # Here, for instance, we slug an array field.
52
+ #
53
+ # class Person
54
+ # include Mongoid::Document
55
+ # include Mongoid::Slug
56
+ #
57
+ # field :names, :type => Array
58
+ # slug :names do |doc|
59
+ # doc.names.join(' ')
60
+ # end
61
+ #
62
+ def slug(*fields, &block)
25
63
  options = fields.extract_options!
26
-
27
- self.slug_name = options[:as] || :slug
28
- self.slug_scope = options[:scope] || nil
29
-
30
- class_eval <<-CODE
31
- def slug_any?
32
- #{!!options[:any]}
33
- end
34
- CODE
35
-
64
+ self.slug_scope = options[:scope]
65
+ self.slug_name = options[:as] || :slug
36
66
  self.slugged_fields = fields
37
67
 
38
- if options[:scoped]
39
- ActiveSupport::Deprecation.warn <<-EOM
40
-
41
- The :scoped => true option is deprecated and now default for embedded
42
- child documents. Please use :scope => :association_name if you wish
43
- to scope by a reference association.
44
- EOM
45
- end
68
+ self.slug_builder =
69
+ if block_given?
70
+ block
71
+ else
72
+ lambda do |doc|
73
+ slugged_fields.map { |f| doc.send(f) }.join(',')
74
+ end
75
+ end
46
76
 
47
77
  field slug_name
48
78
 
49
79
  if options[:index]
50
- index slug_name, :unique => !slug_scope
80
+ index(slug_name, :unique => !slug_scope)
51
81
  end
52
82
 
53
83
  if options[:permanent]
@@ -56,9 +86,12 @@ module Mongoid #:nodoc:
56
86
  before_save :generate_slug
57
87
  end
58
88
 
89
+ # Build a finder based on the slug name.
90
+ #
91
+ # Defaults to `find_by_slug`.
59
92
  instance_eval <<-CODE
60
93
  def self.find_by_#{slug_name}(slug)
61
- where(slug_name => slug).first rescue nil
94
+ where(slug_name => slug).first
62
95
  end
63
96
  CODE
64
97
  end
@@ -66,23 +99,21 @@ module Mongoid #:nodoc:
66
99
 
67
100
  # Regenerates slug.
68
101
  #
69
- # This method should come in handy when generating slugs for an existing
70
- # collection.
102
+ # Should come in handy when generating slugs for an existing collection.
71
103
  def slug!
72
- self.send(:generate_slug!)
73
- save if self.send("#{slug_name}_changed?")
104
+ generate_slug!
105
+ save
74
106
  end
75
107
 
108
+ # Returns the slug.
76
109
  def to_param
77
110
  self.send(slug_name)
78
111
  end
79
112
 
80
113
  private
81
114
 
82
- attr_reader :slug_counter
83
-
84
115
  def build_slug
85
- ("#{slug_base} #{slug_counter}").to_url
116
+ ("#{slug_builder.call(self)} #{@slug_counter}").to_url
86
117
  end
87
118
 
88
119
  def find_unique_slug
@@ -95,34 +126,22 @@ module Mongoid #:nodoc:
95
126
  end
96
127
  end
97
128
 
98
- def generate_slug!
99
- self.send("#{slug_name}=", find_unique_slug)
100
- end
101
-
102
129
  def generate_slug
103
- generate_slug! if new_record? || slugged_fields_changed?
130
+ if new_record? || slugged_fields_changed?
131
+ generate_slug!
132
+ end
104
133
  end
105
134
 
106
- def increment_slug_counter
107
- @slug_counter = (slug_counter.to_i + 1).to_s
135
+ def generate_slug!
136
+ self.send("#{slug_name}=", find_unique_slug)
108
137
  end
109
138
 
110
- def slug_base
111
- values = self.class.slugged_fields.map do |field|
112
- self.send(field)
113
- end
114
-
115
- if slug_any?
116
- values.detect { |value| value.present? }
117
- else
118
- values.join(' ')
119
- end
139
+ def increment_slug_counter
140
+ @slug_counter = (@slug_counter.to_i + 1).to_s
120
141
  end
121
142
 
122
143
  def slugged_fields_changed?
123
- self.class.slugged_fields.any? do |field|
124
- self.send("#{field}_changed?")
125
- end
144
+ slugged_fields.any? { |f| self.send("#{f}_changed?") }
126
145
  end
127
146
 
128
147
  def unique_slug?(slug)
@@ -1,5 +1,5 @@
1
1
  module Mongoid #:nodoc:
2
2
  module Slug
3
- VERSION = '0.6.4'
3
+ VERSION = '0.7.0'
4
4
  end
5
5
  end
@@ -3,5 +3,7 @@ class Article
3
3
  include Mongoid::Slug
4
4
  field :brief
5
5
  field :title
6
- slug :title, :brief, :any => true
6
+ slug :title, :brief do |doc|
7
+ [doc.title, doc.brief].reject(&:blank?).first
8
+ end
7
9
  end
@@ -0,0 +1,10 @@
1
+ class Caption
2
+ include Mongoid::Document
3
+ include Mongoid::Slug
4
+ field :identity
5
+ field :title
6
+ field :medium
7
+ slug :identity, :title do |doc|
8
+ [doc.identity.gsub(/\s*\([^)]+\)/, ''), doc.title].join(' ')
9
+ end
10
+ end
@@ -1,3 +1,2 @@
1
1
  class ComicBook < Book
2
-
3
- end
2
+ end
@@ -160,25 +160,6 @@ module Mongoid
160
160
 
161
161
  end
162
162
 
163
- context "when :any is passed as an argument" do
164
- let!(:article) do
165
- Article.create(
166
- :brief => "This is the brief",
167
- :title => "This is the title")
168
- end
169
-
170
- it "uses the first available field for the slug if any option is used" do
171
- article.to_param.should eql 'this-is-the-title'
172
- article.title = ""
173
- article.save
174
- article.to_param.should eql 'this-is-the-brief'
175
-
176
- article.title = nil
177
- article.save
178
- article.to_param.should eql 'this-is-the-brief'
179
- end
180
- end
181
-
182
163
  context "when :as is passed as an argument" do
183
164
  let!(:person) do
184
165
  Person.create(:name => "John Doe")
@@ -247,6 +228,34 @@ module Mongoid
247
228
  end
248
229
  end
249
230
 
231
+ context "when :slug is given a block" do
232
+ let(:caption) do
233
+ Caption.create(:identity => 'Edward Hopper (American, 1882-1967)',
234
+ :title => 'Soir Bleu, 1914',
235
+ :medium => 'Oil on Canvas')
236
+ end
237
+
238
+ it "generates a slug" do
239
+ caption.to_param.should eql 'edward-hopper-soir-bleu-1914'
240
+ end
241
+
242
+ it "updates the slug" do
243
+ caption.title = 'Road in Maine, 1914'
244
+ caption.save
245
+ caption.to_param.should eql "edward-hopper-road-in-maine-1914"
246
+ end
247
+
248
+ it "does not change slug if slugged fields have changed but generated slug is identical" do
249
+ caption.identity = 'Edward Hopper'
250
+ caption.save
251
+ caption.to_param.should eql 'edward-hopper-soir-bleu-1914'
252
+ end
253
+
254
+ it "finds by slug" do
255
+ Caption.find_by_slug(caption.to_param).should eql caption
256
+ end
257
+ end
258
+
250
259
  it "works with non-Latin characters" do
251
260
  book.title = "Капитал"
252
261
  book.save
@@ -265,16 +274,6 @@ module Mongoid
265
274
  book.to_param.should eql 'paul-cezanne'
266
275
  end
267
276
 
268
- it "deprecates the :scoped option" do
269
- ActiveSupport::Deprecation.should_receive(:warn)
270
- class Oldie
271
- include Mongoid::Document
272
- include Mongoid::Slug
273
- field :name
274
- slug :name, :scoped => true
275
- end
276
- end
277
-
278
277
  context "when :index is passed as an argument" do
279
278
  before do
280
279
  Book.collection.drop_indexes
@@ -299,8 +298,6 @@ module Mongoid
299
298
  Book.index_information["slug_1"]["unique"].should be_true
300
299
  end
301
300
  end
302
-
303
- it "has no effect in embedded objects"
304
301
  end
305
302
 
306
303
  context "when :index is not passed as an argument" do
@@ -343,5 +340,12 @@ module Mongoid
343
340
  foo.reload.slug.should eql 'john'
344
341
  end
345
342
  end
343
+
344
+ describe ".find_by_slug" do
345
+ it "returns nil if no document is found" do
346
+ Book.create(:title => "A Thousand Plateaus")
347
+ Book.find_by_slug(:title => "Anti Oedipus").should be_nil
348
+ end
349
+ end
346
350
  end
347
351
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: mongoid_slug
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.6.4
5
+ version: 0.7.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Paper Cavalier
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-02-27 00:00:00 +00:00
13
+ date: 2011-03-31 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -21,7 +21,7 @@ dependencies:
21
21
  requirements:
22
22
  - - ~>
23
23
  - !ruby/object:Gem::Version
24
- version: 2.0.0.rc.7
24
+ version: 2.0.0
25
25
  type: :runtime
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
@@ -35,50 +35,6 @@ dependencies:
35
35
  version: 1.2.0
36
36
  type: :runtime
37
37
  version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: bson_ext
40
- prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ~>
45
- - !ruby/object:Gem::Version
46
- version: 1.2.0
47
- type: :development
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
50
- name: database_cleaner
51
- prerelease: false
52
- requirement: &id004 !ruby/object:Gem::Requirement
53
- none: false
54
- requirements:
55
- - - ~>
56
- - !ruby/object:Gem::Version
57
- version: 0.6.0
58
- type: :development
59
- version_requirements: *id004
60
- - !ruby/object:Gem::Dependency
61
- name: rspec
62
- prerelease: false
63
- requirement: &id005 !ruby/object:Gem::Requirement
64
- none: false
65
- requirements:
66
- - - ~>
67
- - !ruby/object:Gem::Version
68
- version: 2.4.0
69
- type: :development
70
- version_requirements: *id005
71
- - !ruby/object:Gem::Dependency
72
- name: ruby-debug19
73
- prerelease: false
74
- requirement: &id006 !ruby/object:Gem::Requirement
75
- none: false
76
- requirements:
77
- - - ~>
78
- - !ruby/object:Gem::Version
79
- version: 0.11.0
80
- type: :development
81
- version_requirements: *id006
82
38
  description: Mongoid Slug generates a URL slug or permalink based on one or more fields in a Mongoid model.
83
39
  email:
84
40
  - code@papercavalier.com
@@ -96,6 +52,7 @@ files:
96
52
  - spec/models/article.rb
97
53
  - spec/models/author.rb
98
54
  - spec/models/book.rb
55
+ - spec/models/caption.rb
99
56
  - spec/models/comic_book.rb
100
57
  - spec/models/partner.rb
101
58
  - spec/models/person.rb
@@ -135,6 +92,7 @@ test_files:
135
92
  - spec/models/article.rb
136
93
  - spec/models/author.rb
137
94
  - spec/models/book.rb
95
+ - spec/models/caption.rb
138
96
  - spec/models/comic_book.rb
139
97
  - spec/models/partner.rb
140
98
  - spec/models/person.rb