much-slug 0.0.1 → 0.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 -7
- data/Gemfile +3 -1
- data/README.md +174 -1
- data/lib/much-slug.rb +48 -0
- data/lib/much-slug/activerecord.rb +65 -0
- data/lib/much-slug/has_slug_registry.rb +32 -0
- data/lib/much-slug/slug.rb +18 -0
- data/lib/much-slug/version.rb +1 -1
- data/much-slug.gemspec +5 -1
- data/test/support/factory.rb +9 -0
- data/test/unit/activerecord_tests.rb +228 -0
- data/test/unit/has_slug_registry_tests.rb +95 -0
- data/test/unit/much-slug_tests.rb +23 -0
- data/test/unit/slug_tests.rb +104 -0
- metadata +73 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
---
|
|
2
|
-
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
5
|
-
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6372c10543d9dba9890742245b1166dd1465626c4f997c5fdf832edf10f62d9e
|
|
4
|
+
data.tar.gz: 05071c2003b22836474735f9f0fbb2cfbcb3daabdf197a00e62aba9f51300afb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 15d9851869d05018c136b65e0d11d91e834ae20af6fe8149b790aeee32607c2a01d2abd24c1f2f987b01c053556d33590768e9a58753163238c856fd1b458ad5
|
|
7
|
+
data.tar.gz: 91da2c4a9735b817a50238d9ce96842280d9a119c638acbe9188a73a3a7f5708f8af2fa942821c1c6de4ebd056e5806d0582cbb28db920ce77aa9b6bb0aa959a
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -4,7 +4,180 @@ Friendly, human-readable identifiers for database records.
|
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
MuchSlug creates derived slug values on database records. Typically this means deriving the slug value from record attributes and syncing/caching that value in a dedicated field on the record.
|
|
8
|
+
|
|
9
|
+
### ActiveRecord
|
|
10
|
+
|
|
11
|
+
Given a `:slug` field on a record:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
class AddSlugToProjects < ActiveRecord::Migration[5.2]
|
|
15
|
+
def change
|
|
16
|
+
add_column(:projects, :slug, :string, index: { unique: true })
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Mix-in `MuchSlug::ActiveRecord` and configure:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require "much-slug/activerecord"
|
|
25
|
+
|
|
26
|
+
class ProjectRecord < ApplicationRecord
|
|
27
|
+
self.table_name = "projects"
|
|
28
|
+
|
|
29
|
+
include MuchSlug::ActiveRecord
|
|
30
|
+
has_slug(
|
|
31
|
+
source: -> { "#{self.id}-#{self.name}" },
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# ...
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The record will save slug values as it is saved:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
project = ProjectRecord.last
|
|
42
|
+
project.id # => 123
|
|
43
|
+
project.name # => "Sprockets 2.0"
|
|
44
|
+
project.slug # => nil
|
|
45
|
+
|
|
46
|
+
# slug values are updated late-bound after record saves
|
|
47
|
+
project.save
|
|
48
|
+
project.slug # => "123-Sprockets-2-0"
|
|
49
|
+
|
|
50
|
+
project.name = "Widgets For Me"
|
|
51
|
+
project.slug # => "123-Sprockets-2-0"
|
|
52
|
+
project.save
|
|
53
|
+
project.slug # => "123-Widgets-For-Me"
|
|
54
|
+
|
|
55
|
+
# new Projects also have their slugs assigned once they are saved
|
|
56
|
+
project = Project.new(name: "Do The Things")
|
|
57
|
+
project.slug # => nil
|
|
58
|
+
project.save!
|
|
59
|
+
project.slug # => "124-Do-The-Things"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Notes
|
|
63
|
+
|
|
64
|
+
#### Attribute
|
|
65
|
+
|
|
66
|
+
By default, the record attribute for a slug is `"slug"`. You can override this when configuring slugs:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
require "much-slug/activerecord"
|
|
70
|
+
|
|
71
|
+
class ProjectRecord < ApplicationRecord
|
|
72
|
+
self.table_name = "projects"
|
|
73
|
+
|
|
74
|
+
include MuchSlug::ActiveRecord
|
|
75
|
+
has_slug(
|
|
76
|
+
source: -> { "#{self.id}-#{self.name}" },
|
|
77
|
+
)
|
|
78
|
+
has_slug(
|
|
79
|
+
attribute: :full_slug
|
|
80
|
+
source: -> { "#{self.id}-#{self.full_name}" },
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# ...
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Preprocessor
|
|
88
|
+
|
|
89
|
+
By default, MuchSlug doesn't pre-process the slug value source before generating the slug value. You can specify a custom pre-processor by passing any Proc-like object:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
require "much-slug/activerecord"
|
|
93
|
+
|
|
94
|
+
class ProjectRecord < ApplicationRecord
|
|
95
|
+
self.table_name = "projects"
|
|
96
|
+
|
|
97
|
+
include MuchSlug::ActiveRecord
|
|
98
|
+
has_slug(
|
|
99
|
+
source: -> { "#{self.id}-#{self.name}" },
|
|
100
|
+
preprocessor: :downcase
|
|
101
|
+
)
|
|
102
|
+
has_slug(
|
|
103
|
+
attribute: :full_slug
|
|
104
|
+
source: -> { "#{self.id}-#{self.full_name}" },
|
|
105
|
+
preprocessor: -> { |source_value| source_value[0..30] }
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# ...
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Separator
|
|
113
|
+
|
|
114
|
+
MuchSlug replaces any non-word characters with a separator. This helps make slugs URL-friendly. By default, MuchSlug uses `"-"` for the separator. You can specify a custom separator value when configuring slugs:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
require "much-slug/activerecord"
|
|
118
|
+
|
|
119
|
+
class ProjectRecord < ApplicationRecord
|
|
120
|
+
self.table_name = "projects"
|
|
121
|
+
|
|
122
|
+
include MuchSlug::ActiveRecord
|
|
123
|
+
has_slug(
|
|
124
|
+
source: -> { "#{self.id}.#{self.name}" },
|
|
125
|
+
separator: "."
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# ...
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
project = ProjectRecord.last
|
|
132
|
+
project.id # => 123
|
|
133
|
+
project.name # => "Sprockets 2.0"
|
|
134
|
+
project.save
|
|
135
|
+
project.slug # => "123.Sprockets.2.0"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Allowing Underscores
|
|
139
|
+
|
|
140
|
+
By default, MuchSlug doesn't allow underscores in source values and treats them like non-word characters. This means it replaces underscores with the separator. You can override this to allow underscores when configuring slugs:
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
require "much-slug/activerecord"
|
|
144
|
+
|
|
145
|
+
class ProjectRecord < ApplicationRecord
|
|
146
|
+
self.table_name = "projects"
|
|
147
|
+
|
|
148
|
+
include MuchSlug::ActiveRecord
|
|
149
|
+
has_slug(
|
|
150
|
+
source: -> { "#{self.id}-#{self.name}" }
|
|
151
|
+
)
|
|
152
|
+
has_slug(
|
|
153
|
+
attribute: :full_slug
|
|
154
|
+
source: -> { "#{self.id}-#{self.full_name}" },
|
|
155
|
+
allow_underscores: true
|
|
156
|
+
|
|
157
|
+
# ...
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
project = ProjectRecord.last
|
|
161
|
+
project.id # => 123
|
|
162
|
+
project.name # => "SP_2.0"
|
|
163
|
+
project.full_name # => "Sprockets_2.0"
|
|
164
|
+
project.save
|
|
165
|
+
project.slug # => "123-SP-2-0"
|
|
166
|
+
project.full_slug # => "123-Sprockets_2-0"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Manual Slug Updates
|
|
170
|
+
|
|
171
|
+
Slugs are updated anytime a record is saved with changes that affect the slug. If you want an explicit, intention-revealing way to update the slugs manually, use `MuchSlug.update_slugs`:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
project = ProjectRecord.last
|
|
175
|
+
project.id # => 123
|
|
176
|
+
project.name # => "Sprockets 2.0"
|
|
177
|
+
|
|
178
|
+
MuchSlug.update_slugs(project)
|
|
179
|
+
project.slug # => "123-Sprockets-2-0"
|
|
180
|
+
```
|
|
8
181
|
|
|
9
182
|
## Installation
|
|
10
183
|
|
data/lib/much-slug.rb
CHANGED
|
@@ -1,4 +1,52 @@
|
|
|
1
1
|
require "much-slug/version"
|
|
2
|
+
require "much-slug/has_slug_registry"
|
|
3
|
+
require "much-slug/slug"
|
|
2
4
|
|
|
3
5
|
module MuchSlug
|
|
6
|
+
def self.default_attribute
|
|
7
|
+
"slug"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.default_preprocessor
|
|
11
|
+
:to_s
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.default_separator
|
|
15
|
+
"-".freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.default_allow_underscores
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.update_slugs(record)
|
|
23
|
+
record.send("much_slug_has_slug_update_slug_values")
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.has_slug_changed_slug_values(record_instance)
|
|
28
|
+
record_instance.class.much_slug_has_slug_registry.each do |attribute, entry|
|
|
29
|
+
# ArgumentError: no receiver given` raised when calling `instance_exec`
|
|
30
|
+
# on non-lambda Procs, specifically e.g :downcase.to_proc.
|
|
31
|
+
# Can't call `instance_eval` on stabby lambdas b/c `instance_eval` auto
|
|
32
|
+
# passes the receiver as the first argument to the block and stabby
|
|
33
|
+
# lambdas may not expect that and will ArgumentError.
|
|
34
|
+
slug_source_value =
|
|
35
|
+
if entry.source_proc.lambda?
|
|
36
|
+
record_instance.instance_exec(&entry.source_proc)
|
|
37
|
+
else
|
|
38
|
+
record_instance.instance_eval(&entry.source_proc)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
slug_value =
|
|
42
|
+
Slug.new(
|
|
43
|
+
slug_source_value,
|
|
44
|
+
preprocessor: entry.preprocessor_proc,
|
|
45
|
+
separator: entry.separator,
|
|
46
|
+
allow_underscores: entry.allow_underscores
|
|
47
|
+
)
|
|
48
|
+
next if record_instance.send(attribute) == slug_value
|
|
49
|
+
yield attribute, slug_value
|
|
50
|
+
end
|
|
51
|
+
end
|
|
4
52
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "much-plugin"
|
|
2
|
+
require "much-slug"
|
|
3
|
+
|
|
4
|
+
module MuchSlug
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
include MuchPlugin
|
|
7
|
+
|
|
8
|
+
plugin_included do
|
|
9
|
+
@much_slug_has_slug_registry = MuchSlug::HasSlugRegistry.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
plugin_class_methods do
|
|
13
|
+
def has_slug(
|
|
14
|
+
source:,
|
|
15
|
+
attribute: nil,
|
|
16
|
+
preprocessor: nil,
|
|
17
|
+
separator: nil,
|
|
18
|
+
allow_underscores: nil,
|
|
19
|
+
skip_unique_validation: false,
|
|
20
|
+
unique_scope: nil)
|
|
21
|
+
registered_attribute =
|
|
22
|
+
self.much_slug_has_slug_registry.register(
|
|
23
|
+
attribute: attribute,
|
|
24
|
+
source: source,
|
|
25
|
+
preprocessor: preprocessor,
|
|
26
|
+
separator: separator,
|
|
27
|
+
allow_underscores: allow_underscores,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# since the slug isn't written until an after callback we can't always
|
|
31
|
+
# validate presence of it
|
|
32
|
+
validates_presence_of(registered_attribute, :on => :update)
|
|
33
|
+
|
|
34
|
+
unless skip_unique_validation
|
|
35
|
+
validates_uniqueness_of(registered_attribute, {
|
|
36
|
+
:case_sensitive => true,
|
|
37
|
+
:scope => unique_scope,
|
|
38
|
+
:allow_nil => true,
|
|
39
|
+
:allow_blank => true
|
|
40
|
+
})
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
after_create :much_slug_has_slug_update_slug_values
|
|
44
|
+
after_update :much_slug_has_slug_update_slug_values
|
|
45
|
+
|
|
46
|
+
registered_attribute
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def much_slug_has_slug_registry
|
|
50
|
+
@much_slug_has_slug_registry
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
plugin_instance_methods do
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def much_slug_has_slug_update_slug_values
|
|
58
|
+
MuchSlug.has_slug_changed_slug_values(self) do |attribute, slug_value|
|
|
59
|
+
self.send("#{attribute}=", slug_value)
|
|
60
|
+
self.update_column(attribute, slug_value)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "much-slug"
|
|
2
|
+
|
|
3
|
+
module MuchSlug
|
|
4
|
+
class HasSlugRegistry < ::Hash
|
|
5
|
+
def initialize
|
|
6
|
+
super{ |h, k| h[k] = Entry.new }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def register(
|
|
10
|
+
attribute:,
|
|
11
|
+
source:,
|
|
12
|
+
preprocessor:,
|
|
13
|
+
separator:,
|
|
14
|
+
allow_underscores:)
|
|
15
|
+
(attribute || MuchSlug.default_attribute).to_s.tap do |a|
|
|
16
|
+
if allow_underscores.nil?
|
|
17
|
+
allow_underscores = MuchSlug.default_allow_underscores
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
entry = self[a]
|
|
21
|
+
entry.source_proc = source.to_proc
|
|
22
|
+
entry.preprocessor_proc = (preprocessor || MuchSlug.default_preprocessor).to_proc
|
|
23
|
+
entry.separator = separator || MuchSlug.default_separator
|
|
24
|
+
entry.allow_underscores = !!allow_underscores
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Entry
|
|
29
|
+
attr_accessor :source_proc, :preprocessor_proc, :separator, :allow_underscores
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module MuchSlug
|
|
2
|
+
module Slug
|
|
3
|
+
def self.new(string, preprocessor:, separator:, allow_underscores: true)
|
|
4
|
+
regexp_escaped_sep = Regexp.escape(separator)
|
|
5
|
+
|
|
6
|
+
slug = preprocessor.call(string.to_s.dup)
|
|
7
|
+
# Turn unwanted chars into the separator
|
|
8
|
+
slug.gsub!(/[^\w#{regexp_escaped_sep}]+/, separator)
|
|
9
|
+
# Turn underscores into the separator, unless allowing
|
|
10
|
+
slug.gsub!(/_/, separator) unless allow_underscores
|
|
11
|
+
# No more than one of the separator in a row.
|
|
12
|
+
slug.gsub!(/#{regexp_escaped_sep}{2,}/, separator)
|
|
13
|
+
# Remove leading/trailing separator.
|
|
14
|
+
slug.gsub!(/\A#{regexp_escaped_sep}|#{regexp_escaped_sep}\z/, "")
|
|
15
|
+
slug
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/much-slug/version.rb
CHANGED
data/much-slug.gemspec
CHANGED
|
@@ -18,6 +18,10 @@ Gem::Specification.new do |gem|
|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
gem.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
gem.
|
|
21
|
+
gem.required_ruby_version = "~> 2.4"
|
|
22
22
|
|
|
23
|
+
gem.add_dependency("much-plugin", ["~> 0.2.1"])
|
|
24
|
+
|
|
25
|
+
gem.add_development_dependency("assert", ["~> 2.18.1"])
|
|
26
|
+
gem.add_development_dependency("ardb", ["~> 0.28.3"])
|
|
23
27
|
end
|
data/test/support/factory.rb
CHANGED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
require "assert"
|
|
2
|
+
require "much-slug/activerecord"
|
|
3
|
+
|
|
4
|
+
require "much-plugin"
|
|
5
|
+
require "ardb/record_spy"
|
|
6
|
+
|
|
7
|
+
module MuchSlug::ActiveRecord
|
|
8
|
+
class UnitTests < Assert::Context
|
|
9
|
+
desc "MuchSlug::ActiveRecord"
|
|
10
|
+
setup do
|
|
11
|
+
source_attribute = @source_attribute = Factory.string.to_sym
|
|
12
|
+
slug_attribute = @slug_attribute = Factory.string
|
|
13
|
+
@record_class = Ardb::RecordSpy.new do
|
|
14
|
+
include MuchSlug::ActiveRecord
|
|
15
|
+
attr_accessor source_attribute, slug_attribute, MuchSlug.default_attribute
|
|
16
|
+
attr_reader :slug_db_column_updates
|
|
17
|
+
attr_reader :save_called, :save_bang_called
|
|
18
|
+
|
|
19
|
+
def update_column(*args)
|
|
20
|
+
@slug_db_column_updates ||= []
|
|
21
|
+
@slug_db_column_updates << args
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@has_slug_attribute = Factory.string
|
|
26
|
+
@has_slug_preprocessor = :downcase
|
|
27
|
+
@has_slug_separator = Factory.non_word_chars.sample
|
|
28
|
+
@has_slug_allow_underscores = Factory.boolean
|
|
29
|
+
|
|
30
|
+
Assert.stub_tap(@record_class.much_slug_has_slug_registry, :register) { |**kargs|
|
|
31
|
+
@register_called_with = kargs
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
subject{ @record_class }
|
|
35
|
+
|
|
36
|
+
should have_imeths :has_slug
|
|
37
|
+
should have_imeths :much_slug_has_slug_registry
|
|
38
|
+
|
|
39
|
+
should "not have any has_slug registry entries by default" do
|
|
40
|
+
assert_kind_of MuchSlug::HasSlugRegistry, subject.much_slug_has_slug_registry
|
|
41
|
+
assert_empty subject.much_slug_has_slug_registry
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
should "register a new has_slug entry using `has_slug`" do
|
|
45
|
+
subject.has_slug(
|
|
46
|
+
source: @source_attribute,
|
|
47
|
+
attribute: @has_slug_attribute,
|
|
48
|
+
preprocessor: @has_slug_preprocessor,
|
|
49
|
+
separator: @has_slug_separator,
|
|
50
|
+
allow_underscores: @has_slug_allow_underscores
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
exp_kargs = {
|
|
54
|
+
attribute: @has_slug_attribute,
|
|
55
|
+
source: @source_attribute,
|
|
56
|
+
preprocessor: @has_slug_preprocessor,
|
|
57
|
+
separator: @has_slug_separator,
|
|
58
|
+
allow_underscores: @has_slug_allow_underscores
|
|
59
|
+
}
|
|
60
|
+
assert_equal exp_kargs, @register_called_with
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
should "add validations using `has_slug`" do
|
|
64
|
+
subject.has_slug(
|
|
65
|
+
source: @source_attribute,
|
|
66
|
+
attribute: @has_slug_attribute
|
|
67
|
+
)
|
|
68
|
+
exp_attr_name = @has_slug_attribute
|
|
69
|
+
|
|
70
|
+
validation = subject.validations.find{ |v| v.type == :presence }
|
|
71
|
+
assert_not_nil validation
|
|
72
|
+
assert_equal [exp_attr_name], validation.columns
|
|
73
|
+
assert_equal :update, validation.options[:on]
|
|
74
|
+
|
|
75
|
+
validation = subject.validations.find{ |v| v.type == :uniqueness }
|
|
76
|
+
assert_not_nil validation
|
|
77
|
+
assert_equal [exp_attr_name], validation.columns
|
|
78
|
+
assert_equal true, validation.options[:case_sensitive]
|
|
79
|
+
assert_nil validation.options[:scope]
|
|
80
|
+
assert_equal true, validation.options[:allow_nil]
|
|
81
|
+
assert_equal true, validation.options[:allow_blank]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
should "not add a unique validation if skipping unique validation" do
|
|
85
|
+
subject.has_slug(
|
|
86
|
+
source: @source_attribute,
|
|
87
|
+
attribute: @has_slug_attribute,
|
|
88
|
+
skip_unique_validation: true
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
validation = subject.validations.find{ |v| v.type == :uniqueness }
|
|
92
|
+
assert_nil validation
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
should "allow customizing its validations using `has_slug`" do
|
|
96
|
+
unique_scope = Factory.string.to_sym
|
|
97
|
+
subject.has_slug(
|
|
98
|
+
source: @source_attribute,
|
|
99
|
+
attribute: @has_slug_attribute,
|
|
100
|
+
unique_scope: unique_scope
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
validation = subject.validations.find{ |v| v.type == :uniqueness }
|
|
104
|
+
assert_not_nil validation
|
|
105
|
+
assert_equal unique_scope, validation.options[:scope]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
should "add callbacks using `has_slug`" do
|
|
109
|
+
subject.has_slug(source: @source_attribute)
|
|
110
|
+
|
|
111
|
+
callback = subject.callbacks.find{ |v| v.type == :after_create }
|
|
112
|
+
assert_not_nil callback
|
|
113
|
+
assert_equal [:much_slug_has_slug_update_slug_values], callback.args
|
|
114
|
+
|
|
115
|
+
callback = subject.callbacks.find{ |v| v.type == :after_update }
|
|
116
|
+
assert_not_nil callback
|
|
117
|
+
assert_equal [:much_slug_has_slug_update_slug_values], callback.args
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
should "raise an argument error if `has_slug` isn't passed a source" do
|
|
121
|
+
assert_raises(ArgumentError){ subject.has_slug }
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class InitTests < UnitTests
|
|
126
|
+
desc "when init"
|
|
127
|
+
setup do
|
|
128
|
+
@preprocessor = [:downcase, :upcase, :capitalize].sample
|
|
129
|
+
@separator = Factory.non_word_chars.sample
|
|
130
|
+
@allow_underscores = Factory.boolean
|
|
131
|
+
|
|
132
|
+
@registered_default_attribute =
|
|
133
|
+
@record_class.has_slug(source: @source_attribute)
|
|
134
|
+
|
|
135
|
+
source_attribute = @source_attribute
|
|
136
|
+
@registered_custom_attribute =
|
|
137
|
+
@record_class.has_slug(
|
|
138
|
+
source: -> { self.send(source_attribute) },
|
|
139
|
+
attribute: @slug_attribute,
|
|
140
|
+
preprocessor: @preprocessor,
|
|
141
|
+
separator: @separator,
|
|
142
|
+
allow_underscores: @allow_underscores
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@record = @record_class.new
|
|
146
|
+
|
|
147
|
+
# create a string that has mixed case and an underscore so we can test
|
|
148
|
+
# that it uses the preprocessor and allow underscores options when
|
|
149
|
+
# generating a slug
|
|
150
|
+
@source_value = "#{Factory.string.downcase}_#{Factory.string.upcase}"
|
|
151
|
+
@record.send("#{@source_attribute}=", @source_value)
|
|
152
|
+
|
|
153
|
+
@exp_default_slug =
|
|
154
|
+
MuchSlug::Slug.new(
|
|
155
|
+
@source_value,
|
|
156
|
+
preprocessor: MuchSlug.default_preprocessor.to_proc,
|
|
157
|
+
separator: MuchSlug.default_separator,
|
|
158
|
+
allow_underscores: false
|
|
159
|
+
)
|
|
160
|
+
@exp_custom_slug =
|
|
161
|
+
MuchSlug::Slug.new(
|
|
162
|
+
@source_value,
|
|
163
|
+
preprocessor: @preprocessor.to_proc,
|
|
164
|
+
separator: @separator,
|
|
165
|
+
allow_underscores: @allow_underscores
|
|
166
|
+
)
|
|
167
|
+
end
|
|
168
|
+
subject{ @record }
|
|
169
|
+
|
|
170
|
+
should "default its slug attribute" do
|
|
171
|
+
assert_equal MuchSlug.default_attribute, @registered_default_attribute
|
|
172
|
+
assert_equal @slug_attribute, @registered_custom_attribute
|
|
173
|
+
|
|
174
|
+
subject.instance_eval{ much_slug_has_slug_update_slug_values }
|
|
175
|
+
assert_equal 2, subject.slug_db_column_updates.size
|
|
176
|
+
|
|
177
|
+
exp = @exp_default_slug
|
|
178
|
+
assert_equal exp, subject.send(MuchSlug.default_attribute)
|
|
179
|
+
assert_includes [MuchSlug.default_attribute, exp], subject.slug_db_column_updates
|
|
180
|
+
|
|
181
|
+
exp = @exp_custom_slug
|
|
182
|
+
assert_equal exp, subject.send(@slug_attribute)
|
|
183
|
+
assert_includes [@slug_attribute, exp], subject.slug_db_column_updates
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
should "not set its slug if it hasn't changed" do
|
|
187
|
+
subject.send("#{MuchSlug.default_attribute}=", @exp_default_slug)
|
|
188
|
+
subject.send("#{@slug_attribute}=", @exp_custom_slug)
|
|
189
|
+
|
|
190
|
+
subject.instance_eval{ much_slug_has_slug_update_slug_values }
|
|
191
|
+
assert_nil subject.slug_db_column_updates
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
should "slug its source even if its already a valid slug" do
|
|
195
|
+
slug_source = Factory.slug
|
|
196
|
+
subject.send("#{@source_attribute}=", slug_source)
|
|
197
|
+
# ensure the preprocessor doesn't change our source
|
|
198
|
+
Assert.stub(slug_source, @preprocessor){ slug_source }
|
|
199
|
+
|
|
200
|
+
subject.instance_eval{ much_slug_has_slug_update_slug_values }
|
|
201
|
+
|
|
202
|
+
exp =
|
|
203
|
+
MuchSlug::Slug.new(
|
|
204
|
+
slug_source,
|
|
205
|
+
preprocessor: @preprocessor.to_proc,
|
|
206
|
+
separator: @separator,
|
|
207
|
+
allow_underscores: @allow_underscores
|
|
208
|
+
)
|
|
209
|
+
assert_equal exp, subject.send(@slug_attribute)
|
|
210
|
+
assert_includes [@slug_attribute, exp], subject.slug_db_column_updates
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
should "manually update slugs" do
|
|
214
|
+
result = MuchSlug.update_slugs(@record)
|
|
215
|
+
|
|
216
|
+
assert_true result
|
|
217
|
+
assert_equal 2, subject.slug_db_column_updates.size
|
|
218
|
+
|
|
219
|
+
exp = @exp_default_slug
|
|
220
|
+
assert_equal exp, subject.send(MuchSlug.default_attribute)
|
|
221
|
+
assert_includes [MuchSlug.default_attribute, exp], subject.slug_db_column_updates
|
|
222
|
+
|
|
223
|
+
exp = @exp_custom_slug
|
|
224
|
+
assert_equal exp, subject.send(@slug_attribute)
|
|
225
|
+
assert_includes [@slug_attribute, exp], subject.slug_db_column_updates
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require "assert"
|
|
2
|
+
require "much-slug/has_slug_registry"
|
|
3
|
+
|
|
4
|
+
class MuchSlug::HasSlugRegistry
|
|
5
|
+
class UnitTests < Assert::Context
|
|
6
|
+
desc "MuchSlug::HasSlugRegistry"
|
|
7
|
+
setup do
|
|
8
|
+
@class = MuchSlug::HasSlugRegistry
|
|
9
|
+
end
|
|
10
|
+
subject{ @class }
|
|
11
|
+
|
|
12
|
+
should "subclass Hash" do
|
|
13
|
+
assert subject < ::Hash
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class InitTests < UnitTests
|
|
18
|
+
desc "when init"
|
|
19
|
+
setup do
|
|
20
|
+
@attribute = Factory.string.to_sym
|
|
21
|
+
@source = :to_s
|
|
22
|
+
@preprocessor = :downcase
|
|
23
|
+
@separator = "|"
|
|
24
|
+
@allow_underscores = Factory.boolean
|
|
25
|
+
|
|
26
|
+
@registry = @class.new
|
|
27
|
+
end
|
|
28
|
+
subject{ @registry }
|
|
29
|
+
|
|
30
|
+
should have_imeths :register
|
|
31
|
+
|
|
32
|
+
should "default empty entries for unregisterd attributes" do
|
|
33
|
+
entry = subject[Factory.string]
|
|
34
|
+
assert_kind_of @class::Entry, entry
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
should "register new entries" do
|
|
38
|
+
registered_attribute =
|
|
39
|
+
subject.register(
|
|
40
|
+
attribute: @attribute,
|
|
41
|
+
source: @source,
|
|
42
|
+
preprocessor: @preprocessor,
|
|
43
|
+
separator: @separator,
|
|
44
|
+
allow_underscores: @allow_underscores
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
assert_equal @attribute.to_s, registered_attribute
|
|
48
|
+
|
|
49
|
+
entry = subject[registered_attribute]
|
|
50
|
+
assert_kind_of @class::Entry, entry
|
|
51
|
+
assert_equal @source.to_proc, entry.source_proc
|
|
52
|
+
assert_equal @preprocessor.to_proc, entry.preprocessor_proc
|
|
53
|
+
assert_equal @separator, entry.separator
|
|
54
|
+
assert_equal @allow_underscores, entry.allow_underscores
|
|
55
|
+
|
|
56
|
+
assert_same entry, subject[registered_attribute]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
should "default registered settings if none are provided" do
|
|
60
|
+
registered_attribute =
|
|
61
|
+
subject.register(
|
|
62
|
+
attribute: nil,
|
|
63
|
+
source: @source,
|
|
64
|
+
preprocessor: nil,
|
|
65
|
+
separator: nil,
|
|
66
|
+
allow_underscores: nil
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
assert_equal MuchSlug.default_attribute.to_s, registered_attribute
|
|
70
|
+
|
|
71
|
+
entry = subject[registered_attribute]
|
|
72
|
+
assert_equal MuchSlug.default_preprocessor.to_proc, entry.preprocessor_proc
|
|
73
|
+
assert_equal MuchSlug.default_separator, entry.separator
|
|
74
|
+
assert_equal MuchSlug.default_allow_underscores, entry.allow_underscores
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class EntryUnitTests < UnitTests
|
|
79
|
+
desc "Entry"
|
|
80
|
+
setup do
|
|
81
|
+
@entry_class = MuchSlug::HasSlugRegistry::Entry
|
|
82
|
+
end
|
|
83
|
+
subject{ @entry_class }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class EntryInitTests < EntryUnitTests
|
|
87
|
+
desc "when init"
|
|
88
|
+
setup do
|
|
89
|
+
@entry = @entry_class.new
|
|
90
|
+
end
|
|
91
|
+
subject{ @entry }
|
|
92
|
+
|
|
93
|
+
should have_accessors :source_proc, :preprocessor_proc, :separator, :allow_underscores
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "assert"
|
|
2
|
+
require "much-slug"
|
|
3
|
+
|
|
4
|
+
module MuchSlug
|
|
5
|
+
class UnitTests < Assert::Context
|
|
6
|
+
desc "MuchSlug"
|
|
7
|
+
setup do
|
|
8
|
+
@module = MuchSlug
|
|
9
|
+
end
|
|
10
|
+
subject{ @module }
|
|
11
|
+
|
|
12
|
+
should have_imeths :default_attribute, :default_preprocessor
|
|
13
|
+
should have_imeths :default_separator, :default_allow_underscores
|
|
14
|
+
should have_imeths :update_slugs
|
|
15
|
+
|
|
16
|
+
should "know its default settings" do
|
|
17
|
+
assert_equal "slug", subject.default_attribute
|
|
18
|
+
assert_equal :to_s, subject.default_preprocessor
|
|
19
|
+
assert_equal "-", subject.default_separator
|
|
20
|
+
assert_equal false, subject.default_allow_underscores
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require "assert"
|
|
2
|
+
require "much-slug/slug"
|
|
3
|
+
|
|
4
|
+
module MuchSlug::Slug
|
|
5
|
+
class UnitTests < Assert::Context
|
|
6
|
+
desc "MuchSlug::Slug"
|
|
7
|
+
setup do
|
|
8
|
+
@no_op_pp = proc{ |slug| slug }
|
|
9
|
+
@separator = "-"
|
|
10
|
+
@args = {
|
|
11
|
+
:preprocessor => @no_op_pp,
|
|
12
|
+
:separator => @separator
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@module = MuchSlug::Slug
|
|
16
|
+
end
|
|
17
|
+
subject{ @module }
|
|
18
|
+
|
|
19
|
+
should have_imeths :new
|
|
20
|
+
|
|
21
|
+
should "always dup the given string" do
|
|
22
|
+
string = Factory.string
|
|
23
|
+
assert_not_same string, subject.new(string, **@args)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
should "not change strings that are made up of valid chars" do
|
|
27
|
+
string = Factory.string
|
|
28
|
+
assert_equal string, subject.new(string, **@args)
|
|
29
|
+
|
|
30
|
+
string = "#{Factory.string}#{@separator}#{Factory.string.upcase}"
|
|
31
|
+
assert_equal string, subject.new(string, @args)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
should "turn invalid chars into a separator" do
|
|
35
|
+
string = Factory.integer(3).times.map do
|
|
36
|
+
"#{Factory.string(3)}#{Factory.non_word_chars.sample}#{Factory.string(3)}"
|
|
37
|
+
end.join(Factory.non_word_chars.sample)
|
|
38
|
+
assert_equal string.gsub(/[^\w]+/, @separator), subject.new(string, **@args)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
should "allow passing a custom preprocessor proc" do
|
|
42
|
+
string = "#{Factory.string}#{@separator}#{Factory.string.upcase}"
|
|
43
|
+
exp = string.downcase
|
|
44
|
+
args = @args.merge(:preprocessor => :downcase.to_proc)
|
|
45
|
+
assert_equal exp, subject.new(string, **args)
|
|
46
|
+
|
|
47
|
+
preprocessor = proc{ |s| s.gsub(/[A-Z]/, "a") }
|
|
48
|
+
exp = preprocessor.call(string)
|
|
49
|
+
args = @args.merge(:preprocessor => preprocessor)
|
|
50
|
+
assert_equal exp, subject.new(string, **args)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
should "allow passing a custom separator" do
|
|
54
|
+
separator = Factory.non_word_chars.sample
|
|
55
|
+
|
|
56
|
+
invalid_char = (Factory.non_word_chars - [separator]).sample
|
|
57
|
+
string = "#{Factory.string}#{invalid_char}#{Factory.string}"
|
|
58
|
+
exp = string.gsub(/[^\w]+/, separator)
|
|
59
|
+
assert_equal exp, subject.new(string, **@args.merge(:separator => separator))
|
|
60
|
+
|
|
61
|
+
# it won"t change the separator in the strings
|
|
62
|
+
string = "#{Factory.string}#{separator}#{Factory.string}"
|
|
63
|
+
exp = string
|
|
64
|
+
assert_equal string, subject.new(string, **@args.merge(:separator => separator))
|
|
65
|
+
|
|
66
|
+
# it will change the default separator now
|
|
67
|
+
string = "#{Factory.string}#{@separator}#{Factory.string}"
|
|
68
|
+
exp = string.gsub(@separator, separator)
|
|
69
|
+
assert_equal exp, subject.new(string, **@args.merge(:separator => separator))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
should "change underscores into its separator if not allowed" do
|
|
73
|
+
string = "#{Factory.string}#{@separator}#{Factory.string}"
|
|
74
|
+
assert_equal string, subject.new(string, **@args)
|
|
75
|
+
|
|
76
|
+
exp = string.gsub("_", @separator)
|
|
77
|
+
assert_equal exp, subject.new(string, **@args.merge(:allow_underscores => false))
|
|
78
|
+
|
|
79
|
+
assert_equal string, subject.new(string, **@args.merge(:allow_underscores => true))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
should "not allow multiple separators in a row" do
|
|
83
|
+
string = "#{Factory.string}#{@separator}#{@separator}#{Factory.string}"
|
|
84
|
+
assert_equal string.gsub(/-{2,}/, @separator), subject.new(string, **@args)
|
|
85
|
+
|
|
86
|
+
# remove separators that were added from changing invalid chars
|
|
87
|
+
invalid_chars =
|
|
88
|
+
(Factory.integer(3) + 1).times.map{ Factory.non_word_chars.sample }.join
|
|
89
|
+
string = "#{Factory.string}#{invalid_chars}#{Factory.string}"
|
|
90
|
+
assert_equal string.gsub(/[^\w]+/, @separator), subject.new(string, **@args)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
should "remove leading and trailing separators" do
|
|
94
|
+
string = "-#{Factory.string}#{@separator}#{Factory.string}-"
|
|
95
|
+
assert_equal string[1..-2], subject.new(string, **@args)
|
|
96
|
+
|
|
97
|
+
# remove separators that were added from changing invalid chars
|
|
98
|
+
invalid_char = Factory.non_word_chars.sample
|
|
99
|
+
string =
|
|
100
|
+
"#{invalid_char}#{Factory.string}#{@separator}#{Factory.string}#{invalid_char}"
|
|
101
|
+
assert_equal string[1..-2], subject.new(string, **@args)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
metadata
CHANGED
|
@@ -1,75 +1,111 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: much-slug
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
|
-
authors:
|
|
6
|
+
authors:
|
|
7
7
|
- Kelly Redding
|
|
8
8
|
- Collin Redding
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
date: 2020-02-06 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: much-plugin
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: 0.2.1
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: 0.2.1
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
16
29
|
name: assert
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: 2.18.1
|
|
35
|
+
type: :development
|
|
17
36
|
prerelease: false
|
|
18
|
-
|
|
19
|
-
requirements:
|
|
20
|
-
- - ~>
|
|
21
|
-
- !ruby/object:Gem::Version
|
|
22
|
-
version: 2.
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 2.18.1
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: ardb
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - "~>"
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: 0.28.3
|
|
23
49
|
type: :development
|
|
24
|
-
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - "~>"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: 0.28.3
|
|
25
56
|
description: Friendly, human-readable identifiers for database records.
|
|
26
|
-
email:
|
|
57
|
+
email:
|
|
27
58
|
- kelly@kellyredding.com
|
|
28
59
|
- collin.redding@me.com
|
|
29
60
|
executables: []
|
|
30
|
-
|
|
31
61
|
extensions: []
|
|
32
|
-
|
|
33
62
|
extra_rdoc_files: []
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- .gitignore
|
|
63
|
+
files:
|
|
64
|
+
- ".gitignore"
|
|
37
65
|
- Gemfile
|
|
38
66
|
- LICENSE
|
|
39
67
|
- README.md
|
|
40
68
|
- lib/much-slug.rb
|
|
69
|
+
- lib/much-slug/activerecord.rb
|
|
70
|
+
- lib/much-slug/has_slug_registry.rb
|
|
71
|
+
- lib/much-slug/slug.rb
|
|
41
72
|
- lib/much-slug/version.rb
|
|
42
73
|
- log/.gitkeep
|
|
43
74
|
- much-slug.gemspec
|
|
44
75
|
- test/helper.rb
|
|
45
76
|
- test/support/factory.rb
|
|
77
|
+
- test/unit/activerecord_tests.rb
|
|
78
|
+
- test/unit/has_slug_registry_tests.rb
|
|
79
|
+
- test/unit/much-slug_tests.rb
|
|
80
|
+
- test/unit/slug_tests.rb
|
|
46
81
|
- tmp/.gitkeep
|
|
47
82
|
homepage: https://github.com/redding/much-slug
|
|
48
|
-
licenses:
|
|
83
|
+
licenses:
|
|
49
84
|
- MIT
|
|
50
85
|
metadata: {}
|
|
51
|
-
|
|
52
86
|
post_install_message:
|
|
53
87
|
rdoc_options: []
|
|
54
|
-
|
|
55
|
-
require_paths:
|
|
88
|
+
require_paths:
|
|
56
89
|
- lib
|
|
57
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '2.4'
|
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: '0'
|
|
66
100
|
requirements: []
|
|
67
|
-
|
|
68
|
-
rubyforge_project:
|
|
69
|
-
rubygems_version: 2.7.7
|
|
101
|
+
rubygems_version: 3.0.4
|
|
70
102
|
signing_key:
|
|
71
103
|
specification_version: 4
|
|
72
104
|
summary: Friendly, human-readable identifiers for database records.
|
|
73
|
-
test_files:
|
|
105
|
+
test_files:
|
|
74
106
|
- test/helper.rb
|
|
75
107
|
- test/support/factory.rb
|
|
108
|
+
- test/unit/activerecord_tests.rb
|
|
109
|
+
- test/unit/has_slug_registry_tests.rb
|
|
110
|
+
- test/unit/much-slug_tests.rb
|
|
111
|
+
- test/unit/slug_tests.rb
|