much-slug 0.1.0 → 0.1.1
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/.l.yml +8 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -0
- data/.t.yml +4 -0
- data/Gemfile +4 -2
- data/README.md +10 -20
- data/lib/much-slug.rb +11 -8
- data/lib/much-slug/activerecord.rb +48 -49
- data/lib/much-slug/has_slug_registry.rb +37 -24
- data/lib/much-slug/slug.rb +17 -15
- data/lib/much-slug/version.rb +3 -1
- data/log/{.gitkeep → .keep} +0 -0
- data/much-slug.gemspec +10 -7
- data/test/helper.rb +4 -11
- data/test/support/factory.rb +4 -3
- data/test/unit/activerecord_tests.rb +161 -126
- data/test/unit/has_slug_registry_tests.rb +43 -48
- data/test/unit/much-slug_tests.rb +7 -8
- data/test/unit/slug_tests.rb +62 -46
- metadata +33 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d01ea124052633b468872a2c8ab13273d39999bd8fa4a068fbb009c777b41451
|
4
|
+
data.tar.gz: c1f745df34088bfb001719692c5033deaef9346c857046e9f756da65859c7d23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95199593bb0065a2d17f926a910cd3175657522f07f07aac8b776c69289693cb4568d3f619f46aa96493763efa1d50250dd9260de0d2f886aaafb780704df984
|
7
|
+
data.tar.gz: d6cdb5f3e2b35665f59b8daf266cd560e3eded1dc30584519b45b2d2769f7db6f38e7d6a31a96d4b58f1792bc3481ce7471c4e687055138297e9d7f730c43164
|
data/.l.yml
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.5.8
|
data/.t.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@ MuchSlug creates derived slug values on database records. Typically this means d
|
|
11
11
|
Given a `:slug` field on a record:
|
12
12
|
|
13
13
|
```ruby
|
14
|
-
class AddSlugToProjects < ActiveRecord::Migration[
|
14
|
+
class AddSlugToProjects < ActiveRecord::Migration[6.1]
|
15
15
|
def change
|
16
16
|
add_column(:projects, :slug, :string, index: { unique: true })
|
17
17
|
end
|
@@ -21,14 +21,12 @@ end
|
|
21
21
|
Mix-in `MuchSlug::ActiveRecord` and configure:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
require "much-slug/activerecord"
|
25
|
-
|
26
24
|
class ProjectRecord < ApplicationRecord
|
27
25
|
self.table_name = "projects"
|
28
26
|
|
29
27
|
include MuchSlug::ActiveRecord
|
30
28
|
has_slug(
|
31
|
-
source: -> { "#{
|
29
|
+
source: -> { "#{id}-#{name}" },
|
32
30
|
)
|
33
31
|
|
34
32
|
# ...
|
@@ -66,18 +64,16 @@ project.slug # => "124-Do-The-Things"
|
|
66
64
|
By default, the record attribute for a slug is `"slug"`. You can override this when configuring slugs:
|
67
65
|
|
68
66
|
```ruby
|
69
|
-
require "much-slug/activerecord"
|
70
|
-
|
71
67
|
class ProjectRecord < ApplicationRecord
|
72
68
|
self.table_name = "projects"
|
73
69
|
|
74
70
|
include MuchSlug::ActiveRecord
|
75
71
|
has_slug(
|
76
|
-
source: -> { "#{
|
72
|
+
source: -> { "#{id}-#{name}" },
|
77
73
|
)
|
78
74
|
has_slug(
|
79
75
|
attribute: :full_slug
|
80
|
-
source: -> { "#{
|
76
|
+
source: -> { "#{id}-#{full_name}" },
|
81
77
|
)
|
82
78
|
|
83
79
|
# ...
|
@@ -89,19 +85,17 @@ end
|
|
89
85
|
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
86
|
|
91
87
|
```ruby
|
92
|
-
require "much-slug/activerecord"
|
93
|
-
|
94
88
|
class ProjectRecord < ApplicationRecord
|
95
89
|
self.table_name = "projects"
|
96
90
|
|
97
91
|
include MuchSlug::ActiveRecord
|
98
92
|
has_slug(
|
99
|
-
source: -> { "#{
|
93
|
+
source: -> { "#{id}-#{name}" },
|
100
94
|
preprocessor: :downcase
|
101
95
|
)
|
102
96
|
has_slug(
|
103
97
|
attribute: :full_slug
|
104
|
-
source: -> { "#{
|
98
|
+
source: -> { "#{id}-#{full_name}" },
|
105
99
|
preprocessor: -> { |source_value| source_value[0..30] }
|
106
100
|
)
|
107
101
|
|
@@ -114,14 +108,12 @@ end
|
|
114
108
|
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
109
|
|
116
110
|
```ruby
|
117
|
-
require "much-slug/activerecord"
|
118
|
-
|
119
111
|
class ProjectRecord < ApplicationRecord
|
120
112
|
self.table_name = "projects"
|
121
113
|
|
122
114
|
include MuchSlug::ActiveRecord
|
123
115
|
has_slug(
|
124
|
-
source: -> { "#{
|
116
|
+
source: -> { "#{id}.#{name}" },
|
125
117
|
separator: "."
|
126
118
|
)
|
127
119
|
|
@@ -140,18 +132,16 @@ project.slug # => "123.Sprockets.2.0"
|
|
140
132
|
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
133
|
|
142
134
|
```ruby
|
143
|
-
require "much-slug/activerecord"
|
144
|
-
|
145
135
|
class ProjectRecord < ApplicationRecord
|
146
136
|
self.table_name = "projects"
|
147
137
|
|
148
138
|
include MuchSlug::ActiveRecord
|
149
139
|
has_slug(
|
150
|
-
source: -> { "#{
|
140
|
+
source: -> { "#{id}-#{name}" }
|
151
141
|
)
|
152
142
|
has_slug(
|
153
143
|
attribute: :full_slug
|
154
|
-
source: -> { "#{
|
144
|
+
source: -> { "#{id}-#{full_name}" },
|
155
145
|
allow_underscores: true
|
156
146
|
|
157
147
|
# ...
|
@@ -183,7 +173,7 @@ project.slug # => "123-Sprockets-2-0"
|
|
183
173
|
|
184
174
|
Add this line to your application's Gemfile:
|
185
175
|
|
186
|
-
gem
|
176
|
+
gem "much-slug"
|
187
177
|
|
188
178
|
And then execute:
|
189
179
|
|
data/lib/much-slug.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "much-slug/activerecord"
|
2
4
|
require "much-slug/has_slug_registry"
|
3
5
|
require "much-slug/slug"
|
6
|
+
require "much-slug/version"
|
4
7
|
|
5
8
|
module MuchSlug
|
6
9
|
def self.default_attribute
|
@@ -12,7 +15,7 @@ module MuchSlug
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def self.default_separator
|
15
|
-
"-"
|
18
|
+
"-"
|
16
19
|
end
|
17
20
|
|
18
21
|
def self.default_allow_underscores
|
@@ -24,8 +27,8 @@ module MuchSlug
|
|
24
27
|
true
|
25
28
|
end
|
26
29
|
|
27
|
-
def self.has_slug_changed_slug_values(
|
28
|
-
|
30
|
+
def self.has_slug_changed_slug_values(record)
|
31
|
+
record.class.much_slug_has_slug_registry.each do |attribute, entry|
|
29
32
|
# ArgumentError: no receiver given` raised when calling `instance_exec`
|
30
33
|
# on non-lambda Procs, specifically e.g :downcase.to_proc.
|
31
34
|
# Can't call `instance_eval` on stabby lambdas b/c `instance_eval` auto
|
@@ -33,9 +36,9 @@ module MuchSlug
|
|
33
36
|
# lambdas may not expect that and will ArgumentError.
|
34
37
|
slug_source_value =
|
35
38
|
if entry.source_proc.lambda?
|
36
|
-
|
39
|
+
record.instance_exec(&entry.source_proc)
|
37
40
|
else
|
38
|
-
|
41
|
+
record.instance_eval(&entry.source_proc)
|
39
42
|
end
|
40
43
|
|
41
44
|
slug_value =
|
@@ -43,9 +46,9 @@ module MuchSlug
|
|
43
46
|
slug_source_value,
|
44
47
|
preprocessor: entry.preprocessor_proc,
|
45
48
|
separator: entry.separator,
|
46
|
-
allow_underscores: entry.allow_underscores
|
49
|
+
allow_underscores: entry.allow_underscores,
|
47
50
|
)
|
48
|
-
next if
|
51
|
+
next if record.public_send(attribute) == slug_value
|
49
52
|
yield attribute, slug_value
|
50
53
|
end
|
51
54
|
end
|
@@ -1,64 +1,63 @@
|
|
1
|
-
|
2
|
-
require "much-slug"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
include MuchPlugin
|
3
|
+
require "much-mixin"
|
4
|
+
require "much-slug"
|
7
5
|
|
8
|
-
|
9
|
-
@much_slug_has_slug_registry = MuchSlug::HasSlugRegistry.new
|
10
|
-
end
|
6
|
+
module MuchSlug; end
|
11
7
|
|
12
|
-
|
13
|
-
|
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
|
-
)
|
8
|
+
module MuchSlug::ActiveRecord
|
9
|
+
include MuchMixin
|
29
10
|
|
30
|
-
|
31
|
-
|
32
|
-
|
11
|
+
mixin_class_methods do
|
12
|
+
def much_slug_has_slug_registry
|
13
|
+
@much_slug_has_slug_registry ||= MuchSlug::HasSlugRegistry.new
|
14
|
+
end
|
33
15
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
16
|
+
def has_slug(
|
17
|
+
source:,
|
18
|
+
attribute: nil,
|
19
|
+
preprocessor: nil,
|
20
|
+
separator: nil,
|
21
|
+
allow_underscores: nil,
|
22
|
+
skip_unique_validation: false,
|
23
|
+
unique_scope: nil)
|
24
|
+
registered_attribute =
|
25
|
+
much_slug_has_slug_registry.register(
|
26
|
+
attribute: attribute,
|
27
|
+
source: source,
|
28
|
+
preprocessor: preprocessor,
|
29
|
+
separator: separator,
|
30
|
+
allow_underscores: allow_underscores,
|
31
|
+
)
|
42
32
|
|
43
|
-
|
44
|
-
|
33
|
+
# since the slug isn't written until an after callback we can't always
|
34
|
+
# validate presence of it
|
35
|
+
validates_presence_of(registered_attribute, on: :update)
|
45
36
|
|
46
|
-
|
37
|
+
unless skip_unique_validation
|
38
|
+
validates_uniqueness_of(
|
39
|
+
registered_attribute,
|
40
|
+
case_sensitive: true,
|
41
|
+
scope: unique_scope,
|
42
|
+
allow_nil: true,
|
43
|
+
allow_blank: true,
|
44
|
+
)
|
47
45
|
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
after_create :much_slug_has_slug_update_slug_values
|
48
|
+
after_update :much_slug_has_slug_update_slug_values
|
49
|
+
|
50
|
+
registered_attribute
|
52
51
|
end
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
54
|
+
mixin_instance_methods do
|
55
|
+
private
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
57
|
+
def much_slug_has_slug_update_slug_values
|
58
|
+
MuchSlug.has_slug_changed_slug_values(self) do |attribute, slug_value|
|
59
|
+
public_send("#{attribute}=", slug_value)
|
60
|
+
update_column(attribute, slug_value)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|
@@ -1,32 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "much-slug"
|
2
4
|
|
3
|
-
module MuchSlug
|
4
|
-
class HasSlugRegistry < ::Hash
|
5
|
-
def initialize
|
6
|
-
super{ |h, k| h[k] = Entry.new }
|
7
|
-
end
|
5
|
+
module MuchSlug; end
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
7
|
+
class MuchSlug::HasSlugRegistry < ::Hash
|
8
|
+
def initialize
|
9
|
+
super(){ |h, k| h[k] = Entry.new }
|
10
|
+
end
|
19
11
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
12
|
+
def register(
|
13
|
+
attribute:,
|
14
|
+
source:,
|
15
|
+
preprocessor:,
|
16
|
+
separator:,
|
17
|
+
allow_underscores:)
|
18
|
+
attribute = (attribute || MuchSlug.default_attribute).to_s
|
19
|
+
source_proc = source.to_proc
|
20
|
+
preprocessor_proc = (preprocessor || MuchSlug.default_preprocessor).to_proc
|
21
|
+
separator ||= MuchSlug.default_separator
|
22
|
+
allow_underscores =
|
23
|
+
if allow_underscores.nil?
|
24
|
+
MuchSlug.default_allow_underscores
|
25
|
+
else
|
26
|
+
!!allow_underscores
|
25
27
|
end
|
26
|
-
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
entry = self[attribute]
|
30
|
+
entry.source_proc = source_proc
|
31
|
+
entry.preprocessor_proc = preprocessor_proc
|
32
|
+
entry.separator = separator
|
33
|
+
entry.allow_underscores = allow_underscores
|
34
|
+
|
35
|
+
attribute
|
31
36
|
end
|
37
|
+
|
38
|
+
Entry =
|
39
|
+
Struct.new(
|
40
|
+
:source_proc,
|
41
|
+
:preprocessor_proc,
|
42
|
+
:separator,
|
43
|
+
:allow_underscores,
|
44
|
+
)
|
32
45
|
end
|
data/lib/much-slug/slug.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
|
-
|
2
|
-
module Slug
|
3
|
-
def self.new(string, preprocessor:, separator:, allow_underscores: true)
|
4
|
-
regexp_escaped_sep = Regexp.escape(separator)
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
3
|
+
module MuchSlug; end
|
4
|
+
|
5
|
+
module MuchSlug::Slug
|
6
|
+
def self.new(string, preprocessor:, separator:, allow_underscores: true)
|
7
|
+
regexp_escaped_sep = Regexp.escape(separator)
|
8
|
+
|
9
|
+
slug = preprocessor.call(string.to_s.dup)
|
10
|
+
# Turn unwanted chars into the separator
|
11
|
+
slug.gsub!(/[^\w#{regexp_escaped_sep}]+/, separator)
|
12
|
+
# Turn underscores into the separator, unless allowing
|
13
|
+
slug.gsub!(/_/, separator) unless allow_underscores
|
14
|
+
# No more than one of the separator in a row.
|
15
|
+
slug.gsub!(/#{regexp_escaped_sep}{2,}/, separator)
|
16
|
+
# Remove leading/trailing separator.
|
17
|
+
slug.gsub!(/\A#{regexp_escaped_sep}|#{regexp_escaped_sep}\z/, "")
|
18
|
+
slug
|
17
19
|
end
|
18
20
|
end
|
data/lib/much-slug/version.rb
CHANGED
data/log/{.gitkeep → .keep}
RENAMED
File without changes
|
data/much-slug.gemspec
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require "much-slug/version"
|
5
7
|
|
@@ -11,17 +13,18 @@ Gem::Specification.new do |gem|
|
|
11
13
|
gem.summary = "Friendly, human-readable identifiers for database records."
|
12
14
|
gem.description = "Friendly, human-readable identifiers for database records."
|
13
15
|
gem.homepage = "https://github.com/redding/much-slug"
|
14
|
-
gem.license =
|
16
|
+
gem.license = "MIT"
|
15
17
|
|
16
|
-
gem.files = `git ls-files`.split(
|
18
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
19
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
20
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
21
|
gem.require_paths = ["lib"]
|
20
22
|
|
21
|
-
gem.required_ruby_version = "~> 2.
|
23
|
+
gem.required_ruby_version = "~> 2.5"
|
22
24
|
|
23
|
-
gem.
|
25
|
+
gem.add_development_dependency("ardb", ["~> 0.29.2"])
|
26
|
+
gem.add_development_dependency("assert", ["~> 2.19.3"])
|
27
|
+
gem.add_development_dependency("much-style-guide", ["~> 0.6.0"])
|
24
28
|
|
25
|
-
gem.
|
26
|
-
gem.add_development_dependency("ardb", ["~> 0.28.3"])
|
29
|
+
gem.add_dependency("much-mixin", ["~> 0.2.4"])
|
27
30
|
end
|