schnecke 0.2.0 → 0.3.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 +4 -4
- data/.rubocop.yml +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +54 -14
- data/lib/schnecke/schnecke.rb +49 -19
- data/lib/schnecke/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdf704d77a580357657db281c7fd5021045fcd5c2b7cca9f32f6f468cf33d895
|
|
4
|
+
data.tar.gz: 995fc538958ecf0ae7b1ce67cb81498e4223b3538782cc036a9763eefa1c0e4f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd373216be05945333cc1ddd6850892151549db1e887cc64ce462ae4f98ee4e86b698e34d591b71f6fd667f5cb65ae8a3634a175a9186f88ee3c97035fcc06af
|
|
7
|
+
data.tar.gz: d62fc13e0f3151f338314d79e7664267812b2de7f5a70ef6e1ba5fbb24ae550a569bf7266858906ee8f7502387aa76bf7687b9f8cf3c73a6a65a0a584213604f
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -22,10 +22,10 @@ Or install it yourself as:
|
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
|
-
Given a class `
|
|
25
|
+
Given a class `SomeObject` which has the attribute `name` and has a `slug` column defined, we can do the following:
|
|
26
26
|
|
|
27
27
|
```ruby
|
|
28
|
-
class
|
|
28
|
+
class SomeObject
|
|
29
29
|
include Schnecke
|
|
30
30
|
slug :name
|
|
31
31
|
end
|
|
@@ -34,18 +34,37 @@ end
|
|
|
34
34
|
This will take the value in `name` and automatically set the slug based on it. If the slug needs to be based on multiple attributes of a model, simply pass in the array of attributes as follows:
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
|
-
class
|
|
37
|
+
class SomeObject
|
|
38
38
|
include Schnecke
|
|
39
39
|
slug [:first_name, :last_name]
|
|
40
40
|
end
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
Under the hood, this library adds a `before_validate` callback that automatically runs a method called `assign_slug`. You are welcome to call this method explicity if you so desire.
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
obj = SomeObject.new(name: 'Hello World!')
|
|
47
|
+
obj.assign_slug
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
It is important to note that if the attribute used to hold the slug (`slug` by default, see next section) already contains a value, the slug assignment **WILL NOT HAPPEN**. This means, if you manually assign the slug by explicitly setting the slug value yourself, it will not be modified. If you would like the slug to be overwrriten you can explicitly call the `reassign_slug` method.
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
obj = SomeObject.new(name: 'Hello World!', slug: 'hi')
|
|
54
|
+
|
|
55
|
+
# This will do nothing as the slug was already set
|
|
56
|
+
obj.assign_slug
|
|
57
|
+
|
|
58
|
+
# This will cause the slug to be assigned
|
|
59
|
+
obj.reassign_slug
|
|
60
|
+
```
|
|
61
|
+
|
|
43
62
|
### Slug column
|
|
44
63
|
|
|
45
64
|
By default it is assumed that the generated slug will be assigned to the `slug` attribute of the model. If one needs to place the slug in a different columm, this can be done by defining the `column` attribute:
|
|
46
65
|
|
|
47
66
|
```ruby
|
|
48
|
-
class
|
|
67
|
+
class SomeObject
|
|
49
68
|
include Schnecke
|
|
50
69
|
slug :name, column: :some_other_column
|
|
51
70
|
end
|
|
@@ -59,14 +78,14 @@ By default the maxium length of a slug is 32 characters *NOT INCLUDING* any pote
|
|
|
59
78
|
|
|
60
79
|
|
|
61
80
|
```ruby
|
|
62
|
-
class
|
|
81
|
+
class SomeObject
|
|
63
82
|
include Schnecke
|
|
64
83
|
slug :name, limit_length: 15
|
|
65
84
|
end
|
|
66
85
|
```
|
|
67
86
|
|
|
68
87
|
```ruby
|
|
69
|
-
class
|
|
88
|
+
class SomeObject
|
|
70
89
|
include Schnecke
|
|
71
90
|
slug :name, limit_length: false
|
|
72
91
|
end
|
|
@@ -74,21 +93,21 @@ end
|
|
|
74
93
|
|
|
75
94
|
### Slug Uniquness
|
|
76
95
|
|
|
77
|
-
By default slugs are unique to the object that defines the slug. For example if we have the 2 objects, `
|
|
96
|
+
By default slugs are unique to the object that defines the slug. For example if we have the 2 objects, `SomeObject` and `SomeOtherObject` as defined as below, then the slugs will be unique for all slugs for all type `SomeObject` objcets and all type `SomeOtherObject` objects.
|
|
78
97
|
|
|
79
98
|
```ruby
|
|
80
|
-
class
|
|
99
|
+
class SomeObject
|
|
81
100
|
include Schnecke
|
|
82
101
|
slug :name
|
|
83
102
|
end
|
|
84
103
|
|
|
85
|
-
class
|
|
104
|
+
class SomeOtherObject
|
|
86
105
|
include Schnecke
|
|
87
106
|
slug :name
|
|
88
107
|
end
|
|
89
108
|
```
|
|
90
109
|
|
|
91
|
-
This means that the slug `foo` can exists 2 times; once for any object of type `
|
|
110
|
+
This means that the slug `foo` can exists 2 times; once for any object of type `SomeObject` and once for any object of type `SomeOtherObject`. Currently there is no way to create globally unique slugs. If this is something that is required, then something like [`friendly_id`](https://github.com/norman/friendly_id) might be more appropriate for your use case.
|
|
92
111
|
|
|
93
112
|
### Handling non-unique slugs
|
|
94
113
|
|
|
@@ -98,7 +117,7 @@ It is important to note that the maximum length of a slug does not include the a
|
|
|
98
117
|
|
|
99
118
|
### Defining a custom uniqueness scope
|
|
100
119
|
|
|
101
|
-
There are times when we want slugs not be unique for all objects of type `
|
|
120
|
+
There are times when we want slugs not be unique for all objects of type `SomeObject`, but rather for a smaller scope. For example, let's say we have a system with multiple `Accounts`, each containing `Record`s. If we want the slug for the `Record` to be unique only within the scope of an `account` we can do by providing the uniqueness scope when setting up the slug.
|
|
102
121
|
|
|
103
122
|
```ruby
|
|
104
123
|
class Record
|
|
@@ -121,12 +140,33 @@ class Tag
|
|
|
121
140
|
end
|
|
122
141
|
```
|
|
123
142
|
|
|
143
|
+
### Callbacks
|
|
144
|
+
|
|
145
|
+
Two callbacks, `before_assign_slug` and `after_assign_slug`, are provided so that you can run arbitrary code before and after the slug assignment process. Both of these callbacks will always run regardless of whether or not a slug is to be assigned. The only time `after_assign_slug` is not run is if there is an exception raised during the assignment process.
|
|
146
|
+
|
|
147
|
+
Note, since `reassign_slug` is just a forced assignment of a slug, both callbacks will run as well.
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
class SomeObject
|
|
151
|
+
include Schnecke
|
|
152
|
+
slug :name
|
|
153
|
+
|
|
154
|
+
def before_assign_slug(opts={})
|
|
155
|
+
puts 'Hello world! I get run before the slug assignment process'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def after_assign_slug(opts={})
|
|
159
|
+
puts 'Goodbye world! I get run after the slug assignment process'
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
124
164
|
### Advanced Usage
|
|
125
165
|
|
|
126
166
|
If you need to change how the slug is generated, how duplicates are handled, etc., you can overwrite the methods in your class. For example to change how slugs are generated you can overwrite the `slugify` method.
|
|
127
167
|
|
|
128
168
|
```ruby
|
|
129
|
-
class
|
|
169
|
+
class SomeObject
|
|
130
170
|
include Schnecke
|
|
131
171
|
slug :name
|
|
132
172
|
|
|
@@ -140,7 +180,7 @@ end
|
|
|
140
180
|
Note, by default the library will validate to ensure that the slug only contains lowercase alpphanumeric letters and '-' or '_'. If your new method changes the alloweable set of characters you can either disable this validation, or pass in your own validation pattern.
|
|
141
181
|
|
|
142
182
|
```ruby
|
|
143
|
-
class
|
|
183
|
+
class SomeObject
|
|
144
184
|
include Schnecke
|
|
145
185
|
slug :name, require_format: false
|
|
146
186
|
|
|
@@ -149,7 +189,7 @@ class A
|
|
|
149
189
|
end
|
|
150
190
|
end
|
|
151
191
|
|
|
152
|
-
class
|
|
192
|
+
class SomeOtherObject
|
|
153
193
|
include Schnecke
|
|
154
194
|
slug :name, require_format: /\A[a-z]+\z/
|
|
155
195
|
|
data/lib/schnecke/schnecke.rb
CHANGED
|
@@ -64,7 +64,47 @@ module Schnecke
|
|
|
64
64
|
# Note, a slug will not be assigned if one already exists. If one needs to
|
|
65
65
|
# force the assignment of a slug, pass `force: true`
|
|
66
66
|
def assign_slug(opts = {})
|
|
67
|
-
|
|
67
|
+
before_assign_slug(opts)
|
|
68
|
+
perform_slug_assign(opts)
|
|
69
|
+
after_assign_slug(opts)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Reassign the slug
|
|
73
|
+
#
|
|
74
|
+
# Unlike assign_slug, this will cause a slug to be created even if one
|
|
75
|
+
# already exists.
|
|
76
|
+
def reassign_slug(opts = {})
|
|
77
|
+
opts[:force] = true
|
|
78
|
+
assign_slug(opts)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Callback that is handled before the slug assignment process.
|
|
82
|
+
#
|
|
83
|
+
# When this is called, no validations, or decisions about whether or not
|
|
84
|
+
# a slug should be created have been made. As such, this will always run
|
|
85
|
+
# regardless of whether or not the slug assignment process proceeds or not.
|
|
86
|
+
def before_assign_slug(opts = {})
|
|
87
|
+
# Left blank, but can be implemented by user
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Callback that is handled after the slug is assigned
|
|
91
|
+
#
|
|
92
|
+
# Unless an error is raised during the slug assignment process, this method
|
|
93
|
+
# will always be called regardless of whether or not the slug was assigned
|
|
94
|
+
def after_assign_slug(opts = {})
|
|
95
|
+
# Left blank, but can be implemented by user
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
protected
|
|
99
|
+
|
|
100
|
+
# Assign the slug
|
|
101
|
+
#
|
|
102
|
+
# This is automatically called before model validation.
|
|
103
|
+
#
|
|
104
|
+
# Note, a slug will not be assigned if one already exists. If one needs to
|
|
105
|
+
# force the assignment of a slug, pass `force: true`
|
|
106
|
+
def perform_slug_assign(opts = {})
|
|
107
|
+
validate_slug_source
|
|
68
108
|
validate_slug_column
|
|
69
109
|
|
|
70
110
|
return if !should_create_slug? && !opts[:force]
|
|
@@ -79,7 +119,7 @@ module Schnecke
|
|
|
79
119
|
end
|
|
80
120
|
|
|
81
121
|
# Make sure it is not too long
|
|
82
|
-
candidate_slug =
|
|
122
|
+
candidate_slug = truncate_slug(candidate_slug)
|
|
83
123
|
|
|
84
124
|
# If there is a duplicate, create a unique one
|
|
85
125
|
if slug_exists?(candidate_slug)
|
|
@@ -89,16 +129,6 @@ module Schnecke
|
|
|
89
129
|
self[schnecke_config[:slug_column]] = candidate_slug
|
|
90
130
|
end
|
|
91
131
|
|
|
92
|
-
# Reassign the slug
|
|
93
|
-
#
|
|
94
|
-
# Unlike assign_slug, this will cause a slug to be created even if one
|
|
95
|
-
# already exists.
|
|
96
|
-
def reassign_slug
|
|
97
|
-
assign_slug(force: true)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
protected
|
|
101
|
-
|
|
102
132
|
# Slugify a string
|
|
103
133
|
#
|
|
104
134
|
# This will take a string and convert it to a slug by removing punctuation
|
|
@@ -106,9 +136,9 @@ module Schnecke
|
|
|
106
136
|
#
|
|
107
137
|
# This can be overriden if a different slug generation method is needed
|
|
108
138
|
def slugify(str)
|
|
109
|
-
return if str.blank?
|
|
139
|
+
return str if str.blank?
|
|
110
140
|
|
|
111
|
-
str.gsub
|
|
141
|
+
str = str.gsub(/[\p{Pc}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]/, '')
|
|
112
142
|
str.parameterize
|
|
113
143
|
end
|
|
114
144
|
|
|
@@ -131,7 +161,7 @@ module Schnecke
|
|
|
131
161
|
#
|
|
132
162
|
# This can be overriden if a different behavior is desired
|
|
133
163
|
def slugify_duplicate(slug)
|
|
134
|
-
return if slug.blank?
|
|
164
|
+
return slug if slug.blank?
|
|
135
165
|
|
|
136
166
|
seq = 2
|
|
137
167
|
new_slug = slug_concat([slug, seq])
|
|
@@ -156,7 +186,7 @@ module Schnecke
|
|
|
156
186
|
|
|
157
187
|
private
|
|
158
188
|
|
|
159
|
-
def
|
|
189
|
+
def validate_slug_source
|
|
160
190
|
source = arrayify(schnecke_config[:slug_source])
|
|
161
191
|
source.each do |attr|
|
|
162
192
|
unless respond_to?(attr)
|
|
@@ -185,9 +215,9 @@ module Schnecke
|
|
|
185
215
|
parts.join(schnecke_config[:slug_separator])
|
|
186
216
|
end
|
|
187
217
|
|
|
188
|
-
def
|
|
189
|
-
return if slug.blank?
|
|
190
|
-
return if schnecke_config[:limit_length].blank?
|
|
218
|
+
def truncate_slug(slug)
|
|
219
|
+
return slug if slug.blank?
|
|
220
|
+
return slug if schnecke_config[:limit_length].blank?
|
|
191
221
|
|
|
192
222
|
slug[0, schnecke_config[:limit_length]]
|
|
193
223
|
end
|
data/lib/schnecke/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: schnecke
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick R. Schmid
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-10-
|
|
11
|
+
date: 2022-10-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|