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 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,3 @@
1
+ module SequelGenerateSlug
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -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
@@ -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: []