ransack 2.1.0 → 2.4.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +32 -22
  4. data/CHANGELOG.md +60 -1
  5. data/CONTRIBUTING.md +11 -6
  6. data/Gemfile +21 -17
  7. data/README.md +56 -29
  8. data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_association.rb +2 -9
  9. data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_dependency.rb +25 -3
  10. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
  11. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
  12. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
  13. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
  14. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
  15. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
  16. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
  17. data/lib/{polyamorous.rb → polyamorous/polyamorous.rb} +3 -3
  18. data/lib/ransack.rb +2 -2
  19. data/lib/ransack/adapters/active_record/base.rb +1 -0
  20. data/lib/ransack/adapters/active_record/context.rb +60 -68
  21. data/lib/ransack/adapters/active_record/ransack/constants.rb +17 -2
  22. data/lib/ransack/adapters/active_record/ransack/context.rb +2 -6
  23. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +12 -5
  24. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -1
  25. data/lib/ransack/constants.rb +2 -3
  26. data/lib/ransack/context.rb +19 -18
  27. data/lib/ransack/helpers/form_builder.rb +5 -11
  28. data/lib/ransack/helpers/form_helper.rb +1 -1
  29. data/lib/ransack/locale/ar.yml +70 -0
  30. data/lib/ransack/locale/az.yml +1 -1
  31. data/lib/ransack/locale/ca.yml +70 -0
  32. data/lib/ransack/locale/es.yml +22 -22
  33. data/lib/ransack/locale/fa.yml +70 -0
  34. data/lib/ransack/locale/fi.yml +71 -0
  35. data/lib/ransack/locale/sk.yml +70 -0
  36. data/lib/ransack/locale/zh-CN.yml +12 -12
  37. data/lib/ransack/nodes/condition.rb +8 -0
  38. data/lib/ransack/nodes/grouping.rb +1 -1
  39. data/lib/ransack/predicate.rb +2 -1
  40. data/lib/ransack/search.rb +1 -0
  41. data/lib/ransack/translate.rb +115 -115
  42. data/lib/ransack/version.rb +1 -1
  43. data/ransack.gemspec +5 -21
  44. data/spec/helpers/polyamorous_helper.rb +3 -8
  45. data/spec/{ransack → polyamorous}/join_association_spec.rb +7 -0
  46. data/spec/{ransack → polyamorous}/join_dependency_spec.rb +18 -7
  47. data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
  48. data/spec/ransack/adapters/active_record/base_spec.rb +8 -8
  49. data/spec/ransack/adapters/active_record/context_spec.rb +60 -17
  50. data/spec/ransack/helpers/form_helper_spec.rb +51 -51
  51. data/spec/ransack/predicate_spec.rb +54 -2
  52. data/spec/ransack/search_spec.rb +78 -13
  53. data/spec/spec_helper.rb +4 -0
  54. data/spec/support/schema.rb +13 -2
  55. metadata +29 -136
  56. data/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
  57. data/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
  58. data/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -32
  59. data/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
  60. data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -32
  61. data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -113
@@ -0,0 +1,71 @@
1
+ fi:
2
+ ransack:
3
+ search: "haku"
4
+ predicate: "predikaatti"
5
+ and: "ja"
6
+ or: "tai"
7
+ any: "jokin"
8
+ all: "kaikki"
9
+ combinator: "kombinaattori"
10
+ attribute: "attribuutti"
11
+ value: "arvo"
12
+ condition: "ehto"
13
+ sort: "järjestys"
14
+ asc: "nouseva"
15
+ desc: "laskeva"
16
+ predicates:
17
+ eq: "sama kuin"
18
+ eq_any: "sama kuin jokin"
19
+ eq_all: "sama kuin kaikki"
20
+ not_eq: "eri kuin"
21
+ not_eq_any: "eri kuin jokin"
22
+ not_eq_all: "eri kuin kaikki"
23
+ matches: "täsmää"
24
+ matches_any: "täsmää jonkun kanssa"
25
+ matches_all: "täsmää kaikkien kanssa"
26
+ does_not_match: "ei täsmää"
27
+ does_not_match_any: "ei täsmää minkään kanssa"
28
+ does_not_match_all: "ei täsmää kaikkien kanssa"
29
+ lt: "vähemmän kuin"
30
+ lt_any: "vähemmän kuin jokin"
31
+ lt_all: "vähemmän kuin mikään"
32
+ lteq: "vähemmän tai sama kuin"
33
+ lteq_any: "vähemmän tai sama kuin jokin"
34
+ lteq_all: "vähemmän tai sama kuin mikään"
35
+ gt: "suurempi kuin"
36
+ gt_any: "suurempi kuin jokin"
37
+ gt_all: "suurempi kuin mikään"
38
+ gteq: "suurempi tai yhtä suuri kuin"
39
+ gteq_any: "suurempi tai yhtä suuri kuin jokin"
40
+ gteq_all: "suurempi tai yhtä suuri kuin mikään"
41
+ in: "kohteessa"
42
+ in_any: "jossakin kohteessa"
43
+ in_all: "kaikissa kohteissa"
44
+ not_in: "ei kohteessa"
45
+ not_in_any: "ei jossakin kohteista"
46
+ not_in_all: "ei missään kohteista"
47
+ cont: "sisältää"
48
+ cont_any: "sisältää jonkin"
49
+ cont_all: "sisältää kaikki"
50
+ not_cont: "ei sisällä"
51
+ not_cont_any: "ei sisällä jotakin"
52
+ not_cont_all: "ei sisällä mitään"
53
+ start: "alkaa"
54
+ start_any: "alkaa jollakin"
55
+ start_all: "alkaa kaikilla"
56
+ not_start: "ei ala"
57
+ not_start_any: "ei ala jollakin"
58
+ not_start_all: "ei ala millään"
59
+ end: "päättyy"
60
+ end_any: "päättyy jollakin"
61
+ end_all: "päättyy millään"
62
+ not_end: "ei pääty"
63
+ not_end_any: "ei pääty jollakin"
64
+ not_end_all: "ei pääty millään"
65
+ 'true': "on tosi"
66
+ 'false': "ei ole tosi"
67
+ present: "on läsnä"
68
+ blank: "on tyhjä"
69
+ 'null': "on määrittämätön"
70
+ not_null: "on määritetty"
71
+
@@ -0,0 +1,70 @@
1
+ sk:
2
+ ransack:
3
+ search: "vyhľadávanie"
4
+ predicate: "predikát"
5
+ and: "a"
6
+ or: "alebo"
7
+ any: "akýkoľvek"
8
+ all: "každý"
9
+ combinator: "kombinátor"
10
+ attribute: "atribút"
11
+ value: "hodnota"
12
+ condition: "podmienka"
13
+ sort: "poradie"
14
+ asc: "vzostupne"
15
+ desc: "zostupne"
16
+ predicates:
17
+ eq: "sa rovná"
18
+ eq_any: "sa rovná akémukoľvek"
19
+ eq_all: "sa rovná všetkým"
20
+ not_eq: "sa nerovná"
21
+ not_eq_any: "sa nerovná akémukoľvek"
22
+ not_eq_all: "sa nerovná všetkým"
23
+ matches: "zodpovedá"
24
+ matches_any: "zodpovedá akémukoľvek"
25
+ matches_all: "zodpovedá všetkým"
26
+ does_not_match: "nezodpovedá"
27
+ does_not_match_any: "nezodpovedá akémukoľvek"
28
+ does_not_match_all: "nezodpovedá všetkým"
29
+ lt: "menší ako"
30
+ lt_any: "menší ako akýkoľvek"
31
+ lt_all: "menší ako všetky"
32
+ lteq: "menší alebo rovný"
33
+ lteq_any: "menší alebo rovný akémukoľvek"
34
+ lteq_all: "menší alebo rovný všetkým"
35
+ gt: "väčší ako"
36
+ gt_any: "väčší ako akýkoľvek"
37
+ gt_all: "väčší ako všetky"
38
+ gteq: "väčší alebo rovný"
39
+ gteq_any: "väčší alebo rovný akémukoľvek"
40
+ gteq_all: "väčší alebo rovný všetkým"
41
+ in: "v"
42
+ in_any: "v akejkoľvek"
43
+ in_all: "vo všetkých"
44
+ not_in: "nie je v"
45
+ not_in_any: "nie je v akejkoľvek"
46
+ not_in_all: "nie je vo všetkých"
47
+ cont: "obsahuje"
48
+ cont_any: "obsahuje akúkoľvek"
49
+ cont_all: "obsahuje všetky"
50
+ not_cont: "neobsahuje"
51
+ not_cont_any: "neobsahuje akúkoľvek"
52
+ not_cont_all: "neobsahuje všetky"
53
+ start: "začína na"
54
+ start_any: "začína s akoukoľvek"
55
+ start_all: "začína so všetkými"
56
+ not_start: "nezačíná s"
57
+ not_start_any: "nezačíná s akoukoľvek"
58
+ not_start_all: "nezačíná so všetkými"
59
+ end: "končí s"
60
+ end_any: "končí s akoukoľvek"
61
+ end_all: "končí so všetkými"
62
+ not_end: "nekončí s"
63
+ not_end_any: "nekončí s akoukoľvek"
64
+ not_end_all: "nekončí so všetkými"
65
+ 'true': "je pravdivé"
66
+ 'false': "nie je pravdivé"
67
+ present: "je vyplnené"
68
+ blank: "je prázdne"
69
+ 'null': "je null"
70
+ not_null: "nie je null"
@@ -50,18 +50,18 @@ zh-CN:
50
50
  not_cont: "不包含"
51
51
  not_cont_any: "不包含任意一个值"
52
52
  not_cont_all: "不包含所有值"
53
- start: "以改值开始"
54
- start_any: "以任意一个值开始"
55
- start_all: "以所有值开始"
56
- not_start: "不以改值开始"
57
- not_start_any: "不以任意一个值开始"
58
- not_start_all: "不以所有值开始"
59
- end: "以改值结尾"
60
- end_any: "以任意一个值结尾"
61
- end_all: "以所有值结尾"
62
- not_end: "不以改值结尾"
63
- not_end_any: "不以任意一个值结尾"
64
- not_end_all: "不以所有值结尾"
53
+ start: "始于"
54
+ start_any: "始于任一值"
55
+ start_all: "始于任意值"
56
+ not_start: "非始于"
57
+ not_start_any: "非始于任一值"
58
+ not_start_all: "非始于任意值"
59
+ end: "止于"
60
+ end_any: "止于任一值"
61
+ end_all: "止于任意值"
62
+ not_end: "非止于"
63
+ not_end_any: "非止于任一值"
64
+ not_end_all: "非止于任意值"
65
65
  'true': "等于true"
66
66
  'false': "等于false"
67
67
  present: "有值"
@@ -255,6 +255,14 @@ module Ransack
255
255
  end
256
256
  end
257
257
 
258
+ def attr_value_for_attribute(attr)
259
+ return attr.attr if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
260
+
261
+ predicate.case_insensitive ? attr.attr.lower : attr.attr
262
+ rescue
263
+ attr.attr
264
+ end
265
+
258
266
 
259
267
  def default_type
260
268
  predicate.type || (attributes.first && attributes.first.type)
@@ -108,7 +108,7 @@ module Ransack
108
108
  alias :g= :groupings=
109
109
 
110
110
  def method_missing(method_id, *args)
111
- method_name = method_id.to_s
111
+ method_name = method_id.to_s.dup
112
112
  writer = method_name.sub!(/\=$/, ''.freeze)
113
113
  if attribute_method?(method_name)
114
114
  if writer
@@ -1,7 +1,7 @@
1
1
  module Ransack
2
2
  class Predicate
3
3
  attr_reader :name, :arel_predicate, :type, :formatter, :validator,
4
- :compound, :wants_array
4
+ :compound, :wants_array, :case_insensitive
5
5
 
6
6
  class << self
7
7
 
@@ -42,6 +42,7 @@ module Ransack
42
42
  @compound = opts[:compound]
43
43
  @wants_array = opts.fetch(:wants_array,
44
44
  @compound || Constants::IN_NOT_IN.include?(@arel_predicate))
45
+ @case_insensitive = opts[:case_insensitive]
45
46
  end
46
47
 
47
48
  def eql?(other)
@@ -18,6 +18,7 @@ module Ransack
18
18
  params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
19
19
  if params.is_a? Hash
20
20
  params = params.dup
21
+ params = params.transform_values { |v| v.is_a?(String) ? v.strip : v }
21
22
  params.delete_if { |k, v| [*v].all?{ |i| i.blank? && i != false } }
22
23
  else
23
24
  params = {}
@@ -6,151 +6,151 @@ I18n.load_path += Dir[
6
6
 
7
7
  module Ransack
8
8
  module Translate
9
- def self.word(key, options = {})
10
- I18n.translate(:"ransack.#{key}", :default => key.to_s)
11
- end
12
-
13
- def self.predicate(key, options = {})
14
- I18n.translate(:"ransack.predicates.#{key}", :default => key.to_s)
15
- end
16
-
17
- def self.attribute(key, options = {})
18
- unless context = options.delete(:context)
19
- raise ArgumentError, "A context is required to translate attributes"
9
+ class << self
10
+ def word(key, options = {})
11
+ I18n.translate(:"ransack.#{key}", default: key.to_s)
20
12
  end
21
13
 
22
- original_name = key.to_s
23
- base_class = context.klass
24
- base_ancestors = base_class.ancestors.select {
25
- |x| x.respond_to?(:model_name)
26
- }
27
- attributes_str = original_name.dup # will be modified by ⬇
28
- predicate = Predicate.detect_and_strip_from_string!(attributes_str)
29
- attribute_names = attributes_str.split(/_and_|_or_/)
30
- combinator = attributes_str.match(/_and_/) ? :and : :or
31
- defaults = base_ancestors.map do |klass|
32
- "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
14
+ def predicate(key, options = {})
15
+ I18n.translate(:"ransack.predicates.#{key}", default: key.to_s)
33
16
  end
34
17
 
35
- translated_names = attribute_names.map do |name|
36
- attribute_name(context, name, options[:include_associations])
37
- end
18
+ def attribute(key, options = {})
19
+ unless context = options.delete(:context)
20
+ raise ArgumentError, "A context is required to translate attributes"
21
+ end
38
22
 
39
- interpolations = {
40
- :attributes => translated_names.join(" #{Translate.word(combinator)} ")
41
- }
23
+ original_name = key.to_s
24
+ base_class = context.klass
25
+ base_ancestors = base_class.ancestors.select {
26
+ |x| x.respond_to?(:model_name)
27
+ }
28
+ attributes_str = original_name.dup # will be modified by ⬇
29
+ predicate = Predicate.detect_and_strip_from_string!(attributes_str)
30
+ attribute_names = attributes_str.split(/_and_|_or_/)
31
+ combinator = attributes_str =~ /_and_/ ? :and : :or
32
+ defaults = base_ancestors.map do |klass|
33
+ "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
34
+ end
42
35
 
43
- if predicate
44
- defaults << "%{attributes} %{predicate}".freeze
45
- interpolations[:predicate] = Translate.predicate(predicate)
46
- else
47
- defaults << "%{attributes}".freeze
48
- end
36
+ translated_names = attribute_names.map do |name|
37
+ attribute_name(context, name, options[:include_associations])
38
+ end
49
39
 
50
- defaults << options.delete(:default) if options[:default]
51
- options.reverse_merge! :count => 1, :default => defaults
52
- I18n.translate(defaults.shift, options.merge(interpolations))
53
- end
40
+ interpolations = {
41
+ attributes: translated_names.join(" #{Translate.word(combinator)} ")
42
+ }
54
43
 
55
- def self.association(key, options = {})
56
- unless context = options.delete(:context)
57
- raise ArgumentError, "A context is required to translate associations"
44
+ if predicate
45
+ defaults << "%{attributes} %{predicate}".freeze
46
+ interpolations[:predicate] = Translate.predicate(predicate)
47
+ else
48
+ defaults << "%{attributes}".freeze
49
+ end
50
+
51
+ defaults << options.delete(:default) if options[:default]
52
+ options.reverse_merge! count: 1, default: defaults
53
+ I18n.translate(defaults.shift, **options.merge(interpolations))
58
54
  end
59
55
 
60
- defaults =
61
- if key.blank?
62
- [:"ransack.models.#{i18n_key(context.klass)}",
63
- :"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"]
64
- else
65
- [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
56
+ def association(key, options = {})
57
+ unless context = options.delete(:context)
58
+ raise ArgumentError, "A context is required to translate associations"
66
59
  end
67
- defaults << context.traverse(key).model_name.human
68
- options = { :count => 1, :default => defaults }
69
- I18n.translate(defaults.shift, options)
70
- end
71
60
 
72
- private
61
+ defaults =
62
+ if key.blank?
63
+ [:"ransack.models.#{i18n_key(context.klass)}",
64
+ :"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"]
65
+ else
66
+ [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
67
+ end
68
+ defaults << context.traverse(key).model_name.human
69
+ options = { :count => 1, :default => defaults }
70
+ I18n.translate(defaults.shift, **options)
71
+ end
73
72
 
74
- def self.attribute_name(context, name, include_associations = nil)
75
- @context, @name = context, name
76
- @assoc_path = context.association_path(name)
77
- @attr_name = @name.sub(/^#{@assoc_path}_/, ''.freeze)
78
- associated_class = @context.traverse(@assoc_path) if @assoc_path.present?
79
- @include_associated = include_associations && associated_class
73
+ private
80
74
 
81
- defaults = default_attribute_name << fallback_args
82
- options = { :count => 1, :default => defaults }
83
- interpolations = build_interpolations(associated_class)
75
+ def attribute_name(context, name, include_associations = nil)
76
+ @context, @name = context, name
77
+ @assoc_path = context.association_path(name)
78
+ @attr_name = @name.sub(/^#{@assoc_path}_/, ''.freeze)
79
+ associated_class = @context.traverse(@assoc_path) if @assoc_path.present?
80
+ @include_associated = include_associations && associated_class
84
81
 
85
- I18n.translate(defaults.shift, options.merge(interpolations))
86
- end
82
+ defaults = default_attribute_name << fallback_args
83
+ options = { count: 1, default: defaults }
84
+ interpolations = build_interpolations(associated_class)
87
85
 
88
- def self.default_attribute_name
89
- ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym]
90
- end
86
+ I18n.translate(defaults.shift, **options.merge(interpolations))
87
+ end
91
88
 
92
- def self.fallback_args
93
- if @include_associated
94
- '%{association_name} %{attr_fallback_name}'.freeze
95
- else
96
- '%{attr_fallback_name}'.freeze
89
+ def default_attribute_name
90
+ ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym]
97
91
  end
98
- end
99
92
 
100
- def self.build_interpolations(associated_class)
101
- {
102
- :attr_fallback_name => attr_fallback_name(associated_class),
103
- :association_name => association_name
104
- }
105
- .reject { |_, value| value.nil? }
106
- end
93
+ def fallback_args
94
+ if @include_associated
95
+ '%{association_name} %{attr_fallback_name}'.freeze
96
+ else
97
+ '%{attr_fallback_name}'.freeze
98
+ end
99
+ end
107
100
 
108
- def self.attr_fallback_name(associated_class)
109
- I18n.t(
110
- :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}",
111
- :default => default_interpolation(associated_class)
101
+ def build_interpolations(associated_class)
102
+ {
103
+ attr_fallback_name: attr_fallback_name(associated_class),
104
+ association_name: association_name
105
+ }.reject { |_, value| value.nil? }
106
+ end
107
+
108
+ def attr_fallback_name(associated_class)
109
+ I18n.t(
110
+ :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}",
111
+ default: default_interpolation(associated_class)
112
112
  )
113
- end
113
+ end
114
114
 
115
- def self.fallback_class(associated_class)
116
- i18n_key(associated_class || @context.klass)
117
- end
115
+ def fallback_class(associated_class)
116
+ i18n_key(associated_class || @context.klass)
117
+ end
118
118
 
119
- def self.association_name
120
- association(@assoc_path, :context => @context) if @include_associated
121
- end
119
+ def association_name
120
+ association(@assoc_path, context: @context) if @include_associated
121
+ end
122
122
 
123
- def self.default_interpolation(associated_class)
124
- [
125
- associated_attribute(associated_class),
126
- ".attributes.#{@attr_name}".to_sym,
127
- @attr_name.humanize
128
- ]
129
- .flatten
130
- end
123
+ def default_interpolation(associated_class)
124
+ [
125
+ associated_attribute(associated_class),
126
+ ".attributes.#{@attr_name}".to_sym,
127
+ @attr_name.humanize
128
+ ].flatten
129
+ end
131
130
 
132
- def self.associated_attribute(associated_class)
133
- if associated_class
134
- translated_attribute(associated_class)
135
- else
136
- translated_ancestor_attributes
131
+ def associated_attribute(associated_class)
132
+ if associated_class
133
+ translated_attribute(associated_class)
134
+ else
135
+ translated_ancestor_attributes
136
+ end
137
137
  end
138
- end
139
138
 
140
- def self.translated_attribute(associated_class)
141
- key = "#{associated_class.i18n_scope}.attributes.#{
139
+ def translated_attribute(associated_class)
140
+ key = "#{associated_class.i18n_scope}.attributes.#{
142
141
  i18n_key(associated_class)}.#{@attr_name}"
143
- ["#{key}.one".to_sym, key.to_sym]
144
- end
142
+ ["#{key}.one".to_sym, key.to_sym]
143
+ end
145
144
 
146
- def self.translated_ancestor_attributes
147
- @context.klass.ancestors
148
- .select { |ancestor| ancestor.respond_to?(:model_name) }
149
- .map { |ancestor| translated_attribute(ancestor) }
150
- end
145
+ def translated_ancestor_attributes
146
+ @context.klass.ancestors
147
+ .select { |ancestor| ancestor.respond_to?(:model_name) }
148
+ .map { |ancestor| translated_attribute(ancestor) }
149
+ end
151
150
 
152
- def self.i18n_key(klass)
153
- raise "not implemented"
151
+ def i18n_key(klass)
152
+ raise "not implemented"
153
+ end
154
154
  end
155
155
  end
156
156
  end