slugalicious 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/{README.textile → README.md} +67 -61
- data/lib/slugalicious.rb +30 -29
- data/slugalicious.gemspec +14 -9
- data/templates/slug.rb +20 -14
- metadata +53 -41
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 79cad2b0eaf39ef1dfb8aff6d8ae448a64ff13e2
|
4
|
+
data.tar.gz: cc1b3ce56cb2a89e02534e47daeaafeea411cda7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: de5185ea3c49a8a7d6bdc5dccf37c9d71f027b2dc24e28fa82733ee423e39fcc114875f7dfd9e9797e59901742bdc03356c7df5d0d5a91dbd066ab207cf625ff
|
7
|
+
data.tar.gz: 9996a8eda4f63d23bfa5c71a9f05d08c68871f91a0ba95d32ead34e6fa8698baa5bda5435ddbfc05d19f1ba8257c78e1bf36b0617c888a553c672a5f385af855
|
@@ -1,23 +1,27 @@
|
|
1
|
-
|
1
|
+
Slugalicious -- Easy and powerful URL slugging for Rails 4
|
2
|
+
==========================================================
|
2
3
|
|
3
|
-
_
|
4
|
+
_(no monkey-patching required)_
|
4
5
|
|
5
|
-
|
|
6
|
-
|
7
|
-
|
|
6
|
+
| | |
|
7
|
+
|:------------|:--------------------------------|
|
8
|
+
| **Author** | Tim Morgan |
|
9
|
+
| **Version** | 2.1 (Jul 9, 2013) |
|
10
|
+
| **License** | Released under the MIT license. |
|
8
11
|
|
9
|
-
|
12
|
+
### Note about version 2.0
|
10
13
|
|
11
14
|
Version 2.0 is so-versioned because it breaks the API for previous versions.
|
12
15
|
Previously, where you would have used
|
13
16
|
{Slugalicious::ClassMethods#find_from_slug find_from_slug}, you would now use
|
14
17
|
{Slugalicious::ClassMethods#find_from_slug! find_from_slug!}. The old method now
|
15
|
-
returns
|
18
|
+
returns `nil` when an object is not found, rather than raising an exception.
|
16
19
|
|
17
|
-
|
20
|
+
About
|
21
|
+
-----
|
18
22
|
|
19
23
|
Slugalicious is an easy-to-use slugging library that helps you generate pretty
|
20
|
-
URLs for your ActiveRecord objects. It's built for Rails
|
24
|
+
URLs for your ActiveRecord objects. It's built for Rails 4 and is cordoned off
|
21
25
|
in a monkey patching-free zone.
|
22
26
|
|
23
27
|
Slugalicious is easy to use and powerful enough to cover all of the most common
|
@@ -31,68 +35,70 @@ generates a unique slug. If all else fails, Slugalicious will fall back on a
|
|
31
35
|
less pretty but guaranteed-unique backup slug generation strategy.
|
32
36
|
|
33
37
|
Slugalicious works with the Stringex Ruby library, meaning you get meaningful
|
34
|
-
slugs via the
|
38
|
+
slugs via the `String#to_url` method. Below are two examples of how powerful
|
35
39
|
Stringex is:
|
36
40
|
|
37
|
-
|
41
|
+
```` ruby
|
38
42
|
"$6 Dollar Burger".to_url #=> "six-dollar-burger"
|
39
43
|
"新年好".to_url #=> "xin-nian-hao"
|
40
|
-
|
44
|
+
````
|
41
45
|
|
42
|
-
|
46
|
+
Installation
|
47
|
+
------------
|
43
48
|
|
44
|
-
*Important Note:* Slugalicious is written for Rails
|
49
|
+
*Important Note:* Slugalicious is written for Rails 4.0 and Ruby 1.9 only.
|
45
50
|
|
46
|
-
Firstly, add the gem to your Rails project's
|
51
|
+
Firstly, add the gem to your Rails project's `Gemfile`:
|
47
52
|
|
48
|
-
|
53
|
+
```` ruby
|
49
54
|
gem 'slugalicious'
|
50
|
-
|
55
|
+
````
|
51
56
|
|
52
|
-
Next, use the generator to add the
|
57
|
+
Next, use the generator to add the `Slug` model and its migration to your
|
53
58
|
project:
|
54
59
|
|
55
|
-
|
60
|
+
```` sh
|
56
61
|
rails generate slugalicious
|
57
|
-
|
62
|
+
````
|
58
63
|
|
59
64
|
Then run the migration to set up your database.
|
60
65
|
|
61
|
-
|
66
|
+
Usage
|
67
|
+
-----
|
62
68
|
|
63
|
-
For any model you want to slug, include the
|
64
|
-
|
69
|
+
For any model you want to slug, include the `Slugalicious` module and call
|
70
|
+
`slugged`:
|
65
71
|
|
66
|
-
|
72
|
+
```` ruby
|
67
73
|
class User < ActiveRecord::Base
|
68
74
|
include Slugalicious
|
69
75
|
slugged ->(user) { "#{user.first_name} #{user.last_name}" }
|
70
76
|
end
|
71
|
-
|
77
|
+
````
|
72
78
|
|
73
|
-
Doing this sets the
|
74
|
-
URLs using your models. You can use the
|
79
|
+
Doing this sets the `to_param` method, so you can go ahead and start generating
|
80
|
+
URLs using your models. You can use the `find_from_slug` method to load a record
|
75
81
|
from a slug:
|
76
82
|
|
77
|
-
|
83
|
+
```` ruby
|
78
84
|
user = User.find_from_slug(params[:id])
|
79
|
-
|
85
|
+
````
|
80
86
|
|
81
|
-
|
87
|
+
### Multiple slug generators
|
82
88
|
|
83
|
-
The
|
89
|
+
The `slugged` method takes a list of method names (as symbols) or `Procs` that
|
84
90
|
each attempt to generate a slug. Each of these generators is tried in order
|
85
91
|
until a unique slug is generated. (The output of each of these generators is run
|
86
92
|
through the slugifier to convert it to a URL-safe string. The slugifier is by
|
87
|
-
default
|
93
|
+
default `String#to_url`, provided by the Stringex gem.)
|
88
94
|
|
89
|
-
So, if we had our
|
95
|
+
So, if we had our `User` class, and we first wanted to slug by last name only,
|
90
96
|
but then add in the first name if two people share a last name, we'd call
|
91
|
-
|
97
|
+
`slugged` like so:
|
92
98
|
|
93
|
-
|
99
|
+
```` ruby
|
94
100
|
slugged :last_name, ->(user) { "#{user.first_name} #{user.last_name}" }
|
95
|
-
|
101
|
+
````
|
96
102
|
|
97
103
|
In the event that none of these generators manages to make a unique slug, a
|
98
104
|
fallback generator is used. This generator prepends the ID of the record, making
|
@@ -102,20 +108,20 @@ another user with the same name, and that user will get the slug
|
|
102
108
|
"sancho-sample;2". The semicolon is the default ID separator (and it can be
|
103
109
|
overridden).
|
104
110
|
|
105
|
-
|
111
|
+
### Scoped slugs
|
106
112
|
|
107
113
|
Slugs must normally be unique for a single model type. Thus, if you have a
|
108
|
-
|
114
|
+
`User` named Hammer and a `Product` named hammer, they can both share the
|
109
115
|
"hammer" slug.
|
110
116
|
|
111
117
|
If you want to decrease the uniqueness scope of a slug, you can do so with the
|
112
|
-
|
113
|
-
of a
|
118
|
+
`:scope` option on the `slugged` method. Let's say you wanted to limit the scope
|
119
|
+
of a `Product`'s slug to its associated `Department`; that way you could have a
|
114
120
|
product named "keyboard" in both the Computer Supplies and the Music Supplies
|
115
|
-
departments. To do so, override the
|
116
|
-
symbol) or a
|
121
|
+
departments. To do so, override the `:scope` option with a method name (as
|
122
|
+
symbol) or a `Proc` that limits the scope of the uniqueness requirement:
|
117
123
|
|
118
|
-
|
124
|
+
```` ruby
|
119
125
|
class Product < ActiveRecord::Base
|
120
126
|
include Slugalicious
|
121
127
|
belongs_to :department
|
@@ -127,7 +133,7 @@ class Product < ActiveRecord::Base
|
|
127
133
|
department.name.to_url + "/"
|
128
134
|
end
|
129
135
|
end
|
130
|
-
|
136
|
+
````
|
131
137
|
|
132
138
|
Now, your computer keyboard's slug will be "computer-supplies/keyboard" and your
|
133
139
|
piano keyboard's slug will be "music-supplies/keyboard". There's an important
|
@@ -135,30 +141,30 @@ thing to notice here: The method or proc you use to scope the slug must return a
|
|
135
141
|
proper URL substring. That typically means you need to URL-escape it and add a
|
136
142
|
slash at the end, as shown in the example above.
|
137
143
|
|
138
|
-
When you call
|
144
|
+
When you call `to_param` on your piano keyboard, instead of just "keyboard", you
|
139
145
|
will get "music-supplies/keyboard". Likewise, you can use the
|
140
|
-
|
146
|
+
`find_from_slug_path` method to find a record from its full path, slug and scope
|
141
147
|
included. You would usually use this method in conjunction with route globbing.
|
142
|
-
For example, we could set up our
|
148
|
+
For example, we could set up our `routes.rb` file like so:
|
143
149
|
|
144
|
-
|
150
|
+
```` ruby
|
145
151
|
get '/products/*path', 'products#show', as: :products
|
146
|
-
|
152
|
+
````
|
147
153
|
|
148
|
-
Then, in our
|
154
|
+
Then, in our `ProductsController`, we load the product from the path slug like
|
149
155
|
so:
|
150
156
|
|
151
|
-
|
157
|
+
```` ruby
|
152
158
|
def find_product
|
153
159
|
@product = Product.find_from_slug_path(params[:path])
|
154
160
|
end
|
155
|
-
|
161
|
+
````
|
156
162
|
|
157
|
-
This is why it's very convenient to have your
|
163
|
+
This is why it's very convenient to have your `:scope` method/proc not only
|
158
164
|
return the uniqueness constraint, but also the scoped portion of the URL
|
159
165
|
preceding the slug.
|
160
166
|
|
161
|
-
|
167
|
+
### Altering and expiring slugs
|
162
168
|
|
163
169
|
When a model is created, it gets one slug, marked as the active slug (by
|
164
170
|
default). This slug is the first generator that produces a unique slug string.
|
@@ -169,7 +175,7 @@ that slug is made the active slug. (Priority goes to the first slug generator
|
|
169
175
|
that produces an existing slug [active or inactive]).
|
170
176
|
|
171
177
|
If none of the slug generators generates a known, existing slug belonging to the
|
172
|
-
object, then the first unique slug is used. A new
|
178
|
+
object, then the first unique slug is used. A new `Slug` instance is created and
|
173
179
|
marked as active, and any other slugs belonging to the object are marked as
|
174
180
|
inactive.
|
175
181
|
|
@@ -181,9 +187,9 @@ current.
|
|
181
187
|
A common application of this is to have inactive slugs 301-redirect to the
|
182
188
|
active slug, as a way of both updating search engines' indexes and ensuring that
|
183
189
|
people know the URL has changed. As an example of how do this, we alter the
|
184
|
-
|
190
|
+
`find_product` method shown above to be like so:
|
185
191
|
|
186
|
-
|
192
|
+
```` ruby
|
187
193
|
def find_product
|
188
194
|
@product = Product.find_from_slug_path(params[:path])
|
189
195
|
unless @product.active_slug?(params[:path].split('/').last)
|
@@ -192,7 +198,7 @@ def find_product
|
|
192
198
|
end
|
193
199
|
return true
|
194
200
|
end
|
195
|
-
|
201
|
+
````
|
196
202
|
|
197
203
|
The old URL will remain indefinitely, but users who hit it will be redirected to
|
198
204
|
the new URL. Ideally, links to the old URL will be replaced over time with links
|
@@ -207,9 +213,9 @@ keyboard), you'd find its slug is "keyboard;2" or similar.
|
|
207
213
|
To prevent the slug namespace from becoming more and more polluted over time,
|
208
214
|
websites generally expire inactive slugs after a period of time. To do this in
|
209
215
|
Slugalicious, write a task that periodically checks for and deletes old,
|
210
|
-
inactive
|
216
|
+
inactive `Slug` records. Such a task could be invoked through a cron job, for
|
211
217
|
instance. An example:
|
212
218
|
|
213
|
-
|
219
|
+
```` ruby
|
214
220
|
Slug.inactive.where([ "created_at < ?", 30.days.ago ]).delete_all
|
215
|
-
|
221
|
+
````
|
data/lib/slugalicious.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'slugalicious_generator'
|
2
2
|
require 'stringex'
|
3
3
|
|
4
|
-
# Adds the
|
4
|
+
# Adds the `slugged` method to an `ActiveRecord::Base` subclass. You can then
|
5
5
|
# call this method to add slugging support to your model. See the
|
6
6
|
# {ClassMethods#slugged} method for more details.
|
7
7
|
#
|
@@ -47,7 +47,7 @@ module Slugalicious
|
|
47
47
|
# not found.
|
48
48
|
|
49
49
|
def find_from_slug(slug, scope=nil)
|
50
|
-
Slug.from_slug(self, scope, slug).first.try(:sluggable)
|
50
|
+
Slug.from_slug(self, scope, slug).first.try!(:sluggable)
|
51
51
|
end
|
52
52
|
|
53
53
|
# Locates a record from a given path, that consists of a slug and its scope,
|
@@ -67,17 +67,17 @@ module Slugalicious
|
|
67
67
|
protected
|
68
68
|
|
69
69
|
# Call this method to indicate that your model uses slugging. Pass a list of
|
70
|
-
#
|
70
|
+
# **slug generators**: either symbols (method names) or procs that return
|
71
71
|
# strings. These strings will be used to generate the slug. You must pass at
|
72
72
|
# least one generator. If you pass more than one, the first one that returns
|
73
73
|
# a unique slug will be used.
|
74
74
|
#
|
75
75
|
# The generator does not need to sanitize or parameterize its output; the
|
76
|
-
#
|
76
|
+
# `:slugifier` option can be used to override the default parameterization.
|
77
77
|
#
|
78
78
|
# In the event that no generator returns a unique slug, the slug returned by
|
79
79
|
# the last generator will have the ID of the record appended to it. The ID
|
80
|
-
# and the slug will be separated by the
|
80
|
+
# and the slug will be separated by the `:id_separator` option (semicolon by
|
81
81
|
# default). _This_ slug is hopefully unique, because if not, an exception is
|
82
82
|
# raised.
|
83
83
|
#
|
@@ -86,28 +86,28 @@ module Slugalicious
|
|
86
86
|
#
|
87
87
|
# h2. Scopes
|
88
88
|
#
|
89
|
-
# You can scope your slugs to certain URL subpaths using the
|
90
|
-
# option. The
|
89
|
+
# You can scope your slugs to certain URL subpaths using the `:scope`
|
90
|
+
# option. The `:scope:` option takes a method name or a `Proc` that, when
|
91
91
|
# run, returns a string that scopes the uniqueness constraint of a slug.
|
92
92
|
# Rather than being globally unique, the slug must only be unique among
|
93
93
|
# other slugs that share the same scope.
|
94
94
|
#
|
95
|
-
#
|
95
|
+
# **Important note:** The method or `Proc` that you use for the `:scope`
|
96
96
|
# option should return the portion of the URL preceding the slug, _slash
|
97
|
-
# included_. Let's say you have slugged your
|
97
|
+
# included_. Let's say you have slugged your `User` model's `login` field,
|
98
98
|
# and you have two scopes: customers and merchants. In that case, you would
|
99
|
-
# want the
|
99
|
+
# want the `:scope` method/proc to return either "clients/" or "merchants/".
|
100
100
|
#
|
101
|
-
# The string returned by the
|
102
|
-
# URL to an object. If you have a client
|
103
|
-
# call to
|
101
|
+
# The string returned by the `:scope` option will be used to build the full
|
102
|
+
# URL to an object. If you have a client `User` with login "fancylad", a
|
103
|
+
# call to `to_param` will return "clients/fancyland". The scope portion of
|
104
104
|
# that URL path is used un-sanitized, un-escaped, and un-processed. It is
|
105
105
|
# therefore up to _you_ to ensure your scopes are valid URL strings, using
|
106
|
-
# say
|
106
|
+
# say `String#to_url` (included as part of this gem).
|
107
107
|
#
|
108
108
|
# @overload slugged(generator, ..., options={})
|
109
|
-
# @param [Proc, Symbol] generator If it's a
|
110
|
-
# that will be called that will return a
|
109
|
+
# @param [Proc, Symbol] generator If it's a `Symbol`, indicates a method
|
110
|
+
# that will be called that will return a `String` to be used for the
|
111
111
|
# slug.
|
112
112
|
# @param [Hash] options Additonal options that control slug generation.
|
113
113
|
# @option options [Proc] :slugifier (&:to_url) A proc that, when given a
|
@@ -116,13 +116,13 @@ module Slugalicious
|
|
116
116
|
# the "last-resort" slug between the slug and the model ID. This should
|
117
117
|
# be an URL-safe character that would never be produced by your
|
118
118
|
# slugifier.
|
119
|
-
# @option options [Symbol, Proc] :scope A method name or
|
119
|
+
# @option options [Symbol, Proc] :scope A method name or `Proc` to run
|
120
120
|
# (receives the object being slugged) that returns a string. Slugs must
|
121
121
|
# be unique across all objects for which this method/proc returns the
|
122
122
|
# same value. If not provided, slugs must be globally unique for this
|
123
123
|
# model. The string returned should be equal to the portion of the URL
|
124
124
|
# path that precedes the slug.
|
125
|
-
# @option options [Array<String>, String] :blacklist ([ 'new', 'edit', 'delete' ])
|
125
|
+
# @option options [Array<String>, String] :blacklist ([ 'new', 'edit', 'delete', 'destroy' ])
|
126
126
|
# A list of slugs that are disallowed. You would use this to prevent
|
127
127
|
# slugs from sharing the same name as actions in your resource
|
128
128
|
# controller.
|
@@ -145,28 +145,28 @@ module Slugalicious
|
|
145
145
|
elsif options[:scope] then
|
146
146
|
raise ArgumentError, ":scope must be a symbol or proc"
|
147
147
|
end
|
148
|
-
self._slug_blacklist = Array.wrap(options[:blacklist] || %w( new edit delete ))
|
148
|
+
self._slug_blacklist = Array.wrap(options[:blacklist] || %w( new edit delete destroy ))
|
149
149
|
|
150
150
|
after_save :make_slug
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
154
|
def slug_object
|
155
|
-
slugs.loaded? ? slugs.detect(&:active) : slugs.active.first
|
155
|
+
slugs.loaded? ? slugs.detect(&:active?) : slugs.active.first
|
156
156
|
end
|
157
157
|
private :slug_object
|
158
158
|
|
159
|
-
# @return [String, nil] The slug for this object, or
|
159
|
+
# @return [String, nil] The slug for this object, or `nil` if none has been
|
160
160
|
# assigned.
|
161
161
|
|
162
162
|
def slug
|
163
163
|
Rails.cache.fetch("Slug/#{self.class.to_s}/#{id}/slug") do
|
164
|
-
slug_object.try(:slug)
|
164
|
+
slug_object.try!(:slug)
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
168
168
|
# @return [String, nil] The full slug and path for this object, with scope
|
169
|
-
# included, or
|
169
|
+
# included, or `nil` if none has been assigned.
|
170
170
|
|
171
171
|
def slug_with_path
|
172
172
|
Rails.cache.fetch("Slug/#{self.class.to_s}/#{id}/slug_with_path") do
|
@@ -175,9 +175,9 @@ module Slugalicious
|
|
175
175
|
end
|
176
176
|
|
177
177
|
# @param [String] slug A slug for this object.
|
178
|
-
# @return [true, false, nil]
|
179
|
-
# (should not redirect),
|
180
|
-
#
|
178
|
+
# @return [true, false, nil] `true` if the slug is the currently active one
|
179
|
+
# (should not redirect), `false` if it's inactive (should redirect), and
|
180
|
+
# `nil` if it's not a known slug for the object (should 404).
|
181
181
|
|
182
182
|
def active_slug?(slug)
|
183
183
|
@active_slug ||= begin
|
@@ -200,7 +200,7 @@ module Slugalicious
|
|
200
200
|
slugs_in_use = if slugs.loaded? then
|
201
201
|
slugs.map(&:slug)
|
202
202
|
else
|
203
|
-
slugs.select(:slug).
|
203
|
+
slugs.select(:slug).map(&:slug)
|
204
204
|
end
|
205
205
|
|
206
206
|
# grab a list of all potential slugs derived from the generators
|
@@ -232,7 +232,7 @@ module Slugalicious
|
|
232
232
|
if self.class._slug_scope then
|
233
233
|
scope = scope.where(scope: self.class._slug_scope[self])
|
234
234
|
end
|
235
|
-
taken_slug_objects = scope
|
235
|
+
taken_slug_objects = scope
|
236
236
|
|
237
237
|
# subtract them out from all the potential slugs to make the available slugs
|
238
238
|
available_slugs = potential_slugs - taken_slug_objects.map(&:slug)
|
@@ -243,7 +243,8 @@ module Slugalicious
|
|
243
243
|
Slug.create!(sluggable: self,
|
244
244
|
slug: available_slugs.first,
|
245
245
|
active: true,
|
246
|
-
scope: self.class._slug_scope.try(:call, self))
|
246
|
+
scope: self.class._slug_scope.try!(:call, self))
|
247
|
+
slugs(true)
|
247
248
|
end
|
248
249
|
|
249
250
|
@active_slug = nil
|
data/slugalicious.gemspec
CHANGED
@@ -5,20 +5,19 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "slugalicious"
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Tim Morgan"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2013-07-09"
|
13
13
|
s.description = "Slugalicious adds simple and powerful slugging to your ActiveRecord models."
|
14
14
|
s.email = "git@timothymorgan.info"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.
|
17
|
+
"README.md"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
"LICENSE",
|
21
|
-
"README.textile",
|
22
21
|
"lib/slugalicious.rb",
|
23
22
|
"lib/slugalicious_generator.rb",
|
24
23
|
"slugalicious.gemspec",
|
@@ -28,37 +27,43 @@ Gem::Specification.new do |s|
|
|
28
27
|
s.homepage = "http://github.com/riscfuture/slugalicious"
|
29
28
|
s.require_paths = ["lib"]
|
30
29
|
s.required_ruby_version = Gem::Requirement.new(">= 1.9")
|
31
|
-
s.rubygems_version = "
|
30
|
+
s.rubygems_version = "2.0.3"
|
32
31
|
s.summary = "Easy-to-use and powerful slugging for Rails 3"
|
33
32
|
|
34
33
|
if s.respond_to? :specification_version then
|
35
|
-
s.specification_version =
|
34
|
+
s.specification_version = 4
|
36
35
|
|
37
36
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
38
|
-
s.add_runtime_dependency(%q<rails>, [">=
|
37
|
+
s.add_runtime_dependency(%q<rails>, [">= 4.0"])
|
39
38
|
s.add_runtime_dependency(%q<stringex>, [">= 0"])
|
40
39
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
41
40
|
s.add_development_dependency(%q<yard>, [">= 0"])
|
41
|
+
s.add_development_dependency(%q<redcarpet>, [">= 0"])
|
42
42
|
s.add_development_dependency(%q<RedCloth>, [">= 0"])
|
43
43
|
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
44
44
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
45
|
+
s.add_development_dependency(%q<factory_girl>, [">= 0"])
|
45
46
|
else
|
46
|
-
s.add_dependency(%q<rails>, [">=
|
47
|
+
s.add_dependency(%q<rails>, [">= 4.0"])
|
47
48
|
s.add_dependency(%q<stringex>, [">= 0"])
|
48
49
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
49
50
|
s.add_dependency(%q<yard>, [">= 0"])
|
51
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
50
52
|
s.add_dependency(%q<RedCloth>, [">= 0"])
|
51
53
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
52
54
|
s.add_dependency(%q<rspec>, [">= 0"])
|
55
|
+
s.add_dependency(%q<factory_girl>, [">= 0"])
|
53
56
|
end
|
54
57
|
else
|
55
|
-
s.add_dependency(%q<rails>, [">=
|
58
|
+
s.add_dependency(%q<rails>, [">= 4.0"])
|
56
59
|
s.add_dependency(%q<stringex>, [">= 0"])
|
57
60
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
58
61
|
s.add_dependency(%q<yard>, [">= 0"])
|
62
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
59
63
|
s.add_dependency(%q<RedCloth>, [">= 0"])
|
60
64
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
61
65
|
s.add_dependency(%q<rspec>, [">= 0"])
|
66
|
+
s.add_dependency(%q<factory_girl>, [">= 0"])
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
data/templates/slug.rb
CHANGED
@@ -5,15 +5,21 @@
|
|
5
5
|
# redirect purposes. Once a slug is old enough, it is deleted and its value can
|
6
6
|
# be used for new records (no longer redirects).
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# Associations
|
9
|
+
# ============
|
9
10
|
#
|
10
|
-
# |
|
11
|
+
# | | |
|
12
|
+
# |:------------|:--------------------------------------|
|
13
|
+
# | `sluggable` | The record that this slug references. |
|
11
14
|
#
|
12
|
-
#
|
15
|
+
# Properties
|
16
|
+
# ==========
|
13
17
|
#
|
14
|
-
# |
|
15
|
-
#
|
16
|
-
# |
|
18
|
+
# | | |
|
19
|
+
# |:---------|:-------------------------------------------------------------------------------------------------|
|
20
|
+
# | `slug` | The slug, lowercased and normalized. Slugs must be unique to their `sluggable_type` and `scope`. |
|
21
|
+
# | `active` | Whether this is the most recently generated slug for the sluggable. |
|
22
|
+
# | `scope` | Freeform data scoping this slug to a certain subset of records within the model. |
|
17
23
|
|
18
24
|
class Slug < ActiveRecord::Base
|
19
25
|
belongs_to :sluggable, polymorphic: true
|
@@ -27,8 +33,8 @@ class Slug < ActiveRecord::Base
|
|
27
33
|
scope :from_slug, ->(klass, scope, slug) {
|
28
34
|
where(sluggable_type: klass.to_s, slug: slug, scope: scope)
|
29
35
|
}
|
30
|
-
scope :active, where(active: true)
|
31
|
-
scope :inactive, where(active: false)
|
36
|
+
scope :active, -> { where(active: true) }
|
37
|
+
scope :inactive, -> { where(active: false) }
|
32
38
|
|
33
39
|
validates :sluggable_type,
|
34
40
|
presence: true
|
@@ -38,22 +44,22 @@ class Slug < ActiveRecord::Base
|
|
38
44
|
validates :slug,
|
39
45
|
presence: true,
|
40
46
|
length: { maximum: 126 },
|
41
|
-
uniqueness: { case_sensitive: false, scope: [ :scope, :sluggable_type ] }
|
47
|
+
uniqueness: { case_sensitive: false, scope: [ :scope, :sluggable_type ] }
|
42
48
|
validates :scope,
|
43
49
|
length: { maximum: 126 },
|
44
|
-
|
50
|
+
allow_nil: true
|
45
51
|
validate :one_active_slug_per_object
|
46
|
-
|
52
|
+
|
47
53
|
after_save :invalidate_cache
|
48
54
|
after_destroy :invalidate_cache
|
49
|
-
|
55
|
+
|
50
56
|
# Marks a slug as active and deactivates all other slugs assigned to the
|
51
57
|
# record.
|
52
58
|
|
53
59
|
def activate!
|
54
60
|
self.class.transaction do
|
55
61
|
Slug.for(sluggable_type, sluggable_id).update_all(active: false)
|
56
|
-
|
62
|
+
update_column :active, true
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
@@ -63,7 +69,7 @@ class Slug < ActiveRecord::Base
|
|
63
69
|
return unless new_record? or (active? and active_changed?)
|
64
70
|
errors.add(:active, :one_per_sluggable) if active? and Slug.active.for(sluggable_type, sluggable_id).count > 0
|
65
71
|
end
|
66
|
-
|
72
|
+
|
67
73
|
def invalidate_cache
|
68
74
|
Rails.cache.delete "Slug/#{sluggable_type}/#{sluggable_id}/slug"
|
69
75
|
Rails.cache.delete "Slug/#{sluggable_type}/#{sluggable_id}/slug_with_path"
|
metadata
CHANGED
@@ -1,126 +1,139 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slugalicious
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
5
|
-
prerelease:
|
4
|
+
version: 2.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Tim Morgan
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-07-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rails
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
19
|
+
version: '4.0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
26
|
+
version: '4.0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: stringex
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: jeweler
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - '>='
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - '>='
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: yard
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - '>='
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: redcarpet
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
76
81
|
- !ruby/object:Gem::Version
|
77
82
|
version: '0'
|
78
83
|
- !ruby/object:Gem::Dependency
|
79
84
|
name: RedCloth
|
80
85
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
86
|
requirements:
|
83
|
-
- -
|
87
|
+
- - '>='
|
84
88
|
- !ruby/object:Gem::Version
|
85
89
|
version: '0'
|
86
90
|
type: :development
|
87
91
|
prerelease: false
|
88
92
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
93
|
requirements:
|
91
|
-
- -
|
94
|
+
- - '>='
|
92
95
|
- !ruby/object:Gem::Version
|
93
96
|
version: '0'
|
94
97
|
- !ruby/object:Gem::Dependency
|
95
98
|
name: sqlite3
|
96
99
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
100
|
requirements:
|
99
|
-
- -
|
101
|
+
- - '>='
|
100
102
|
- !ruby/object:Gem::Version
|
101
103
|
version: '0'
|
102
104
|
type: :development
|
103
105
|
prerelease: false
|
104
106
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
107
|
requirements:
|
107
|
-
- -
|
108
|
+
- - '>='
|
108
109
|
- !ruby/object:Gem::Version
|
109
110
|
version: '0'
|
110
111
|
- !ruby/object:Gem::Dependency
|
111
112
|
name: rspec
|
112
113
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: factory_girl
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
116
130
|
- !ruby/object:Gem::Version
|
117
131
|
version: '0'
|
118
132
|
type: :development
|
119
133
|
prerelease: false
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
135
|
requirements:
|
123
|
-
- -
|
136
|
+
- - '>='
|
124
137
|
- !ruby/object:Gem::Version
|
125
138
|
version: '0'
|
126
139
|
description: Slugalicious adds simple and powerful slugging to your ActiveRecord models.
|
@@ -129,37 +142,36 @@ executables: []
|
|
129
142
|
extensions: []
|
130
143
|
extra_rdoc_files:
|
131
144
|
- LICENSE
|
132
|
-
- README.
|
145
|
+
- README.md
|
133
146
|
files:
|
134
147
|
- LICENSE
|
135
|
-
- README.textile
|
136
148
|
- lib/slugalicious.rb
|
137
149
|
- lib/slugalicious_generator.rb
|
138
150
|
- slugalicious.gemspec
|
139
151
|
- templates/create_slugs.rb
|
140
152
|
- templates/slug.rb
|
153
|
+
- README.md
|
141
154
|
homepage: http://github.com/riscfuture/slugalicious
|
142
155
|
licenses: []
|
156
|
+
metadata: {}
|
143
157
|
post_install_message:
|
144
158
|
rdoc_options: []
|
145
159
|
require_paths:
|
146
160
|
- lib
|
147
161
|
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
-
none: false
|
149
162
|
requirements:
|
150
|
-
- -
|
163
|
+
- - '>='
|
151
164
|
- !ruby/object:Gem::Version
|
152
165
|
version: '1.9'
|
153
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
-
none: false
|
155
167
|
requirements:
|
156
|
-
- -
|
168
|
+
- - '>='
|
157
169
|
- !ruby/object:Gem::Version
|
158
170
|
version: '0'
|
159
171
|
requirements: []
|
160
172
|
rubyforge_project:
|
161
|
-
rubygems_version:
|
173
|
+
rubygems_version: 2.0.3
|
162
174
|
signing_key:
|
163
|
-
specification_version:
|
175
|
+
specification_version: 4
|
164
176
|
summary: Easy-to-use and powerful slugging for Rails 3
|
165
177
|
test_files: []
|