Permalink
Browse files

Updates to slug generation.

Slug is generated only when creating model and you didn't set it
manually. When updating, unless you set :frozen => false, slug won't be
regenerated. See README.

Closes #4 and Closes #5
  • Loading branch information...
1 parent cd90524 commit e62f0417370d06046f02cfaba4fba69e3d2a9d5d @pk committed Mar 4, 2010
Showing with 98 additions and 11 deletions.
  1. +37 −5 README.rdoc
  2. +22 −5 lib/sequel_sluggable.rb
  3. +39 −1 spec/sequel_sluggable_spec.rb
View
@@ -11,8 +11,7 @@ Install:
== Usage
This plug-in provide functionality to allow Sequel::Model to have a slug.
-Slug is created in the *before_save* hook which is called when you're
-*creating* or *updating* your model.
+Slug is created in ether the *before_create* or *before_update* hooks.
To use plug-in you add plug-in to your model:
@@ -21,18 +20,51 @@ To use plug-in you add plug-in to your model:
end
You can use following options:
+*frozen*:: Should slug be frozen once it's generated? Default true.
+*sluggator*:: Proc or Symbol to call to create slug.
*source*:: Column which value will be used to generate slug.
*target*:: Column where slug will be written, defaults to *:slug*.
-*sluggator*:: Proc or Symbol to call to create slug.
-Options *target* and *sluggator* are optional.
+Options *frozen*, *sluggator* and *target* are optional.
<b>Options are inherited when you use inheritance for your models</b>. However
you can only set options via plugin method.
You can access options for current model via reader <b>Model#sluggable_options</b>
which is readonly.
+== When is slug generated?
+
+By default slug is generated *ONLY* when model is created and you
+<strong>didn't set it</strong>. <strong>When you update model slug
+is not updated by default</strong> but if you set :frozen => false,
+slug will be regenerated on update. Some examples:
+
+ class Item < Sequel::Model
+ plugin :source => :name
+ ...
+ end
+
+ Item.create(:name => 'X') # Generates slug
+
+ i = Item.new(:name => 'X')
+ i.slug = 'X Y' # Sets slug manualy
+ i.save # Slug is not regenerated but the set slug is used
+ i.slug # => x-y
+ i.update(:name => 'Y') # Won't regenerate slug, because slug is frozen by default
+ i.slug # => x-y
+
+ BUT:
+
+ class Item < Sequel::Model
+ plugin :source => :name, :frozen => false
+ ...
+ end
+
+ i = Item.create(:name => 'X') # Generates slug
+ i.update(:name => 'Y') # Will regenerate slug, because slug is now not frozen
+ i.slug # => y
+
== Access/Set slug
You can access slug via your normal Sequel reader. By default that will be
@@ -41,7 +73,7 @@ You can access slug via your normal Sequel reader. By default that will be
Writer for the slug is generated depending on your :target option. Default will
be <b>Model#slug=</b> otherwise <b>Model#:target=</b>. You can call setter
-to set the slug before the before_save callback calls it.
+to set the slug before the creating or updating model.
== Algorithm customization
View
@@ -61,9 +61,10 @@ def inherited(klass)
#
# Options:
# @param [Hash] plugin options
+ # @option frozen [Boolean] :Is slug frozen, default true
+ # @option sluggator [Proc, Symbol] :Algorithm to convert string to slug.
# @option source [Symbol] :Column to get value to be slugged from.
# @option target [Symbol] :Column to write value of the slug to.
- # @option sluggator [Proc, Symbol] :Algorithm to convert string to slug.
def sluggable_options=(options)
raise ArgumentError, "You must provide :source column" unless options[:source]
sluggator = options[:sluggator]
@@ -72,18 +73,26 @@ def sluggable_options=(options)
end
options[:source] = options[:source].to_sym
options[:target] = options[:target] ? options[:target].to_sym : DEFAULT_TARGET_COLUMN
+ options[:frozen] = options[:frozen].nil? ? true : !!options[:frozen]
@sluggable_options = options
end
end
module InstanceMethods
# Sets a slug column to the slugged value
- def before_save
+ def before_create
super
- target = "#{self.class.sluggable_options[:target]}="
- source = self.class.sluggable_options[:source]
- self.send(target, self.send(source))
+ target = self.class.sluggable_options[:target]
+ set_target_column unless self.send(target)
+ end
+
+ # Sets a slug column to the slugged value
+ def before_update
+ super
+ target = self.class.sluggable_options[:target]
+ frozen = self.class.sluggable_options[:frozen]
+ set_target_column if !self.send(target) || !frozen
end
private
@@ -96,6 +105,14 @@ def to_slug(value)
value.chomp.downcase.gsub(/[^a-z0-9]+/,'-')
end
+ # Sets target column with source column which
+ # effectively triggers slug generation
+ def set_target_column
+ target = self.class.sluggable_options[:target]
+ source = self.class.sluggable_options[:source]
+ self.send("#{target}=", self.send(source))
+ end
+
end # InstanceMethods
end # Sluggable
end # Plugins
@@ -32,7 +32,8 @@ class Item < Sequel::Model; end
Item.plugin :sluggable,
:source => :name,
:target => :slug,
- :sluggator => @sluggator
+ :sluggator => @sluggator,
+ :frozen => false
end
it "should accept source option" do
@@ -47,6 +48,16 @@ class Item < Sequel::Model; end
Item.sluggable_options[:sluggator].should eql @sluggator
end
+ it "should accept frozen option" do
+ Item.sluggable_options[:frozen].should be_false
+ end
+
+ it "should have frozen true by default" do
+ class Item < Sequel::Model; end
+ Item.plugin :sluggable, :source => :name
+ Item.sluggable_options[:frozen].should be_true
+ end
+
it "should require source option" do
class Item < Sequel::Model; end
lambda { Item.plugin :sluggable }.should raise_error(ArgumentError, "You must provide :source column")
@@ -177,4 +188,31 @@ def my_custom_sluggator(v)
end
end
+ describe "slug generation and regeneration" do
+ it "should generate slug when creating model and slug is not set" do
+ Item.create(:name => 'Pavel Kunc').slug.should eql 'pavel-kunc'
+ end
+
+ it "should not regenerate slug when creating model and slug is set" do
+ i = Item.new(:name => 'Pavel Kunc')
+ i.slug = 'Kunc Pavel'
+ i.save
+ i.slug.should eql 'kunc-pavel'
+ end
+
+ it "should regenerate slug when updating model and slug is not frozen" do
+ class Item < Sequel::Model; end
+ Item.plugin :sluggable, :source => :name, :target => :slug, :frozen => false
+ i = Item.create(:name => 'Pavel Kunc')
+ i.update(:name => 'Kunc Pavel')
+ i.slug.should eql 'kunc-pavel'
+ end
+
+ it "should not regenerate slug when updating model" do
+ i = Item.create(:name => 'Pavel Kunc')
+ i.update(:name => 'Kunc Pavel')
+ i.slug.should eql 'pavel-kunc'
+ end
+
+ end
end

0 comments on commit e62f041

Please sign in to comment.