sequel-generate-slug 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 -0
- data/lib/sequel/plugins/generate_slug.rb +125 -0
- data/lib/version.rb +3 -0
- data/spec/plugins/generate_slug_spec.rb +54 -0
- data/spec/spec_helper.rb +13 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 901bfe33eea0397cdd68a73aa321a1bb69376989124bf0c66c58d305b241fa02
|
4
|
+
data.tar.gz: dd0a5c584970f8482dde914357dedef59b11b971457391f7cbeba2b21f40a809
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 559e13aa146f388868f9fffb19e660bb561e70b0b199a4535c8ce2be24b7100e0f7d448f2b0f1589eedecaebbc73362c409f0efa7e3d2008ff648932943a3f0e
|
7
|
+
data.tar.gz: eac8f1102296fe5bf5085a3d3e61bc0e4956d8618a939648d185014dce31a73734c717f21370ce2999d7363786f9a4a7fb7e7854484f59570ac2dc85f5b7bce0
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stringex'
|
4
|
+
|
5
|
+
module Sequel
|
6
|
+
module Plugins
|
7
|
+
# GenerateSlug plugin generates a unique slug based on the specified source
|
8
|
+
# and additional columns. The plugin is designed to work with the Sequel
|
9
|
+
# ORM.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# class Post < Sequel::Model
|
13
|
+
# plugin :generate_slug, column: 'slug', source: 'title', additional: 'identifier'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# post = Post.create(title: 'Hello World', identifier: 'abcdef')
|
17
|
+
# post.slug # => 'hello-world'
|
18
|
+
#
|
19
|
+
# post = Post.create(title: 'Hello World', identifier: 'abcdef')
|
20
|
+
# post.slug # => 'hello-world-abcdef'
|
21
|
+
#
|
22
|
+
# post = Post.create(title: 'Hello World', identifier: 'abcdef')
|
23
|
+
# post.slug # => 'hello-world-abcdef-1'
|
24
|
+
module GenerateSlug
|
25
|
+
# Configure the plugin with the given options.
|
26
|
+
#
|
27
|
+
# @param model [Sequel::Model] the model to configure the plugin for
|
28
|
+
# @param opts [Hash] the options to configure the plugin with
|
29
|
+
# @option opts [Symbol] :column (:slug) the column to store the generated slug
|
30
|
+
# @option opts [Symbol] :source (:title) the source column to generate the slug from
|
31
|
+
# @option opts [Symbol] :additional (:identifier) the additional column to use in slug generation if necessary
|
32
|
+
def self.configure(model, opts = {})
|
33
|
+
model.instance_exec do
|
34
|
+
@slug_column = opts[:column] || :slug
|
35
|
+
@source_column = opts[:source] || :title
|
36
|
+
@additional_column = opts[:additional] || :identifier
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module ClassMethods
|
41
|
+
# @!attribute [r] slug_column
|
42
|
+
# @return [Symbol] the column to store the generated slug
|
43
|
+
attr_reader :slug_column
|
44
|
+
|
45
|
+
# @!attribute [r] source_column
|
46
|
+
# @return [Symbol] the source column to generate the slug from
|
47
|
+
attr_reader :source_column
|
48
|
+
|
49
|
+
# @!attribute [r] additional_column
|
50
|
+
# @return [Symbol] the additional column to use in slug generation if necessary
|
51
|
+
attr_reader :additional_column
|
52
|
+
|
53
|
+
Plugins.inherited_instance_variables(self, :@slug_column => nil, :@source_column => nil, :@additional_column => nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
module InstanceMethods
|
57
|
+
# Set the slug before validation if the record is new or the slug needs updating.
|
58
|
+
def before_validation
|
59
|
+
set_slug if new? || slug_needs_update?
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Generate a unique slug based on the source and additional columns.
|
66
|
+
#
|
67
|
+
# @return [String] the generated slug
|
68
|
+
def generate_slug
|
69
|
+
base_slug = get_column_value(model.source_column).to_url
|
70
|
+
additional_column = get_column_value(model.additional_column)
|
71
|
+
|
72
|
+
slug = find_unique_slug(base_slug, additional_column)
|
73
|
+
slug ||= generate_slug_with_random_number(base_slug)
|
74
|
+
|
75
|
+
slug
|
76
|
+
end
|
77
|
+
|
78
|
+
# Find a unique slug based on the given base_slug and additional_column.
|
79
|
+
#
|
80
|
+
# @param base_slug [String] the base slug to start with
|
81
|
+
# @param additional_column [String, nil] the additional column value to use in slug generation if necessary
|
82
|
+
# @return [String, nil] the unique slug or nil if not found
|
83
|
+
def find_unique_slug(base_slug, additional_column)
|
84
|
+
variations = [base_slug]
|
85
|
+
variations << "#{base_slug}-#{additional_column}" unless additional_column.nil? || additional_column.empty?
|
86
|
+
|
87
|
+
variations.each do |variation|
|
88
|
+
return variation if self.class.where(model.slug_column.to_sym => variation).none?
|
89
|
+
end
|
90
|
+
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
# Generate a unique slug with a random number appended to the base_slug.
|
95
|
+
#
|
96
|
+
# @param base_slug [String] the base slug to start with
|
97
|
+
# @return [String] the generated slug with a random number appended
|
98
|
+
def generate_slug_with_random_number(base_slug)
|
99
|
+
loop do
|
100
|
+
new_slug = "#{base_slug}-#{rand(1000)}"
|
101
|
+
break new_slug if self.class.where(model.slug_column.to_sym => new_slug).none?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Check if the slug needs to be updated based on the modified columns.
|
106
|
+
#
|
107
|
+
# @return [Boolean] true if the slug needs to be updated, false otherwise
|
108
|
+
def slug_needs_update?
|
109
|
+
modified?(model.source_column.to_sym) || modified?(model.additional_column.to_sym)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Set the slug for the model instance.
|
113
|
+
#
|
114
|
+
# @param slug [String] the slug to set
|
115
|
+
# @return [void]
|
116
|
+
def set_slug(slug = generate_slug)
|
117
|
+
slug_column = model.slug_column
|
118
|
+
method_name = :"#{slug_column}="
|
119
|
+
|
120
|
+
set_column_value(method_name, slug) if respond_to?(slug_column) && respond_to?(method_name) && !modified?(slug_column)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sequel::Plugins::GenerateSlug do
|
4
|
+
before do
|
5
|
+
DB.create_table :assets do
|
6
|
+
primary_key :id
|
7
|
+
String :name
|
8
|
+
String :identifier
|
9
|
+
String :slug
|
10
|
+
end
|
11
|
+
|
12
|
+
class Asset < Sequel::Model(:assets)
|
13
|
+
plugin :generate_slug, source: :name, additional: :identifier
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
DB.drop_table :assets
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'before_create' do
|
22
|
+
context 'when slug is not provided' do
|
23
|
+
it 'generates unique slug' do
|
24
|
+
asset = Asset.create(name: 'Test Asset', identifier: '123')
|
25
|
+
expect(asset.slug).not_to be_empty
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when slug is provided' do
|
30
|
+
it 'does not override provided slug' do
|
31
|
+
asset = Asset.create(name: 'Test Asset', identifier: '123', slug: 'test-asset')
|
32
|
+
expect(asset.slug).to eq('test-asset')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'before_update' do
|
38
|
+
context 'when source or additional columns are modified' do
|
39
|
+
it 'updates slug' do
|
40
|
+
asset = Asset.create(name: 'Test Asset', identifier: '123')
|
41
|
+
asset.update(name: 'New Name')
|
42
|
+
expect(asset.slug).to eq('new-name')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when slug column is modified' do
|
47
|
+
it 'does not update slug' do
|
48
|
+
asset = Asset.create(name: 'Test Asset', identifier: '123')
|
49
|
+
asset.update(slug: 'updated-asset')
|
50
|
+
expect(asset.slug).to eq('updated-asset')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'sequel'
|
3
|
+
|
4
|
+
require './lib/sequel/plugins/generate_slug'
|
5
|
+
|
6
|
+
DB = Sequel.connect('sqlite:/')
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.order = :random
|
10
|
+
Kernel.srand config.seed
|
11
|
+
|
12
|
+
config.pattern = 'spec/**/*_spec.rb'
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sequel-generate-slug
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Zhuk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sequel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: stringex
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Sequel plugin for generating and maintaining slugs in a database using
|
70
|
+
the base name and an optional additional column.
|
71
|
+
email:
|
72
|
+
- zhuk_andriy@hotmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- lib/sequel/plugins/generate_slug.rb
|
78
|
+
- lib/version.rb
|
79
|
+
- spec/plugins/generate_slug_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
homepage: https://github.com/andrewzhuk/sequel-generate-slug
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata:
|
85
|
+
source_code_uri: https://github.com/andrewzhuk/sequel-generate-slug
|
86
|
+
bug_tracker_uri: https://github.com/andrewzhuk/sequel-generate-slug/issues
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 2.5.0
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubygems_version: 3.3.26
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: Sequel plugin for generating and maintaining slugs.
|
106
|
+
test_files: []
|