mongoid-slugify 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mongoid-slugify.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
7
+ gem 'database_cleaner'
8
+ gem 'bson_ext'
data/README.rdoc ADDED
@@ -0,0 +1,40 @@
1
+ == Installation
2
+
3
+ Add mongoid-slugify to your Gemfile:
4
+
5
+ gem 'mongoid-slugify'
6
+
7
+ == Usage
8
+
9
+ class Product
10
+ include Mongoid::Document
11
+ include Mongoid::Slugify
12
+
13
+ field :title
14
+
15
+ private
16
+ def generate_slug
17
+ title.parameterize
18
+ end
19
+ end
20
+
21
+ As you can see, you should make 2 things:
22
+
23
+ - include Mongoid::Slugify module to your model
24
+ - provide a way to generate initial slug for your model (based on any fields, that you want)
25
+
26
+ If do those - Mongooid::Slugify will save the generated slug to `slug` field of your model and will care about slug uniqueness
27
+ (it will append "-1", "-2", etc to your slugs until it finds free one).
28
+
29
+ Mongooid::Slugify gives you these additional functions:
30
+
31
+ - redefines to_param method, so that it returns slug, if it's present, and model id otherwise
32
+ - Model.(find_by_slug/find_by_slug!/find_by_slug_or_id/find_by_slug_or_id!) methods.
33
+ If you don't want to generate slugs for all your existing objects (so that to_param will return model ids) - you should prefer the latter two in your controllers.
34
+
35
+ == Warning
36
+
37
+ This library will NEVER (at least not in the nearest future) provide a way to generate initial slugs. I just don't need it.
38
+ Don't try to add this functionality to my library.
39
+
40
+ If you need all-out-of-the-box solution - look at Mongoid Slug [https://github.com/hakanensari/mongoid-slug], it's far more full featured and actively developed.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module Slugify
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,65 @@
1
+ require 'mongoid'
2
+ require 'mongoid/slugify/version'
3
+ require 'active_support/concern'
4
+
5
+ module Mongoid
6
+ module Slugify
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ field :slug
11
+ index :slug, :unique => true
12
+ before_save :assign_slug
13
+ end
14
+
15
+ module ClassMethods
16
+ def find_by_slug(slug)
17
+ where(:slug => slug).first
18
+ end
19
+
20
+ def find_by_slug!(slug)
21
+ find_by_slug(slug) || raise(Mongoid::Errors::DocumentNotFound.new(self, slug))
22
+ end
23
+
24
+ def find_by_slug_or_id(slug_or_id)
25
+ find_by_slug(slug_or_id) || where(:id => id).first
26
+ end
27
+
28
+ def find_by_slug_or_id!(slug_or_id)
29
+ find_by_slug(slug_or_id) || find(slug_or_id)
30
+ end
31
+ end
32
+
33
+ module InstanceMethods
34
+ def to_param
35
+ slug || super
36
+ end
37
+
38
+ private
39
+ def generate_slug
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def generate_unique_slug
44
+ current_slug = generate_slug
45
+ pattern = /^#{Regexp.escape(current_slug)}(?:-(\d+))?$/
46
+
47
+ appropriate_class = self.class
48
+ while (appropriate_class.superclass.include?(Mongoid::Document))
49
+ appropriate_class = appropriate_class.superclass
50
+ end
51
+
52
+ existing_slugs = appropriate_class.where(:slug => pattern, :_id.ne => _id).only(:slug).map { |record| record.slug }
53
+ if existing_slugs.count > 0
54
+ max_counter = existing_slugs.map { |slug| (pattern.match(slug)[1] || 0).to_i }.max
55
+ current_slug += "-#{max_counter + 1}"
56
+ end
57
+ current_slug
58
+ end
59
+
60
+ def assign_slug
61
+ self.slug = generate_unique_slug
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1 @@
1
+ require "mongoid/slugify"
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/mongoid/slugify/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Pavel Forkert"]
6
+ gem.email = ["fxposter@gmail.com"]
7
+ gem.description = "Provides a simple way to add slug generation to a Mongoid model"
8
+ gem.summary = "Managing slugs in Mongoid models"
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "mongoid-slugify"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Mongoid::Slugify::VERSION
17
+
18
+ gem.add_runtime_dependency 'activesupport', '>= 3.0', '< 3.2'
19
+ gem.add_runtime_dependency 'mongoid', '~> 2.0'
20
+ end
@@ -0,0 +1,233 @@
1
+ #encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ class Author
5
+ include Mongoid::Document
6
+ include Mongoid::Slugify
7
+ field :first_name
8
+ field :last_name
9
+ referenced_in :book
10
+ references_many :characters,
11
+ :class_name => 'Person',
12
+ :foreign_key => :author_id
13
+
14
+ private
15
+ def generate_slug
16
+ [first_name, last_name].reject(&:blank?).join('-').parameterize
17
+ end
18
+ end
19
+
20
+ class Book
21
+ include Mongoid::Document
22
+ include Mongoid::Slugify
23
+ field :title
24
+ embeds_many :subjects
25
+ references_many :authors
26
+
27
+ private
28
+ def generate_slug
29
+ title.parameterize
30
+ end
31
+ end
32
+
33
+ class ComicBook < Book
34
+ end
35
+
36
+ class Person
37
+ include Mongoid::Document
38
+ include Mongoid::Slugify
39
+ field :name
40
+ embeds_many :relationships
41
+ referenced_in :author, :inverse_of => :characters
42
+
43
+ private
44
+ def generate_slug
45
+ name.parameterize
46
+ end
47
+ end
48
+
49
+ class Caption
50
+ include Mongoid::Document
51
+ include Mongoid::Slugify
52
+ field :identity
53
+ field :title
54
+ field :medium
55
+
56
+ private
57
+ def generate_slug
58
+ [identity.gsub(/\s*\([^)]+\)/, ''), title].join(' ').parameterize
59
+ end
60
+ end
61
+
62
+ module Mongoid
63
+ describe Slugify do
64
+ let(:book) do
65
+ Book.create(:title => "A Thousand Plateaus")
66
+ end
67
+
68
+ context "when the object is top-level" do
69
+ it "generates a slug" do
70
+ book.to_param.should eql "a-thousand-plateaus"
71
+ end
72
+
73
+ it "updates the slug" do
74
+ book.title = "Anti Oedipus"
75
+ book.save
76
+ book.to_param.should eql "anti-oedipus"
77
+ end
78
+
79
+ it "generates a unique slug by appending a counter to duplicate text" do
80
+ 15.times{ |x|
81
+ dup = Book.create(:title => book.title)
82
+ dup.to_param.should eql "a-thousand-plateaus-#{x+1}"
83
+ }
84
+ end
85
+
86
+ it "does not update slug if slugged fields have not changed" do
87
+ book.save
88
+ book.to_param.should eql "a-thousand-plateaus"
89
+ end
90
+
91
+ it "does not change slug if slugged fields have changed but generated slug is identical" do
92
+ book.title = "a thousand plateaus"
93
+ book.save
94
+ book.to_param.should eql "a-thousand-plateaus"
95
+ end
96
+
97
+ it "finds by slug" do
98
+ Book.find_by_slug(book.to_param).should eql book
99
+ end
100
+ end
101
+
102
+ context "when the slug is composed of multiple fields" do
103
+ let!(:author) do
104
+ Author.create(
105
+ :first_name => "Gilles",
106
+ :last_name => "Deleuze")
107
+ end
108
+
109
+ it "generates a slug" do
110
+ author.to_param.should eql "gilles-deleuze"
111
+ end
112
+
113
+ it "updates the slug" do
114
+ author.first_name = "Félix"
115
+ author.last_name = "Guattari"
116
+ author.save
117
+ author.to_param.should eql "felix-guattari"
118
+ end
119
+
120
+ it "generates a unique slug by appending a counter to duplicate text" do
121
+ dup = Author.create(
122
+ :first_name => author.first_name,
123
+ :last_name => author.last_name)
124
+ dup.to_param.should eql 'gilles-deleuze-1'
125
+
126
+ dup2 = Author.create(
127
+ :first_name => author.first_name,
128
+ :last_name => author.last_name)
129
+
130
+ dup.save
131
+ dup2.to_param.should eql 'gilles-deleuze-2'
132
+ end
133
+
134
+ it "does not update slug if slugged fields have changed but generated slug is identical" do
135
+ author.last_name = "DELEUZE"
136
+ author.save
137
+ author.to_param.should eql 'gilles-deleuze'
138
+ end
139
+
140
+ it "finds by slug" do
141
+ Author.find_by_slug("gilles-deleuze").should eql author
142
+ end
143
+ end
144
+
145
+ context "when :slug is given a block" do
146
+ let(:caption) do
147
+ Caption.create(:identity => 'Edward Hopper (American, 1882-1967)',
148
+ :title => 'Soir Bleu, 1914',
149
+ :medium => 'Oil on Canvas')
150
+ end
151
+
152
+ it "generates a slug" do
153
+ caption.to_param.should eql 'edward-hopper-soir-bleu-1914'
154
+ end
155
+
156
+ it "updates the slug" do
157
+ caption.title = 'Road in Maine, 1914'
158
+ caption.save
159
+ caption.to_param.should eql "edward-hopper-road-in-maine-1914"
160
+ end
161
+
162
+ it "does not change slug if slugged fields have changed but generated slug is identical" do
163
+ caption.identity = 'Edward Hopper'
164
+ caption.save
165
+ caption.to_param.should eql 'edward-hopper-soir-bleu-1914'
166
+ end
167
+
168
+ it "finds by slug" do
169
+ Caption.find_by_slug(caption.to_param).should eql caption
170
+ end
171
+ end
172
+
173
+ context "when :index is passed as an argument" do
174
+ before do
175
+ Book.collection.drop_indexes
176
+ Author.collection.drop_indexes
177
+ end
178
+
179
+ it "defines an index on the slug in top-level objects" do
180
+ Book.create_indexes
181
+ Book.collection.index_information.should have_key "slug_1"
182
+ end
183
+
184
+ context "when slug is not scoped by a reference association" do
185
+ it "defines a unique index" do
186
+ Book.create_indexes
187
+ Book.index_information["slug_1"]["unique"].should be_true
188
+ end
189
+ end
190
+ end
191
+
192
+ context "when :index is not passed as an argument" do
193
+ it "does not define an index on the slug" do
194
+ Person.create_indexes
195
+ Person.collection.index_information.should_not have_key "permalink_1"
196
+ end
197
+ end
198
+
199
+ context "when the object has STI" do
200
+ it "scopes by the superclass" do
201
+ book = Book.create(:title => "Anti Oedipus")
202
+ comic_book = ComicBook.create(:title => "Anti Oedipus")
203
+ comic_book.slug.should_not eql(book.slug)
204
+ end
205
+ end
206
+
207
+ describe ".find_by_slug" do
208
+ let!(:book) { Book.create(:title => "A Thousand Plateaus") }
209
+
210
+ it "returns nil if no document is found" do
211
+ Book.find_by_slug(:title => "Anti Oedipus").should be_nil
212
+ end
213
+
214
+ it "returns the document if it is found" do
215
+ Book.find_by_slug(book.slug).should == book
216
+ end
217
+ end
218
+
219
+ describe ".find_by_slug!" do
220
+ let!(:book) { Book.create(:title => "A Thousand Plateaus") }
221
+
222
+ it "raises a Mongoid::Errors::DocumentNotFound error if no document is found" do
223
+ lambda {
224
+ Book.find_by_slug!(:title => "Anti Oedipus")
225
+ }.should raise_error(Mongoid::Errors::DocumentNotFound)
226
+ end
227
+
228
+ it "returns the document when it is found" do
229
+ Book.find_by_slug!(book.slug).should == book
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,20 @@
1
+ require "bundler/setup"
2
+ Bundler.require(:default, :development)
3
+
4
+ Mongoid.configure do |config|
5
+ name = "mongoid_slugify_test"
6
+ config.master = Mongo::Connection.new.db(name)
7
+ end
8
+
9
+ DatabaseCleaner.strategy = :truncation
10
+ DatabaseCleaner.orm = :mongoid
11
+
12
+ RSpec.configure do |config|
13
+ config.before :each do
14
+ DatabaseCleaner.start
15
+ end
16
+
17
+ config.after :each do
18
+ DatabaseCleaner.clean
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-slugify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pavel Forkert
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-17 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &70314068885540 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ - - <
23
+ - !ruby/object:Gem::Version
24
+ version: '3.2'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: *70314068885540
28
+ - !ruby/object:Gem::Dependency
29
+ name: mongoid
30
+ requirement: &70314068884600 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: '2.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: *70314068884600
39
+ description: Provides a simple way to add slug generation to a Mongoid model
40
+ email:
41
+ - fxposter@gmail.com
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - .gitignore
47
+ - Gemfile
48
+ - README.rdoc
49
+ - Rakefile
50
+ - lib/mongoid-slugify.rb
51
+ - lib/mongoid/slugify.rb
52
+ - lib/mongoid/slugify/version.rb
53
+ - mongoid-slugify.gemspec
54
+ - spec/mongoid/slugify_spec.rb
55
+ - spec/spec_helper.rb
56
+ homepage: ''
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.10
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Managing slugs in Mongoid models
80
+ test_files:
81
+ - spec/mongoid/slugify_spec.rb
82
+ - spec/spec_helper.rb