mini-fast-sys 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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/mini-fast-sys.gemspec +12 -0
  3. data/ransack-4.4.1/LICENSE +20 -0
  4. data/ransack-4.4.1/README.md +101 -0
  5. data/ransack-4.4.1/lib/polyamorous/activerecord/join_association.rb +70 -0
  6. data/ransack-4.4.1/lib/polyamorous/activerecord/join_association_7_2.rb +55 -0
  7. data/ransack-4.4.1/lib/polyamorous/activerecord/join_dependency.rb +102 -0
  8. data/ransack-4.4.1/lib/polyamorous/activerecord/reflection.rb +11 -0
  9. data/ransack-4.4.1/lib/polyamorous/join.rb +70 -0
  10. data/ransack-4.4.1/lib/polyamorous/polyamorous.rb +27 -0
  11. data/ransack-4.4.1/lib/polyamorous/swapping_reflection_class.rb +11 -0
  12. data/ransack-4.4.1/lib/polyamorous/tree_node.rb +7 -0
  13. data/ransack-4.4.1/lib/ransack/active_record.rb +14 -0
  14. data/ransack-4.4.1/lib/ransack/adapters/active_record/base.rb +152 -0
  15. data/ransack-4.4.1/lib/ransack/adapters/active_record/context.rb +373 -0
  16. data/ransack-4.4.1/lib/ransack/configuration.rb +209 -0
  17. data/ransack-4.4.1/lib/ransack/constants.rb +176 -0
  18. data/ransack-4.4.1/lib/ransack/context.rb +190 -0
  19. data/ransack-4.4.1/lib/ransack/helpers/form_builder.rb +277 -0
  20. data/ransack-4.4.1/lib/ransack/helpers/form_helper.rb +311 -0
  21. data/ransack-4.4.1/lib/ransack/helpers.rb +2 -0
  22. data/ransack-4.4.1/lib/ransack/invalid_search_error.rb +3 -0
  23. data/ransack-4.4.1/lib/ransack/locale/ar.yml +70 -0
  24. data/ransack-4.4.1/lib/ransack/locale/az.yml +70 -0
  25. data/ransack-4.4.1/lib/ransack/locale/bg.yml +70 -0
  26. data/ransack-4.4.1/lib/ransack/locale/ca.yml +70 -0
  27. data/ransack-4.4.1/lib/ransack/locale/cs.yml +70 -0
  28. data/ransack-4.4.1/lib/ransack/locale/da.yml +70 -0
  29. data/ransack-4.4.1/lib/ransack/locale/de.yml +70 -0
  30. data/ransack-4.4.1/lib/ransack/locale/el.yml +70 -0
  31. data/ransack-4.4.1/lib/ransack/locale/en.yml +70 -0
  32. data/ransack-4.4.1/lib/ransack/locale/es.yml +70 -0
  33. data/ransack-4.4.1/lib/ransack/locale/fa.yml +70 -0
  34. data/ransack-4.4.1/lib/ransack/locale/fi.yml +71 -0
  35. data/ransack-4.4.1/lib/ransack/locale/fr.yml +70 -0
  36. data/ransack-4.4.1/lib/ransack/locale/hu.yml +70 -0
  37. data/ransack-4.4.1/lib/ransack/locale/id.yml +70 -0
  38. data/ransack-4.4.1/lib/ransack/locale/it.yml +70 -0
  39. data/ransack-4.4.1/lib/ransack/locale/ja.yml +70 -0
  40. data/ransack-4.4.1/lib/ransack/locale/ko.yml +70 -0
  41. data/ransack-4.4.1/lib/ransack/locale/nl.yml +70 -0
  42. data/ransack-4.4.1/lib/ransack/locale/pt-BR.yml +70 -0
  43. data/ransack-4.4.1/lib/ransack/locale/ro.yml +70 -0
  44. data/ransack-4.4.1/lib/ransack/locale/ru.yml +70 -0
  45. data/ransack-4.4.1/lib/ransack/locale/sk.yml +70 -0
  46. data/ransack-4.4.1/lib/ransack/locale/sv.yml +70 -0
  47. data/ransack-4.4.1/lib/ransack/locale/tr.yml +70 -0
  48. data/ransack-4.4.1/lib/ransack/locale/uk.yml +72 -0
  49. data/ransack-4.4.1/lib/ransack/locale/zh-CN.yml +70 -0
  50. data/ransack-4.4.1/lib/ransack/locale/zh-TW.yml +70 -0
  51. data/ransack-4.4.1/lib/ransack/naming.rb +58 -0
  52. data/ransack-4.4.1/lib/ransack/nodes/attribute.rb +59 -0
  53. data/ransack-4.4.1/lib/ransack/nodes/bindable.rb +52 -0
  54. data/ransack-4.4.1/lib/ransack/nodes/condition.rb +399 -0
  55. data/ransack-4.4.1/lib/ransack/nodes/grouping.rb +206 -0
  56. data/ransack-4.4.1/lib/ransack/nodes/node.rb +34 -0
  57. data/ransack-4.4.1/lib/ransack/nodes/sort.rb +54 -0
  58. data/ransack-4.4.1/lib/ransack/nodes/value.rb +115 -0
  59. data/ransack-4.4.1/lib/ransack/predicate.rb +75 -0
  60. data/ransack-4.4.1/lib/ransack/ransacker.rb +24 -0
  61. data/ransack-4.4.1/lib/ransack/search.rb +195 -0
  62. data/ransack-4.4.1/lib/ransack/translate.rb +156 -0
  63. data/ransack-4.4.1/lib/ransack/version.rb +3 -0
  64. data/ransack-4.4.1/lib/ransack/visitor.rb +87 -0
  65. data/ransack-4.4.1/lib/ransack.rb +33 -0
  66. data/ransack-4.4.1/spec/console.rb +13 -0
  67. data/ransack-4.4.1/spec/factories/articles.rb +7 -0
  68. data/ransack-4.4.1/spec/factories/comments.rb +7 -0
  69. data/ransack-4.4.1/spec/factories/notes.rb +13 -0
  70. data/ransack-4.4.1/spec/factories/people.rb +10 -0
  71. data/ransack-4.4.1/spec/factories/tags.rb +5 -0
  72. data/ransack-4.4.1/spec/helpers/polyamorous_helper.rb +13 -0
  73. data/ransack-4.4.1/spec/helpers/ransack_helper.rb +9 -0
  74. data/ransack-4.4.1/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  75. data/ransack-4.4.1/spec/polyamorous/join_association_spec.rb +29 -0
  76. data/ransack-4.4.1/spec/polyamorous/join_dependency_spec.rb +80 -0
  77. data/ransack-4.4.1/spec/polyamorous/join_spec.rb +19 -0
  78. data/ransack-4.4.1/spec/ransack/adapters/active_record/base_spec.rb +943 -0
  79. data/ransack-4.4.1/spec/ransack/adapters/active_record/context_spec.rb +219 -0
  80. data/ransack-4.4.1/spec/ransack/configuration_spec.rb +201 -0
  81. data/ransack-4.4.1/spec/ransack/helpers/form_builder_spec.rb +169 -0
  82. data/ransack-4.4.1/spec/ransack/helpers/form_helper_spec.rb +1075 -0
  83. data/ransack-4.4.1/spec/ransack/invalid_search_error_spec.rb +27 -0
  84. data/ransack-4.4.1/spec/ransack/nodes/condition_spec.rb +333 -0
  85. data/ransack-4.4.1/spec/ransack/nodes/grouping_spec.rb +111 -0
  86. data/ransack-4.4.1/spec/ransack/nodes/value_spec.rb +126 -0
  87. data/ransack-4.4.1/spec/ransack/predicate_spec.rb +504 -0
  88. data/ransack-4.4.1/spec/ransack/ransacker_spec.rb +69 -0
  89. data/ransack-4.4.1/spec/ransack/search_spec.rb +821 -0
  90. data/ransack-4.4.1/spec/ransack/translate_spec.rb +16 -0
  91. data/ransack-4.4.1/spec/spec_helper.rb +52 -0
  92. data/ransack-4.4.1/spec/support/en.yml +18 -0
  93. data/ransack-4.4.1/spec/support/schema.rb +419 -0
  94. metadata +133 -0
@@ -0,0 +1,373 @@
1
+ require 'ransack/context'
2
+ require 'polyamorous/polyamorous'
3
+
4
+ module Ransack
5
+ module Adapters
6
+ module ActiveRecord
7
+ class Context < ::Ransack::Context
8
+
9
+ def relation_for(object)
10
+ object.all
11
+ end
12
+
13
+ def type_for(attr)
14
+ return nil unless attr && attr.valid?
15
+ relation = attr.arel_attribute.relation
16
+ name = attr.arel_attribute.name.to_s
17
+ table = relation.respond_to?(:table_name) ? relation.table_name : relation.name
18
+ schema_cache = self.klass.connection.schema_cache
19
+ unless schema_cache.send(:data_source_exists?, table)
20
+ raise "No table named #{table} exists."
21
+ end
22
+ column = attr.klass.columns.find { |column| column.name == name }
23
+ column&.type
24
+ end
25
+
26
+ def evaluate(search, opts = {})
27
+ viz = Visitor.new
28
+ relation = @object.where(viz.accept(search.base))
29
+
30
+ if search.sorts.any?
31
+ relation = relation.except(:order)
32
+ # Rather than applying all of the search's sorts in one fell swoop,
33
+ # as the original implementation does, we apply one at a time.
34
+ #
35
+ # If the sort (returned by the Visitor above) is a symbol, we know
36
+ # that it represents a scope on the model and we can apply that
37
+ # scope.
38
+ #
39
+ # Otherwise, we fall back to the applying the sort with the "order"
40
+ # method as the original implementation did. Actually the original
41
+ # implementation used "reorder," which was overkill since we already
42
+ # have a clean slate after "relation.except(:order)" above.
43
+ viz.accept(search.sorts).each do |scope_or_sort|
44
+ if scope_or_sort.is_a?(Symbol)
45
+ relation = relation.send(scope_or_sort)
46
+ else
47
+ case Ransack.options[:postgres_fields_sort_option]
48
+ when :nulls_first
49
+ scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
50
+ when :nulls_last
51
+ scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
52
+ when :nulls_always_first
53
+ scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
54
+ when :nulls_always_last
55
+ scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
56
+ end
57
+
58
+ relation = relation.order(scope_or_sort)
59
+ end
60
+ end
61
+ end
62
+
63
+ opts[:distinct] ? relation.distinct : relation
64
+ end
65
+
66
+ def attribute_method?(str, klass = @klass)
67
+ exists = false
68
+ if ransackable_attribute?(str, klass)
69
+ exists = true
70
+ elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
71
+ remainder = []
72
+ found_assoc = nil
73
+ while !found_assoc && remainder.unshift(segments.pop) &&
74
+ segments.size > 0 do
75
+ assoc, poly_class = unpolymorphize_association(
76
+ segments.join(Constants::UNDERSCORE)
77
+ )
78
+ if found_assoc = get_association(assoc, klass)
79
+ exists = attribute_method?(
80
+ remainder.join(Constants::UNDERSCORE),
81
+ poly_class || found_assoc.klass
82
+ )
83
+ end
84
+ end
85
+ end
86
+ exists
87
+ end
88
+
89
+ def table_for(parent)
90
+ parent.table
91
+ end
92
+
93
+ def klassify(obj)
94
+ if Class === obj && ::ActiveRecord::Base > obj
95
+ obj
96
+ elsif obj.respond_to? :klass
97
+ obj.klass
98
+ elsif obj.respond_to? :base_klass
99
+ obj.base_klass
100
+ else
101
+ raise ArgumentError, "Don't know how to klassify #{obj}"
102
+ end
103
+ end
104
+
105
+ # All dependent Arel::Join nodes used in the search query.
106
+ #
107
+ # This could otherwise be done as `@object.arel.join_sources`, except
108
+ # that ActiveRecord's build_joins sets up its own JoinDependency.
109
+ # This extracts what we need to access the joins using our existing
110
+ # JoinDependency to track table aliases.
111
+ #
112
+ def join_sources
113
+ base, joins = begin
114
+ alias_tracker = @object.alias_tracker
115
+ constraints = @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
116
+
117
+ [
118
+ Arel::SelectManager.new(@object.table),
119
+ constraints
120
+ ]
121
+ end
122
+ joins.each do |aliased_join|
123
+ base.from(aliased_join)
124
+ end
125
+ base.join_sources
126
+ end
127
+
128
+ def alias_tracker
129
+ @join_dependency.send(:alias_tracker)
130
+ end
131
+
132
+ def lock_association(association)
133
+ @lock_associations << association
134
+ end
135
+
136
+ def remove_association(association)
137
+ return if @lock_associations.include?(association)
138
+ @join_dependency.instance_variable_get(:@join_root).children.delete_if { |stashed|
139
+ stashed.eql?(association)
140
+ }
141
+ @object.joins_values.delete_if { |jd|
142
+ jd.instance_variables.include?(:@join_root) &&
143
+ jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
144
+ }
145
+ end
146
+
147
+ # Build an Arel subquery that selects keys for the top query,
148
+ # drawn from the first join association's foreign_key.
149
+ #
150
+ # Example: for an Article that has_and_belongs_to_many Tags
151
+ #
152
+ # context = Article.search.context
153
+ # attribute = Attribute.new(context, "tags_name").tap do |a|
154
+ # context.bind(a, a.name)
155
+ # end
156
+ # context.build_correlated_subquery(attribute.parent).to_sql
157
+ #
158
+ # # SELECT "articles_tags"."article_id" FROM "articles_tags"
159
+ # # INNER JOIN "tags" ON "tags"."id" = "articles_tags"."tag_id"
160
+ # # WHERE "articles_tags"."article_id" = "articles"."id"
161
+ #
162
+ # The WHERE condition on this query makes it invalid by itself,
163
+ # because it is correlated to the primary key on the outer query.
164
+ #
165
+ def build_correlated_subquery(association)
166
+ join_constraints = extract_joins(association)
167
+ join_root = join_constraints.shift
168
+ correlated_key = extract_correlated_key(join_root)
169
+ subquery = Arel::SelectManager.new(association.base_klass)
170
+ subquery.from(join_root.left)
171
+ subquery.project(correlated_key)
172
+ join_constraints.each do |j|
173
+ subquery.join_sources << Arel::Nodes::InnerJoin.new(j.left, j.right)
174
+ end
175
+
176
+ # Handle polymorphic associations where correlated_key is an array
177
+ if correlated_key.is_a?(Array)
178
+ # For polymorphic associations, we need to add conditions for both the foreign key and type
179
+ correlated_key.each_with_index do |key, index|
180
+ if index == 0
181
+ # This is the foreign key
182
+ subquery = subquery.where(key.eq(primary_key))
183
+ else
184
+ # This is the type key, which should be equal to the model name
185
+ subquery = subquery.where(key.eq(@klass.name))
186
+ end
187
+ end
188
+ else
189
+ # Original behavior for non-polymorphic associations
190
+ subquery = subquery.where(correlated_key.eq(primary_key))
191
+ end
192
+
193
+ subquery
194
+ end
195
+
196
+ def primary_key
197
+ @object.table[@object.primary_key]
198
+ end
199
+
200
+ private
201
+
202
+ def extract_correlated_key(join_root)
203
+ case join_root
204
+ when Arel::Nodes::OuterJoin
205
+ # one of join_root.right/join_root.left is expected to be Arel::Nodes::On
206
+ if join_root.right.is_a?(Arel::Nodes::On)
207
+ extract_correlated_key(join_root.right.expr)
208
+ elsif join_root.left.is_a?(Arel::Nodes::On)
209
+ extract_correlated_key(join_root.left.expr)
210
+ else
211
+ raise 'Ransack encountered an unexpected arel structure'
212
+ end
213
+ when Arel::Nodes::Equality
214
+ pk = primary_key
215
+ if join_root.left == pk
216
+ join_root.right
217
+ elsif join_root.right == pk
218
+ join_root.left
219
+ else
220
+ nil
221
+ end
222
+ when Arel::Nodes::And
223
+ # And may have multiple children, so we need to check all, not via left/right
224
+ if join_root.children.any?
225
+ join_root.children.each do |child|
226
+ key = extract_correlated_key(child)
227
+ return key if key
228
+ end
229
+ else
230
+ extract_correlated_key(join_root.left) || extract_correlated_key(join_root.right)
231
+ end
232
+ else
233
+ # eg parent was Arel::Nodes::And and the evaluated side was one of
234
+ # Arel::Nodes::Grouping or MultiTenant::TenantEnforcementClause
235
+ nil
236
+ end
237
+ end
238
+
239
+ def get_parent_and_attribute_name(str, parent = @base)
240
+ attr_name = nil
241
+
242
+ if ransackable_attribute?(str, klassify(parent))
243
+ attr_name = str
244
+ elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
245
+ remainder = []
246
+ found_assoc = nil
247
+ while remainder.unshift(segments.pop) && segments.size > 0 &&
248
+ !found_assoc do
249
+ assoc, klass = unpolymorphize_association(
250
+ segments.join(Constants::UNDERSCORE)
251
+ )
252
+ if found_assoc = get_association(assoc, parent)
253
+ join = build_or_find_association(
254
+ found_assoc.name, parent, klass
255
+ )
256
+ parent, attr_name = get_parent_and_attribute_name(
257
+ remainder.join(Constants::UNDERSCORE), join
258
+ )
259
+ end
260
+ end
261
+ end
262
+
263
+ [parent, attr_name]
264
+ end
265
+
266
+ def get_association(str, parent = @base)
267
+ klass = klassify parent
268
+ ransackable_association?(str, klass) &&
269
+ klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
270
+ end
271
+
272
+ def join_dependency(relation)
273
+ if relation.respond_to?(:join_dependency) # Polyamorous enables this
274
+ relation.join_dependency
275
+ else
276
+ build_joins(relation)
277
+ end
278
+ end
279
+
280
+ # Checkout active_record/relation/query_methods.rb +build_joins+ for
281
+ # reference. Lots of duplicated code maybe we can avoid it
282
+ def build_joins(relation)
283
+ buckets = relation.joins_values + relation.left_outer_joins_values
284
+
285
+ buckets = buckets.group_by do |join|
286
+ case join
287
+ when String
288
+ :string_join
289
+ when Hash, Symbol, Array
290
+ :association_join
291
+ when Polyamorous::JoinDependency, Polyamorous::JoinAssociation
292
+ :stashed_join
293
+ when Arel::Nodes::Join
294
+ :join_node
295
+ else
296
+ raise 'unknown class: %s' % join.class.name
297
+ end
298
+ end
299
+ buckets.default = []
300
+ association_joins = buckets[:association_join]
301
+ stashed_association_joins = buckets[:stashed_join]
302
+ join_nodes = buckets[:join_node].uniq
303
+ string_joins = buckets[:string_join].map(&:strip)
304
+ string_joins.uniq!
305
+
306
+ join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
307
+
308
+ alias_tracker = relation.alias_tracker(join_list)
309
+ join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
310
+ join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
311
+ join_nodes.each do |join|
312
+ join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
313
+ end
314
+ join_dependency
315
+ end
316
+
317
+ def convert_join_strings_to_ast(table, joins)
318
+ joins.map! { |join| table.create_string_join(Arel.sql(join)) unless join.blank? }
319
+ joins.compact!
320
+ joins
321
+ end
322
+
323
+ def build_or_find_association(name, parent = @base, klass = nil)
324
+ find_association(name, parent, klass) or build_association(name, parent, klass)
325
+ end
326
+
327
+ def find_association(name, parent = @base, klass = nil)
328
+ @join_dependency.instance_variable_get(:@join_root).children.detect do |assoc|
329
+ assoc.reflection.name == name && assoc.table &&
330
+ (@associations_pot.empty? || @associations_pot[assoc] == parent || !@associations_pot.key?(assoc)) &&
331
+ (!klass || assoc.reflection.klass == klass)
332
+ end
333
+ end
334
+
335
+ def build_association(name, parent = @base, klass = nil)
336
+ jd = Polyamorous::JoinDependency.new(
337
+ parent.base_klass,
338
+ parent.table,
339
+ Polyamorous::Join.new(name, @join_type, klass),
340
+ @join_type
341
+ )
342
+ found_association = jd.instance_variable_get(:@join_root).children.last
343
+
344
+ @associations_pot[found_association] = parent
345
+
346
+ # TODO maybe we dont need to push associations here, we could loop
347
+ # through the @associations_pot instead
348
+ @join_dependency.instance_variable_get(:@join_root).children.push found_association
349
+
350
+ # Builds the arel nodes properly for this association
351
+ @tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
352
+
353
+ # Leverage the stashed association functionality in AR
354
+ @object = @object.joins(jd)
355
+ found_association
356
+ end
357
+
358
+ def extract_joins(association)
359
+ parent = @join_dependency.instance_variable_get(:@join_root)
360
+ reflection = association.reflection
361
+ join_constraints = association.join_constraints_with_tables(
362
+ parent.table,
363
+ parent.base_klass,
364
+ Arel::Nodes::OuterJoin,
365
+ @join_dependency.instance_variable_get(:@alias_tracker),
366
+ @tables_pot[association]
367
+ )
368
+ join_constraints.to_a.flatten
369
+ end
370
+ end
371
+ end
372
+ end
373
+ end
@@ -0,0 +1,209 @@
1
+ require 'ransack/constants'
2
+ require 'ransack/predicate'
3
+
4
+ module Ransack
5
+ module Configuration
6
+
7
+ mattr_accessor :predicates, :options
8
+
9
+ class PredicateCollection
10
+ attr_reader :sorted_names_with_underscores
11
+
12
+ def initialize
13
+ @collection = {}
14
+ @sorted_names_with_underscores = []
15
+ end
16
+
17
+ delegate :[], :keys, :has_key?, to: :@collection
18
+
19
+ def []=(key, value)
20
+ @sorted_names_with_underscores << [key, '_' + key]
21
+ @sorted_names_with_underscores.sort! { |(a, _), (b, _)| b.length <=> a.length }
22
+
23
+ @collection[key] = value
24
+ end
25
+ end
26
+
27
+ self.predicates = PredicateCollection.new
28
+
29
+ self.options = {
30
+ search_key: :q,
31
+ ignore_unknown_conditions: true,
32
+ hide_sort_order_indicators: false,
33
+ up_arrow: '&#9660;'.freeze,
34
+ down_arrow: '&#9650;'.freeze,
35
+ default_arrow: nil,
36
+ sanitize_scope_args: true,
37
+ postgres_fields_sort_option: nil,
38
+ strip_whitespace: true
39
+ }
40
+
41
+ def configure
42
+ yield self
43
+ end
44
+
45
+ def add_predicate(name, opts = {})
46
+ name = name.to_s
47
+ opts[:name] = name
48
+ compounds = opts.delete(:compounds)
49
+ compounds = true if compounds.nil?
50
+ compounds = false if opts[:wants_array]
51
+
52
+ self.predicates[name] = Predicate.new(opts)
53
+
54
+ Constants::SUFFIXES.each do |suffix|
55
+ compound_name = name + suffix
56
+ self.predicates[compound_name] = Predicate.new(
57
+ opts.merge(
58
+ name: compound_name,
59
+ arel_predicate: arel_predicate_with_suffix(
60
+ opts[:arel_predicate], suffix
61
+ ),
62
+ compound: true
63
+ )
64
+ )
65
+ end if compounds
66
+ end
67
+
68
+ # The default `search_key` name is `:q`. The default key may be overridden
69
+ # in an initializer file like `config/initializers/ransack.rb` as follows:
70
+ #
71
+ # Ransack.configure do |config|
72
+ # # Name the search_key `:query` instead of the default `:q`
73
+ # config.search_key = :query
74
+ # end
75
+ #
76
+ # Sometimes there are situations when the default search parameter name
77
+ # cannot be used, for instance if there were two searches on one page.
78
+ # Another name can be set using the `search_key` option with Ransack
79
+ # `ransack`, `search` and `@search_form_for` methods in controllers & views.
80
+ #
81
+ # In the controller:
82
+ # @search = Log.ransack(params[:log_search], search_key: :log_search)
83
+ #
84
+ # In the view:
85
+ # <%= f.search_form_for @search, as: :log_search %>
86
+ #
87
+ def search_key=(name)
88
+ self.options[:search_key] = name
89
+ end
90
+
91
+ # By default Ransack ignores errors if an unknown predicate, condition or
92
+ # attribute is passed into a search. The default may be overridden in an
93
+ # initializer file like `config/initializers/ransack.rb` as follows:
94
+ #
95
+ # Ransack.configure do |config|
96
+ # # Raise if an unknown predicate, condition or attribute is passed
97
+ # config.ignore_unknown_conditions = false
98
+ # end
99
+ #
100
+ def ignore_unknown_conditions=(boolean)
101
+ self.options[:ignore_unknown_conditions] = boolean
102
+ end
103
+
104
+ # By default Ransack ignores empty predicates. Ransack can also fallback to
105
+ # a default predicate by setting it in an initializer file
106
+ # like `config/initializers/ransack.rb` as follows:
107
+ #
108
+ # Ransack.configure do |config|
109
+ # # Use the 'eq' predicate if an unknown predicate is passed
110
+ # config.default_predicate = 'eq'
111
+ # end
112
+ #
113
+ def default_predicate=(name)
114
+ self.options[:default_predicate] = name
115
+ end
116
+
117
+ # By default, Ransack displays sort order indicator arrows with HTML codes:
118
+ #
119
+ # up_arrow: '&#9660;'
120
+ # down_arrow: '&#9650;'
121
+ #
122
+ # There is also a default arrow which is displayed if a column is not sorted.
123
+ # By default this is nil so nothing will be displayed.
124
+ #
125
+ # Any of the defaults may be globally overridden in an initializer file
126
+ # like `config/initializers/ransack.rb` as follows:
127
+ #
128
+ # Ransack.configure do |config|
129
+ # # Globally set the up arrow to an icon, and the down and default arrows to unicode.
130
+ # config.custom_arrows = {
131
+ # up_arrow: '<i class="fa fa-long-arrow-up"></i>',
132
+ # down_arrow: 'U+02193',
133
+ # default_arrow: 'U+11047'
134
+ # }
135
+ # end
136
+ #
137
+ def custom_arrows=(opts = {})
138
+ self.options[:up_arrow] = opts[:up_arrow].freeze if opts[:up_arrow]
139
+ self.options[:down_arrow] = opts[:down_arrow].freeze if opts[:down_arrow]
140
+ self.options[:default_arrow] = opts[:default_arrow].freeze if opts[:default_arrow]
141
+ end
142
+
143
+ # Ransack sanitizes many values in your custom scopes into booleans.
144
+ # [1, '1', 't', 'T', 'true', 'TRUE'] all evaluate to true.
145
+ # [0, '0', 'f', 'F', 'false', 'FALSE'] all evaluate to false.
146
+ #
147
+ # This default may be globally overridden in an initializer file like
148
+ # `config/initializers/ransack.rb` as follows:
149
+ #
150
+ # Ransack.configure do |config|
151
+ # # Accept my custom scope values as what they are.
152
+ # config.sanitize_custom_scope_booleans = false
153
+ # end
154
+ #
155
+ def sanitize_custom_scope_booleans=(boolean)
156
+ self.options[:sanitize_scope_args] = boolean
157
+ end
158
+
159
+ # The `NULLS FIRST` and `NULLS LAST` options can be used to determine
160
+ # whether nulls appear before or after non-null values in the sort ordering.
161
+ #
162
+ # User may want to configure it like this:
163
+ #
164
+ # Ransack.configure do |c|
165
+ # c.postgres_fields_sort_option = :nulls_first # or e.g. :nulls_always_last
166
+ # end
167
+ #
168
+ # See this feature: https://www.postgresql.org/docs/13/queries-order.html
169
+ #
170
+ def postgres_fields_sort_option=(setting)
171
+ self.options[:postgres_fields_sort_option] = setting
172
+ end
173
+
174
+ # By default, Ransack displays sort order indicator arrows in sort links.
175
+ # The default may be globally overridden in an initializer file like
176
+ # `config/initializers/ransack.rb` as follows:
177
+ #
178
+ # Ransack.configure do |config|
179
+ # # Hide sort link order indicators globally across the application
180
+ # config.hide_sort_order_indicators = true
181
+ # end
182
+ #
183
+ def hide_sort_order_indicators=(boolean)
184
+ self.options[:hide_sort_order_indicators] = boolean
185
+ end
186
+
187
+ # By default, Ransack displays strips all whitespace when searching for a string.
188
+ # The default may be globally changed in an initializer file like
189
+ # `config/initializers/ransack.rb` as follows:
190
+ #
191
+ # Ransack.configure do |config|
192
+ # # Enable whitespace stripping for string searches
193
+ # config.strip_whitespace = true
194
+ # end
195
+ #
196
+ def strip_whitespace=(boolean)
197
+ self.options[:strip_whitespace] = boolean
198
+ end
199
+
200
+ def arel_predicate_with_suffix(arel_predicate, suffix)
201
+ if arel_predicate === Proc
202
+ proc { |v| "#{arel_predicate.call(v)}#{suffix}" }
203
+ else
204
+ "#{arel_predicate}#{suffix}"
205
+ end
206
+ end
207
+
208
+ end
209
+ end