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,275 @@
1
+ module FriendlyId
2
+ # @guide begin
3
+ #
4
+ # ## Setting Up FriendlyId in Your Model
5
+ #
6
+ # To use FriendlyId in your ActiveRecord models, you must first either extend or
7
+ # include the FriendlyId module (it makes no difference), then invoke the
8
+ # {FriendlyId::Base#friendly_id friendly_id} method to configure your desired
9
+ # options:
10
+ #
11
+ # class Foo < ActiveRecord::Base
12
+ # include FriendlyId
13
+ # friendly_id :bar, :use => [:slugged, :simple_i18n]
14
+ # end
15
+ #
16
+ # The most important option is `:use`, which you use to tell FriendlyId which
17
+ # addons it should use. See the documentation for {FriendlyId::Base#friendly_id} for a list of all
18
+ # available addons, or skim through the rest of the docs to get a high-level
19
+ # overview.
20
+ #
21
+ # *A note about single table inheritance (STI): you must extend FriendlyId in
22
+ # all classes that participate in STI, both your parent classes and their
23
+ # children.*
24
+ #
25
+ # ### The Default Setup: Simple Models
26
+ #
27
+ # The simplest way to use FriendlyId is with a model that has a uniquely indexed
28
+ # column with no spaces or special characters, and that is seldom or never
29
+ # updated. The most common example of this is a user name:
30
+ #
31
+ # class User < ActiveRecord::Base
32
+ # extend FriendlyId
33
+ # friendly_id :login
34
+ # validates_format_of :login, :with => /\A[a-z0-9]+\z/i
35
+ # end
36
+ #
37
+ # @user = User.friendly.find "joe" # the old User.find(1) still works, too
38
+ # @user.to_param # returns "joe"
39
+ # redirect_to @user # the URL will be /users/joe
40
+ #
41
+ # In this case, FriendlyId assumes you want to use the column as-is; it will never
42
+ # modify the value of the column, and your application should ensure that the
43
+ # value is unique and admissible in a URL:
44
+ #
45
+ # class City < ActiveRecord::Base
46
+ # extend FriendlyId
47
+ # friendly_id :name
48
+ # end
49
+ #
50
+ # @city.friendly.find "Viña del Mar"
51
+ # redirect_to @city # the URL will be /cities/Viña%20del%20Mar
52
+ #
53
+ # Writing the code to process an arbitrary string into a good identifier for use
54
+ # in a URL can be repetitive and surprisingly tricky, so for this reason it's
55
+ # often better and easier to use {FriendlyId::Slugged slugs}.
56
+ #
57
+ # @guide end
58
+ module Base
59
+ # Configure FriendlyId's behavior in a model.
60
+ #
61
+ # class Post < ActiveRecord::Base
62
+ # extend FriendlyId
63
+ # friendly_id :title, :use => :slugged
64
+ # end
65
+ #
66
+ # When given the optional block, this method will yield the class's instance
67
+ # of {FriendlyId::Configuration} to the block before evaluating other
68
+ # arguments, so configuration values set in the block may be overwritten by
69
+ # the arguments. This order was chosen to allow passing the same proc to
70
+ # multiple models, while being able to override the values it sets. Here is
71
+ # a contrived example:
72
+ #
73
+ # $friendly_id_config_proc = Proc.new do |config|
74
+ # config.base = :name
75
+ # config.use :slugged
76
+ # end
77
+ #
78
+ # class Foo < ActiveRecord::Base
79
+ # extend FriendlyId
80
+ # friendly_id &$friendly_id_config_proc
81
+ # end
82
+ #
83
+ # class Bar < ActiveRecord::Base
84
+ # extend FriendlyId
85
+ # friendly_id :title, &$friendly_id_config_proc
86
+ # end
87
+ #
88
+ # However, it's usually better to use {FriendlyId.defaults} for this:
89
+ #
90
+ # FriendlyId.defaults do |config|
91
+ # config.base = :name
92
+ # config.use :slugged
93
+ # end
94
+ #
95
+ # class Foo < ActiveRecord::Base
96
+ # extend FriendlyId
97
+ # end
98
+ #
99
+ # class Bar < ActiveRecord::Base
100
+ # extend FriendlyId
101
+ # friendly_id :title
102
+ # end
103
+ #
104
+ # In general you should use the block syntax either because of your personal
105
+ # aesthetic preference, or because you need to share some functionality
106
+ # between multiple models that can't be well encapsulated by
107
+ # {FriendlyId.defaults}.
108
+ #
109
+ # ### Order Method Calls in a Block vs Ordering Options
110
+ #
111
+ # When calling this method without a block, you may set the hash options in
112
+ # any order.
113
+ #
114
+ # However, when using block-style invocation, be sure to call
115
+ # FriendlyId::Configuration's {FriendlyId::Configuration#use use} method
116
+ # *prior* to the associated configuration options, because it will include
117
+ # modules into your class, and these modules in turn may add required
118
+ # configuration options to the `@friendly_id_configuraton`'s class:
119
+ #
120
+ # class Person < ActiveRecord::Base
121
+ # friendly_id do |config|
122
+ # # This will work
123
+ # config.use :slugged
124
+ # config.sequence_separator = ":"
125
+ # end
126
+ # end
127
+ #
128
+ # class Person < ActiveRecord::Base
129
+ # friendly_id do |config|
130
+ # # This will fail
131
+ # config.sequence_separator = ":"
132
+ # config.use :slugged
133
+ # end
134
+ # end
135
+ #
136
+ # ### Including Your Own Modules
137
+ #
138
+ # Because :use can accept a name or a Module, {FriendlyId.defaults defaults}
139
+ # can be a convenient place to set up behavior common to all classes using
140
+ # FriendlyId. You can include any module, or more conveniently, define one
141
+ # on-the-fly. For example, let's say you want to make
142
+ # [Babosa](http://github.com/norman/babosa) the default slugging library in
143
+ # place of Active Support, and transliterate all slugs from Russian Cyrillic
144
+ # to ASCII:
145
+ #
146
+ # require "babosa"
147
+ #
148
+ # FriendlyId.defaults do |config|
149
+ # config.base = :name
150
+ # config.use :slugged
151
+ # config.use Module.new {
152
+ # def normalize_friendly_id(text)
153
+ # text.to_slug.normalize! :transliterations => [:russian, :latin]
154
+ # end
155
+ # }
156
+ # end
157
+ #
158
+ #
159
+ # @option options [Symbol,Module] :use The addon or name of an addon to use.
160
+ # By default, FriendlyId provides {FriendlyId::Slugged :slugged},
161
+ # {FriendlyId::Reserved :finders}, {FriendlyId::History :history},
162
+ # {FriendlyId::Reserved :reserved}, {FriendlyId::Scoped :scoped}, and
163
+ # {FriendlyId::SimpleI18n :simple_i18n}.
164
+ #
165
+ # @option options [Array] :reserved_words Available when using `:reserved`,
166
+ # which is loaded by default. Sets an array of words banned for use as
167
+ # the basis of a friendly_id. By default this includes "edit" and "new".
168
+ #
169
+ # @option options [Symbol] :scope Available when using `:scoped`.
170
+ # Sets the relation or column used to scope generated friendly ids. This
171
+ # option has no default value.
172
+ #
173
+ # @option options [Symbol] :sequence_separator Available when using `:slugged`.
174
+ # Configures the sequence of characters used to separate a slug from a
175
+ # sequence. Defaults to `-`.
176
+ #
177
+ # @option options [Symbol] :slug_column Available when using `:slugged`.
178
+ # Configures the name of the column where FriendlyId will store the slug.
179
+ # Defaults to `:slug`.
180
+ #
181
+ # @option options [Integer] :slug_limit Available when using `:slugged`.
182
+ # Configures the limit of the slug. This option has no default value.
183
+ #
184
+ # @option options [Symbol] :slug_generator_class Available when using `:slugged`.
185
+ # Sets the class used to generate unique slugs. You should not specify this
186
+ # unless you're doing some extensive hacking on FriendlyId. Defaults to
187
+ # {FriendlyId::SlugGenerator}.
188
+ #
189
+ # @yield Provides access to the model class's friendly_id_config, which
190
+ # allows an alternate configuration syntax, and conditional configuration
191
+ # logic.
192
+ #
193
+ # @option options [Symbol,Boolean] :dependent Available when using `:history`.
194
+ # Sets the value used for the slugged association's dependent option. Use
195
+ # `false` if you do not want to dependently destroy the associated slugged
196
+ # record. Defaults to `:destroy`.
197
+ #
198
+ # @option options [Symbol] :routes When set to anything other than :friendly,
199
+ # ensures that all routes generated by default do *not* use the slug. This
200
+ # allows `form_for` and `polymorphic_path` to continue to generate paths like
201
+ # `/team/1` instead of `/team/number-one`. You can still generate paths
202
+ # like the latter using: team_path(team.slug). When set to :friendly, or
203
+ # omitted, the default friendly_id behavior is maintained.
204
+ #
205
+ # @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
206
+ def friendly_id(base = nil, options = {}, &block)
207
+ yield friendly_id_config if block
208
+ friendly_id_config.dependent = options.delete :dependent
209
+ friendly_id_config.use options.delete :use
210
+ friendly_id_config.send :set, base ? options.merge(base: base) : options
211
+ include Model
212
+ end
213
+
214
+ # Returns a scope that includes the friendly finders.
215
+ # @see FriendlyId::FinderMethods
216
+ def friendly
217
+ # Guess what? This causes Rails to invoke `extend` on the scope, which has
218
+ # the well-known effect of blowing away Ruby's method cache. It would be
219
+ # possible to make this more performant by subclassing the model's
220
+ # relation class, extending that, and returning an instance of it in this
221
+ # method. FriendlyId 4.0 did something similar. However in 5.0 I've
222
+ # decided to only use Rails's public API in order to improve compatibility
223
+ # and maintainability. If you'd like to improve the performance, your
224
+ # efforts would be best directed at improving it at the root cause
225
+ # of the problem - in Rails - because it would benefit more people.
226
+ all.extending(friendly_id_config.finder_methods)
227
+ end
228
+
229
+ # Returns the model class's {FriendlyId::Configuration friendly_id_config}.
230
+ # @note In the case of Single Table Inheritance (STI), this method will
231
+ # duplicate the parent class's FriendlyId::Configuration and relation class
232
+ # on first access. If you're concerned about thread safety, then be sure
233
+ # to invoke {#friendly_id} in your class for each model.
234
+ def friendly_id_config
235
+ @friendly_id_config ||= base_class.friendly_id_config.dup.tap do |config|
236
+ config.model_class = self
237
+ end
238
+ end
239
+
240
+ def primary_key_type
241
+ @primary_key_type ||= columns_hash[primary_key].type
242
+ end
243
+ end
244
+
245
+ # Instance methods that will be added to all classes using FriendlyId.
246
+ module Model
247
+ def self.included(model_class)
248
+ return if model_class.respond_to?(:friendly)
249
+ end
250
+
251
+ # Convenience method for accessing the class method of the same name.
252
+ def friendly_id_config
253
+ self.class.friendly_id_config
254
+ end
255
+
256
+ # Get the instance's friendly_id.
257
+ def friendly_id
258
+ send friendly_id_config.query_field
259
+ end
260
+
261
+ # Either the friendly_id, or the numeric id cast to a string.
262
+ def to_param
263
+ if friendly_id_config.routes == :friendly
264
+ friendly_id.presence.to_param || super
265
+ else
266
+ super
267
+ end
268
+ end
269
+
270
+ # Clears slug on duplicate records when calling `dup`.
271
+ def dup
272
+ super.tap { |duplicate| duplicate.slug = nil if duplicate.respond_to?("slug=") }
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,71 @@
1
+ require "securerandom"
2
+
3
+ module FriendlyId
4
+ # This class provides the slug candidate functionality.
5
+ # @see FriendlyId::Slugged
6
+ class Candidates
7
+ include Enumerable
8
+
9
+ def initialize(object, *array)
10
+ @object = object
11
+ @raw_candidates = to_candidate_array(object, array.flatten(1))
12
+ end
13
+
14
+ def each(*args, &block)
15
+ return candidates unless block
16
+ candidates.each { |candidate| yield candidate }
17
+ end
18
+
19
+ private
20
+
21
+ def candidates
22
+ @candidates ||= begin
23
+ candidates = normalize(@raw_candidates)
24
+ filter(candidates)
25
+ end
26
+ end
27
+
28
+ def normalize(candidates)
29
+ candidates.map do |candidate|
30
+ @object.normalize_friendly_id(candidate.map(&:call).join(" "))
31
+ end.select { |x| wanted?(x) }
32
+ end
33
+
34
+ def filter(candidates)
35
+ unless candidates.all? { |x| reserved?(x) }
36
+ candidates.reject! { |x| reserved?(x) }
37
+ end
38
+ candidates
39
+ end
40
+
41
+ def to_candidate_array(object, array)
42
+ array.map do |candidate|
43
+ case candidate
44
+ when String
45
+ [-> { candidate }]
46
+ when Array
47
+ to_candidate_array(object, candidate).flatten
48
+ when Symbol
49
+ [object.method(candidate)]
50
+ else
51
+ if candidate.respond_to?(:call)
52
+ [candidate]
53
+ else
54
+ [-> { candidate.to_s }]
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def wanted?(slug)
61
+ slug.present?
62
+ end
63
+
64
+ def reserved?(slug)
65
+ config = @object.friendly_id_config
66
+ return false unless config.uses? ::FriendlyId::Reserved
67
+ return false unless config.reserved_words
68
+ config.reserved_words.include?(slug)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,111 @@
1
+ module FriendlyId
2
+ # The configuration parameters passed to {Base#friendly_id} will be stored in
3
+ # this object.
4
+ class Configuration
5
+ attr_writer :base
6
+
7
+ # The default configuration options.
8
+ attr_reader :defaults
9
+
10
+ # The modules in use
11
+ attr_reader :modules
12
+
13
+ # The model class that this configuration belongs to.
14
+ # @return ActiveRecord::Base
15
+ attr_accessor :model_class
16
+
17
+ # The module to use for finders
18
+ attr_accessor :finder_methods
19
+
20
+ # The value used for the slugged association's dependent option
21
+ attr_accessor :dependent
22
+
23
+ # Route generation preferences
24
+ attr_accessor :routes
25
+
26
+ def initialize(model_class, values = nil)
27
+ @base = nil
28
+ @model_class = model_class
29
+ @defaults = {}
30
+ @modules = []
31
+ @finder_methods = FriendlyId::FinderMethods
32
+ self.routes = :friendly
33
+ set values
34
+ end
35
+
36
+ # Lets you specify the addon modules to use with FriendlyId.
37
+ #
38
+ # This method is invoked by {FriendlyId::Base#friendly_id friendly_id} when
39
+ # passing the `:use` option, or when using {FriendlyId::Base#friendly_id
40
+ # friendly_id} with a block.
41
+ #
42
+ # @example
43
+ # class Book < ActiveRecord::Base
44
+ # extend FriendlyId
45
+ # friendly_id :name, :use => :slugged
46
+ # end
47
+ #
48
+ # @param [#to_s,Module] modules Arguments should be Modules, or symbols or
49
+ # strings that correspond with the name of an addon to use with FriendlyId.
50
+ # By default FriendlyId provides `:slugged`, `:finders`, `:history`,
51
+ # `:reserved`, `:simple_i18n`, and `:scoped`.
52
+ def use(*modules)
53
+ modules.to_a.flatten.compact.map do |object|
54
+ mod = get_module(object)
55
+ mod.setup(@model_class) if mod.respond_to?(:setup)
56
+ @model_class.send(:include, mod) unless uses? object
57
+ end
58
+ end
59
+
60
+ # Returns whether the given module is in use.
61
+ def uses?(mod)
62
+ @model_class < get_module(mod)
63
+ end
64
+
65
+ # The column that FriendlyId will use to find the record when querying by
66
+ # friendly id.
67
+ #
68
+ # This method is generally only used internally by FriendlyId.
69
+ # @return String
70
+ def query_field
71
+ base.to_s
72
+ end
73
+
74
+ # The base column or method used by FriendlyId as the basis of a friendly id
75
+ # or slug.
76
+ #
77
+ # For models that don't use {FriendlyId::Slugged}, this is the column that
78
+ # is used to store the friendly id. For models using {FriendlyId::Slugged},
79
+ # the base is a column or method whose value is used as the basis of the
80
+ # slug.
81
+ #
82
+ # For example, if you have a model representing blog posts and that uses
83
+ # slugs, you likely will want to use the "title" attribute as the base, and
84
+ # FriendlyId will take care of transforming the human-readable title into
85
+ # something suitable for use in a URL.
86
+ #
87
+ # If you pass an argument, it will be used as the base. Otherwise the current
88
+ # value is returned.
89
+ #
90
+ # @param value A symbol referencing a column or method in the model. This
91
+ # value is usually set by passing it as the first argument to
92
+ # {FriendlyId::Base#friendly_id friendly_id}.
93
+ def base(*value)
94
+ if value.empty?
95
+ @base
96
+ else
97
+ self.base = value.first
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def get_module(object)
104
+ Module === object ? object : FriendlyId.const_get(object.to_s.titleize.camelize.gsub(/\s+/, ""))
105
+ end
106
+
107
+ def set(values)
108
+ values&.each { |name, value| send "#{name}=", value }
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,123 @@
1
+ module FriendlyId
2
+ module FinderMethods
3
+ # Finds a record using the given id.
4
+ #
5
+ # If the id is "unfriendly", it will call the original find method.
6
+ # If the id is a numeric string like '123' it will first look for a friendly
7
+ # id matching '123' and then fall back to looking for a record with the
8
+ # numeric id '123'.
9
+ #
10
+ # @param [Boolean] allow_nil (default: false)
11
+ # Use allow_nil: true if you'd like the finder to return nil instead of
12
+ # raising ActivRecord::RecordNotFound
13
+ #
14
+ # ### Example
15
+ #
16
+ # MyModel.friendly.find("bad-slug")
17
+ # #=> raise ActiveRecord::RecordNotFound
18
+ #
19
+ # MyModel.friendly.find("bad-slug", allow_nil: true)
20
+ # #=> nil
21
+ #
22
+ # Since FriendlyId 5.0, if the id is a nonnumeric string like '123-foo' it
23
+ # will *only* search by friendly id and not fall back to the regular find
24
+ # method.
25
+ #
26
+ # If you want to search only by the friendly id, use {#find_by_friendly_id}.
27
+ # @raise ActiveRecord::RecordNotFound
28
+ def find(*args, allow_nil: false)
29
+ id = args.first
30
+ return super(*args) if args.count != 1 || id.unfriendly_id?
31
+ first_by_friendly_id(id).tap { |result| return result unless result.nil? }
32
+ return super(*args) if potential_primary_key?(id)
33
+
34
+ raise_not_found_exception(id) unless allow_nil
35
+ rescue ActiveRecord::RecordNotFound => exception
36
+ raise exception unless allow_nil
37
+ end
38
+
39
+ # Returns true if a record with the given id exists.
40
+ def exists?(conditions = :none)
41
+ return super if conditions.unfriendly_id?
42
+ return true if exists_by_friendly_id?(conditions)
43
+ super
44
+ end
45
+
46
+ # Finds exclusively by the friendly id, completely bypassing original
47
+ # `find`.
48
+ # @raise ActiveRecord::RecordNotFound
49
+ def find_by_friendly_id(id)
50
+ first_by_friendly_id(id) or raise_not_found_exception(id)
51
+ end
52
+
53
+ def exists_by_friendly_id?(id)
54
+ where(friendly_id_config.query_field => parse_friendly_id(id)).exists?
55
+ end
56
+
57
+ private
58
+
59
+ def potential_primary_key?(id)
60
+ key_type = primary_key_type
61
+ # Hook for "ActiveModel::Type::Integer" instance.
62
+ key_type = key_type.type if key_type.respond_to?(:type)
63
+ case key_type
64
+ when :integer
65
+ begin
66
+ Integer(id, 10)
67
+ rescue
68
+ false
69
+ end
70
+ when :uuid
71
+ id.match(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/)
72
+ else
73
+ true
74
+ end
75
+ end
76
+
77
+ def first_by_friendly_id(id)
78
+ find_by(friendly_id_config.query_field => parse_friendly_id(id))
79
+ end
80
+
81
+ # Parse the given value to make it suitable for use as a slug according to
82
+ # your application's rules.
83
+ #
84
+ # This method is not intended to be invoked directly; FriendlyId uses it
85
+ # internally to process a slug into string to use as a finder.
86
+ #
87
+ # However, if FriendlyId's default slug parsing doesn't suit your needs,
88
+ # you can override this method in your model class to control exactly how
89
+ # slugs are generated.
90
+ #
91
+ # ### Example
92
+ #
93
+ # class Person < ActiveRecord::Base
94
+ # extend FriendlyId
95
+ # friendly_id :name_and_location
96
+ #
97
+ # def name_and_location
98
+ # "#{name} from #{location}"
99
+ # end
100
+ #
101
+ # # Use default slug, but lower case
102
+ # # If `id` is "Jane-Doe" or "JANE-DOE", this finds data by "jane-doe"
103
+ # def parse_friendly_id(slug)
104
+ # super.downcase
105
+ # end
106
+ # end
107
+ #
108
+ # @param [#to_s] value The slug to be parsed.
109
+ # @return The parsed slug, which is not modified by default.
110
+ def parse_friendly_id(value)
111
+ value
112
+ end
113
+
114
+ def raise_not_found_exception(id)
115
+ message = "can't find record with friendly id: #{id.inspect}"
116
+ if ActiveRecord.version < Gem::Version.create("5.0")
117
+ raise ActiveRecord::RecordNotFound.new(message)
118
+ else
119
+ raise ActiveRecord::RecordNotFound.new(message, name, friendly_id_config.query_field, id)
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,92 @@
1
+ module FriendlyId
2
+ # @guide begin
3
+ #
4
+ # ## Performing Finds with FriendlyId
5
+ #
6
+ # FriendlyId offers enhanced finders which will search for your record by
7
+ # friendly id, and fall back to the numeric id if necessary. This makes it easy
8
+ # to add FriendlyId to an existing application with minimal code modification.
9
+ #
10
+ # By default, these methods are available only on the `friendly` scope:
11
+ #
12
+ # Restaurant.friendly.find('plaza-diner') #=> works
13
+ # Restaurant.friendly.find(23) #=> also works
14
+ # Restaurant.find(23) #=> still works
15
+ # Restaurant.find('plaza-diner') #=> will not work
16
+ #
17
+ # ### Restoring FriendlyId 4.0-style finders
18
+ #
19
+ # Prior to version 5.0, FriendlyId overrode the default finder methods to perform
20
+ # friendly finds all the time. This required modifying parts of Rails that did
21
+ # not have a public API, which was harder to maintain and at times caused
22
+ # compatiblity problems. In 5.0 we decided to change the library's defaults and add
23
+ # the friendly finder methods only to the `friendly` scope in order to boost
24
+ # compatiblity. However, you can still opt-in to original functionality very
25
+ # easily by using the `:finders` addon:
26
+ #
27
+ # class Restaurant < ActiveRecord::Base
28
+ # extend FriendlyId
29
+ #
30
+ # scope :active, -> {where(:active => true)}
31
+ #
32
+ # friendly_id :name, :use => [:slugged, :finders]
33
+ # end
34
+ #
35
+ # Restaurant.friendly.find('plaza-diner') #=> works
36
+ # Restaurant.find('plaza-diner') #=> now also works
37
+ # Restaurant.active.find('plaza-diner') #=> now also works
38
+ #
39
+ # ### Updating your application to use FriendlyId's finders
40
+ #
41
+ # Unless you've chosen to use the `:finders` addon, be sure to modify the finders
42
+ # in your controllers to use the `friendly` scope. For example:
43
+ #
44
+ # # before
45
+ # def set_restaurant
46
+ # @restaurant = Restaurant.find(params[:id])
47
+ # end
48
+ #
49
+ # # after
50
+ # def set_restaurant
51
+ # @restaurant = Restaurant.friendly.find(params[:id])
52
+ # end
53
+ #
54
+ # #### Active Admin
55
+ #
56
+ # Unless you use the `:finders` addon, you should modify your admin controllers
57
+ # for models that use FriendlyId with something similar to the following:
58
+ #
59
+ # controller do
60
+ # def find_resource
61
+ # scoped_collection.friendly.find(params[:id])
62
+ # end
63
+ # end
64
+ #
65
+ # @guide end
66
+ module Finders
67
+ module ClassMethods
68
+ if (ActiveRecord::VERSION::MAJOR == 4) && (ActiveRecord::VERSION::MINOR == 0)
69
+ def relation_delegate_class(klass)
70
+ relation_class_name = :"#{klass.to_s.gsub("::", "_")}_#{to_s.gsub("::", "_")}"
71
+ klass.const_get(relation_class_name)
72
+ end
73
+ end
74
+ end
75
+
76
+ def self.setup(model_class)
77
+ model_class.instance_eval do
78
+ relation.class.send(:include, friendly_id_config.finder_methods)
79
+ if (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) || ActiveRecord::VERSION::MAJOR >= 5
80
+ model_class.send(:extend, friendly_id_config.finder_methods)
81
+ end
82
+ end
83
+
84
+ # Support for friendly finds on associations for Rails 4.0.1 and above.
85
+ if ::ActiveRecord.const_defined?("AssociationRelation")
86
+ model_class.extend(ClassMethods)
87
+ association_relation_delegate_class = model_class.relation_delegate_class(::ActiveRecord::AssociationRelation)
88
+ association_relation_delegate_class.send(:include, model_class.friendly_id_config.finder_methods)
89
+ end
90
+ end
91
+ end
92
+ end