sequel_sluggable 0.0.4 → 0.0.5
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/CHANGELOG +6 -0
- data/README.rdoc +57 -4
- data/lib/sequel_sluggable.rb +40 -19
- data/lib/version.rb +1 -1
- data/sequel_sluggable.gemspec +4 -3
- data/spec/sequel_sluggable_spec.rb +62 -2
- data/spec/spec_helper.rb +1 -0
- metadata +39 -21
data/CHANGELOG
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
== 0.0.5, 2010-03-04
|
2
|
+
* Allow to generate slug before the before_save + doc update
|
3
|
+
* Slug is generated only before_create and when it's not set manually
|
4
|
+
* Slug is not by default regenerated before_update
|
5
|
+
* New :frozen option (true by default). When true slug is not regenerated
|
6
|
+
during update. When :frozen => false slug is regenerated during update.
|
data/README.rdoc
CHANGED
@@ -11,8 +11,7 @@ Install:
|
|
11
11
|
== Usage
|
12
12
|
|
13
13
|
This plug-in provide functionality to allow Sequel::Model to have a slug.
|
14
|
-
Slug is created in the *
|
15
|
-
*creating* or *updating* your model.
|
14
|
+
Slug is created in ether the *before_create* or *before_update* hooks.
|
16
15
|
|
17
16
|
To use plug-in you add plug-in to your model:
|
18
17
|
|
@@ -21,11 +20,60 @@ To use plug-in you add plug-in to your model:
|
|
21
20
|
end
|
22
21
|
|
23
22
|
You can use following options:
|
23
|
+
*frozen*:: Should slug be frozen once it's generated? Default true.
|
24
|
+
*sluggator*:: Proc or Symbol to call to create slug.
|
24
25
|
*source*:: Column which value will be used to generate slug.
|
25
26
|
*target*:: Column where slug will be written, defaults to *:slug*.
|
26
|
-
*sluggator*:: Proc or Symbol to call to create slug.
|
27
27
|
|
28
|
-
Options *
|
28
|
+
Options *frozen*, *sluggator* and *target* are optional.
|
29
|
+
|
30
|
+
<b>Options are inherited when you use inheritance for your models</b>. However
|
31
|
+
you can only set options via plugin method.
|
32
|
+
|
33
|
+
You can access options for current model via reader <b>Model#sluggable_options</b>
|
34
|
+
which is readonly.
|
35
|
+
|
36
|
+
== When is slug generated?
|
37
|
+
|
38
|
+
By default slug is generated *ONLY* when model is created and you
|
39
|
+
<strong>didn't set it</strong>. <strong>When you update model slug
|
40
|
+
is not updated by default</strong> but if you set :frozen => false,
|
41
|
+
slug will be regenerated on update. Some examples:
|
42
|
+
|
43
|
+
class Item < Sequel::Model
|
44
|
+
plugin :source => :name
|
45
|
+
...
|
46
|
+
end
|
47
|
+
|
48
|
+
Item.create(:name => 'X') # Generates slug
|
49
|
+
|
50
|
+
i = Item.new(:name => 'X')
|
51
|
+
i.slug = 'X Y' # Sets slug manualy
|
52
|
+
i.save # Slug is not regenerated but the set slug is used
|
53
|
+
i.slug # => x-y
|
54
|
+
i.update(:name => 'Y') # Won't regenerate slug, because slug is frozen by default
|
55
|
+
i.slug # => x-y
|
56
|
+
|
57
|
+
BUT:
|
58
|
+
|
59
|
+
class Item < Sequel::Model
|
60
|
+
plugin :source => :name, :frozen => false
|
61
|
+
...
|
62
|
+
end
|
63
|
+
|
64
|
+
i = Item.create(:name => 'X') # Generates slug
|
65
|
+
i.update(:name => 'Y') # Will regenerate slug, because slug is now not frozen
|
66
|
+
i.slug # => y
|
67
|
+
|
68
|
+
== Access/Set slug
|
69
|
+
|
70
|
+
You can access slug via your normal Sequel reader. By default that will be
|
71
|
+
<b>Model#slug</b> method. If you customize this via :target option than you have
|
72
|
+
<b>Model#:target</b>.
|
73
|
+
|
74
|
+
Writer for the slug is generated depending on your :target option. Default will
|
75
|
+
be <b>Model#slug=</b> otherwise <b>Model#:target=</b>. You can call setter
|
76
|
+
to set the slug before the creating or updating model.
|
29
77
|
|
30
78
|
== Algorithm customization
|
31
79
|
|
@@ -72,6 +120,11 @@ will use it's own default implementation which does following:
|
|
72
120
|
bump version in a commit by itself I can ignore when I pull)
|
73
121
|
* Send me a pull request. Bonus points for topic branches.
|
74
122
|
|
123
|
+
== Contributors
|
124
|
+
|
125
|
+
* Pavel Kunc
|
126
|
+
* Jakub "Botanicus" Stastny
|
127
|
+
|
75
128
|
== Copyright
|
76
129
|
|
77
130
|
Copyright (c) 2009 Pavel Kunc. See LICENSE for details.
|
data/lib/sequel_sluggable.rb
CHANGED
@@ -12,6 +12,24 @@ module Sequel
|
|
12
12
|
# Plugin configuration
|
13
13
|
def self.configure(model, opts={})
|
14
14
|
model.sluggable_options = opts
|
15
|
+
model.sluggable_options.freeze
|
16
|
+
|
17
|
+
model.class_eval do
|
18
|
+
# Sets the slug to the normalized URL friendly string
|
19
|
+
#
|
20
|
+
# Compute slug for the value
|
21
|
+
#
|
22
|
+
# @param [String] String to be slugged
|
23
|
+
# @return [String]
|
24
|
+
define_method("#{sluggable_options[:target]}=") do |value|
|
25
|
+
sluggator = self.class.sluggable_options[:sluggator]
|
26
|
+
slug = sluggator.call(value, self) if sluggator.respond_to?(:call)
|
27
|
+
slug ||= self.send(sluggator, value) if sluggator
|
28
|
+
slug ||= to_slug(value)
|
29
|
+
super(slug)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
15
33
|
end
|
16
34
|
|
17
35
|
module ClassMethods
|
@@ -43,9 +61,10 @@ module Sequel
|
|
43
61
|
#
|
44
62
|
# Options:
|
45
63
|
# @param [Hash] plugin options
|
64
|
+
# @option frozen [Boolean] :Is slug frozen, default true
|
65
|
+
# @option sluggator [Proc, Symbol] :Algorithm to convert string to slug.
|
46
66
|
# @option source [Symbol] :Column to get value to be slugged from.
|
47
67
|
# @option target [Symbol] :Column to write value of the slug to.
|
48
|
-
# @option sluggator [Proc, Symbol] :Algorithm to convert string to slug.
|
49
68
|
def sluggable_options=(options)
|
50
69
|
raise ArgumentError, "You must provide :source column" unless options[:source]
|
51
70
|
sluggator = options[:sluggator]
|
@@ -54,6 +73,7 @@ module Sequel
|
|
54
73
|
end
|
55
74
|
options[:source] = options[:source].to_sym
|
56
75
|
options[:target] = options[:target] ? options[:target].to_sym : DEFAULT_TARGET_COLUMN
|
76
|
+
options[:frozen] = options[:frozen].nil? ? true : !!options[:frozen]
|
57
77
|
@sluggable_options = options
|
58
78
|
end
|
59
79
|
end
|
@@ -61,29 +81,22 @@ module Sequel
|
|
61
81
|
module InstanceMethods
|
62
82
|
|
63
83
|
# Sets a slug column to the slugged value
|
64
|
-
def
|
84
|
+
def before_create
|
65
85
|
super
|
66
|
-
target =
|
67
|
-
|
68
|
-
|
86
|
+
target = self.class.sluggable_options[:target]
|
87
|
+
set_target_column unless self.send(target)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sets a slug column to the slugged value
|
91
|
+
def before_update
|
92
|
+
super
|
93
|
+
target = self.class.sluggable_options[:target]
|
94
|
+
frozen = self.class.sluggable_options[:frozen]
|
95
|
+
set_target_column if !self.send(target) || !frozen
|
69
96
|
end
|
70
97
|
|
71
98
|
private
|
72
99
|
|
73
|
-
# Sets the slug to the normalized URL friendly string
|
74
|
-
#
|
75
|
-
# Compute slug for the value
|
76
|
-
#
|
77
|
-
# @param [String] String to be slugged
|
78
|
-
# @return [String]
|
79
|
-
def slug=(value)
|
80
|
-
sluggator = self.class.sluggable_options[:sluggator]
|
81
|
-
slug = sluggator.call(value, self) if sluggator.respond_to?(:call)
|
82
|
-
slug ||= self.send(sluggator, value) if sluggator
|
83
|
-
slug ||= to_slug(value)
|
84
|
-
super(slug)
|
85
|
-
end
|
86
|
-
|
87
100
|
# Generate slug from the passed value
|
88
101
|
#
|
89
102
|
# @param [String] String to be slugged
|
@@ -92,6 +105,14 @@ module Sequel
|
|
92
105
|
value.chomp.downcase.gsub(/[^a-z0-9]+/,'-')
|
93
106
|
end
|
94
107
|
|
108
|
+
# Sets target column with source column which
|
109
|
+
# effectively triggers slug generation
|
110
|
+
def set_target_column
|
111
|
+
target = self.class.sluggable_options[:target]
|
112
|
+
source = self.class.sluggable_options[:source]
|
113
|
+
self.send("#{target}=", self.send(source))
|
114
|
+
end
|
115
|
+
|
95
116
|
end # InstanceMethods
|
96
117
|
end # Sluggable
|
97
118
|
end # Plugins
|
data/lib/version.rb
CHANGED
data/sequel_sluggable.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{sequel_sluggable}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Pavel Kunc"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-03-04}
|
13
13
|
s.description = %q{Sequel plugin which provides Slug functionality for model.}
|
14
14
|
s.email = %q{pavel.kunc@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
21
|
".gitignore",
|
22
|
+
"CHANGELOG",
|
22
23
|
"LICENSE",
|
23
24
|
"README.rdoc",
|
24
25
|
"Rakefile",
|
@@ -33,7 +34,7 @@ Gem::Specification.new do |s|
|
|
33
34
|
s.homepage = %q{http://github.com/pk/sequel_sluggable}
|
34
35
|
s.rdoc_options = ["--charset=UTF-8"]
|
35
36
|
s.require_paths = ["lib"]
|
36
|
-
s.rubygems_version = %q{1.3.
|
37
|
+
s.rubygems_version = %q{1.3.6}
|
37
38
|
s.summary = %q{Sequel plugin which provides Slug functionality for model.}
|
38
39
|
s.test_files = [
|
39
40
|
"spec/sequel_sluggable_spec.rb",
|
@@ -32,7 +32,8 @@ describe "SequelSluggable" do
|
|
32
32
|
Item.plugin :sluggable,
|
33
33
|
:source => :name,
|
34
34
|
:target => :slug,
|
35
|
-
:sluggator => @sluggator
|
35
|
+
:sluggator => @sluggator,
|
36
|
+
:frozen => false
|
36
37
|
end
|
37
38
|
|
38
39
|
it "should accept source option" do
|
@@ -47,6 +48,16 @@ describe "SequelSluggable" do
|
|
47
48
|
Item.sluggable_options[:sluggator].should eql @sluggator
|
48
49
|
end
|
49
50
|
|
51
|
+
it "should accept frozen option" do
|
52
|
+
Item.sluggable_options[:frozen].should be_false
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should have frozen true by default" do
|
56
|
+
class Item < Sequel::Model; end
|
57
|
+
Item.plugin :sluggable, :source => :name
|
58
|
+
Item.sluggable_options[:frozen].should be_true
|
59
|
+
end
|
60
|
+
|
50
61
|
it "should require source option" do
|
51
62
|
class Item < Sequel::Model; end
|
52
63
|
lambda { Item.plugin :sluggable }.should raise_error(ArgumentError, "You must provide :source column")
|
@@ -76,10 +87,32 @@ describe "SequelSluggable" do
|
|
76
87
|
|
77
88
|
it "should not mess with parent settings when inherited" do
|
78
89
|
class SubItem < Item; end
|
79
|
-
SubItem.
|
90
|
+
SubItem.plugin :sluggable, :source => :test
|
80
91
|
SubItem.sluggable_options[:source].should eql :test
|
81
92
|
Item.sluggable_options[:source].should eql :name
|
82
93
|
end
|
94
|
+
|
95
|
+
it "should not allow changing the options directly" do
|
96
|
+
lambda { Item.sluggable_options[:source] = 'xy' }.should raise_error
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#:target= method" do
|
101
|
+
before(:each) do
|
102
|
+
Item.plugin :sluggable,
|
103
|
+
:source => :name,
|
104
|
+
:target => :sluggie
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should allow to set slug with Model#:target= method" do
|
108
|
+
i = Item.new(:name => 'Pavel Kunc')
|
109
|
+
i.sluggie = i.name
|
110
|
+
i.sluggie.should eql 'pavel-kunc'
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should work with different Model#:target= method than default" do
|
114
|
+
Item.create(:name => 'Pavel Kunc').sluggie.should eql 'pavel-kunc'
|
115
|
+
end
|
83
116
|
end
|
84
117
|
|
85
118
|
describe "::find_by_pk_or_slug" do
|
@@ -155,4 +188,31 @@ describe "SequelSluggable" do
|
|
155
188
|
end
|
156
189
|
end
|
157
190
|
|
191
|
+
describe "slug generation and regeneration" do
|
192
|
+
it "should generate slug when creating model and slug is not set" do
|
193
|
+
Item.create(:name => 'Pavel Kunc').slug.should eql 'pavel-kunc'
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should not regenerate slug when creating model and slug is set" do
|
197
|
+
i = Item.new(:name => 'Pavel Kunc')
|
198
|
+
i.slug = 'Kunc Pavel'
|
199
|
+
i.save
|
200
|
+
i.slug.should eql 'kunc-pavel'
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should regenerate slug when updating model and slug is not frozen" do
|
204
|
+
class Item < Sequel::Model; end
|
205
|
+
Item.plugin :sluggable, :source => :name, :target => :slug, :frozen => false
|
206
|
+
i = Item.create(:name => 'Pavel Kunc')
|
207
|
+
i.update(:name => 'Kunc Pavel')
|
208
|
+
i.slug.should eql 'kunc-pavel'
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should not regenerate slug when updating model" do
|
212
|
+
i = Item.create(:name => 'Pavel Kunc')
|
213
|
+
i.update(:name => 'Kunc Pavel')
|
214
|
+
i.slug.should eql 'pavel-kunc'
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
158
218
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel_sluggable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 5
|
9
|
+
version: 0.0.5
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Pavel Kunc
|
@@ -9,49 +14,59 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-03-04 00:00:00 +00:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: sequel
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 3
|
29
|
+
- 0
|
30
|
+
- 0
|
23
31
|
version: 3.0.0
|
24
|
-
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
25
34
|
- !ruby/object:Gem::Dependency
|
26
35
|
name: sqlite3-ruby
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
38
|
requirements:
|
31
39
|
- - ">="
|
32
40
|
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
33
43
|
version: "0"
|
34
|
-
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
35
46
|
- !ruby/object:Gem::Dependency
|
36
47
|
name: rspec
|
37
|
-
|
38
|
-
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
50
|
requirements:
|
41
51
|
- - ">="
|
42
52
|
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
43
55
|
version: "0"
|
44
|
-
|
56
|
+
type: :development
|
57
|
+
version_requirements: *id003
|
45
58
|
- !ruby/object:Gem::Dependency
|
46
59
|
name: yard
|
47
|
-
|
48
|
-
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
prerelease: false
|
61
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
50
62
|
requirements:
|
51
63
|
- - ">="
|
52
64
|
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
53
67
|
version: "0"
|
54
|
-
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id004
|
55
70
|
description: Sequel plugin which provides Slug functionality for model.
|
56
71
|
email: pavel.kunc@gmail.com
|
57
72
|
executables: []
|
@@ -64,6 +79,7 @@ extra_rdoc_files:
|
|
64
79
|
files:
|
65
80
|
- .document
|
66
81
|
- .gitignore
|
82
|
+
- CHANGELOG
|
67
83
|
- LICENSE
|
68
84
|
- README.rdoc
|
69
85
|
- Rakefile
|
@@ -87,18 +103,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
103
|
requirements:
|
88
104
|
- - ">="
|
89
105
|
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
90
108
|
version: "0"
|
91
|
-
version:
|
92
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
110
|
requirements:
|
94
111
|
- - ">="
|
95
112
|
- !ruby/object:Gem::Version
|
113
|
+
segments:
|
114
|
+
- 0
|
96
115
|
version: "0"
|
97
|
-
version:
|
98
116
|
requirements: []
|
99
117
|
|
100
118
|
rubyforge_project:
|
101
|
-
rubygems_version: 1.3.
|
119
|
+
rubygems_version: 1.3.6
|
102
120
|
signing_key:
|
103
121
|
specification_version: 3
|
104
122
|
summary: Sequel plugin which provides Slug functionality for model.
|