mongoid-slugify 0.0.1

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