mongoid_slug 0.6.4 → 0.7.0

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