sequel-generate-slug 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|