simple_slug 0.3.5 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4e372bf7c9afde482560d5e69ed52ce343e0c08e
4
- data.tar.gz: 3f51178cc10919580d3e167839c93d1ceaa5fedd
2
+ SHA256:
3
+ metadata.gz: ac49bba6dff11ad2a2bce99cef0ff717ca35a626fb22c23ef708369b596c57fe
4
+ data.tar.gz: eb3955cec13641066e6d15aef572b78491fc8b6c59500658edbd72e05126083d
5
5
  SHA512:
6
- metadata.gz: e6b3a471ce38ccb23d33ca1be285012b7980d1d5c15f6a982b869c8d348ee879d405728c1fe0eeea80e112b4471a2765e6ceec7b38da3b01c1ea7e1dcd64be6f
7
- data.tar.gz: 5872ee8fe9b193d425ec3090c403a19fe7fec864f1fdf94f0a6039a65782248bef837600d49ba3b2679299ba6008353ea83bd8d6d749678f2b867d0bde8431ec
6
+ metadata.gz: 961d7deede6a55925d4020f2b09f3e40b86012d3574f5745318a69ea34b952cef7f00b251562832ec053fcc61db52b268f647092f57e934e7278f58a34c8b789
7
+ data.tar.gz: dbc20244f3751330ac668edf4a92b381c9c1b2480a924be0b90cf0d37a194703cd6c9833e6cc1d852f8b912b08c815a93c916666660460b780a7cce02ed1341b
@@ -1,4 +1,4 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.3.0
4
+ - 2.6.3
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # SimpleSlug
2
2
 
3
3
  [![Build Status](https://travis-ci.org/leschenko/simple_slug.png?branch=master)](https://travis-ci.org/leschenko/simple_slug)
4
- [![Dependency Status](https://gemnasium.com/leschenko/simple_slug.png)](https://gemnasium.com/leschenko/simple_slug)
5
4
 
6
5
  Simple friendly url generator for ActiveRecord models with history.
7
6
 
@@ -37,6 +36,14 @@ class User < ActiveRecord::Base
37
36
  end
38
37
  ```
39
38
 
39
+ Add localization with `slug_YOUR_LOCALE` like columns:
40
+
41
+ ```ruby
42
+ class User < ActiveRecord::Base
43
+ simple_slug :full_name, locales: [nil, :it]
44
+ end
45
+ ```
46
+
40
47
  If you want to control when slug is generated, define `should_generate_new_slug?` method:
41
48
 
42
49
  ```ruby
@@ -1,13 +1,14 @@
1
1
  class CreateSimpleSlugHistorySlug < ActiveRecord::Migration
2
2
  def change
3
- create_table :simple_slug_history_slug do |t|
4
- t.string :slug, null: false
3
+ create_table :simple_slug_history_slugs do |t|
4
+ t.string :slug, null: false, limit: 191
5
+ t.string :locale, limit: 10
5
6
  t.integer :sluggable_id, null: false
6
7
  t.string :sluggable_type, limit: 50, null: false
7
- t.datetime :created_at
8
+ t.timestamps
8
9
  end
9
10
 
10
- add_index :simple_slug_history_slug, [:slug, :sluggable_type], unique: true
11
- add_index :simple_slug_history_slug, [:sluggable_type, :sluggable_id]
11
+ add_index :simple_slug_history_slugs, :slug
12
+ add_index :simple_slug_history_slugs, [:sluggable_type, :sluggable_id], name: 'simple_slug_history_slugs_on_sluggable_type_and_sluggable_id'
12
13
  end
13
14
  end
@@ -4,10 +4,12 @@ require 'simple_slug/model_addition'
4
4
  require 'simple_slug/railtie' if Object.const_defined?(:Rails)
5
5
 
6
6
  module SimpleSlug
7
+ autoload :Adapter, 'simple_slug/adapter'
8
+ autoload :ModelAddition, 'simple_slug/model_addition'
7
9
  autoload :HistorySlug, 'simple_slug/history_slug'
8
10
 
9
11
  mattr_accessor :excludes
10
- @@excludes = %w(new edit show index session login logout sign_in sign_out users admin stylesheets assets javascripts images)
12
+ @@excludes = %w(new edit show index session login logout sign_in sign_out users admin stylesheets javascripts images fonts assets)
11
13
 
12
14
  mattr_accessor :slug_regexp
13
15
  @@slug_regexp = /\A(?:\w+[\w\d\-_]*|--\d+)\z/
@@ -15,48 +17,23 @@ module SimpleSlug
15
17
  mattr_accessor :slug_column
16
18
  @@slug_column = 'slug'
17
19
 
20
+ mattr_accessor :min_length
21
+ @@min_length = 3
22
+
18
23
  mattr_accessor :max_length
19
- @@max_length = 240
24
+ @@max_length = 191
20
25
 
21
26
  mattr_accessor :callback_type
22
27
  @@callback_type = :before_validation
23
28
 
24
- mattr_accessor :add_validation
25
- @@add_validation = true
29
+ mattr_accessor :validation
30
+ @@validation = true
26
31
 
27
32
  STARTS_WITH_NUMBER_REGEXP =/\A\d+/
28
- CYRILLIC_LOCALES = [:uk, :ru, :be].freeze
29
- ES_LOCALES = [:es].freeze
30
-
31
- ES_NORMALIZE_SINGLE = [
32
- 'ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåçèéêëìíîïñòóôõöøùúûüýÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıĴĵĶķĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƒƠơƯưǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǺǻǾǿ',
33
- 'AAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaceeeeiiiinoooooouuuuyyAaAaAaCcCcCcCcDdDdEeEeEeEeEeGgGgGgGgHhHhIiIiIiIiIiJjKkLlLlLlLlllNnNnNnnOoOoOoRrRrRrSsSsSsSsTtTtTtUuUuUuUuUuUuWwYyYZzZzZzsfOoUuAaIiOoUuUuUuUuUuAaOo'
34
- ].freeze
35
-
36
- ES_NORMALIZE_MULTI = [['Æ', 'AE'], ['æ', 'ae'], ['IJ', 'IJ'], ['ij', 'ij'], ['Œ', 'OE'], ['œ', 'oe'], ['Ǽ', 'AE'], ['ǽ', 'ae']].freeze
33
+ NUMBER_REGEXP =/\A\d+\z/
34
+ RESOLVE_SUFFIX_REGEXP = /--\d+\z/
37
35
 
38
36
  def self.setup
39
37
  yield self
40
38
  end
41
-
42
- def self.normalize_i18n_fix(base, locale=I18n.locale)
43
- locale_sym = locale.to_sym
44
- if CYRILLIC_LOCALES.include?(locale_sym)
45
- normalize_cyrillic(base)
46
- elsif ES_LOCALES.include?(locale_sym)
47
- normalize_es(base)
48
- else
49
- base
50
- end
51
- end
52
-
53
- def self.normalize_cyrillic(base)
54
- base.tr('АаВЕеіКкМНОоРрСсТуХх', 'AaBEeiKkMHOoPpCcTyXx')
55
- end
56
-
57
- def self.normalize_es(base)
58
- base = base.tr(*ES_NORMALIZE_SINGLE)
59
- ES_NORMALIZE_MULTI.each{|d| base.gsub!(*d) }
60
- base
61
- end
62
39
  end
@@ -0,0 +1,123 @@
1
+ module SimpleSlug
2
+ class Adapter
3
+ attr_reader :model, :options, :locales
4
+ attr_accessor :current_locale
5
+
6
+ def initialize(model)
7
+ @model = model
8
+ @options = model.simple_slug_options
9
+ @locales = Array(@options[:locales] || [nil])
10
+ end
11
+
12
+ def finder_method
13
+ options[:history] ? :find_by : :find_by!
14
+ end
15
+
16
+ def valid_locale?(locale)
17
+ locales.include?(locale)
18
+ end
19
+
20
+ def current_locale
21
+ valid_locale?(I18n.locale) ? I18n.locale : nil
22
+ end
23
+
24
+ def column_names
25
+ locales.map{|l| column_name(l) }
26
+ end
27
+
28
+ def column_name(locale=I18n.locale)
29
+ [options[:slug_column], (locale if valid_locale?(locale))].compact.join('_')
30
+ end
31
+
32
+ def get(record)
33
+ record.send(column_name)
34
+ end
35
+
36
+ def get_prev(record)
37
+ record.send("#{column_name}_was")
38
+ end
39
+
40
+ def set(record, value)
41
+ record.send("#{column_name}=", value)
42
+ end
43
+
44
+ def each_locale
45
+ locales.each do |l|
46
+ with_locale(l || I18n.default_locale) { yield }
47
+ end
48
+ end
49
+
50
+ def reset(record)
51
+ each_locale{ set record, get_prev(record) }
52
+ end
53
+
54
+ def save_history(record)
55
+ each_locale do
56
+ slug_was = record.saved_change_to_attribute(column_name).try!(:first)
57
+ next if slug_was.blank?
58
+ ::SimpleSlug::HistorySlug.where(sluggable_type: record.class.name, slug: slug_was, locale: current_locale).first_or_initialize.update(sluggable_id: record.id)
59
+ end
60
+ end
61
+
62
+ def generate(record, force: false)
63
+ each_locale do
64
+ next unless force || record.should_generate_new_slug?
65
+ simple_slug = normalize(slug_base(record))
66
+ simple_slug = "__#{record.id || rand(9999)}" if simple_slug.blank? && options[:fallback_on_blank]
67
+ return if simple_slug == get(record).to_s.sub(SimpleSlug::RESOLVE_SUFFIX_REGEXP, '')
68
+ set(record, resolve(record, simple_slug))
69
+ end
70
+ end
71
+
72
+ def normalize(base)
73
+ parameterize_args = ActiveSupport::VERSION::MAJOR > 4 ? {separator: '-'} : '-'
74
+ normalized = I18n.transliterate(base).parameterize(parameterize_args).downcase
75
+ normalized = "_#{normalized}" if normalized =~ SimpleSlug::STARTS_WITH_NUMBER_REGEXP
76
+ normalized = normalized.first(options[:max_length]) if options[:max_length]
77
+ normalized
78
+ end
79
+
80
+ def add_suffix(slug_value)
81
+ "#{slug_value}--#{rand(99999)}"
82
+ end
83
+
84
+ def slug_base(record)
85
+ options[:slug_method].map{|m| record.send(m).to_s }.reject(&:blank?).join(' ')
86
+ end
87
+
88
+ def resolve(record, slug_value)
89
+ return slug_value unless slug_exists?(record, slug_value)
90
+ loop do
91
+ slug_with_suffix = add_suffix(slug_value)
92
+ break slug_with_suffix unless slug_exists?(record, slug_with_suffix)
93
+ end
94
+ end
95
+
96
+ def slug_exists?(record, slug_value)
97
+ model_slug_exists?(record, slug_value) || history_slug_exists?(record, slug_value)
98
+ end
99
+
100
+ def model_slug_exists?(record, slug_value)
101
+ base_scope = record.class.unscoped.where(column_name => slug_value)
102
+ base_scope = base_scope.where('id != ?', record.id) if record.persisted?
103
+ base_scope.exists?
104
+ end
105
+
106
+ def history_slug_exists?(record, slug_value)
107
+ return false unless options[:history]
108
+ base_scope = SimpleSlug::HistorySlug.where(sluggable_type: record.class.name, slug: slug_value)
109
+ base_scope = base_scope.where('sluggable_id != ?', record.id) if record.persisted?
110
+ base_scope.exists?
111
+ end
112
+
113
+ def with_locale(locale)
114
+ if defined? Globalize
115
+ Globalize.with_locale(locale) do
116
+ I18n.with_locale(locale) { yield }
117
+ end
118
+ else
119
+ I18n.with_locale(locale) { yield }
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,5 +1,6 @@
1
1
  module SimpleSlug
2
2
  class HistorySlug < ActiveRecord::Base
3
+ self.table_name = 'simple_slug_history_slugs'
3
4
  belongs_to :sluggable, polymorphic: true
4
5
  end
5
6
  end
@@ -6,33 +6,35 @@ module SimpleSlug
6
6
 
7
7
  module SingletonMethods
8
8
  def simple_slug(*args)
9
- class_attribute :simple_slug_options, instance_writer: false
9
+ class_attribute :simple_slug_options, :simple_slug_adapter, instance_writer: false
10
10
  options = args.extract_options!
11
11
  self.simple_slug_options = options.reverse_merge(
12
12
  slug_column: SimpleSlug.slug_column,
13
13
  slug_method: args,
14
14
  slug_regexp: SimpleSlug.slug_regexp,
15
+ min_length: SimpleSlug.min_length,
15
16
  max_length: SimpleSlug.max_length,
16
17
  callback_type: SimpleSlug.callback_type,
17
- add_validation: SimpleSlug.add_validation
18
+ validation: SimpleSlug.validation
18
19
  )
20
+ self.simple_slug_adapter = SimpleSlug::Adapter.new(self)
19
21
 
20
22
  include InstanceMethods
21
23
  extend ClassMethods
22
24
 
23
25
  send(simple_slug_options[:callback_type], :simple_slug_generate, if: :should_generate_new_slug?) if simple_slug_options[:callback_type]
24
26
 
25
- if simple_slug_options[:add_validation]
26
- simple_slug_locales.each do |locale|
27
- validates simple_slug_column(locale),
28
- presence: true,
29
- exclusion: {in: SimpleSlug.excludes},
30
- format: {with: simple_slug_options[:slug_regexp]}
31
- end
27
+ if simple_slug_options[:validation]
28
+ validates *simple_slug_adapter.column_names,
29
+ presence: true,
30
+ uniqueness: {case_sensitive: true},
31
+ exclusion: {in: SimpleSlug.excludes},
32
+ format: {with: simple_slug_options[:slug_regexp]},
33
+ length: {minimum: simple_slug_options[:min_length], maximum: simple_slug_options[:max_length]}.reject{|_, v| v.blank? }
32
34
  end
33
35
 
34
36
  if simple_slug_options[:history]
35
- after_save :simple_slug_reset_unsaved_slug, :simple_slug_create_history_slug
37
+ after_save :simple_slug_reset, :simple_slug_save_history
36
38
  after_destroy :simple_slug_cleanup_history
37
39
  include InstanceHistoryMethods
38
40
  end
@@ -42,157 +44,45 @@ module SimpleSlug
42
44
  module ClassMethods
43
45
  def simple_slug_find(id_param)
44
46
  return unless id_param
45
- if id_param.is_a?(Integer) || id_param =~ /\A\d+\z/
47
+ if id_param.is_a?(Integer) || id_param =~ SimpleSlug::NUMBER_REGEXP
46
48
  find(id_param)
47
49
  else
48
- finder_method = simple_slug_options[:history] ? :find_by : :find_by!
49
- send(finder_method, simple_slug_column => id_param) or find(::SimpleSlug::HistorySlug.find_by!(slug: id_param).sluggable_id)
50
+ send(simple_slug_adapter.finder_method, simple_slug_adapter.column_name => id_param) or simple_slug_history_find(id_param)
50
51
  end
51
52
  end
52
53
 
53
- alias_method :friendly_find, :simple_slug_find
54
-
55
- def simple_slug_column(locale=I18n.locale)
56
- if simple_slug_localized?(locale)
57
- [simple_slug_options[:slug_column], locale].compact.join('_')
58
- else
59
- simple_slug_options[:slug_column]
60
- end
61
- end
62
-
63
- def simple_slug_columns
64
- simple_slug_locales.map{|locale| simple_slug_column(locale) }
54
+ def simple_slug_history_find(slug, locale=I18n.locale)
55
+ find(SimpleSlug::HistorySlug.find_by!(locale: (locale if simple_slug_adapter.valid_locale?(locale)), slug: slug).sluggable_id)
65
56
  end
66
57
 
67
- def simple_slug_locales
68
- Array(simple_slug_options[:locales] || [nil])
69
- end
70
-
71
- def simple_slug_localized?(locale=I18n.locale)
72
- return unless locale
73
- simple_slug_locales.include?(locale.to_sym)
74
- end
58
+ alias_method :friendly_find, :simple_slug_find
75
59
  end
76
60
 
77
61
  module InstanceMethods
78
62
  def to_param
79
- simple_slug_stored_slug.presence || super
63
+ simple_slug_adapter.get_prev(self).presence || super
80
64
  end
81
65
 
82
66
  def should_generate_new_slug?
83
- return true if simple_slug_options[:history]
84
- if simple_slug_options[:locales]
85
- should_generate_new_slug_for_locales.present?
86
- else
87
- simple_slug_get.blank?
88
- end
89
- end
90
-
91
- def should_generate_new_slug_for_locales
92
- return unless simple_slug_options[:locales]
93
- simple_slug_options[:locales].find_all {|locale| simple_slug_get(locale).blank?}
67
+ simple_slug_adapter.column_names.any?{|cn| send(cn).blank? }
94
68
  end
95
69
 
96
70
  def simple_slug_generate(force=false)
97
- locales = if simple_slug_options[:locales]
98
- ((!force && should_generate_new_slug_for_locales) || simple_slug_options[:locales])
99
- else
100
- [nil]
101
- end
102
- locales.each do |locale|
103
- simple_slug_generate_for_locale(locale, force)
104
- end
105
- end
106
-
107
- def simple_slug_generate_for_locale(locale=I18n.locale, force=false)
108
- simple_slug_with_locale(locale) do
109
- simple_slug = simple_slug_normalize(simple_slug_base)
110
- simple_slug = simple_slug.first(simple_slug_options[:max_length]) if simple_slug_options[:max_length]
111
- simple_slug = "__#{id || rand(9999)}" if simple_slug.blank? && simple_slug_options[:fallback_on_blank]
112
- return if !force && simple_slug == simple_slug_get(locale).to_s.sub(/--\d+\z/, '')
113
- resolved_simple_slug = simple_slug_resolve(simple_slug, locale)
114
- simple_slug_set(resolved_simple_slug, locale)
115
- end
116
- end
117
-
118
- def simple_slug_with_locale(locale)
119
- if defined? Globalize
120
- Globalize.with_locale(locale) do
121
- I18n.with_locale(locale) { yield }
122
- end
123
- else
124
- I18n.with_locale(locale) { yield }
125
- end
126
- end
127
-
128
- def simple_slug_base
129
- simple_slug_options[:slug_method].map{|m| send(m).to_s }.reject(&:blank?).join(' ')
130
- end
131
-
132
- def simple_slug_normalize(base)
133
- parameterize_args = ActiveSupport::VERSION::MAJOR > 4 ? {separator: '-'} : '-'
134
- normalized = I18n.transliterate(SimpleSlug.normalize_i18n_fix(base)).parameterize(parameterize_args).downcase
135
- normalized.to_s =~ SimpleSlug::STARTS_WITH_NUMBER_REGEXP ? "_#{normalized}" : normalized
136
- end
137
-
138
- def simple_slug_resolve(slug_value, locale=I18n.locale)
139
- if simple_slug_exists?(slug_value, locale)
140
- loop do
141
- slug_value_with_suffix = simple_slug_next(slug_value)
142
- break slug_value_with_suffix unless simple_slug_exists?(slug_value_with_suffix, locale)
143
- end
144
- else
145
- slug_value
146
- end
147
- end
148
-
149
- def simple_slug_next(slug_value)
150
- "#{slug_value}--#{rand(99999)}"
151
- end
152
-
153
- def simple_slug_exists?(slug_value, locale=I18n.locale)
154
- simple_slug_base_exists?(slug_value, locale) || simple_slug_history_exists?(slug_value)
155
- end
156
-
157
- def simple_slug_base_exists?(slug_value, locale=I18n.locale)
158
- base_scope = self.class.unscoped.where(self.class.simple_slug_column(locale) => slug_value)
159
- base_scope = base_scope.where('id != ?', id) if persisted?
160
- base_scope.exists?
161
- end
162
-
163
- def simple_slug_history_exists?(slug_value)
164
- return false unless simple_slug_options[:history]
165
- base_scope = ::SimpleSlug::HistorySlug.where(sluggable_type: self.class.name, slug: slug_value)
166
- base_scope = base_scope.where('sluggable_id != ?', id) if persisted?
167
- base_scope.exists?
168
- end
169
-
170
- def simple_slug_set(value, locale=I18n.locale)
171
- send "#{self.class.simple_slug_column(locale)}=", value
172
- end
173
-
174
- def simple_slug_get(locale=I18n.locale)
175
- send self.class.simple_slug_column(locale)
176
- end
177
-
178
- def simple_slug_stored_slug(locale=I18n.locale)
179
- send("#{self.class.simple_slug_column(locale)}_was")
71
+ simple_slug_adapter.generate(self, force: force)
180
72
  end
181
73
  end
182
74
 
183
75
  module InstanceHistoryMethods
184
- def simple_slug_reset_unsaved_slug
185
- return true if errors.blank?
186
- simple_slug_set simple_slug_stored_slug
76
+ def simple_slug_reset
77
+ errors.blank? || simple_slug_adapter.reset(self)
187
78
  end
188
79
 
189
80
  def simple_slug_cleanup_history
190
81
  ::SimpleSlug::HistorySlug.where(sluggable_type: self.class.name, sluggable_id: id).delete_all
191
82
  end
192
83
 
193
- def simple_slug_create_history_slug
194
- return true unless slug_changed?
195
- ::SimpleSlug::HistorySlug.where(sluggable_type: self.class.name, slug: simple_slug_get).first_or_create{|hs| hs.sluggable_id = id }
84
+ def simple_slug_save_history
85
+ simple_slug_adapter.save_history(self)
196
86
  end
197
87
  end
198
88
  end
@@ -1,11 +1,9 @@
1
1
  module SimpleSlug
2
2
  class Railtie < Rails::Railtie
3
3
  initializer 'simple_slug.model_additions' do
4
-
5
4
  ActiveSupport.on_load :active_record do
6
5
  include SimpleSlug::ModelAddition
7
6
  end
8
-
9
7
  end
10
8
  end
11
9
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleSlug
2
- VERSION = '0.3.5'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'simple_slug/version'
@@ -18,10 +17,11 @@ Gem::Specification.new do |spec|
18
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
18
  spec.require_paths = ['lib']
20
19
 
21
- spec.add_dependency 'activerecord', '>= 4.0.0', '< 5.3'
20
+ spec.add_dependency 'activerecord', '>= 4.0.0', '< 6.1'
22
21
  spec.add_dependency 'i18n', '~> 0.7'
23
22
 
24
23
  spec.add_development_dependency 'bundler', '~> 1.5'
25
24
  spec.add_development_dependency 'rake'
26
25
  spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'sqlite3'
27
27
  end
@@ -1,17 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe SimpleSlug do
4
- context 'defaults' do
5
- it 'slug column' do
4
+ describe 'config' do
5
+ it 'has column name' do
6
6
  expect(SimpleSlug.slug_column).to eq 'slug'
7
7
  end
8
8
 
9
- it 'excludes' do
9
+ it 'has excludes' do
10
10
  expect(SimpleSlug.excludes).to include('new', 'edit')
11
11
  end
12
12
 
13
- it 'max length' do
14
- expect( SimpleSlug.max_length).to eq 240
13
+ it 'has max length' do
14
+ expect( SimpleSlug.max_length).to eq 191
15
15
  end
16
16
  end
17
17
  end
@@ -1,54 +1,79 @@
1
1
  require 'spec_helper'
2
2
 
3
- class SlugHistoryRspecModel < RspecActiveModelBase
3
+ class SlugHistoryRspecModel < RspecActiveRecordBase
4
4
  simple_slug :name, history: true
5
+
6
+ def should_generate_new_slug?
7
+ true
8
+ end
5
9
  end
6
10
 
7
- describe 'slug history' do
8
- describe 'history records handling' do
9
- before do
10
- expect_any_instance_of(SlugHistoryRspecModel).to receive(:simple_slug_exists?).and_return(false)
11
+ class SlugLocalizedHistoryRspecModel < RspecActiveRecordBase
12
+ simple_slug :name_for_slug, history: true, locales: [nil, :en]
13
+
14
+ def name_for_slug
15
+ [name, (I18n.locale unless I18n.locale == I18n.default_locale)].compact.join(' ')
16
+ end
17
+
18
+ def should_generate_new_slug?
19
+ true
20
+ end
21
+ end
22
+
23
+ describe 'history' do
24
+ before :each do
25
+ RspecActiveRecordBase.delete_all
26
+ SimpleSlug::HistorySlug.delete_all
27
+ end
28
+
29
+ describe 'persistence' do
30
+ it 'save previous on change' do
31
+ sluggable = SlugHistoryRspecModel.create(id: 1, name: 'Hello')
32
+ expect(SimpleSlug::HistorySlug.where(sluggable_type: 'SlugHistoryRspecModel', sluggable_id: 1).exists?).to be_falsey
33
+ sluggable.update(name: 'Bye')
34
+ hs = SimpleSlug::HistorySlug.where(sluggable_type: 'SlugHistoryRspecModel', sluggable_id: 1).to_a
35
+ expect(hs.size).to eq 1
36
+ expect(hs.first.locale).to be_falsey
37
+ expect(hs.first.slug).to eq 'hello'
11
38
  end
12
39
 
13
- it 'create' do
14
- relation = double
15
- expect(::SimpleSlug::HistorySlug).to receive(:where).once.ordered.with(sluggable_type: 'SlugHistoryRspecModel', slug: 'hello').and_return(relation)
16
- expect(relation).to receive(:first_or_create)
17
- SlugHistoryRspecModel.create(id: 1, name: 'Hello')
40
+ it 'remove on destroy' do
41
+ sluggable = SlugHistoryRspecModel.create(id: 1, name: 'Hello')
42
+ sluggable.update(name: 'Bye')
43
+ expect{ sluggable.destroy }.to change{ SimpleSlug::HistorySlug.where(sluggable_type: 'SlugHistoryRspecModel', sluggable_id: 1).count }.from(1).to(0)
18
44
  end
19
45
 
20
- it 'cleanup' do
21
- relation = double
22
- expect(relation).to receive(:first_or_create)
23
- allow(::SimpleSlug::HistorySlug).to receive(:where).and_return(relation)
24
- expect(relation).to receive(:delete_all)
25
- SlugHistoryRspecModel.create(name: 'Hello', id: 1).destroy
46
+ context 'localized' do
47
+ it 'save previous on change' do
48
+ SlugLocalizedHistoryRspecModel.create(id: 1, name: 'Hello').update(name: 'Bye')
49
+ hs = SimpleSlug::HistorySlug.where(sluggable_type: 'SlugLocalizedHistoryRspecModel', sluggable_id: 1).to_a
50
+ expect(hs.map(&:locale)).to match_array [nil, 'en']
51
+ expect(hs.map(&:slug)).to match_array %w(hello hello-en)
52
+ end
26
53
  end
27
54
  end
28
55
 
29
56
  describe 'conflicts' do
30
- it 'history slug exists' do
31
- record = SlugGenerationRspecModel.new(name: 'Hi')
32
- allow(record).to receive(:simple_slug_base_exists?).and_return(false)
33
- expect(record).to receive(:simple_slug_history_exists?).once.ordered.and_return(true)
34
- expect(record).to receive(:simple_slug_history_exists?).once.ordered.and_return(false)
35
- record.save
36
- expect(record.slug).to start_with('hi--')
57
+ it 'resolve with suffix' do
58
+ SlugHistoryRspecModel.create(name: 'Hello').update(name: 'Bye')
59
+ record = SlugHistoryRspecModel.create(name: 'Hello')
60
+ expect(record.slug).to start_with('hello--')
37
61
  end
38
- end
39
62
 
40
- describe '#friendly_find' do
41
- before do
42
- allow(SlugHistoryRspecModel).to receive(:find_by)
63
+ context 'localized' do
64
+ it 'resolve with suffix' do
65
+ SlugLocalizedHistoryRspecModel.create(name: 'Hello').update(name: 'Bye')
66
+ record = SlugLocalizedHistoryRspecModel.create(name: 'Hello')
67
+ expect(record.slug).to start_with('hello--')
68
+ expect(record.slug_en).to start_with('hello-en--')
69
+ end
43
70
  end
71
+ end
44
72
 
45
- it 'find from history' do
46
- record = double('history')
47
- allow(record).to receive(:sluggable_id).and_return(1)
48
- expect(::SimpleSlug::HistorySlug).to receive(:find_by!).with(slug: 'title').and_return(record)
49
- expect(SlugHistoryRspecModel).to receive(:find).with(1).and_return(record)
50
- SlugHistoryRspecModel.friendly_find('title')
73
+ describe 'find' do
74
+ it 'use history' do
75
+ SlugLocalizedHistoryRspecModel.create(id: 1, name: 'Hello').update(name: 'Bye')
76
+ expect(SlugLocalizedHistoryRspecModel.friendly_find('hello')).to be_truthy
51
77
  end
52
78
  end
53
-
54
79
  end
@@ -1,220 +1,192 @@
1
1
  require 'spec_helper'
2
2
 
3
- class SlugGenerationRspecModel < RspecActiveModelBase
3
+ class SlugRspecModel < RspecActiveRecordBase
4
4
  simple_slug :name
5
5
  end
6
6
 
7
- class SlugGenerationRspecModelWithoutValidation < RspecActiveModelBase
8
- simple_slug :name, add_validation: false
7
+ class SlugWithFallbackOnBlankRspecModel < RspecActiveRecordBase
8
+ simple_slug :name, fallback_on_blank: true
9
9
  end
10
10
 
11
- class SlugGenerationRspecModelWithoutCallback < RspecActiveModelBase
12
- simple_slug :name, callback_type: nil
11
+ class SlugWithoutMaxLengthRspecModel < RspecActiveRecordBase
12
+ simple_slug :name, max_length: nil
13
13
  end
14
14
 
15
- class SlugGenerationRspecModelWithFallbackOnBlank < RspecActiveModelBase
16
- simple_slug :name, fallback_on_blank: true
15
+
16
+ class SlugWithoutValidationRspecModel < RspecActiveRecordBase
17
+ simple_slug :name, validation: false
17
18
  end
18
19
 
19
- class SlugGenerationRspecModelLocalized < RspecActiveModelBase
20
- attr_accessor :slug_en, :name_en
21
- alias_method :slug_en_was, :slug_en
20
+ class SlugWithoutCallbackRspecModel < RspecActiveRecordBase
21
+ simple_slug :name, callback_type: nil
22
+ end
22
23
 
23
- simple_slug :name, locales: [nil, :en]
24
+ class SlugLocalizedRspecModel < RspecActiveRecordBase
25
+ simple_slug :name_for_slug, history: true, locales: [nil, :en]
24
26
 
25
- def name
26
- I18n.locale == :en ? name_en : @name
27
+ def name_for_slug
28
+ [name, (I18n.locale unless I18n.locale == I18n.default_locale)].compact.join(' ')
27
29
  end
28
30
  end
29
31
 
30
32
  describe SimpleSlug::ModelAddition do
31
- describe 'slug generation' do
32
- before do
33
- allow_any_instance_of(SlugGenerationRspecModel). to receive(:simple_slug_exists?).and_return(false)
34
- allow_any_instance_of(SlugGenerationRspecModelWithFallbackOnBlank). to receive(:simple_slug_exists?).and_return(false)
35
- end
36
-
37
- it 'after save' do
38
- expect(SlugGenerationRspecModel.create(name: 'Hello').slug).to eq 'hello'
39
- end
33
+ before :each do
34
+ RspecActiveRecordBase.delete_all
35
+ SimpleSlug::HistorySlug.delete_all
36
+ end
40
37
 
41
- it 'skip excludes' do
42
- expect(SlugGenerationRspecModel.new(name: 'new')).not_to be_valid
38
+ describe 'slug' do
39
+ it 'generate on save' do
40
+ expect(SlugRspecModel.create(name: 'Hello').slug).to eq 'hello'
43
41
  end
44
42
 
45
- it 'skip integers' do
46
- expect(SlugGenerationRspecModel.new(name: '123')).not_to be_valid
43
+ it 'add prefix for numbers' do
44
+ expect(SlugRspecModel.create(name: '123').slug).to eq '_123'
47
45
  end
48
46
 
49
- it 'skip spaces' do
50
- expect(SlugGenerationRspecModel.new(slug: 'test test')).not_to be_valid
47
+ it 'reject excludes' do
48
+ expect(SlugRspecModel.new(name: 'new')).not_to be_valid
51
49
  end
52
50
 
53
- it 'skip punctuation' do
54
- expect(SlugGenerationRspecModel.new(slug: 'test.test')).not_to be_valid
51
+ it 'reject spaces' do
52
+ expect(SlugRspecModel.new(slug: 'test test')).not_to be_valid
55
53
  end
56
54
 
57
- it 'es chars' do
58
- I18n.with_locale(:es) do
59
- expect(SlugGenerationRspecModel.create(name: 'áǼßHello').slug).to eq 'aaeshello'
60
- end
55
+ it 'reject punctuation' do
56
+ expect(SlugRspecModel.new(slug: 'test.test')).not_to be_valid
61
57
  end
62
58
 
63
- it 'fallback on blank' do
64
- SlugGenerationRspecModelWithFallbackOnBlank.create({})
65
- expect(SlugGenerationRspecModelWithFallbackOnBlank.create({}).slug).to start_with '__'
59
+ it 'fallback to prefixed id on blank slug source' do
60
+ expect(SlugWithFallbackOnBlankRspecModel.create({}).slug).to start_with '__'
66
61
  end
67
62
 
68
- it 'skip slug generation' do
69
- allow_any_instance_of(SlugGenerationRspecModel).to receive(:should_generate_new_slug?).and_return(false)
70
- expect(SlugGenerationRspecModel.create(name: 'Hello').slug).to be_blank
63
+ describe '#should_generate_new_slug?' do
64
+ it 'can omit generation' do
65
+ allow_any_instance_of(SlugRspecModel).to receive(:should_generate_new_slug?).and_return(false)
66
+ expect(SlugRspecModel.create(name: 'Hello').slug).to be_blank
67
+ end
71
68
  end
72
69
  end
73
70
 
74
- describe 'resolve conflicts' do
75
- it 'duplicate slug' do
76
- record = SlugGenerationRspecModel.new(name: 'Hi')
77
- expect(record).to receive(:simple_slug_exists?).once.ordered.with('hi', nil).and_return(true)
78
- expect(record).to receive(:simple_slug_exists?).once.ordered.with(/hi--\d+/, nil).and_return(false)
79
- record.save
80
- expect(record.slug).to start_with('hi--')
71
+ describe 'conflicts' do
72
+ it 'resolve with suffix' do
73
+ SlugRspecModel.create(name: 'Hello')
74
+ record = SlugHistoryRspecModel.create(name: 'Hello')
75
+ expect(record.slug).to start_with('hello--')
81
76
  end
82
77
 
83
- it 'numeric slug' do
84
- record = SlugGenerationRspecModel.new(name: '123')
85
- expect(record).to receive(:simple_slug_exists?).with('_123', nil).and_return(false)
86
- record.save
87
- expect(record.slug).to eq '_123'
78
+ context 'localized' do
79
+ it 'resolve with suffix' do
80
+ SlugLocalizedRspecModel.create(name: 'Hello')
81
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
82
+ expect(record.slug).to start_with('hello--')
83
+ expect(record.slug_en).to start_with('hello-en--')
84
+ end
88
85
  end
89
86
  end
90
87
 
91
88
  describe '#to_param' do
92
89
  before do
93
- allow_any_instance_of(SlugGenerationRspecModel).to receive(:simple_slug_exists?).and_return(false)
90
+ allow_any_instance_of(SlugRspecModel).to receive(:simple_slug_exists?).and_return(false)
94
91
  end
95
92
 
96
- it 'slug if exists' do
97
- expect(SlugGenerationRspecModel.create(name: 'Hello').to_param).to eq 'hello'
93
+ it 'use slug if present' do
94
+ expect(SlugRspecModel.create(name: 'Hello').to_param).to eq 'hello'
98
95
  end
99
96
 
100
- it 'id without slug' do
101
- expect(SlugGenerationRspecModel.create(id: 1).to_param).to eq '1'
97
+ it 'do not use unsaved slug' do
98
+ expect(SlugRspecModel.new(name: 'Hello').to_param).to be_falsey
102
99
  end
103
- end
104
100
 
105
- describe '#friendly_find' do
106
- it '#find if integer like' do
107
- expect(SlugGenerationRspecModel).to receive(:find).with(1)
108
- SlugGenerationRspecModel.friendly_find(1)
101
+ it 'use id if slug blank' do
102
+ expect(SlugRspecModel.create(id: 1).to_param).to eq '1'
109
103
  end
104
+ end
110
105
 
111
- it '#find if numeric string' do
112
- expect(SlugGenerationRspecModel).to receive(:find).with('1')
113
- SlugGenerationRspecModel.friendly_find('1')
106
+ describe 'find' do
107
+ it 'by id on integer like param' do
108
+ expect(SlugRspecModel).to receive(:find).with('1')
109
+ SlugRspecModel.friendly_find('1')
114
110
  end
115
111
 
116
- it 'find by slug' do
117
- expect(SlugGenerationRspecModel).to receive(:find_by!).with('slug' => 'title').and_return(double)
118
- SlugGenerationRspecModel.friendly_find('title')
112
+ it 'by slug' do
113
+ expect(SlugRspecModel).to receive(:find_by!).with('slug' => 'title').and_return(double)
114
+ SlugRspecModel.friendly_find('title')
119
115
  end
120
116
  end
121
117
 
122
118
  describe 'max length' do
123
- before do
124
- allow_any_instance_of(SlugGenerationRspecModel).to receive(:simple_slug_exists?).and_return(false)
125
- end
126
-
127
- after do
128
- SlugGenerationRspecModel.simple_slug_options.delete(:max_length)
129
- end
130
-
131
119
  it 'cuts slug to max length' do
132
- record = SlugGenerationRspecModel.new(name: 'Hello' * 100)
133
- record.simple_slug_generate
134
- expect(record.slug.length).to eq 240
135
- end
136
-
137
- it 'use max length from per model options' do
138
- SlugGenerationRspecModel.simple_slug_options[:max_length] = 100
139
- record = SlugGenerationRspecModel.new(name: 'Hello' * 100)
120
+ record = SlugRspecModel.new(name: 'Hello' * 100)
140
121
  record.simple_slug_generate
141
- expect(record.slug.length).to eq 100
122
+ expect(record.slug.length).to eq 191
142
123
  end
143
124
 
144
- it 'omit max length' do
145
- SimpleSlug.max_length = nil
146
- record = SlugGenerationRspecModel.new(name: 'Hello' * 100)
125
+ it 'return full slug without max_length option' do
126
+ record = SlugWithoutMaxLengthRspecModel.new(name: 'Hello' * 100)
147
127
  record.simple_slug_generate
148
128
  expect(record.slug.length).to eq 500
149
129
  end
150
130
  end
151
131
 
152
- describe 'add_validation' do
153
- it 'skip validation' do
154
- expect(SlugGenerationRspecModelWithoutValidation.validators_on(:slug)).to be_blank
132
+ describe 'validation' do
133
+ it 'optionally skip validations' do
134
+ expect(SlugWithoutValidationRspecModel.validators_on(:slug)).to be_blank
155
135
  end
156
136
  end
157
137
 
158
- describe 'callback_type' do
159
- it 'skip callback' do
160
- expect(SlugGenerationRspecModelWithoutCallback.new).not_to receive(:should_generate_new_slug?)
138
+ describe 'callbacks' do
139
+ it 'optionally skip callback' do
140
+ expect(SlugWithoutCallbackRspecModel.new).not_to receive(:should_generate_new_slug?)
161
141
  end
162
142
  end
163
143
 
164
144
  describe 'localized' do
165
- before do
166
- allow_any_instance_of(SlugGenerationRspecModelLocalized).to receive(:simple_slug_exists?).and_return(false)
167
- end
168
-
169
145
  it 'generate slug for locales' do
170
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
146
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
171
147
  expect(record.slug).to eq 'hello'
172
148
  expect(record.slug_en).to eq 'hello-en'
173
149
  end
174
150
 
175
151
  describe '#should_generate_new_slug?' do
176
- it 'keep generated slugs' do
177
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
178
- record.name = 'bye'
179
- record.slug_en = nil
180
- record.name_en = 'Bye en'
181
- expect{ record.save }.not_to change{ record.slug }
152
+ it 'keep slug when present' do
153
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
154
+ expect{ record.update(name: 'Bye') }.not_to change{ record.slug }
182
155
  end
183
156
 
184
- it 'generate slug for locales with blank slug' do
185
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
157
+ it 'generate slug when blank' do
158
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
186
159
  record.name = 'bye'
187
160
  record.slug_en = nil
188
- record.name_en = 'Bye en'
189
161
  expect{ record.save }.to change{ record.slug_en }.to('bye-en')
190
162
  end
191
163
  end
192
164
 
193
165
  describe '#to_param' do
194
- it 'generate not localized for default locale' do
195
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
166
+ it 'use unlocalized column for default locale' do
167
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
196
168
  expect(record.to_param).to eq 'hello'
197
169
  end
198
170
 
199
- it 'generate localized' do
200
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
171
+ it 'use localized column for non-default locales' do
172
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
201
173
  I18n.with_locale(:en) do
202
174
  expect(record.to_param).to eq 'hello-en'
203
175
  end
204
176
  end
205
177
  end
206
178
 
207
- describe '#simple_slug_find' do
208
- it 'use default slug column with default locale' do
209
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
210
- expect(SlugGenerationRspecModelLocalized).to receive(:find_by!).with('slug' => 'hello').and_return(record)
211
- SlugGenerationRspecModelLocalized.simple_slug_find('hello')
179
+ describe 'find' do
180
+ it 'use default slug column for default locale' do
181
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
182
+ expect(SlugLocalizedRspecModel.simple_slug_find('hello')).to eq record
212
183
  end
213
184
 
214
- it 'use localized slug column' do
215
- record = SlugGenerationRspecModelLocalized.create(name: 'Hello', name_en: 'Hello en')
216
- expect(SlugGenerationRspecModelLocalized).to receive(:find_by!).with('slug_en' => 'hello-en').and_return(record)
217
- I18n.with_locale(:en) { SlugGenerationRspecModelLocalized.simple_slug_find('hello-en') }
185
+ it 'use localized slug column for non-default locale' do
186
+ record = SlugLocalizedRspecModel.create(name: 'Hello')
187
+ I18n.with_locale(:en) do
188
+ expect(SlugLocalizedRspecModel.simple_slug_find('hello-en')).to eq record
189
+ end
218
190
  end
219
191
  end
220
192
  end
@@ -1,48 +1,44 @@
1
+ require 'sqlite3'
1
2
  require 'active_record'
2
3
  require 'i18n'
3
4
  require 'active_support/core_ext'
4
5
  require 'byebug'
5
6
  require 'simple_slug'
6
7
 
7
- # just silence warning
8
8
  I18n.enforce_available_locales = false
9
9
  I18n.default_locale = :uk
10
10
 
11
- class RspecActiveModelBase
12
- include ActiveModel::Model
13
- include ActiveModel::AttributeMethods
14
- extend ActiveModel::Callbacks
15
-
16
- include SimpleSlug::ModelAddition
17
-
18
- define_model_callbacks :validation, :save, :destroy
19
-
20
- attr_accessor :id, :slug, :name, :created_at
21
- alias_method :slug_was, :slug
22
-
23
- def self.create(attributes, *)
24
- record = new(attributes)
25
- record.save
26
- record
27
- end
28
-
29
- def save
30
- run_callbacks(:validation) { run_callbacks(:save) { } }
31
- end
32
-
33
- def destroy
34
- run_callbacks(:destroy) { @destroyed = true }
35
- end
36
-
37
- def persisted?
38
- true
11
+ ActiveRecord::Base.establish_connection(
12
+ adapter: 'sqlite3',
13
+ database: ':memory:'
14
+ )
15
+
16
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
17
+
18
+ RSpec.configure do |config|
19
+ config.before(:suite) do
20
+ ActiveRecord::Migration.verbose = false
21
+
22
+ ActiveRecord::Schema.define do
23
+ create_table :rspec_active_record_bases, force: true do |t|
24
+ t.string :name
25
+ t.string :slug, limit: 191
26
+ t.string :slug_en, limit: 191
27
+ t.timestamps
28
+ end
29
+
30
+ create_table :simple_slug_history_slugs, force: true do |t|
31
+ t.string :slug, null: false, limit: 191
32
+ t.string :locale, limit: 10
33
+ t.integer :sluggable_id, null: false
34
+ t.string :sluggable_type, limit: 50, null: false
35
+ t.timestamps
36
+ end
37
+ end
39
38
  end
39
+ end
40
40
 
41
- def slug_changed?
42
- slug.present?
43
- end
44
41
 
45
- def destroyed?
46
- !!@destroyed
47
- end
48
- end
42
+ class RspecActiveRecordBase < ActiveRecord::Base
43
+ include SimpleSlug::ModelAddition
44
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_slug
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Leschenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-04 00:00:00.000000000 Z
11
+ date: 2020-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 4.0.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5.3'
22
+ version: '6.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 4.0.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5.3'
32
+ version: '6.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: i18n
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +86,20 @@ dependencies:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: sqlite3
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
89
103
  description: Simple friendly url generator for ActiveRecord with history."
90
104
  email:
91
105
  - leschenko.al@gmail.com
@@ -101,6 +115,7 @@ files:
101
115
  - Rakefile
102
116
  - db/migrate/20140113000001_create_simple_slug_history_slug.rb
103
117
  - lib/simple_slug.rb
118
+ - lib/simple_slug/adapter.rb
104
119
  - lib/simple_slug/history_slug.rb
105
120
  - lib/simple_slug/model_addition.rb
106
121
  - lib/simple_slug/railtie.rb
@@ -129,8 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
144
  - !ruby/object:Gem::Version
130
145
  version: '0'
131
146
  requirements: []
132
- rubyforge_project:
133
- rubygems_version: 2.6.14
147
+ rubygems_version: 3.0.6
134
148
  signing_key:
135
149
  specification_version: 4
136
150
  summary: Friendly url generator with history.