slugable 0.0.2 → 0.0.3
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/README.md +39 -3
- data/changelog.md +15 -1
- data/db/schema.rb +16 -1
- data/lib/slugable/has_slug.rb +73 -11
- data/lib/slugable/version.rb +1 -1
- data/spec/slugable/has_slug_spec.rb +116 -5
- metadata +8 -2
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
* adds support for seo friendly url
|
4
4
|
* one helper method has_slug
|
5
5
|
* support for ancestry models 'https://github.com/stefankroes/ancestry'
|
6
|
+
* be default cache ancestry models url, can be changed
|
6
7
|
|
7
8
|
## Installation
|
8
9
|
|
@@ -22,11 +23,10 @@ Or install it yourself as:
|
|
22
23
|
|
23
24
|
in model use method has_slug
|
24
25
|
|
25
|
-
|
26
26
|
class Item < ActiveRecord::Base
|
27
27
|
attr_accessor :name, :slug
|
28
28
|
|
29
|
-
has_slug # default :from => :name, :to => :slug
|
29
|
+
has_slug # default :from => :name, :to => :slug, :formatter => :parameterize, :cache_tree => true
|
30
30
|
end
|
31
31
|
|
32
32
|
# then in code
|
@@ -35,13 +35,25 @@ in model use method has_slug
|
|
35
35
|
|
36
36
|
item.to_slug # => "my-name-is"
|
37
37
|
|
38
|
+
item.slug = "new-slug
|
39
|
+
|
40
|
+
item.to_slug_was # => "my-name-is"
|
41
|
+
item.to_slug_will # => "new-slug"
|
42
|
+
item.to_slug # => "new-slug"
|
43
|
+
|
38
44
|
you can override defaults by passing hash
|
39
45
|
|
40
46
|
|
41
47
|
class Page < ActiveRecord::Base
|
42
48
|
attr_accessor :title, :seo_url
|
43
49
|
|
44
|
-
has_slug :from => :title, :to => :seo_url
|
50
|
+
has_slug :from => :title, :to => :seo_url, :formatter => :my_style
|
51
|
+
end
|
52
|
+
|
53
|
+
class String
|
54
|
+
def my_style
|
55
|
+
self.parameterize
|
56
|
+
end
|
45
57
|
end
|
46
58
|
|
47
59
|
# then in code
|
@@ -71,6 +83,30 @@ if you have model with ancestry gem 'https://github.com/stefankroes/ancestry'
|
|
71
83
|
child.slug # => "child"
|
72
84
|
child.to_slug # => ["root", "child"]
|
73
85
|
|
86
|
+
branch = Category.create!(:name => "branch", :slug => "branch")
|
87
|
+
child.parent = branch
|
88
|
+
child.slug = "renamed"
|
89
|
+
|
90
|
+
child.to_slug_was # => ["root", "child"]
|
91
|
+
child.to_slug_will # => ["branch", "renamed"]
|
92
|
+
|
93
|
+
child.to_slug # => ["root", "child"]
|
94
|
+
child.save!
|
95
|
+
child.to_slug # => ["branch", "renamed"]
|
96
|
+
|
97
|
+
## configuration
|
98
|
+
|
99
|
+
By default all ancestry structure are cached to prevent useless calls to fetch same record from database just for slug values.
|
100
|
+
You can pass :cache_tree option to disable it like this.
|
101
|
+
|
102
|
+
|
103
|
+
class Category < ActiveRecord::Base
|
104
|
+
attr_accessor :name, :slug
|
105
|
+
|
106
|
+
has_ancestry
|
107
|
+
has_slug :cache_tree => false
|
108
|
+
end
|
109
|
+
|
74
110
|
## Contributing
|
75
111
|
|
76
112
|
1. Fork it
|
data/changelog.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
# 0.0.
|
1
|
+
# 0.0.3 (March 04, 2013)
|
2
|
+
## added
|
3
|
+
* config for allowing or disabling caching
|
4
|
+
* `to_slug_was` for getting old values of object `to_slug`
|
5
|
+
* `to_slug_will` for getting future values of object `to_slug`
|
6
|
+
* options for specifying string formatting method
|
7
|
+
* options for enabling or disabling cacing slugs for tree
|
8
|
+
|
9
|
+
# 0.0.3.beta (December 27, 2012)
|
10
|
+
## changed
|
11
|
+
* skip cache in tree
|
12
|
+
## fixed
|
13
|
+
* tests and schema for appropriate testing
|
14
|
+
|
15
|
+
# 0.0.2 (December 5, 2012)
|
2
16
|
## fixed
|
3
17
|
* fill in slug from name parameter if slug.parameterize is blank
|
4
18
|
|
data/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@ ActiveRecord::Schema.define do
|
|
10
10
|
|
11
11
|
create_table "pages", :force => true do |t|
|
12
12
|
t.string "title"
|
13
|
-
t.string "
|
13
|
+
t.string "seo_url"
|
14
14
|
t.datetime "created_at", :null => false
|
15
15
|
t.datetime "updated_at", :null => false
|
16
16
|
end
|
@@ -22,4 +22,19 @@ ActiveRecord::Schema.define do
|
|
22
22
|
t.datetime "created_at", :null => false
|
23
23
|
t.datetime "updated_at", :null => false
|
24
24
|
end
|
25
|
+
|
26
|
+
create_table "products", :force => true do |t|
|
27
|
+
t.string "name"
|
28
|
+
t.string "slug"
|
29
|
+
t.datetime "created_at", :null => false
|
30
|
+
t.datetime "updated_at", :null => false
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table "tree_items", :force => true do |t|
|
34
|
+
t.string "name"
|
35
|
+
t.string "ancestry"
|
36
|
+
t.string "slug"
|
37
|
+
t.datetime "created_at", :null => false
|
38
|
+
t.datetime "updated_at", :null => false
|
39
|
+
end
|
25
40
|
end
|
data/lib/slugable/has_slug.rb
CHANGED
@@ -13,10 +13,12 @@ module Slugable
|
|
13
13
|
# has_slug :from => :name, :to => :slug # generate to_slug
|
14
14
|
#
|
15
15
|
def has_slug(options={})
|
16
|
-
defaults = {:from => :name, :to => :slug}
|
16
|
+
defaults = {:from => :name, :to => :slug, :formatter => :parameterize, :cache_tree => true}
|
17
17
|
options.reverse_merge!(defaults)
|
18
18
|
from = options.delete(:from)
|
19
19
|
to = options.delete(:to)
|
20
|
+
formatter = options.delete(:formatter)
|
21
|
+
cache_tree = options.delete(:cache_tree)
|
20
22
|
before_save :"fill_slug_from_#{from}_to_#{to}", :"format_slug_from_#{from}_to_#{to}"
|
21
23
|
after_save :"update_my_#{to}_cache"
|
22
24
|
|
@@ -39,7 +41,7 @@ module Slugable
|
|
39
41
|
# end
|
40
42
|
code =<<-method
|
41
43
|
def format_slug_from_#{from}_to_#{to}
|
42
|
-
self.#{to} = #{to}.
|
44
|
+
self.#{to} = #{to}.send(:#{formatter})
|
43
45
|
end
|
44
46
|
method
|
45
47
|
class_eval(code)
|
@@ -98,12 +100,12 @@ module Slugable
|
|
98
100
|
#
|
99
101
|
# def to_slug
|
100
102
|
# if respond_to?(:path_ids)
|
101
|
-
# slugs =
|
102
|
-
#
|
103
|
-
# ""
|
103
|
+
# slugs = if true
|
104
|
+
# path_ids.map{|id| self.class.cached_slug(id)}.select{|i| i.size > 0 }
|
104
105
|
# else
|
105
|
-
# slug
|
106
|
+
# path.map{|record| record.send(:"slug")}.select{|i| i.size > 0 }
|
106
107
|
# end
|
108
|
+
# slugs.empty? ? "" : slugs
|
107
109
|
# else
|
108
110
|
# send(:slug)
|
109
111
|
# end
|
@@ -111,19 +113,79 @@ module Slugable
|
|
111
113
|
code =<<-method
|
112
114
|
def to_#{to}
|
113
115
|
if respond_to?(:path_ids)
|
114
|
-
slugs =
|
115
|
-
|
116
|
-
""
|
116
|
+
slugs = if #{cache_tree}
|
117
|
+
path_ids.map{|id| self.class.cached_#{to}(id)}.select{|i| i.size > 0 }
|
117
118
|
else
|
118
|
-
|
119
|
+
path.map{|record| record.send(:"#{to}")}.select{|i| i.size > 0 }
|
119
120
|
end
|
121
|
+
slugs.empty? ? "" : slugs
|
120
122
|
else
|
121
123
|
send(:#{to})
|
122
124
|
end
|
123
125
|
end
|
124
126
|
method
|
125
127
|
class_eval(code)
|
126
|
-
end
|
127
128
|
|
129
|
+
|
130
|
+
# generate this
|
131
|
+
#
|
132
|
+
# def to_slug_was
|
133
|
+
# if respond_to?(:ancestry_was)
|
134
|
+
# old_slugs = if true
|
135
|
+
# ancestry_was.to_s.split("/").map { |ancestor_id| self.class.cached_slug(ancestor_id.to_i) }
|
136
|
+
# else
|
137
|
+
# ancestry_was.to_s.split("/").map { |ancestor_id| self.class.find(ancestor_id).send(:slug) }
|
138
|
+
# end
|
139
|
+
# old_slugs << send(:slug_was)
|
140
|
+
# else
|
141
|
+
# send(:slug_was)
|
142
|
+
# end
|
143
|
+
# end
|
144
|
+
code =<<-method
|
145
|
+
def to_#{to}_was
|
146
|
+
if respond_to?(:ancestry_was)
|
147
|
+
old_slugs = if #{cache_tree}
|
148
|
+
ancestry_was.to_s.split("/").map { |ancestor_id| self.class.cached_#{to}(ancestor_id.to_i) }
|
149
|
+
else
|
150
|
+
ancestry_was.to_s.split("/").map { |ancestor_id| self.class.find(ancestor_id).send(:#{to}) }
|
151
|
+
end
|
152
|
+
old_slugs << send(:#{to}_was)
|
153
|
+
else
|
154
|
+
send(:#{to}_was)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
method
|
158
|
+
class_eval(code)
|
159
|
+
|
160
|
+
# generate this
|
161
|
+
#
|
162
|
+
# def to_slug_will
|
163
|
+
# if respond_to?(:ancestry)
|
164
|
+
# old_slugs = if true
|
165
|
+
# ancestry.to_s.split("/").map { |ancestor_id| self.class.cached_slug(ancestor_id.to_i) }
|
166
|
+
# else
|
167
|
+
# ancestry.to_s.split("/").map { |ancestor_id| self.class.find(ancestor_id).send(:slug) }
|
168
|
+
# end
|
169
|
+
# old_slugs << send(:slug)
|
170
|
+
# else
|
171
|
+
# send(:slug_was)
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
code =<<-method
|
175
|
+
def to_#{to}_will
|
176
|
+
if respond_to?(:ancestry)
|
177
|
+
new_slugs = if #{cache_tree}
|
178
|
+
ancestry.to_s.split("/").map { |ancestor_id| self.class.cached_#{to}(ancestor_id.to_i) }
|
179
|
+
else
|
180
|
+
ancestry.to_s.split("/").map { |ancestor_id| self.class.find(ancestor_id).send(:#{to}) }
|
181
|
+
end
|
182
|
+
new_slugs << send(:#{to}).send(:#{formatter})
|
183
|
+
else
|
184
|
+
send(:#{to}).send(:#{formatter})
|
185
|
+
end
|
186
|
+
end
|
187
|
+
method
|
188
|
+
class_eval(code)
|
189
|
+
end
|
128
190
|
end
|
129
191
|
end
|
data/lib/slugable/version.rb
CHANGED
@@ -3,24 +3,37 @@ require "spec_helper"
|
|
3
3
|
ActiveRecord::Base.send :extend, Slugable::HasSlug
|
4
4
|
|
5
5
|
class Item < ActiveRecord::Base
|
6
|
-
|
6
|
+
attr_accessible :name, :slug
|
7
7
|
|
8
8
|
has_slug
|
9
9
|
end
|
10
10
|
|
11
11
|
class Page < ActiveRecord::Base
|
12
|
-
|
12
|
+
attr_accessible :title, :seo_url
|
13
13
|
|
14
14
|
has_slug :from => :title, :to => :seo_url
|
15
15
|
end
|
16
16
|
|
17
17
|
class Category < ActiveRecord::Base
|
18
|
-
|
18
|
+
attr_accessible :name, :slug
|
19
19
|
|
20
20
|
has_ancestry
|
21
21
|
has_slug
|
22
22
|
end
|
23
23
|
|
24
|
+
class TreeItem < ActiveRecord::Base
|
25
|
+
attr_accessible :name, :slug
|
26
|
+
|
27
|
+
has_ancestry
|
28
|
+
has_slug :cache_tree => false
|
29
|
+
end
|
30
|
+
|
31
|
+
class Product < ActiveRecord::Base
|
32
|
+
attr_accessible :name, :slug
|
33
|
+
|
34
|
+
has_slug :formatter => :my_formatter
|
35
|
+
end
|
36
|
+
|
24
37
|
|
25
38
|
describe Slugable::HasSlug do
|
26
39
|
before(:each) do
|
@@ -37,11 +50,14 @@ describe Slugable::HasSlug do
|
|
37
50
|
end
|
38
51
|
|
39
52
|
it "should fill in slug parameter from attribute name and parametrize it" do
|
40
|
-
|
53
|
+
name = "my name is"
|
54
|
+
name.should_receive(:parameterize).and_return("my-name-is")
|
55
|
+
|
56
|
+
item = Item.create!(:name => name)
|
41
57
|
item.slug.should eq "my-name-is"
|
42
58
|
end
|
43
59
|
|
44
|
-
it "should fill in slug from attribute name if parameterize version of slug is blank"
|
60
|
+
it "should fill in slug from attribute name if parameterize version of slug is blank" do
|
45
61
|
item = Item.create!(:name => "my name is", :slug => "/")
|
46
62
|
item.slug.should eq "my-name-is"
|
47
63
|
end
|
@@ -75,6 +91,25 @@ describe Slugable::HasSlug do
|
|
75
91
|
page = Page.create!(:title => "my name is", :seo_url => "my url")
|
76
92
|
page.seo_url.should eq "my-url"
|
77
93
|
end
|
94
|
+
|
95
|
+
it "should be able to change parameterize method" do
|
96
|
+
name = "product"
|
97
|
+
name.should_receive(:my_formatter).and_return("hello")
|
98
|
+
product = Product.create!(:name => "my name is", :slug => name)
|
99
|
+
product.slug.should eq "hello"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should be able to disable tree caching" do
|
103
|
+
tree_item = TreeItem.create!(:name => "my name is", :parent => TreeItem.create!(:name => "root"))
|
104
|
+
tree_item.should_receive(:path).and_return([])
|
105
|
+
tree_item.to_slug
|
106
|
+
tree_item.should_receive(:path).and_return([])
|
107
|
+
tree_item.to_slug
|
108
|
+
|
109
|
+
tree_item = Category.create!(:name => "my name is", :parent => Category.create!(:name => "root"))
|
110
|
+
tree_item.should_not_receive(:path)
|
111
|
+
tree_item.to_slug
|
112
|
+
end
|
78
113
|
end
|
79
114
|
|
80
115
|
describe "to_slug" do
|
@@ -112,6 +147,82 @@ describe Slugable::HasSlug do
|
|
112
147
|
end
|
113
148
|
end
|
114
149
|
|
150
|
+
describe "to_slug_was" do
|
151
|
+
context "default options" do
|
152
|
+
it "should define method to_slug_was" do
|
153
|
+
Item.new.should respond_to :to_slug_was
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should return old slug in string" do
|
157
|
+
item = Item.create!(:name => "my name is", :slug => "my-url")
|
158
|
+
item.slug = "new-slug"
|
159
|
+
item.to_slug_was.should eq "my-url"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "given options" do
|
164
|
+
it "should define method to_seo_url_was" do
|
165
|
+
Page.new.should respond_to :to_seo_url_was
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should return future slug in string" do
|
169
|
+
page = Page.create!(:title => "my name is", :seo_url => "my-url")
|
170
|
+
page.seo_url = "hello-world"
|
171
|
+
page.to_seo_url_was.should eq "my-url"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "ancestry model" do
|
176
|
+
it "should return array of old slugs" do
|
177
|
+
root = Category.create!(:name => "root", :slug => "root")
|
178
|
+
child = Category.new(:name => "child", :slug => "child")
|
179
|
+
child.save!
|
180
|
+
|
181
|
+
child.parent = root
|
182
|
+
child.slug = "moved"
|
183
|
+
child.to_slug_was.should eq ["child"]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "to_slug_will" do
|
189
|
+
context "default options" do
|
190
|
+
it "should define method to_slug_will" do
|
191
|
+
Item.new.should respond_to :to_slug_will
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should return future slug in string" do
|
195
|
+
item = Item.create!(:name => "my name is", :slug => "my-url")
|
196
|
+
item.slug = "new slug"
|
197
|
+
item.to_slug_will.should eq "new-slug"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "given options" do
|
202
|
+
it "should define method to_seo_url_will" do
|
203
|
+
Page.new.should respond_to :to_seo_url_will
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should return slug in string" do
|
207
|
+
page = Page.create!(:title => "my name is", :seo_url => "my-url")
|
208
|
+
page.seo_url = "hello world"
|
209
|
+
page.to_seo_url_will.should eq "hello-world"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "ancestry model" do
|
214
|
+
it "should return array of slugs" do
|
215
|
+
root = Category.create!(:name => "root", :slug => "root")
|
216
|
+
child = Category.new(:name => "child", :slug => "child")
|
217
|
+
child.save!
|
218
|
+
|
219
|
+
child.parent = root
|
220
|
+
child.slug = "move d"
|
221
|
+
child.to_slug_will.should eq ["root", "move-d"]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
115
226
|
describe "ancestry methods" do
|
116
227
|
describe "all_slugs" do
|
117
228
|
it "ancestry model class should respond to all_slugs" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slugable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -157,12 +157,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
157
157
|
- - ! '>='
|
158
158
|
- !ruby/object:Gem::Version
|
159
159
|
version: '0'
|
160
|
+
segments:
|
161
|
+
- 0
|
162
|
+
hash: 2198894308197098802
|
160
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
164
|
none: false
|
162
165
|
requirements:
|
163
166
|
- - ! '>='
|
164
167
|
- !ruby/object:Gem::Version
|
165
168
|
version: '0'
|
169
|
+
segments:
|
170
|
+
- 0
|
171
|
+
hash: 2198894308197098802
|
166
172
|
requirements: []
|
167
173
|
rubyforge_project:
|
168
174
|
rubygems_version: 1.8.24
|