ultra-clean-lib 0.0.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.
@@ -0,0 +1,146 @@
1
+ module FriendlyId
2
+ # @guide begin
3
+ #
4
+ # ## History: Avoiding 404's When Slugs Change
5
+ #
6
+ # FriendlyId's {FriendlyId::History History} module adds the ability to store a
7
+ # log of a model's slugs, so that when its friendly id changes, it's still
8
+ # possible to perform finds by the old id.
9
+ #
10
+ # The primary use case for this is avoiding broken URLs.
11
+ #
12
+ # ### Setup
13
+ #
14
+ # In order to use this module, you must add a table to your database schema to
15
+ # store the slug records. FriendlyId provides a generator for this purpose:
16
+ #
17
+ # rails generate friendly_id
18
+ # rake db:migrate
19
+ #
20
+ # This will add a table named `friendly_id_slugs`, used by the {FriendlyId::Slug}
21
+ # model.
22
+ #
23
+ # ### Considerations
24
+ #
25
+ # Because recording slug history requires creating additional database records,
26
+ # this module has an impact on the performance of the associated model's `create`
27
+ # method.
28
+ #
29
+ # ### Example
30
+ #
31
+ # class Post < ActiveRecord::Base
32
+ # extend FriendlyId
33
+ # friendly_id :title, :use => :history
34
+ # end
35
+ #
36
+ # class PostsController < ApplicationController
37
+ #
38
+ # before_filter :find_post
39
+ #
40
+ # ...
41
+ #
42
+ # def find_post
43
+ # @post = Post.friendly.find params[:id]
44
+ #
45
+ # # If an old id or a numeric id was used to find the record, then
46
+ # # the request slug will not match the current slug, and we should do
47
+ # # a 301 redirect to the new path
48
+ # if params[:id] != @post.slug
49
+ # return redirect_to @post, :status => :moved_permanently
50
+ # end
51
+ # end
52
+ # end
53
+ #
54
+ # @guide end
55
+ module History
56
+ module Configuration
57
+ def dependent_value
58
+ dependent.nil? ? :destroy : dependent
59
+ end
60
+ end
61
+
62
+ def self.setup(model_class)
63
+ model_class.instance_eval do
64
+ friendly_id_config.use :slugged
65
+ friendly_id_config.class.send :include, History::Configuration
66
+ friendly_id_config.finder_methods = FriendlyId::History::FinderMethods
67
+ FriendlyId::Finders.setup(model_class) if friendly_id_config.uses? :finders
68
+ end
69
+ end
70
+
71
+ # Configures the model instance to use the History add-on.
72
+ def self.included(model_class)
73
+ model_class.class_eval do
74
+ has_many :slugs, -> { order(id: :desc) }, **{
75
+ as: :sluggable,
76
+ dependent: @friendly_id_config.dependent_value,
77
+ class_name: Slug.to_s
78
+ }
79
+
80
+ after_save :create_slug
81
+ end
82
+ end
83
+
84
+ module FinderMethods
85
+ include ::FriendlyId::FinderMethods
86
+
87
+ def exists_by_friendly_id?(id)
88
+ super || joins(:slugs).where(slug_history_clause(parse_friendly_id(id))).exists?
89
+ end
90
+
91
+ private
92
+
93
+ def first_by_friendly_id(id)
94
+ super || slug_table_record(parse_friendly_id(id))
95
+ end
96
+
97
+ def slug_table_record(id)
98
+ select(quoted_table_name + ".*").joins(:slugs).where(slug_history_clause(id)).order(Slug.arel_table[:id].desc).first
99
+ end
100
+
101
+ def slug_history_clause(id)
102
+ Slug.arel_table[:sluggable_type].eq(base_class.to_s).and(Slug.arel_table[:slug].eq(id))
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # If we're updating, don't consider historic slugs for the same record
109
+ # to be conflicts. This will allow a record to revert to a previously
110
+ # used slug.
111
+ def scope_for_slug_generator
112
+ relation = super.joins(:slugs)
113
+ unless new_record?
114
+ relation = relation.merge(Slug.where("sluggable_id <> ?", id))
115
+ end
116
+ if friendly_id_config.uses?(:scoped)
117
+ relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope))
118
+ end
119
+ relation
120
+ end
121
+
122
+ def create_slug
123
+ return unless friendly_id
124
+ return if history_is_up_to_date?
125
+ # Allow reversion back to a previously used slug
126
+ relation = slugs.where(slug: friendly_id)
127
+ if friendly_id_config.uses?(:scoped)
128
+ relation = relation.where(scope: serialized_scope)
129
+ end
130
+ relation.destroy_all unless relation.empty?
131
+ slugs.create! do |record|
132
+ record.slug = friendly_id
133
+ record.scope = serialized_scope if friendly_id_config.uses?(:scoped)
134
+ end
135
+ end
136
+
137
+ def history_is_up_to_date?
138
+ latest_history = slugs.first
139
+ check = latest_history.try(:slug) == friendly_id
140
+ if friendly_id_config.uses?(:scoped)
141
+ check &&= latest_history.scope == serialized_scope
142
+ end
143
+ check
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,107 @@
1
+ # FriendlyId Global Configuration
2
+ #
3
+ # Use this to set up shared configuration options for your entire application.
4
+ # Any of the configuration options shown here can also be applied to single
5
+ # models by passing arguments to the `friendly_id` class method or defining
6
+ # methods in your model.
7
+ #
8
+ # To learn more, check out the guide:
9
+ #
10
+ # http://norman.github.io/friendly_id/file.Guide.html
11
+
12
+ FriendlyId.defaults do |config|
13
+ # ## Reserved Words
14
+ #
15
+ # Some words could conflict with Rails's routes when used as slugs, or are
16
+ # undesirable to allow as slugs. Edit this list as needed for your app.
17
+ config.use :reserved
18
+
19
+ config.reserved_words = %w[new edit index session login logout users admin
20
+ stylesheets assets javascripts images]
21
+
22
+ # This adds an option to treat reserved words as conflicts rather than exceptions.
23
+ # When there is no good candidate, a UUID will be appended, matching the existing
24
+ # conflict behavior.
25
+
26
+ # config.treat_reserved_as_conflict = true
27
+
28
+ # ## Friendly Finders
29
+ #
30
+ # Uncomment this to use friendly finders in all models. By default, if
31
+ # you wish to find a record by its friendly id, you must do:
32
+ #
33
+ # MyModel.friendly.find('foo')
34
+ #
35
+ # If you uncomment this, you can do:
36
+ #
37
+ # MyModel.find('foo')
38
+ #
39
+ # This is significantly more convenient but may not be appropriate for
40
+ # all applications, so you must explicitly opt-in to this behavior. You can
41
+ # always also configure it on a per-model basis if you prefer.
42
+ #
43
+ # Something else to consider is that using the :finders addon boosts
44
+ # performance because it will avoid Rails-internal code that makes runtime
45
+ # calls to `Module.extend`.
46
+ #
47
+ # config.use :finders
48
+ #
49
+ # ## Slugs
50
+ #
51
+ # Most applications will use the :slugged module everywhere. If you wish
52
+ # to do so, uncomment the following line.
53
+ #
54
+ # config.use :slugged
55
+ #
56
+ # By default, FriendlyId's :slugged addon expects the slug column to be named
57
+ # 'slug', but you can change it if you wish.
58
+ #
59
+ # config.slug_column = 'slug'
60
+ #
61
+ # By default, slug has no size limit, but you can change it if you wish.
62
+ #
63
+ # config.slug_limit = 255
64
+ #
65
+ # When FriendlyId can not generate a unique ID from your base method, it appends
66
+ # a UUID, separated by a single dash. You can configure the character used as the
67
+ # separator. If you're upgrading from FriendlyId 4, you may wish to replace this
68
+ # with two dashes.
69
+ #
70
+ # config.sequence_separator = '-'
71
+ #
72
+ # Note that you must use the :slugged addon **prior** to the line which
73
+ # configures the sequence separator, or else FriendlyId will raise an undefined
74
+ # method error.
75
+ #
76
+ # ## Tips and Tricks
77
+ #
78
+ # ### Controlling when slugs are generated
79
+ #
80
+ # As of FriendlyId 5.0, new slugs are generated only when the slug field is
81
+ # nil, but if you're using a column as your base method can change this
82
+ # behavior by overriding the `should_generate_new_friendly_id?` method that
83
+ # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
84
+ # more like 4.0.
85
+ # Note: Use(include) Slugged module in the config if using the anonymous module.
86
+ # If you have `friendly_id :name, use: slugged` in the model, Slugged module
87
+ # is included after the anonymous module defined in the initializer, so it
88
+ # overrides the `should_generate_new_friendly_id?` method from the anonymous module.
89
+ #
90
+ # config.use :slugged
91
+ # config.use Module.new {
92
+ # def should_generate_new_friendly_id?
93
+ # slug.blank? || <your_column_name_here>_changed?
94
+ # end
95
+ # }
96
+ #
97
+ # FriendlyId uses Rails's `parameterize` method to generate slugs, but for
98
+ # languages that don't use the Roman alphabet, that's not usually sufficient.
99
+ # Here we use the Babosa library to transliterate Russian Cyrillic slugs to
100
+ # ASCII. If you use this, don't forget to add "babosa" to your Gemfile.
101
+ #
102
+ # config.use Module.new {
103
+ # def normalize_friendly_id(text)
104
+ # text.to_slug.normalize! :transliterations => [:russian, :latin]
105
+ # end
106
+ # }
107
+ end
@@ -0,0 +1,21 @@
1
+ MIGRATION_CLASS =
2
+ if ActiveRecord::VERSION::MAJOR >= 5
3
+ ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
4
+ else
5
+ ActiveRecord::Migration
6
+ end
7
+
8
+ class CreateFriendlyIdSlugs < MIGRATION_CLASS
9
+ def change
10
+ create_table :friendly_id_slugs do |t|
11
+ t.string :slug, null: false
12
+ t.integer :sluggable_id, null: false
13
+ t.string :sluggable_type, limit: 50
14
+ t.string :scope
15
+ t.datetime :created_at
16
+ end
17
+ add_index :friendly_id_slugs, [:sluggable_type, :sluggable_id]
18
+ add_index :friendly_id_slugs, [:slug, :sluggable_type], length: {slug: 140, sluggable_type: 50}
19
+ add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: {slug: 70, sluggable_type: 50, scope: 70}, unique: true
20
+ end
21
+ end
@@ -0,0 +1,76 @@
1
+ module FriendlyId
2
+ # Instances of these classes will never be considered a friendly id.
3
+ # @see FriendlyId::ObjectUtils#friendly_id
4
+ UNFRIENDLY_CLASSES = [
5
+ Array,
6
+ FalseClass,
7
+ Hash,
8
+ NilClass,
9
+ Numeric,
10
+ Symbol,
11
+ TrueClass
12
+ ]
13
+
14
+ # Utility methods for determining whether any object is a friendly id.
15
+ #
16
+ # Monkey-patching Object is a somewhat extreme measure not to be taken lightly
17
+ # by libraries, but in this case I decided to do it because to me, it feels
18
+ # cleaner than adding a module method to {FriendlyId}. I've given the methods
19
+ # names that unambigously refer to the library of their origin, which should
20
+ # be sufficient to avoid conflicts with other libraries.
21
+ module ObjectUtils
22
+ # True if the id is definitely friendly, false if definitely unfriendly,
23
+ # else nil.
24
+ #
25
+ # An object is considired "definitely unfriendly" if its class is or
26
+ # inherits from ActiveRecord::Base, Array, Hash, NilClass, Numeric, or
27
+ # Symbol.
28
+ #
29
+ # An object is considered "definitely friendly" if it responds to +to_i+,
30
+ # and its value when cast to an integer and then back to a string is
31
+ # different from its value when merely cast to a string:
32
+ #
33
+ # 123.friendly_id? #=> false
34
+ # :id.friendly_id? #=> false
35
+ # {:name => 'joe'}.friendly_id? #=> false
36
+ # ['name = ?', 'joe'].friendly_id? #=> false
37
+ # nil.friendly_id? #=> false
38
+ # "123".friendly_id? #=> nil
39
+ # "abc123".friendly_id? #=> true
40
+ def friendly_id?
41
+ true if respond_to?(:to_i) && to_i.to_s != to_s
42
+ end
43
+
44
+ # True if the id is definitely unfriendly, false if definitely friendly,
45
+ # else nil.
46
+ def unfriendly_id?
47
+ val = friendly_id?
48
+ !val unless val.nil?
49
+ end
50
+ end
51
+
52
+ module UnfriendlyUtils
53
+ def friendly_id?
54
+ false
55
+ end
56
+
57
+ def unfriendly_id?
58
+ true
59
+ end
60
+ end
61
+
62
+ def self.mark_as_unfriendly(klass)
63
+ klass.send(:include, FriendlyId::UnfriendlyUtils)
64
+ end
65
+ end
66
+
67
+ Object.send :include, FriendlyId::ObjectUtils
68
+
69
+ # Considered unfriendly if object is an instance of an unfriendly class or
70
+ # one of its descendants.
71
+
72
+ FriendlyId::UNFRIENDLY_CLASSES.each { |klass| FriendlyId.mark_as_unfriendly(klass) }
73
+
74
+ ActiveSupport.on_load(:active_record) do
75
+ FriendlyId.mark_as_unfriendly(ActiveRecord::Base)
76
+ end
@@ -0,0 +1,50 @@
1
+ module FriendlyId
2
+ # @guide begin
3
+ #
4
+ # ## Reserved Words
5
+ #
6
+ # The {FriendlyId::Reserved Reserved} module adds the ability to exclude a list of
7
+ # words from use as FriendlyId slugs.
8
+ #
9
+ # With Ruby on Rails, FriendlyId's generator generates an initializer that
10
+ # reserves some words such as "new" and "edit" using {FriendlyId.defaults
11
+ # FriendlyId.defaults}.
12
+ #
13
+ # Note that the error messages for fields will appear on the field
14
+ # `:friendly_id`. If you are using Rails's scaffolded form errors display, then
15
+ # it will have no field to highlight. If you'd like to change this so that
16
+ # scaffolding works as expected, one way to accomplish this is to move the error
17
+ # message to a different field. For example:
18
+ #
19
+ # class Person < ActiveRecord::Base
20
+ # extend FriendlyId
21
+ # friendly_id :name, use: :slugged
22
+ #
23
+ # after_validation :move_friendly_id_error_to_name
24
+ #
25
+ # def move_friendly_id_error_to_name
26
+ # errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
27
+ # end
28
+ # end
29
+ #
30
+ # @guide end
31
+ module Reserved
32
+ # When included, this module adds configuration options to the model class's
33
+ # friendly_id_config.
34
+ def self.included(model_class)
35
+ model_class.class_eval do
36
+ friendly_id_config.class.send :include, Reserved::Configuration
37
+ validates_exclusion_of :friendly_id, in: ->(_) {
38
+ friendly_id_config.reserved_words || []
39
+ }
40
+ end
41
+ end
42
+
43
+ # This module adds the `:reserved_words` configuration option to
44
+ # {FriendlyId::Configuration FriendlyId::Configuration}.
45
+ module Configuration
46
+ attr_accessor :reserved_words
47
+ attr_accessor :treat_reserved_as_conflict
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,175 @@
1
+ require "friendly_id/slugged"
2
+
3
+ module FriendlyId
4
+ # @guide begin
5
+ #
6
+ # ## Unique Slugs by Scope
7
+ #
8
+ # The {FriendlyId::Scoped} module allows FriendlyId to generate unique slugs
9
+ # within a scope.
10
+ #
11
+ # This allows, for example, two restaurants in different cities to have the slug
12
+ # `joes-diner`:
13
+ #
14
+ # class Restaurant < ActiveRecord::Base
15
+ # extend FriendlyId
16
+ # belongs_to :city
17
+ # friendly_id :name, :use => :scoped, :scope => :city
18
+ # end
19
+ #
20
+ # class City < ActiveRecord::Base
21
+ # extend FriendlyId
22
+ # has_many :restaurants
23
+ # friendly_id :name, :use => :slugged
24
+ # end
25
+ #
26
+ # City.friendly.find("seattle").restaurants.friendly.find("joes-diner")
27
+ # City.friendly.find("chicago").restaurants.friendly.find("joes-diner")
28
+ #
29
+ # Without :scoped in this case, one of the restaurants would have the slug
30
+ # `joes-diner` and the other would have `joes-diner-f9f3789a-daec-4156-af1d-fab81aa16ee5`.
31
+ #
32
+ # The value for the `:scope` option can be the name of a `belongs_to` relation, or
33
+ # a column.
34
+ #
35
+ # Additionally, the `:scope` option can receive an array of scope values:
36
+ #
37
+ # class Cuisine < ActiveRecord::Base
38
+ # extend FriendlyId
39
+ # has_many :restaurants
40
+ # friendly_id :name, :use => :slugged
41
+ # end
42
+ #
43
+ # class City < ActiveRecord::Base
44
+ # extend FriendlyId
45
+ # has_many :restaurants
46
+ # friendly_id :name, :use => :slugged
47
+ # end
48
+ #
49
+ # class Restaurant < ActiveRecord::Base
50
+ # extend FriendlyId
51
+ # belongs_to :city
52
+ # friendly_id :name, :use => :scoped, :scope => [:city, :cuisine]
53
+ # end
54
+ #
55
+ # All supplied values will be used to determine scope.
56
+ #
57
+ # ### Finding Records by Friendly ID
58
+ #
59
+ # If you are using scopes your friendly ids may not be unique, so a simple find
60
+ # like:
61
+ #
62
+ # Restaurant.friendly.find("joes-diner")
63
+ #
64
+ # may return the wrong record. In these cases it's best to query through the
65
+ # relation:
66
+ #
67
+ # @city.restaurants.friendly.find("joes-diner")
68
+ #
69
+ # Alternatively, you could pass the scope value as a query parameter:
70
+ #
71
+ # Restaurant.where(:city_id => @city.id).friendly.find("joes-diner")
72
+ #
73
+ #
74
+ # ### Finding All Records That Match a Scoped ID
75
+ #
76
+ # Query the slug column directly:
77
+ #
78
+ # Restaurant.where(:slug => "joes-diner")
79
+ #
80
+ # ### Routes for Scoped Models
81
+ #
82
+ # Recall that FriendlyId is a database-centric library, and does not set up any
83
+ # routes for scoped models. You must do this yourself in your application. Here's
84
+ # an example of one way to set this up:
85
+ #
86
+ # # in routes.rb
87
+ # resources :cities do
88
+ # resources :restaurants
89
+ # end
90
+ #
91
+ # # in views
92
+ # <%= link_to 'Show', [@city, @restaurant] %>
93
+ #
94
+ # # in controllers
95
+ # @city = City.friendly.find(params[:city_id])
96
+ # @restaurant = @city.restaurants.friendly.find(params[:id])
97
+ #
98
+ # # URLs:
99
+ # http://example.org/cities/seattle/restaurants/joes-diner
100
+ # http://example.org/cities/chicago/restaurants/joes-diner
101
+ #
102
+ # @guide end
103
+ module Scoped
104
+ # FriendlyId::Config.use will invoke this method when present, to allow
105
+ # loading dependent modules prior to overriding them when necessary.
106
+ def self.setup(model_class)
107
+ model_class.friendly_id_config.use :slugged
108
+ end
109
+
110
+ # Sets up behavior and configuration options for FriendlyId's scoped slugs
111
+ # feature.
112
+ def self.included(model_class)
113
+ model_class.class_eval do
114
+ friendly_id_config.class.send :include, Configuration
115
+ end
116
+ end
117
+
118
+ def serialized_scope
119
+ friendly_id_config.scope_columns.sort.map { |column| "#{column}:#{send(column)}" }.join(",")
120
+ end
121
+
122
+ def scope_for_slug_generator
123
+ if friendly_id_config.uses?(:History)
124
+ return super
125
+ end
126
+ relation = self.class.base_class.unscoped.friendly
127
+ friendly_id_config.scope_columns.each do |column|
128
+ relation = relation.where(column => send(column))
129
+ end
130
+ primary_key_name = self.class.primary_key
131
+ relation.where(self.class.arel_table[primary_key_name].not_eq(send(primary_key_name)))
132
+ end
133
+ private :scope_for_slug_generator
134
+
135
+ def slug_generator
136
+ friendly_id_config.slug_generator_class.new(scope_for_slug_generator, friendly_id_config)
137
+ end
138
+ private :slug_generator
139
+
140
+ def should_generate_new_friendly_id?
141
+ (changed & friendly_id_config.scope_columns).any? || super
142
+ end
143
+
144
+ # This module adds the `:scope` configuration option to
145
+ # {FriendlyId::Configuration FriendlyId::Configuration}.
146
+ module Configuration
147
+ # Gets the scope value.
148
+ #
149
+ # When setting this value, the argument should be a symbol referencing a
150
+ # `belongs_to` relation, or a column.
151
+ #
152
+ # @return Symbol The scope value
153
+ attr_accessor :scope
154
+
155
+ # Gets the scope columns.
156
+ #
157
+ # Checks to see if the `:scope` option passed to
158
+ # {FriendlyId::Base#friendly_id} refers to a relation, and if so, returns
159
+ # the realtion's foreign key. Otherwise it assumes the option value was
160
+ # the name of column and returns it cast to a String.
161
+ #
162
+ # @return String The scope column
163
+ def scope_columns
164
+ [@scope].flatten.map { |s| (reflection_foreign_key(s) or s).to_s }
165
+ end
166
+
167
+ private
168
+
169
+ def reflection_foreign_key(scope)
170
+ reflection = model_class.reflections[scope] || model_class.reflections[scope.to_s]
171
+ reflection.try(:foreign_key)
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,69 @@
1
+ module FriendlyId
2
+ module SequentiallySlugged
3
+ class Calculator
4
+ attr_accessor :scope, :slug, :slug_column, :sequence_separator
5
+
6
+ def initialize(scope, slug, slug_column, sequence_separator, base_class)
7
+ @scope = scope
8
+ @slug = slug
9
+ table_name = scope.connection.quote_table_name(base_class.arel_table.name)
10
+ @slug_column = "#{table_name}.#{scope.connection.quote_column_name(slug_column)}"
11
+ @sequence_separator = sequence_separator
12
+ end
13
+
14
+ def next_slug
15
+ slug + sequence_separator + next_sequence_number.to_s
16
+ end
17
+
18
+ private
19
+
20
+ def conflict_query
21
+ base = "#{slug_column} = ? OR #{slug_column} LIKE ?"
22
+ # Awful hack for SQLite3, which does not pick up '\' as the escape character
23
+ # without this.
24
+ base << " ESCAPE '\\'" if /sqlite/i.match?(scope.connection.adapter_name)
25
+ base
26
+ end
27
+
28
+ def next_sequence_number
29
+ last_sequence_number ? last_sequence_number + 1 : 2
30
+ end
31
+
32
+ def last_sequence_number
33
+ # Reject slug_conflicts that doesn't come from the first_candidate
34
+ # Map all sequence numbers and take the maximum
35
+ slug_conflicts
36
+ .reject { |slug_conflict| !regexp.match(slug_conflict) }
37
+ .map { |slug_conflict| regexp.match(slug_conflict)[1].to_i }
38
+ .max
39
+ end
40
+
41
+ # Return the unnumbered (shortest) slug first, followed by the numbered ones
42
+ # in ascending order.
43
+ def ordering_query
44
+ "#{sql_length}(#{slug_column}) ASC, #{slug_column} ASC"
45
+ end
46
+
47
+ def regexp
48
+ /#{slug}#{sequence_separator}(\d+)\z/
49
+ end
50
+
51
+ def sequential_slug_matcher
52
+ # Underscores (matching a single character) and percent signs (matching
53
+ # any number of characters) need to be escaped. While this looks like
54
+ # an excessive number of backslashes, it is correct.
55
+ "#{slug}#{sequence_separator}".gsub(/[_%]/, '\\\\\&') + "%"
56
+ end
57
+
58
+ def slug_conflicts
59
+ scope
60
+ .where(conflict_query, slug, sequential_slug_matcher)
61
+ .order(Arel.sql(ordering_query)).pluck(Arel.sql(slug_column))
62
+ end
63
+
64
+ def sql_length
65
+ /sqlserver/i.match?(scope.connection.adapter_name) ? "LEN" : "LENGTH"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,40 @@
1
+ require_relative "sequentially_slugged/calculator"
2
+
3
+ module FriendlyId
4
+ module SequentiallySlugged
5
+ def self.setup(model_class)
6
+ model_class.friendly_id_config.use :slugged
7
+ end
8
+
9
+ def resolve_friendly_id_conflict(candidate_slugs)
10
+ candidate = candidate_slugs.first
11
+ return if candidate.nil?
12
+
13
+ Calculator.new(
14
+ scope_for_slug_generator,
15
+ candidate,
16
+ slug_column,
17
+ friendly_id_config.sequence_separator,
18
+ slug_base_class
19
+ ).next_slug
20
+ end
21
+
22
+ private
23
+
24
+ def slug_base_class
25
+ if friendly_id_config.uses?(:history)
26
+ Slug
27
+ else
28
+ self.class.base_class
29
+ end
30
+ end
31
+
32
+ def slug_column
33
+ if friendly_id_config.uses?(:history)
34
+ :slug
35
+ else
36
+ friendly_id_config.slug_column
37
+ end
38
+ end
39
+ end
40
+ end