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 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 *before_save* hook which is called when you're
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 *target* and *sluggator* are optional.
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.
@@ -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 before_save
84
+ def before_create
65
85
  super
66
- target = "#{self.class.sluggable_options[:target]}="
67
- source = self.class.sluggable_options[:source]
68
- self.send(target, self.send(source))
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
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  module Plugins
3
3
  module Sluggable
4
- VERSION = '0.0.4'.freeze
4
+ VERSION = '0.0.5'
5
5
  end
6
6
  end
7
7
  end
@@ -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.4"
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-02-16}
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.5}
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.sluggable_options[:source] = :test
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
@@ -11,6 +11,7 @@ DB.create_table :items do
11
11
  primary_key :id
12
12
  String :name
13
13
  String :slug
14
+ String :sluggie
14
15
  end
15
16
 
16
17
  class Item < Sequel::Model; end
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
- version: 0.0.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-02-16 00:00:00 +00:00
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
- type: :runtime
18
- version_requirement:
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
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: sqlite3-ruby
27
- type: :development
28
- version_requirement:
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
- version:
44
+ type: :development
45
+ version_requirements: *id002
35
46
  - !ruby/object:Gem::Dependency
36
47
  name: rspec
37
- type: :development
38
- version_requirement:
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
- version:
56
+ type: :development
57
+ version_requirements: *id003
45
58
  - !ruby/object:Gem::Dependency
46
59
  name: yard
47
- type: :development
48
- version_requirement:
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
- version:
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.5
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.