ransack 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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