ransack 2.1.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +13 -1
  4. data/CHANGELOG.md +24 -0
  5. data/Gemfile +4 -12
  6. data/README.md +5 -3
  7. data/lib/ransack.rb +1 -0
  8. data/lib/ransack/adapters/active_record/context.rb +40 -12
  9. data/lib/ransack/adapters/active_record/ransack/constants.rb +5 -2
  10. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +1 -1
  11. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -1
  12. data/lib/ransack/constants.rb +1 -0
  13. data/lib/ransack/context.rb +19 -18
  14. data/lib/ransack/helpers/form_helper.rb +1 -1
  15. data/lib/ransack/locale/az.yml +1 -1
  16. data/lib/ransack/locale/ca.yml +70 -0
  17. data/lib/ransack/locale/es.yml +22 -22
  18. data/lib/ransack/locale/fa.yml +70 -0
  19. data/lib/ransack/locale/fi.yml +71 -0
  20. data/lib/ransack/translate.rb +115 -115
  21. data/lib/ransack/version.rb +1 -1
  22. data/{lib → polyamorous/lib}/polyamorous.rb +7 -3
  23. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -0
  24. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -0
  25. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.1_ruby_2/join_association.rb +1 -2
  26. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -0
  27. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +2 -3
  28. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +1 -2
  29. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +12 -0
  30. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +0 -9
  31. data/{lib → polyamorous/lib}/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +25 -1
  32. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +2 -0
  33. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +2 -0
  34. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +81 -0
  35. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +2 -0
  36. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +2 -0
  37. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +2 -0
  38. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +2 -0
  39. data/{lib → polyamorous/lib}/polyamorous/join.rb +0 -0
  40. data/{lib → polyamorous/lib}/polyamorous/swapping_reflection_class.rb +0 -0
  41. data/{lib → polyamorous/lib}/polyamorous/tree_node.rb +0 -0
  42. data/polyamorous/lib/polyamorous/version.rb +3 -0
  43. data/polyamorous/polyamorous.gemspec +35 -0
  44. data/ransack.gemspec +3 -3
  45. data/spec/helpers/polyamorous_helper.rb +6 -2
  46. data/spec/ransack/adapters/active_record/context_spec.rb +41 -0
  47. data/spec/ransack/join_dependency_spec.rb +18 -7
  48. data/spec/ransack/predicate_spec.rb +16 -2
  49. data/spec/ransack/search_spec.rb +26 -2
  50. data/spec/spec_helper.rb +1 -0
  51. data/spec/support/schema.rb +6 -0
  52. metadata +58 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2eab955e89328399bcb163a575725ea51969653edb1edd66773b6f60554fd12
4
- data.tar.gz: 1e6d34f5d87238a18c7ae2194cd0d5320d9460b97b8d8c30ec79d82de6aa139a
3
+ metadata.gz: be6fda6bbab697d2f125847b2761df81991190d8d9799b5480ac2044441978a0
4
+ data.tar.gz: 9f39f157e148aa2c4638b8e9c7ab5a8b14221f315e880c04e5f76f3db2a1b473
5
5
  SHA512:
6
- metadata.gz: 52c4a46725d9bb474fbb229c39d9638c730d77bd53211f00f639b78690fcd72aaaa2418b56704c30024c36c39ca7cebde0e86b76cba13704a26f4416791ad141
7
- data.tar.gz: bd6880992a977511ea37df47d0828e8ee1e855dff39e2620eaed89bc043ffde2960e3006ebcc0609c9738af10faea6947a1db1f91f1f3eebc65b631d97137054
6
+ metadata.gz: 2fc2082431692f609ddb39171876ade4e1eb4ce715ad917f196db6714a1f55872852c61dec20a047bf48b31f9cab812417fb94f1e9ae9786771320cbd6bc54a4
7
+ data.tar.gz: ea1ed1a393fe699432f7012facf66e01b48725d73eb7c8956f17435b447ce745fb65c235425cc28079ba1b1c74d64ba758710f25793f738ea92a49f5409abb68
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  pkg/*
5
5
  coverage/*
6
6
  .DS_Store
7
+ .byebug_history
@@ -3,9 +3,20 @@ language: ruby
3
3
  sudo: false
4
4
 
5
5
  rvm:
6
- - 2.5
6
+ - 2.6.0
7
+
8
+ services:
9
+ - mysql
7
10
 
8
11
  env:
12
+ - RAILS=6-0-stable DB=sqlite3
13
+ - RAILS=6-0-stable DB=mysql
14
+ - RAILS=6-0-stable DB=postgres
15
+
16
+ - RAILS=v6.0.0 DB=sqlite3
17
+ - RAILS=v6.0.0 DB=mysql
18
+ - RAILS=v6.0.0 DB=postgres
19
+
9
20
  - RAILS=5-2-stable DB=sqlite3
10
21
  - RAILS=5-2-stable DB=mysql
11
22
  - RAILS=5-2-stable DB=postgres
@@ -35,3 +46,4 @@ before_script:
35
46
  addons:
36
47
  code_climate:
37
48
  repo_token: 8b701c4364d51a0217105e08c06922d600cec3d9e60d546a89e3ddfe46e0664e
49
+ postgresql: "9.6"
@@ -2,6 +2,30 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.3.0 - 2019-08-18
6
+
7
+ * Arabic translations PR [979](https://github.com/activerecord-hackery/ransack/pull/979)
8
+
9
+ * Rails 6 PR [1027](https://github.com/activerecord-hackery/ransack/pull/1027)
10
+ *vrodokanakis*
11
+
12
+ * Make polyamorous a separate gem PR [1002](https://github.com/activerecord-hackery/ransack/pull/1002)
13
+
14
+ * Catalan translations PR[1007](https://github.com/activerecord-hackery/ransack/pull/1007)
15
+ *roslavych*
16
+
17
+ * Don't escape period characters with wildcard searches in mysql2 PR [1013](https://github.com/activerecord-hackery/ransack/pull/1013)
18
+ *daflip*
19
+
20
+ * Farsi translations PR [1030](https://github.com/activerecord-hackery/ransack/pull/1030)
21
+
22
+ * Finnish translations PR [1049](https://github.com/activerecord-hackery/ransack/pull/1049)
23
+
24
+ * Fix wrong table alias when using nested join. for ActiveRecord >= 5.2
25
+ PR [374](https://github.com/activerecord-hackery/ransack/pull/374)
26
+
27
+ *hiichan*
28
+
5
29
  ## Version 2.1.1 - 2018-12-05
6
30
 
7
31
  * Add `arabic` translation
data/Gemfile CHANGED
@@ -3,7 +3,7 @@ gemspec
3
3
 
4
4
  gem 'rake'
5
5
 
6
- rails = ENV['RAILS'] || '5-0-stable'
6
+ rails = ENV['RAILS'] || '6-0-stable'
7
7
 
8
8
  gem 'pry'
9
9
 
@@ -16,29 +16,21 @@ when /\// # A path
16
16
  gem 'activerecord', path: "#{rails}/activerecord", require: false
17
17
  gem 'actionpack', path: "#{rails}/actionpack"
18
18
  when /^v/ # A tagged version
19
- git 'git://github.com/rails/rails.git', :tag => rails do
19
+ git 'https://github.com/rails/rails.git', :tag => rails do
20
20
  gem 'activesupport'
21
21
  gem 'activemodel'
22
22
  gem 'activerecord', require: false
23
23
  gem 'actionpack'
24
24
  end
25
- if rails >= 'v5.2.0'
26
- gem 'mysql2', '~> 0.4.4'
27
- end
28
25
  else
29
- git 'git://github.com/rails/rails.git', :branch => rails do
26
+ git 'https://github.com/rails/rails.git', :branch => rails do
30
27
  gem 'activesupport'
31
28
  gem 'activemodel'
32
29
  gem 'activerecord', require: false
33
30
  gem 'actionpack'
34
31
  end
35
- if rails == '3-0-stable'
36
- gem 'mysql2', '< 0.3'
37
- end
38
- if rails == '5-2-stable'
39
- gem 'mysql2', '~> 0.4.4'
40
- end
41
32
  end
33
+ gem 'mysql2', '~> 0.5.2'
42
34
 
43
35
  group :test do
44
36
  # TestUnit was removed from Ruby 2.2 but still needed for testing Rails 3.x.
data/README.md CHANGED
@@ -30,9 +30,13 @@ If you're viewing this at
30
30
  you're reading the documentation for the master branch with the latest features.
31
31
  [View documentation for the last release (2.0.0).](https://github.com/activerecord-hackery/ransack/tree/v2.0.0)
32
32
 
33
+ ## Rails 6 Support
34
+
35
+ Rails 6 is right around the corner, but not yet released. Ransack master branch supports ```6.0.0.rc2```. Full Rails 6 support will be provided after the official Rails 6 release. See [this issue](https://github.com/activerecord-hackery/ransack/issues/1032) for details.
36
+
33
37
  ## Getting started
34
38
 
35
- Ransack is compatible with Rails 5.0, 5.1 and 5.2 on Ruby 2.2 and later.
39
+ Ransack is compatible with Rails 6.0.0.rc2, 5.0, 5.1 and 5.2 on Ruby 2.2 and later.
36
40
  If you are using Rails <5.0 use the 1.8 line of Ransack.
37
41
  If you are using Ruby 1.8 or an earlier JRuby and run into compatibility
38
42
  issues, you can use an earlier version of Ransack, say, up to 1.3.0.
@@ -717,8 +721,6 @@ class Employee < ActiveRecord::Base
717
721
  %i(activated hired_since)
718
722
  end
719
723
  end
720
-
721
- private_class_method :ransackable_scopes
722
724
  end
723
725
 
724
726
  Employee.ransack({ activated: true, hired_since: '2013-01-01' })
@@ -1,6 +1,7 @@
1
1
  require 'active_support/core_ext'
2
2
  require 'ransack/configuration'
3
3
  require 'ransack/adapters'
4
+ require 'polyamorous'
4
5
 
5
6
  Ransack::Adapters.object_mapper.require_constants
6
7
 
@@ -107,9 +107,15 @@ module Ransack
107
107
  base, joins =
108
108
  if ::ActiveRecord::VERSION::STRING > Constants::RAILS_5_2_0
109
109
  alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
110
+ constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
111
+ @join_dependency.join_constraints(@object.joins_values, alias_tracker)
112
+ else
113
+ @join_dependency.join_constraints(@object.joins_values, @join_type, alias_tracker)
114
+ end
115
+
110
116
  [
111
117
  Arel::SelectManager.new(@object.table),
112
- @join_dependency.join_constraints(@object.joins_values, @join_type, alias_tracker)
118
+ constraints
113
119
  ]
114
120
  else
115
121
  [
@@ -163,7 +169,7 @@ module Ransack
163
169
  def build_correlated_subquery(association)
164
170
  join_constraints = extract_joins(association)
165
171
  join_root = join_constraints.shift
166
- correlated_key = join_root.right.expr.left
172
+ correlated_key = extract_correlated_key(join_root)
167
173
  subquery = Arel::SelectManager.new(association.base_klass)
168
174
  subquery.from(join_root.left)
169
175
  subquery.project(correlated_key)
@@ -179,6 +185,20 @@ module Ransack
179
185
 
180
186
  private
181
187
 
188
+ def extract_correlated_key(join_root)
189
+ correlated_key = join_root.right.expr.left
190
+
191
+ if correlated_key.is_a? Arel::Nodes::And
192
+ correlated_key = correlated_key.left.left
193
+ elsif correlated_key.is_a? Arel::Nodes::Equality
194
+ correlated_key = correlated_key.left
195
+ elsif correlated_key.is_a? Arel::Nodes::Grouping
196
+ correlated_key = join_root.right.expr.right.left
197
+ else
198
+ correlated_key
199
+ end
200
+ end
201
+
182
202
  def get_parent_and_attribute_name(str, parent = @base)
183
203
  attr_name = nil
184
204
 
@@ -261,7 +281,11 @@ module Ransack
261
281
  end
262
282
  else
263
283
  alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
264
- join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins)
284
+ join_dependency = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
285
+ Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
286
+ else
287
+ Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins)
288
+ end
265
289
  join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
266
290
  join_nodes.each do |join|
267
291
  join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
@@ -289,7 +313,15 @@ module Ransack
289
313
  end
290
314
 
291
315
  def build_association(name, parent = @base, klass = nil)
292
- if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2_0
316
+ if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
317
+ jd = Polyamorous::JoinDependency.new(
318
+ parent.base_klass,
319
+ parent.table,
320
+ Polyamorous::Join.new(name, @join_type, klass),
321
+ @join_type
322
+ )
323
+ found_association = jd.instance_variable_get(:@join_root).children.last
324
+ elsif ::Gem::Version.new(::ActiveRecord::VERSION::STRING) < ::Gem::Version.new(Constants::RAILS_5_2_0)
293
325
  jd = Polyamorous::JoinDependency.new(
294
326
  parent.base_klass,
295
327
  Polyamorous::Join.new(name, @join_type, klass),
@@ -300,7 +332,7 @@ module Ransack
300
332
  alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, parent.table.name, [])
301
333
  jd = Polyamorous::JoinDependency.new(
302
334
  parent.base_klass,
303
- parent.base_klass.arel_table,
335
+ parent.table,
304
336
  Polyamorous::Join.new(name, @join_type, klass),
305
337
  alias_tracker
306
338
  )
@@ -308,13 +340,12 @@ module Ransack
308
340
  else
309
341
  jd = Polyamorous::JoinDependency.new(
310
342
  parent.base_klass,
311
- parent.base_klass.arel_table,
312
- Polyamorous::Join.new(name, @join_type, klass),
343
+ parent.table,
344
+ Polyamorous::Join.new(name, @join_type, klass)
313
345
  )
314
346
  found_association = jd.instance_variable_get(:@join_root).children.last
315
347
  end
316
348
 
317
-
318
349
  @associations_pot[found_association] = parent
319
350
 
320
351
  # TODO maybe we dont need to push associations here, we could loop
@@ -325,14 +356,11 @@ module Ransack
325
356
  if ::ActiveRecord::VERSION::STRING > Constants::RAILS_5_2_0
326
357
  @join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root))
327
358
  else
328
- @join_dependency.send(
329
- :construct_tables!, jd.instance_variable_get(:@join_root), found_association
330
- )
359
+ @join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root), found_association)
331
360
  end
332
361
 
333
362
  # Leverage the stashed association functionality in AR
334
363
  @object = @object.joins(jd)
335
-
336
364
  found_association
337
365
  end
338
366
 
@@ -102,8 +102,11 @@ module Ransack
102
102
  # replace % \ to \% \\
103
103
  def escape_wildcards(unescaped)
104
104
  case ActiveRecord::Base.connection.adapter_name
105
- when "Mysql2".freeze, "PostgreSQL".freeze
106
- # Necessary for PostgreSQL and MySQL
105
+ when "Mysql2".freeze
106
+ # Necessary for MySQL
107
+ unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1')
108
+ when "PostgreSQL".freeze
109
+ # Necessary for PostgreSQL
107
110
  unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1')
108
111
  else
109
112
  unescaped
@@ -43,7 +43,7 @@ module Ransack
43
43
 
44
44
  def in_predicate?(predicate)
45
45
  return unless defined?(Arel::Nodes::Casted)
46
- predicate.class == Arel::Nodes::In
46
+ predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
47
47
  end
48
48
 
49
49
  def casted_array?(predicate)
@@ -2,7 +2,7 @@ module Ransack
2
2
  module Translate
3
3
 
4
4
  def self.i18n_key(klass)
5
- klass.model_name.i18n_key.to_s.freeze
5
+ klass.model_name.i18n_key
6
6
  end
7
7
  end
8
8
  end
@@ -48,6 +48,7 @@ module Ransack
48
48
  RAILS_5_1 = '5.1'.freeze
49
49
  RAILS_5_2 = '5.2'.freeze
50
50
  RAILS_5_2_0 = '5.2.0'.freeze
51
+ RAILS_6_0 = '6.0.0'.freeze
51
52
 
52
53
  RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
53
54
  RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze
@@ -64,26 +64,28 @@ module Ransack
64
64
 
65
65
  def traverse(str, base = @base)
66
66
  str ||= ''.freeze
67
-
68
- if (segments = str.split(Constants::UNDERSCORE)).size > 0
67
+ segments = str.split(Constants::UNDERSCORE)
68
+ unless segments.empty?
69
69
  remainder = []
70
70
  found_assoc = nil
71
- while !found_assoc && segments.size > 0 do
71
+ until found_assoc || segments.empty?
72
72
  # Strip the _of_Model_type text from the association name, but hold
73
73
  # onto it in klass, for use as the next base
74
74
  assoc, klass = unpolymorphize_association(
75
75
  segments.join(Constants::UNDERSCORE)
76
- )
76
+ )
77
77
  if found_assoc = get_association(assoc, base)
78
78
  base = traverse(
79
79
  remainder.join(Constants::UNDERSCORE), klass || found_assoc.klass
80
- )
80
+ )
81
81
  end
82
82
 
83
83
  remainder.unshift segments.pop
84
84
  end
85
- raise UntraversableAssociationError,
86
- "No association matches #{str}" unless found_assoc
85
+ unless found_assoc
86
+ raise(UntraversableAssociationError,
87
+ "No association matches #{str}")
88
+ end
87
89
  end
88
90
 
89
91
  klassify(base)
@@ -95,18 +97,17 @@ module Ransack
95
97
  path = []
96
98
  segments = str.split(Constants::UNDERSCORE)
97
99
  association_parts = []
98
- if (segments = str.split(Constants::UNDERSCORE)).size > 0
99
- while segments.size > 0 &&
100
- !base.columns_hash[segments.join(Constants::UNDERSCORE)] &&
101
- association_parts << segments.shift do
100
+ unless segments.empty?
101
+ while !segments.empty? &&
102
+ !base.columns_hash[segments.join(Constants::UNDERSCORE)] &&
103
+ association_parts << segments.shift
102
104
  assoc, klass = unpolymorphize_association(
103
105
  association_parts.join(Constants::UNDERSCORE)
104
- )
105
- if found_assoc = get_association(assoc, base)
106
- path += association_parts
107
- association_parts = []
108
- base = klassify(klass || found_assoc)
109
- end
106
+ )
107
+ next unless found_assoc = get_association(assoc, base)
108
+ path += association_parts
109
+ association_parts = []
110
+ base = klassify(klass || found_assoc)
110
111
  end
111
112
  end
112
113
 
@@ -127,7 +128,7 @@ module Ransack
127
128
 
128
129
  def ransackable_attribute?(str, klass)
129
130
  klass.ransackable_attributes(auth_object).include?(str) ||
130
- klass.ransortable_attributes(auth_object).include?(str)
131
+ klass.ransortable_attributes(auth_object).include?(str)
131
132
  end
132
133
 
133
134
  def ransackable_association?(str, klass)
@@ -145,7 +145,7 @@ module Ransack
145
145
  private
146
146
 
147
147
  def parameters_hash(params)
148
- if ::ActiveRecord::VERSION::MAJOR == 5 && params.respond_to?(:to_unsafe_h)
148
+ if ::ActiveRecord::VERSION::MAJOR >= 5 && params.respond_to?(:to_unsafe_h)
149
149
  params.to_unsafe_h
150
150
  else
151
151
  params
@@ -1,4 +1,4 @@
1
- tr:
1
+ az:
2
2
  ransack:
3
3
  search: "axtar"
4
4
  predicate: "təsdiqlə"
@@ -0,0 +1,70 @@
1
+ ca:
2
+ ransack:
3
+ search: "cercar"
4
+ predicate: "predicat"
5
+ and: "i"
6
+ or: "o"
7
+ any: "qualsevol"
8
+ all: "tots"
9
+ combinator: "combinador"
10
+ attribute: "atribut"
11
+ value: "valor"
12
+ condition: "condició"
13
+ sort: "ordenar"
14
+ asc: "ascendent"
15
+ desc: "descendent"
16
+ predicates:
17
+ eq: "és igual a"
18
+ eq_any: "és igual a qualsevol"
19
+ eq_all: "és igual a tots"
20
+ not_eq: "no és igual a"
21
+ not_eq_any: "no és igual a qualsevol"
22
+ not_eq_all: "no és igual a tots"
23
+ matches: "coincideix"
24
+ matches_any: "coincideix a qualsevol"
25
+ matches_all: "coincideix a tots"
26
+ does_not_match: "no coincideix"
27
+ does_not_match_any: "no coincideix amb cap"
28
+ does_not_match_all: "no coincideix amb tots"
29
+ lt: "menor que"
30
+ lt_any: "menor que qualsevol"
31
+ lt_all: "menor o igual a"
32
+ lteq: "menor que o igual a"
33
+ lteq_any: "menor o igual a qualsevol"
34
+ lteq_all: "menor o igual a tots"
35
+ gt: "major que"
36
+ gt_any: "major que qualsevol"
37
+ gt_all: "major que tots"
38
+ gteq: "major que o igual a"
39
+ gteq_any: "major que o igual a qualsevol"
40
+ gteq_all: "major que o igual a tots"
41
+ in: "en"
42
+ in_any: "en qualsevol"
43
+ in_all: "en tots"
44
+ not_in: "no en"
45
+ not_in_any: "no en qualsevol"
46
+ not_in_all: "no en tots"
47
+ cont: "conté"
48
+ cont_any: "conté qualsevol"
49
+ cont_all: "conté tots"
50
+ not_cont: "no conté"
51
+ not_cont_any: "no conté cap"
52
+ not_cont_all: "no conté tota"
53
+ start: "comença per"
54
+ start_any: "comença per qualsevol"
55
+ start_all: "comença per tot"
56
+ not_start: "no comença per"
57
+ not_start_any: "no comença per qualsevol"
58
+ not_start_all: "no comença per tot"
59
+ end: "acaba en"
60
+ end_any: "acaba en qualsevol"
61
+ end_all: "acaba en tot"
62
+ not_end: "no acaba en"
63
+ not_end_any: "no acaba en qualsevol"
64
+ not_end_all: "no acaba en tot"
65
+ 'true': "és verdader"
66
+ 'false': "és fals"
67
+ present: "és present"
68
+ blank: "està en blanc"
69
+ 'null': "és nul"
70
+ not_null: "no és nul"