ransack 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +40 -22
  3. data/CHANGELOG.md +176 -27
  4. data/CONTRIBUTING.md +30 -19
  5. data/Gemfile +8 -3
  6. data/README.md +131 -58
  7. data/Rakefile +5 -2
  8. data/lib/ransack.rb +10 -5
  9. data/lib/ransack/adapters.rb +43 -23
  10. data/lib/ransack/adapters/active_record.rb +2 -2
  11. data/lib/ransack/adapters/active_record/3.0/compat.rb +5 -5
  12. data/lib/ransack/adapters/active_record/3.0/context.rb +5 -3
  13. data/lib/ransack/adapters/active_record/3.1/context.rb +1 -4
  14. data/lib/ransack/adapters/active_record/base.rb +12 -1
  15. data/lib/ransack/adapters/active_record/context.rb +148 -55
  16. data/lib/ransack/adapters/active_record/ransack/constants.rb +53 -53
  17. data/lib/ransack/adapters/active_record/ransack/context.rb +3 -1
  18. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +20 -28
  19. data/lib/ransack/adapters/mongoid/base.rb +21 -6
  20. data/lib/ransack/adapters/mongoid/context.rb +9 -5
  21. data/lib/ransack/configuration.rb +24 -3
  22. data/lib/ransack/constants.rb +11 -22
  23. data/lib/ransack/context.rb +20 -13
  24. data/lib/ransack/helpers/form_builder.rb +5 -6
  25. data/lib/ransack/helpers/form_helper.rb +50 -69
  26. data/lib/ransack/locale/da.yml +70 -0
  27. data/lib/ransack/locale/id.yml +70 -0
  28. data/lib/ransack/locale/ja.yml +70 -0
  29. data/lib/ransack/locale/pt-BR.yml +70 -0
  30. data/lib/ransack/locale/{zh.yml → zh-CN.yml} +1 -1
  31. data/lib/ransack/locale/zh-TW.yml +70 -0
  32. data/lib/ransack/nodes.rb +1 -1
  33. data/lib/ransack/nodes/attribute.rb +4 -1
  34. data/lib/ransack/nodes/bindable.rb +18 -6
  35. data/lib/ransack/nodes/condition.rb +58 -28
  36. data/lib/ransack/nodes/grouping.rb +15 -4
  37. data/lib/ransack/nodes/sort.rb +9 -5
  38. data/lib/ransack/predicate.rb +6 -2
  39. data/lib/ransack/search.rb +6 -5
  40. data/lib/ransack/translate.rb +2 -2
  41. data/lib/ransack/version.rb +1 -1
  42. data/ransack.gemspec +4 -4
  43. data/spec/mongoid/adapters/mongoid/base_spec.rb +20 -1
  44. data/spec/mongoid/nodes/condition_spec.rb +15 -0
  45. data/spec/mongoid/support/mongoid.yml +5 -0
  46. data/spec/mongoid/support/schema.rb +4 -0
  47. data/spec/mongoid_spec_helper.rb +13 -9
  48. data/spec/ransack/adapters/active_record/base_spec.rb +249 -71
  49. data/spec/ransack/adapters/active_record/context_spec.rb +16 -18
  50. data/spec/ransack/helpers/form_builder_spec.rb +5 -2
  51. data/spec/ransack/helpers/form_helper_spec.rb +84 -14
  52. data/spec/ransack/nodes/condition_spec.rb +24 -0
  53. data/spec/ransack/nodes/grouping_spec.rb +56 -0
  54. data/spec/ransack/predicate_spec.rb +5 -5
  55. data/spec/ransack/search_spec.rb +79 -70
  56. data/spec/support/schema.rb +43 -29
  57. metadata +17 -12
@@ -1,6 +1,8 @@
1
1
  require 'ransack/adapters/active_record/base'
2
2
  ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base
3
3
 
4
+ require 'ransack/adapters/active_record/context'
5
+
4
6
  case ActiveRecord::VERSION::STRING
5
7
  when /^3\.0\./
6
8
  require 'ransack/adapters/active_record/3.0/context'
@@ -8,6 +10,4 @@ when /^3\.1\./
8
10
  require 'ransack/adapters/active_record/3.1/context'
9
11
  when /^3\.2\./
10
12
  require 'ransack/adapters/active_record/3.2/context'
11
- else
12
- require 'ransack/adapters/active_record/context'
13
13
  end
@@ -138,16 +138,16 @@ module Arel
138
138
  "#{
139
139
  o.name
140
140
  }(#{
141
- o.distinct ? Ransack::Constants::DISTINCT : Ransack::Constants::EMPTY
141
+ o.distinct ? Ransack::Constants::DISTINCT : ''.freeze
142
142
  }#{
143
- o.expressions.map { |x| visit x }.join(Ransack::Constants::COMMA_SPACE)
143
+ o.expressions.map { |x| visit x }.join(', '.freeze)
144
144
  })#{
145
- o.alias ? " AS #{visit o.alias}" : Ransack::Constants::EMPTY
145
+ o.alias ? " AS #{visit o.alias}" : ''.freeze
146
146
  }"
147
147
  end
148
148
 
149
149
  def visit_Arel_Nodes_And o
150
- o.children.map { |x| visit x }.join(Ransack::Constants::SPACED_AND)
150
+ o.children.map { |x| visit x }.join(' AND '.freeze)
151
151
  end
152
152
 
153
153
  def visit_Arel_Nodes_Not o
@@ -164,7 +164,7 @@ module Arel
164
164
  quote(value, attr && column_for(attr))
165
165
  end
166
166
  }
167
- .join(Ransack::Constants::COMMA_SPACE)
167
+ .join(', '.freeze)
168
168
  })"
169
169
  end
170
170
  end
@@ -7,9 +7,11 @@ module Ransack
7
7
  module Adapters
8
8
  module ActiveRecord
9
9
  class Context < ::Ransack::Context
10
+
10
11
  # Because the AR::Associations namespace is insane
11
- JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
12
- JoinBase = JoinDependency::JoinBase
12
+ if defined? ::ActiveRecord::Associations::ClassMethods::JoinDependency
13
+ JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
14
+ end
13
15
 
14
16
  # Redefine a few things for ActiveRecord 3.0.
15
17
 
@@ -124,7 +126,7 @@ module Ransack
124
126
  end
125
127
 
126
128
  def join_dependency(relation)
127
- if relation.respond_to?(:join_dependency) # Squeel will enable this
129
+ if relation.respond_to?(:join_dependency) # Polyamorous enables this
128
130
  relation.join_dependency
129
131
  else
130
132
  build_join_dependency(relation)
@@ -6,9 +6,6 @@ module Ransack
6
6
  module Adapters
7
7
  module ActiveRecord
8
8
  class Context < ::Ransack::Context
9
- # Because the AR::Associations namespace is insane
10
- JoinDependency = ::ActiveRecord::Associations::JoinDependency
11
- JoinPart = JoinDependency::JoinPart
12
9
 
13
10
  # Redefine a few things for ActiveRecord 3.1.
14
11
 
@@ -137,7 +134,7 @@ module Ransack
137
134
  end
138
135
 
139
136
  def join_dependency(relation)
140
- if relation.respond_to?(:join_dependency) # Squeel will enable this
137
+ if relation.respond_to?(:join_dependency) # Polyamorous enables this
141
138
  relation.join_dependency
142
139
  else
143
140
  build_join_dependency(relation)
@@ -7,7 +7,9 @@ module Ransack
7
7
  alias :search :ransack unless base.respond_to? :search
8
8
  base.class_eval do
9
9
  class_attribute :_ransackers
10
+ class_attribute :_ransack_aliases
10
11
  self._ransackers ||= {}
12
+ self._ransack_aliases ||= {}
11
13
  end
12
14
  end
13
15
 
@@ -20,12 +22,21 @@ module Ransack
20
22
  .new(self, name, opts, &block)
21
23
  end
22
24
 
25
+ def ransack_alias(new_name, old_name)
26
+ self._ransack_aliases.store(new_name.to_s, old_name.to_s)
27
+ end
28
+
23
29
  # Ransackable_attributes, by default, returns all column names
24
30
  # and any defined ransackers as an array of strings.
25
31
  # For overriding with a whitelist array of strings.
26
32
  #
27
33
  def ransackable_attributes(auth_object = nil)
28
- column_names + _ransackers.keys
34
+ if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
35
+ column_names + _ransackers.keys + _ransack_aliases.keys +
36
+ attribute_aliases.keys
37
+ else
38
+ column_names + _ransackers.keys + _ransack_aliases.keys
39
+ end
29
40
  end
30
41
 
31
42
  # Ransackable_associations, by default, returns the names
@@ -8,8 +8,9 @@ module Ransack
8
8
  class Context < ::Ransack::Context
9
9
 
10
10
  # Because the AR::Associations namespace is insane
11
- JoinDependency = ::ActiveRecord::Associations::JoinDependency
12
- JoinPart = JoinDependency::JoinPart
11
+ if defined? ::ActiveRecord::Associations::JoinDependency
12
+ JoinDependency = ::ActiveRecord::Associations::JoinDependency
13
+ end
13
14
 
14
15
  def initialize(object, options = {})
15
16
  super
@@ -22,13 +23,13 @@ module Ransack
22
23
 
23
24
  def type_for(attr)
24
25
  return nil unless attr && attr.valid?
25
- name = attr.arel_attribute.name.to_s
26
- table = attr.arel_attribute.relation.table_name
27
- connection = attr.klass.connection
28
- unless connection.table_exists?(table)
29
- raise "No table named #{table} exists"
26
+ name = attr.arel_attribute.name.to_s
27
+ table = attr.arel_attribute.relation.table_name
28
+ schema_cache = @engine.connection.schema_cache
29
+ unless schema_cache.send(database_table_exists?, table)
30
+ raise "No table named #{table} exists."
30
31
  end
31
- connection.schema_cache.columns_hash(table)[name].type
32
+ schema_cache.columns_hash(table)[name].type
32
33
  end
33
34
 
34
35
  def evaluate(search, opts = {})
@@ -86,7 +87,7 @@ module Ransack
86
87
  "ActiveRecord 4.1 and later does not use join_associations. Use join_sources."
87
88
  end
88
89
 
89
- # All dependent Arel::Join nodes used in the search query
90
+ # All dependent Arel::Join nodes used in the search query.
90
91
  #
91
92
  # This could otherwise be done as `@object.arel.join_sources`, except
92
93
  # that ActiveRecord's build_joins sets up its own JoinDependency.
@@ -94,13 +95,18 @@ module Ransack
94
95
  # JoinDependency to track table aliases.
95
96
  #
96
97
  def join_sources
97
- base =
98
- if ::ActiveRecord::VERSION::MAJOR >= 5
99
- Arel::SelectManager.new(@object.table)
100
- else
101
- Arel::SelectManager.new(@object.engine, @object.table)
102
- end
103
- joins = @join_dependency.join_constraints(@object.joins_values)
98
+ base, joins =
99
+ if ::ActiveRecord::VERSION::MAJOR >= 5
100
+ [
101
+ Arel::SelectManager.new(@object.table),
102
+ @join_dependency.join_constraints(@object.joins_values, @join_type)
103
+ ]
104
+ else
105
+ [
106
+ Arel::SelectManager.new(@object.engine, @object.table),
107
+ @join_dependency.join_constraints(@object.joins_values)
108
+ ]
109
+ end
104
110
  joins.each do |aliased_join|
105
111
  base.from(aliased_join)
106
112
  end
@@ -109,7 +115,7 @@ module Ransack
109
115
 
110
116
  else
111
117
 
112
- # All dependent JoinAssociation items used in the search query
118
+ # All dependent JoinAssociation items used in the search query.
113
119
  #
114
120
  # Deprecated: this goes away in ActiveRecord 4.1. Use join_sources.
115
121
  #
@@ -132,8 +138,74 @@ module Ransack
132
138
  @join_dependency.alias_tracker
133
139
  end
134
140
 
141
+ def lock_association(association)
142
+ @lock_associations << association
143
+ end
144
+
145
+ if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
146
+ def remove_association(association)
147
+ return if @lock_associations.include?(association)
148
+ @join_dependency.join_root.children.delete_if { |stashed|
149
+ stashed.eql?(association)
150
+ }
151
+ @object.joins_values.delete_if { |jd|
152
+ jd.join_root.children.map(&:object_id) == [association.object_id]
153
+ }
154
+ end
155
+ else
156
+ def remove_association(association)
157
+ return if @lock_associations.include?(association)
158
+ @join_dependency.join_parts.delete(association)
159
+ @object.joins_values.delete(association)
160
+ end
161
+ end
162
+
163
+ # Build an Arel subquery that selects keys for the top query,
164
+ # drawn from the first join association's foreign_key.
165
+ #
166
+ # Example: for an Article that has_and_belongs_to_many Tags
167
+ #
168
+ # context = Article.search.context
169
+ # attribute = Attribute.new(context, "tags_name").tap do |a|
170
+ # context.bind(a, a.name)
171
+ # end
172
+ # context.build_correlated_subquery(attribute.parent).to_sql
173
+ #
174
+ # # SELECT "articles_tags"."article_id" FROM "articles_tags"
175
+ # # INNER JOIN "tags" ON "tags"."id" = "articles_tags"."tag_id"
176
+ # # WHERE "articles_tags"."article_id" = "articles"."id"
177
+ #
178
+ # The WHERE condition on this query makes it invalid by itself,
179
+ # because it is correlated to the primary key on the outer query.
180
+ #
181
+ def build_correlated_subquery(association)
182
+ join_constraints = extract_joins(association)
183
+ join_root = join_constraints.shift
184
+ join_table = join_root.left
185
+ correlated_key = join_root.right.expr.left
186
+ subquery = Arel::SelectManager.new(association.base_klass)
187
+ subquery.from(join_root.left)
188
+ subquery.project(correlated_key)
189
+ join_constraints.each do |j|
190
+ subquery.join_sources << Arel::Nodes::InnerJoin.new(j.left, j.right)
191
+ end
192
+ subquery.where(correlated_key.eq(primary_key))
193
+ end
194
+
195
+ def primary_key
196
+ @object.table[@object.primary_key]
197
+ end
198
+
135
199
  private
136
200
 
201
+ def database_table_exists?
202
+ if ::ActiveRecord::VERSION::MAJOR >= 5
203
+ :data_source_exists?
204
+ else
205
+ :table_exists?
206
+ end
207
+ end
208
+
137
209
  def get_parent_and_attribute_name(str, parent = @base)
138
210
  attr_name = nil
139
211
 
@@ -168,7 +240,7 @@ module Ransack
168
240
  end
169
241
 
170
242
  def join_dependency(relation)
171
- if relation.respond_to?(:join_dependency) # Squeel will enable this
243
+ if relation.respond_to?(:join_dependency) # Polyamorous enables this
172
244
  relation.join_dependency
173
245
  else
174
246
  build_joins(relation)
@@ -229,65 +301,86 @@ module Ransack
229
301
  .map { |join| table.create_string_join(Arel.sql(join)) }
230
302
  end
231
303
 
304
+ def build_or_find_association(name, parent = @base, klass = nil)
305
+ find_association(name, parent, klass) or build_association(name, parent, klass)
306
+ end
307
+
232
308
  if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
233
309
 
234
- def build_or_find_association(name, parent = @base, klass = nil)
235
- found_association = @join_dependency.join_root.children
236
- .detect do |assoc|
310
+ def find_association(name, parent = @base, klass = nil)
311
+ @join_dependency.join_root.children.detect do |assoc|
237
312
  assoc.reflection.name == name &&
238
- (@associations_pot.nil? || @associations_pot[assoc] == parent) &&
313
+ (@associations_pot.empty? || @associations_pot[assoc] == parent) &&
239
314
  (!klass || assoc.reflection.klass == klass)
240
315
  end
316
+ end
241
317
 
242
- unless found_association
243
- jd = JoinDependency.new(
244
- parent.base_klass,
245
- Polyamorous::Join.new(name, @join_type, klass),
246
- []
318
+ def build_association(name, parent = @base, klass = nil)
319
+ jd = JoinDependency.new(
320
+ parent.base_klass,
321
+ Polyamorous::Join.new(name, @join_type, klass),
322
+ []
323
+ )
324
+ found_association = jd.join_root.children.last
325
+ @associations_pot[found_association] = parent
326
+
327
+ # TODO maybe we dont need to push associations here, we could loop
328
+ # through the @associations_pot instead
329
+ @join_dependency.join_root.children.push found_association
330
+
331
+ # Builds the arel nodes properly for this association
332
+ @join_dependency.send(
333
+ :construct_tables!, jd.join_root, found_association
247
334
  )
248
- found_association = jd.join_root.children.last
249
- associations found_association, parent
250
335
 
251
- # TODO maybe we dont need to push associations here, we could loop
252
- # through the @associations_pot instead
253
- @join_dependency.join_root.children.push found_association
336
+ # Leverage the stashed association functionality in AR
337
+ @object = @object.joins(jd)
254
338
 
255
- # Builds the arel nodes properly for this association
256
- @join_dependency.send(
257
- :construct_tables!, jd.join_root, found_association
258
- )
259
-
260
- # Leverage the stashed association functionality in AR
261
- @object = @object.joins(jd)
262
- end
263
339
  found_association
264
340
  end
265
341
 
266
- def associations(assoc, parent)
267
- @associations_pot ||= {}
268
- @associations_pot[assoc] = parent
342
+ def extract_joins(association)
343
+ parent = @join_dependency.join_root
344
+ reflection = association.reflection
345
+ join_constraints = association.join_constraints(
346
+ parent.table,
347
+ parent.base_klass,
348
+ association,
349
+ Arel::Nodes::OuterJoin,
350
+ association.tables,
351
+ reflection.scope_chain,
352
+ reflection.chain
353
+ )
354
+ join_constraints.to_a.flatten
269
355
  end
270
356
 
271
357
  else
272
358
 
273
- def build_or_find_association(name, parent = @base, klass = nil)
359
+ def build_association(name, parent = @base, klass = nil)
360
+ @join_dependency.send(
361
+ :build,
362
+ Polyamorous::Join.new(name, @join_type, klass),
363
+ parent
364
+ )
365
+ found_association = @join_dependency.join_associations.last
366
+ # Leverage the stashed association functionality in AR
367
+ @object = @object.joins(found_association)
368
+
369
+ found_association
370
+ end
371
+
372
+ def extract_joins(association)
373
+ query = Arel::SelectManager.new(association.base_klass, association.table)
374
+ association.join_to(query).join_sources
375
+ end
376
+
377
+ def find_association(name, parent = @base, klass = nil)
274
378
  found_association = @join_dependency.join_associations
275
379
  .detect do |assoc|
276
380
  assoc.reflection.name == name &&
277
381
  assoc.parent == parent &&
278
382
  (!klass || assoc.reflection.klass == klass)
279
383
  end
280
- unless found_association
281
- @join_dependency.send(
282
- :build,
283
- Polyamorous::Join.new(name, @join_type, klass),
284
- parent
285
- )
286
- found_association = @join_dependency.join_associations.last
287
- # Leverage the stashed association functionality in AR
288
- @object = @object.joins(found_association)
289
- end
290
- found_association
291
384
  end
292
385
 
293
386
  end
@@ -4,97 +4,97 @@ module Ransack
4
4
 
5
5
  DERIVED_PREDICATES = [
6
6
  [CONT, {
7
- :arel_predicate => 'matches'.freeze,
8
- :formatter => proc { |v| "%#{escape_wildcards(v)}%" }
7
+ arel_predicate: 'matches'.freeze,
8
+ formatter: proc { |v| "%#{escape_wildcards(v)}%" }
9
9
  }
10
10
  ],
11
11
  ['not_cont'.freeze, {
12
- :arel_predicate => 'does_not_match'.freeze,
13
- :formatter => proc { |v| "%#{escape_wildcards(v)}%" }
12
+ arel_predicate: 'does_not_match'.freeze,
13
+ formatter: proc { |v| "%#{escape_wildcards(v)}%" }
14
14
  }
15
15
  ],
16
16
  ['start'.freeze, {
17
- :arel_predicate => 'matches'.freeze,
18
- :formatter => proc { |v| "#{escape_wildcards(v)}%" }
17
+ arel_predicate: 'matches'.freeze,
18
+ formatter: proc { |v| "#{escape_wildcards(v)}%" }
19
19
  }
20
20
  ],
21
21
  ['not_start'.freeze, {
22
- :arel_predicate => 'does_not_match'.freeze,
23
- :formatter => proc { |v| "#{escape_wildcards(v)}%" }
22
+ arel_predicate: 'does_not_match'.freeze,
23
+ formatter: proc { |v| "#{escape_wildcards(v)}%" }
24
24
  }
25
25
  ],
26
26
  ['end'.freeze, {
27
- :arel_predicate => 'matches'.freeze,
28
- :formatter => proc { |v| "%#{escape_wildcards(v)}" }
27
+ arel_predicate: 'matches'.freeze,
28
+ formatter: proc { |v| "%#{escape_wildcards(v)}" }
29
29
  }
30
30
  ],
31
31
  ['not_end'.freeze, {
32
- :arel_predicate => 'does_not_match'.freeze,
33
- :formatter => proc { |v| "%#{escape_wildcards(v)}" }
32
+ arel_predicate: 'does_not_match'.freeze,
33
+ formatter: proc { |v| "%#{escape_wildcards(v)}" }
34
34
  }
35
35
  ],
36
36
  ['true'.freeze, {
37
- :arel_predicate => proc { |v| v ? EQ : NOT_EQ },
38
- :compounds => false,
39
- :type => :boolean,
40
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
41
- :formatter => proc { |v| true }
37
+ arel_predicate: proc { |v| v ? EQ : NOT_EQ },
38
+ compounds: false,
39
+ type: :boolean,
40
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
41
+ formatter: proc { |v| true }
42
42
  }
43
43
  ],
44
44
  ['not_true'.freeze, {
45
- :arel_predicate => proc { |v| v ? NOT_EQ : EQ },
46
- :compounds => false,
47
- :type => :boolean,
48
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
49
- :formatter => proc { |v| true }
45
+ arel_predicate: proc { |v| v ? NOT_EQ : EQ },
46
+ compounds: false,
47
+ type: :boolean,
48
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
49
+ formatter: proc { |v| true }
50
50
  }
51
51
  ],
52
52
  ['false'.freeze, {
53
- :arel_predicate => proc { |v| v ? EQ : NOT_EQ },
54
- :compounds => false,
55
- :type => :boolean,
56
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
57
- :formatter => proc { |v| false }
53
+ arel_predicate: proc { |v| v ? EQ : NOT_EQ },
54
+ compounds: false,
55
+ type: :boolean,
56
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
57
+ formatter: proc { |v| false }
58
58
  }
59
59
  ],
60
60
  ['not_false'.freeze, {
61
- :arel_predicate => proc { |v| v ? NOT_EQ : EQ },
62
- :compounds => false,
63
- :type => :boolean,
64
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
65
- :formatter => proc { |v| false }
61
+ arel_predicate: proc { |v| v ? NOT_EQ : EQ },
62
+ compounds: false,
63
+ type: :boolean,
64
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
65
+ formatter: proc { |v| false }
66
66
  }
67
67
  ],
68
68
  ['present'.freeze, {
69
- :arel_predicate => proc { |v| v ? NOT_EQ_ALL : EQ_ANY },
70
- :compounds => false,
71
- :type => :boolean,
72
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
73
- :formatter => proc { |v| [nil, EMPTY] }
69
+ arel_predicate: proc { |v| v ? NOT_EQ_ALL : EQ_ANY },
70
+ compounds: false,
71
+ type: :boolean,
72
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
73
+ formatter: proc { |v| [nil, ''.freeze].freeze }
74
74
  }
75
75
  ],
76
76
  ['blank'.freeze, {
77
- :arel_predicate => proc { |v| v ? EQ_ANY : NOT_EQ_ALL },
78
- :compounds => false,
79
- :type => :boolean,
80
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
81
- :formatter => proc { |v| [nil, EMPTY] }
77
+ arel_predicate: proc { |v| v ? EQ_ANY : NOT_EQ_ALL },
78
+ compounds: false,
79
+ type: :boolean,
80
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
81
+ formatter: proc { |v| [nil, ''.freeze].freeze }
82
82
  }
83
83
  ],
84
84
  ['null'.freeze, {
85
- :arel_predicate => proc { |v| v ? EQ : NOT_EQ },
86
- :compounds => false,
87
- :type => :boolean,
88
- :validator => proc { |v| BOOLEAN_VALUES.include?(v)},
89
- :formatter => proc { |v| nil }
85
+ arel_predicate: proc { |v| v ? EQ : NOT_EQ },
86
+ compounds: false,
87
+ type: :boolean,
88
+ validator: proc { |v| BOOLEAN_VALUES.include?(v)},
89
+ formatter: proc { |v| nil }
90
90
  }
91
91
  ],
92
92
  ['not_null'.freeze, {
93
- :arel_predicate => proc { |v| v ? NOT_EQ : EQ },
94
- :compounds => false,
95
- :type => :boolean,
96
- :validator => proc { |v| BOOLEAN_VALUES.include?(v) },
97
- :formatter => proc { |v| nil } }
93
+ arel_predicate: proc { |v| v ? NOT_EQ : EQ },
94
+ compounds: false,
95
+ type: :boolean,
96
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
97
+ formatter: proc { |v| nil } }
98
98
  ]
99
99
  ].freeze
100
100
 
@@ -104,7 +104,7 @@ module Ransack
104
104
  case ActiveRecord::Base.connection.adapter_name
105
105
  when "Mysql2".freeze, "PostgreSQL".freeze
106
106
  # Necessary for PostgreSQL and MySQL
107
- unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
107
+ unescaped.to_s.gsub(/([\\|\%|_|.])/, '\\\\\\1')
108
108
  else
109
109
  unescaped
110
110
  end