sequel_sluggable 0.0.4 → 0.0.5

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