slimak 0.2.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 +7 -0
- data/README.md +85 -0
- data/Rakefile +7 -0
- data/lib/generators/slimak/add_slug/add_slug_generator.rb +58 -0
- data/lib/generators/slimak/add_slug/templates/add_slug_migration.rb.erb +10 -0
- data/lib/generators/slimak/add_slug/templates/slimak_initializer.rb.erb +14 -0
- data/lib/slimak/configuration.rb +41 -0
- data/lib/slimak/railtie.rb +14 -0
- data/lib/slimak/sluggable.rb +186 -0
- data/lib/slimak/version.rb +3 -0
- data/lib/slimak.rb +40 -0
- metadata +138 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 551f7dee790133d299f2990df101c7f66aa6d206932469cbfb84bd2dd7359b36
|
|
4
|
+
data.tar.gz: 07b366ca92776c71ac9e5802205ab0784358337040180ee2ed4ac81eff075894
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 14414f7f4238926c9dc980caa7c9f813812409afdb1f7ebf4b36db27aa59889554db238f005304dae9990d6262565d7b7173482140a4cf24f2fcddc2e14463d4
|
|
7
|
+
data.tar.gz: 991c4c516ab9901e93ca03f9fe01aa904ca17b6e54281752966e1009fedda26f265c8b599858b927b627133b0b7c92616db7094730d57f6a16a00e66bd43b81c
|
data/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
```markdown
|
|
2
|
+
# Slimak
|
|
3
|
+
|
|
4
|
+
Slimak generates slugs from multiple attributes (columns) and helps persist them to the database with uniqueness and fast lookup.
|
|
5
|
+
|
|
6
|
+
Features
|
|
7
|
+
- Configure which columns to include in a slug: `slug_columns :name, :assignee_name, :urgency`
|
|
8
|
+
- Returns readable, parameterized slugs (uses ActiveSupport if available)
|
|
9
|
+
- Optionally persists slug to a DB column (default :slug)
|
|
10
|
+
- Ensures uniqueness by appending a numeric suffix when needed (e.g., `paint-the-wall`, `paint-the-wall-2`)
|
|
11
|
+
- Fast lookup via `Model.find_by_slug("...")` and optional DB index/unique constraint
|
|
12
|
+
- Rails Railtie automatically includes into ActiveRecord models
|
|
13
|
+
|
|
14
|
+
Installation
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "slimak"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Usage (ActiveRecord)
|
|
22
|
+
1) Add a slug column and index to your model's table:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
class AddSlugToTasks < ActiveRecord::Migration[6.0]
|
|
26
|
+
def change
|
|
27
|
+
add_column :tasks, :slug, :string
|
|
28
|
+
add_index :tasks, :slug, unique: true
|
|
29
|
+
# If you scope uniqueness (e.g., per project):
|
|
30
|
+
# add_index :tasks, [:project_id, :slug], unique: true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2) Configure the model:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
class Task < ApplicationRecord
|
|
39
|
+
include Slimak::Sluggable # is auto-included by Railtie, but explicit include is fine
|
|
40
|
+
slug_columns :name, :urgency, :assignee_name
|
|
41
|
+
# optional:
|
|
42
|
+
# slug_options column: :permalink
|
|
43
|
+
# slug_options scope: :project_id
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
By default, a slug will be generated and saved before validation if the slug column is blank. If a generated slug collides with an existing record, a numeric suffix will be appended (e.g., -2, -3) until uniqueness is achieved.
|
|
48
|
+
|
|
49
|
+
3) Finding by slug:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
Task.find_by_slug("paint-the-wall-2")
|
|
53
|
+
# or
|
|
54
|
+
Task.find_by_slug!("paint-the-wall")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Usage (Plain Ruby object)
|
|
58
|
+
```ruby
|
|
59
|
+
class FakeTask
|
|
60
|
+
include Slimak::Sluggable
|
|
61
|
+
attr_accessor :name, :urgency, :assignee_name
|
|
62
|
+
slug_columns :name, :urgency, :assignee_name
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
t = FakeTask.new
|
|
66
|
+
t.name = "Paint the wall"
|
|
67
|
+
t.urgency = "Critical"
|
|
68
|
+
t.assignee_name = "Mark"
|
|
69
|
+
t.slug # => "paint-the-wall-critical-mark"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Generators
|
|
73
|
+
```ruby
|
|
74
|
+
rails generate slimak:add_slug ModelName
|
|
75
|
+
|
|
76
|
+
Options: --column=NAME Column to add/store slug (default: slug) --scope=COLUMN_NAME Optional scope column (creates composite unique index)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
Notes & Next steps
|
|
81
|
+
- For robust concurrency-safe uniqueness you should also enforce a UNIQUE index in the database and handle possible race conditions (e.g., retry on unique constraint violation). Slimak already appends numeric suffixes to avoid collisions, but an index prevents races.
|
|
82
|
+
- You can scope uniqueness via `slug_options scope: :project_id` and add a composite unique index [project_id, slug].
|
|
83
|
+
- If you'd like, I can add a Rails generator to create migrations, or implement a DB-retry strategy to handle high concurrency collisions.
|
|
84
|
+
|
|
85
|
+
```
|
data/Rakefile
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/migration"
|
|
3
|
+
|
|
4
|
+
module Slimak
|
|
5
|
+
module Generators
|
|
6
|
+
# Usage:
|
|
7
|
+
# rails generate slimak:add_slug ModelName --column=slug --scope=project_id --migration_version=6.1
|
|
8
|
+
class AddSlugGenerator < Rails::Generators::NamedBase
|
|
9
|
+
include Rails::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
class_option :column, type: :string, default: "slug", desc: "Column name to store slug"
|
|
14
|
+
class_option :scope, type: :string, default: nil, desc: "Optional scope column name for scoped uniqueness"
|
|
15
|
+
class_option :migration_version, type: :string, default: nil, desc: "Rails migration version to use (e.g. 6.0). Defaults to current."
|
|
16
|
+
|
|
17
|
+
# Required by Rails::Generators::Migration to generate unique migration numbers
|
|
18
|
+
def self.next_migration_number(dirname)
|
|
19
|
+
if ActiveRecord::Base.timestamped_migrations
|
|
20
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
21
|
+
else
|
|
22
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_migration_file
|
|
27
|
+
migration_filename = "add_#{options['column'] || 'slug'}_to_#{file_name.pluralize}.rb"
|
|
28
|
+
migration_template "add_slug_migration.rb.erb", "db/migrate/#{migration_filename}", migration_version: migration_version_option
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_initializer_file
|
|
32
|
+
template "slimak_initializer.rb.erb", "config/initializers/slimak.rb"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# prefer provided migration_version option; otherwise infer "6.0" if Rails version not available
|
|
38
|
+
def migration_version_option
|
|
39
|
+
return options["migration_version"] if options["migration_version"].present?
|
|
40
|
+
rails_major = if defined?(Rails) && Rails.respond_to?(:version)
|
|
41
|
+
Rails.version.split(".").first(2).join(".")
|
|
42
|
+
else
|
|
43
|
+
"6.0"
|
|
44
|
+
end
|
|
45
|
+
rails_major
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# helper for templates
|
|
49
|
+
def column_name
|
|
50
|
+
options["column"] || "slug"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def scope_name
|
|
54
|
+
options["scope"]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class Add<%= (column_name + "_to_" + file_name.pluralize).camelize %> < ActiveRecord::Migration[<%= migration_version %>]
|
|
2
|
+
def change
|
|
3
|
+
add_column :<%= file_name.pluralize %>, :<%= column_name %>, :string
|
|
4
|
+
add_index :<%= file_name.pluralize %>, :<%= column_name %>, unique: true
|
|
5
|
+
<% if scope_name.present? -%>
|
|
6
|
+
# Scoped uniqueness: composite unique index on [<%= scope_name %>, :<%= column_name %>]
|
|
7
|
+
add_index :<%= file_name.pluralize %>, [:<%= scope_name %>, :<%= column_name %>], unique: true, name: "index_<%= file_name.pluralize %>_on_<%= scope_name %>_and_<%= column_name %>"
|
|
8
|
+
<% end -%>
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
```ruby
|
|
2
|
+
# Slimak initializer
|
|
3
|
+
# Configure global defaults for Slimak gem here, for example:
|
|
4
|
+
#
|
|
5
|
+
# Slimak.configure do |config|
|
|
6
|
+
# config.column = :slug # default column name to use when generating slugs
|
|
7
|
+
# config.separator = "-" # default separator
|
|
8
|
+
# config.conflict_strategy = :sequence # or :random
|
|
9
|
+
# config.random_suffix_length = 4
|
|
10
|
+
# config.slug_column_limits = { name: 20 } # global per-column limits
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Per-model configuration (slug_columns, slug_options, slug_column_limits) still overrides these defaults.
|
|
14
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Slimak
|
|
2
|
+
# Global configuration object for Slimak.
|
|
3
|
+
# Configure in an initializer or test with:
|
|
4
|
+
# Slimak.configure do |config|
|
|
5
|
+
# config.separator = "_"
|
|
6
|
+
# config.conflict_strategy = :random
|
|
7
|
+
# config.random_suffix_length = 6
|
|
8
|
+
# config.slug_column_limits = { name: 10 }
|
|
9
|
+
# end
|
|
10
|
+
class Configuration
|
|
11
|
+
attr_accessor :column,
|
|
12
|
+
:separator,
|
|
13
|
+
:conflict_strategy,
|
|
14
|
+
:sequence_separator,
|
|
15
|
+
:random_suffix_length,
|
|
16
|
+
:scope,
|
|
17
|
+
:slug_column_limits
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@column = :slug
|
|
21
|
+
@separator = "-"
|
|
22
|
+
@conflict_strategy = :sequence
|
|
23
|
+
@sequence_separator = "-"
|
|
24
|
+
@random_suffix_length = 4
|
|
25
|
+
@scope = nil
|
|
26
|
+
@slug_column_limits = {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Convert config to a plain hash used by Sluggable defaults
|
|
30
|
+
def to_hash
|
|
31
|
+
{
|
|
32
|
+
column: @column,
|
|
33
|
+
separator: @separator,
|
|
34
|
+
conflict_strategy: @conflict_strategy,
|
|
35
|
+
sequence_separator: @sequence_separator,
|
|
36
|
+
random_suffix_length: @random_suffix_length,
|
|
37
|
+
scope: @scope
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Railtie: auto-include Slimak::Sluggable into ActiveRecord::Base for Rails apps.
|
|
2
|
+
if defined?(Rails)
|
|
3
|
+
require "rails/railtie"
|
|
4
|
+
|
|
5
|
+
module Slimak
|
|
6
|
+
class Railtie < ::Rails::Railtie
|
|
7
|
+
initializer "slimak.active_record" do
|
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
|
9
|
+
include Slimak::Sluggable
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
|
|
3
|
+
module Slimak
|
|
4
|
+
# Sluggable concern for ActiveRecord models (also usable on plain Ruby objects).
|
|
5
|
+
module Sluggable
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
if defined?(ActiveRecord) && self <= ActiveRecord::Base
|
|
10
|
+
before_validation :generate_slug_if_blank
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class_methods do
|
|
15
|
+
def slug_columns(*cols)
|
|
16
|
+
@_slimak_slug_columns = cols.flatten.map(&:to_sym)
|
|
17
|
+
@_slimak_slug_column_limits ||= {}
|
|
18
|
+
@_slimak_slug_options ||= default_slug_options
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def slug_column_limits(hash = nil)
|
|
22
|
+
# Merge global limits with model-specific limits; model-specific wins.
|
|
23
|
+
global = Slimak.config.slug_column_limits || {}
|
|
24
|
+
@_slimak_slug_column_limits ||= global.dup
|
|
25
|
+
if hash
|
|
26
|
+
@_slimak_slug_column_limits.merge!(hash.transform_keys(&:to_sym))
|
|
27
|
+
end
|
|
28
|
+
@_slimak_slug_column_limits
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def slug_options(opts = nil)
|
|
32
|
+
@_slimak_slug_options ||= default_slug_options
|
|
33
|
+
@_slimak_slug_options.merge!(opts) if opts.is_a?(Hash)
|
|
34
|
+
@_slimak_slug_options
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Default options come from global Slimak.config so users can set global defaults.
|
|
38
|
+
def default_slug_options
|
|
39
|
+
Slimak.config.to_hash
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def _slimak_slug_columns
|
|
43
|
+
@_slimak_slug_columns || []
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def _slimak_slug_column_limits
|
|
47
|
+
@_slimak_slug_column_limits || Slimak.config.slug_column_limits || {}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def _slimak_slug_options
|
|
51
|
+
@_slimak_slug_options ||= default_slug_options
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def find_by_slug(value)
|
|
55
|
+
col = _slimak_slug_options[:column]
|
|
56
|
+
where(col => value).first
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def find_by_slug!(value)
|
|
60
|
+
find_by_slug(value) or raise ActiveRecord::RecordNotFound, "Couldn't find #{name} with slug=#{value}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def _slug
|
|
65
|
+
col = self.class._slimak_slug_options[:column]
|
|
66
|
+
if respond_to?(col) && !send(col).to_s.strip.empty?
|
|
67
|
+
send(col).to_s
|
|
68
|
+
else
|
|
69
|
+
build_slug_string
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def generate_slug_if_blank
|
|
74
|
+
return unless self.class._slimak_slug_columns.any?
|
|
75
|
+
|
|
76
|
+
slug_col = self.class._slimak_slug_options[:column]
|
|
77
|
+
if respond_to?(slug_col) && !send(slug_col).to_s.strip.empty?
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
candidate = build_unique_slug
|
|
82
|
+
assign_slug_column(slug_col, candidate)
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def build_slug_string
|
|
87
|
+
parts = self.class._slimak_slug_columns.map do |col|
|
|
88
|
+
v = safe_read(col)
|
|
89
|
+
next nil if v.nil?
|
|
90
|
+
formatted = format_component(v.to_s, col)
|
|
91
|
+
formatted unless formatted.to_s.empty?
|
|
92
|
+
end.compact
|
|
93
|
+
|
|
94
|
+
return "" if parts.empty?
|
|
95
|
+
|
|
96
|
+
parameterize(parts.join(" "), self.class._slimak_slug_options[:separator])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def build_unique_slug
|
|
100
|
+
base = build_slug_string
|
|
101
|
+
return base if base.to_s.strip.empty?
|
|
102
|
+
return base unless defined?(ActiveRecord) && self.class.respond_to?(:unscoped)
|
|
103
|
+
|
|
104
|
+
# Merge global options with model options so model options override global.
|
|
105
|
+
opts = Slimak.config.to_hash.merge(self.class._slimak_slug_options || {})
|
|
106
|
+
col = opts[:column]
|
|
107
|
+
scope = opts[:scope]
|
|
108
|
+
|
|
109
|
+
case opts[:conflict_strategy].to_sym
|
|
110
|
+
when :random
|
|
111
|
+
candidate = base.dup
|
|
112
|
+
while slug_exists?(candidate, col, scope)
|
|
113
|
+
suffix = SecureRandom.alphanumeric(opts[:random_suffix_length]).downcase
|
|
114
|
+
candidate = [base, suffix].join(opts[:sequence_separator])
|
|
115
|
+
end
|
|
116
|
+
candidate
|
|
117
|
+
else
|
|
118
|
+
candidate = base.dup
|
|
119
|
+
seq = 2
|
|
120
|
+
while slug_exists?(candidate, col, scope)
|
|
121
|
+
candidate = [base, seq].join(opts[:sequence_separator])
|
|
122
|
+
seq += 1
|
|
123
|
+
end
|
|
124
|
+
candidate
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Safe read helper
|
|
129
|
+
def safe_read(name)
|
|
130
|
+
if respond_to?(name)
|
|
131
|
+
send(name)
|
|
132
|
+
else
|
|
133
|
+
nil
|
|
134
|
+
end
|
|
135
|
+
rescue => _
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def format_component(str, column)
|
|
140
|
+
s = str.dup
|
|
141
|
+
s = ActiveSupport::Inflector.transliterate(s) if defined?(ActiveSupport::Inflector)
|
|
142
|
+
s = s.strip
|
|
143
|
+
limits = self.class._slimak_slug_column_limits || {}
|
|
144
|
+
max = limits && limits[column.to_sym]
|
|
145
|
+
s = s[0, max] if max && max > 0
|
|
146
|
+
s.gsub(/[[:space:]]+/, " ")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def assign_slug_column(slug_col, value)
|
|
150
|
+
if respond_to?("#{slug_col}=")
|
|
151
|
+
send("#{slug_col}=", value)
|
|
152
|
+
elsif respond_to?(:write_attribute)
|
|
153
|
+
write_attribute(slug_col, value)
|
|
154
|
+
else
|
|
155
|
+
(class << self; self; end).class_eval do
|
|
156
|
+
attr_accessor slug_col unless method_defined?(slug_col)
|
|
157
|
+
end
|
|
158
|
+
instance_variable_set("@#{slug_col}", value)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def parameterize(str, separator = "-")
|
|
163
|
+
s = str.to_s
|
|
164
|
+
begin
|
|
165
|
+
s.parameterize(separator: separator)
|
|
166
|
+
rescue ArgumentError
|
|
167
|
+
s.parameterize(separator)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def slug_exists?(candidate, col, scope)
|
|
172
|
+
return false unless defined?(ActiveRecord) && self.class.respond_to?(:unscoped)
|
|
173
|
+
rel = self.class.unscoped.where(col => candidate)
|
|
174
|
+
if scope
|
|
175
|
+
Array(scope).each do |sc|
|
|
176
|
+
val = safe_read(sc)
|
|
177
|
+
rel = rel.where(sc => val)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
if respond_to?(:persisted?) && persisted?
|
|
181
|
+
rel = rel.where.not(id: id)
|
|
182
|
+
end
|
|
183
|
+
rel.exists?
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
data/lib/slimak.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require_relative "slimak/version"
|
|
3
|
+
|
|
4
|
+
# helpers from ActiveSupport for parameterize
|
|
5
|
+
require "active_support"
|
|
6
|
+
require "active_support/core_ext/string/inflections"
|
|
7
|
+
require "active_support/inflector"
|
|
8
|
+
require "active_support/concern"
|
|
9
|
+
|
|
10
|
+
require_relative "slimak/configuration"
|
|
11
|
+
require_relative "slimak/sluggable"
|
|
12
|
+
require_relative "slimak/railtie" if defined?(Rails)
|
|
13
|
+
|
|
14
|
+
module Slimak
|
|
15
|
+
# Global config object; you can reassign in tests if necessary
|
|
16
|
+
@config = Configuration.new
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Accessor for global config
|
|
20
|
+
def config
|
|
21
|
+
@config
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Setter for global config (useful for tests)
|
|
25
|
+
def config=(c)
|
|
26
|
+
@config = c
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Convenience configure block: Slimak.configure { |c| c.separator = "_" }
|
|
30
|
+
def configure
|
|
31
|
+
yield config if block_given?
|
|
32
|
+
config
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Reset global config to defaults
|
|
36
|
+
def reset!
|
|
37
|
+
@config = Configuration.new
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: slimak
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Stanislaw Zawadzki
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-12-12 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5.2'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5.2'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: activerecord
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '8.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '8.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: sqlite3
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '2.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '13.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '13.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: pry
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
description: Slimak creates slugs from multiple model attributes (slug_columns) and
|
|
98
|
+
supports storing them in the DB with uniqueness and fast lookup.
|
|
99
|
+
email:
|
|
100
|
+
- st.zawadzki@gmail.com
|
|
101
|
+
executables: []
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- README.md
|
|
106
|
+
- Rakefile
|
|
107
|
+
- lib/generators/slimak/add_slug/add_slug_generator.rb
|
|
108
|
+
- lib/generators/slimak/add_slug/templates/add_slug_migration.rb.erb
|
|
109
|
+
- lib/generators/slimak/add_slug/templates/slimak_initializer.rb.erb
|
|
110
|
+
- lib/slimak.rb
|
|
111
|
+
- lib/slimak/configuration.rb
|
|
112
|
+
- lib/slimak/railtie.rb
|
|
113
|
+
- lib/slimak/sluggable.rb
|
|
114
|
+
- lib/slimak/version.rb
|
|
115
|
+
homepage: ''
|
|
116
|
+
licenses:
|
|
117
|
+
- MIT
|
|
118
|
+
metadata: {}
|
|
119
|
+
post_install_message:
|
|
120
|
+
rdoc_options: []
|
|
121
|
+
require_paths:
|
|
122
|
+
- lib
|
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - ">="
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: 2.6.0
|
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
requirements: []
|
|
134
|
+
rubygems_version: 3.4.10
|
|
135
|
+
signing_key:
|
|
136
|
+
specification_version: 4
|
|
137
|
+
summary: Multi-column slugs with ActiveRecord persistence & fast lookup
|
|
138
|
+
test_files: []
|