slugalicious 2.0.0 → 2.1.0
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.
- 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: []
|