ransack 2.3.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/SECURITY.md +12 -0
  4. data/.github/workflows/codeql.yml +72 -0
  5. data/.github/workflows/cronjob.yml +102 -0
  6. data/.github/workflows/deploy.yml +35 -0
  7. data/.github/workflows/rubocop.yml +20 -0
  8. data/.github/workflows/test-deploy.yml +29 -0
  9. data/.github/workflows/test.yml +128 -0
  10. data/.nojekyll +0 -0
  11. data/.rubocop.yml +47 -0
  12. data/CHANGELOG.md +228 -1
  13. data/CONTRIBUTING.md +47 -22
  14. data/Gemfile +24 -10
  15. data/README.md +49 -917
  16. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  17. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +75 -0
  18. data/docs/.gitignore +19 -0
  19. data/docs/.nojekyll +0 -0
  20. data/docs/babel.config.js +3 -0
  21. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  22. data/docs/docs/getting-started/_category_.json +4 -0
  23. data/docs/docs/getting-started/advanced-mode.md +46 -0
  24. data/docs/docs/getting-started/configuration.md +47 -0
  25. data/docs/docs/getting-started/search-matches.md +67 -0
  26. data/docs/docs/getting-started/simple-mode.md +288 -0
  27. data/docs/docs/getting-started/sorting.md +79 -0
  28. data/docs/docs/getting-started/using-predicates.md +282 -0
  29. data/docs/docs/going-further/_category_.json +4 -0
  30. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  31. data/docs/docs/going-further/associations.md +70 -0
  32. data/docs/docs/going-further/custom-predicates.md +52 -0
  33. data/docs/docs/going-further/documentation.md +43 -0
  34. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  35. data/docs/docs/going-further/external-guides.md +57 -0
  36. data/docs/docs/going-further/form-customisation.md +63 -0
  37. data/docs/docs/going-further/i18n.md +53 -0
  38. data/docs/docs/going-further/img/create_release.png +0 -0
  39. data/docs/docs/going-further/merging-searches.md +41 -0
  40. data/docs/docs/going-further/other-notes.md +428 -0
  41. data/docs/docs/going-further/polymorphic-search.md +40 -0
  42. data/docs/docs/going-further/ransackers.md +331 -0
  43. data/docs/docs/going-further/release_process.md +36 -0
  44. data/docs/docs/going-further/saving-queries.md +82 -0
  45. data/docs/docs/going-further/searching-postgres.md +57 -0
  46. data/docs/docs/going-further/wiki-contributors.md +82 -0
  47. data/docs/docs/intro.md +99 -0
  48. data/docs/docusaurus.config.js +120 -0
  49. data/docs/package.json +42 -0
  50. data/docs/sidebars.js +31 -0
  51. data/docs/src/components/HomepageFeatures/index.js +64 -0
  52. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  53. data/docs/src/css/custom.css +39 -0
  54. data/docs/src/pages/index.module.css +23 -0
  55. data/docs/src/pages/markdown-page.md +7 -0
  56. data/docs/static/.nojekyll +0 -0
  57. data/docs/static/img/docusaurus.png +0 -0
  58. data/docs/static/img/favicon.ico +0 -0
  59. data/docs/static/img/logo.svg +1 -0
  60. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  61. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  62. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  63. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  64. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  65. data/docs/yarn.lock +8790 -0
  66. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
  67. data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord_6.1_ruby_2}/join_dependency.rb +23 -12
  68. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -0
  69. data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
  70. data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
  71. data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
  72. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  73. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  74. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  75. data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -8
  76. data/lib/ransack/adapters/active_record/base.rb +83 -10
  77. data/lib/ransack/adapters/active_record/context.rb +59 -115
  78. data/lib/ransack/configuration.rb +53 -10
  79. data/lib/ransack/constants.rb +126 -7
  80. data/lib/ransack/context.rb +34 -5
  81. data/lib/ransack/helpers/form_builder.rb +11 -17
  82. data/lib/ransack/helpers/form_helper.rb +14 -5
  83. data/lib/ransack/helpers.rb +1 -1
  84. data/lib/ransack/locale/sk.yml +70 -0
  85. data/lib/ransack/locale/sv.yml +70 -0
  86. data/lib/ransack/nodes/attribute.rb +3 -3
  87. data/lib/ransack/nodes/condition.rb +87 -8
  88. data/lib/ransack/nodes/grouping.rb +4 -4
  89. data/lib/ransack/nodes/node.rb +1 -1
  90. data/lib/ransack/nodes/sort.rb +3 -3
  91. data/lib/ransack/nodes/value.rb +3 -3
  92. data/lib/ransack/predicate.rb +3 -2
  93. data/lib/ransack/ransacker.rb +1 -1
  94. data/lib/ransack/search.rb +15 -7
  95. data/lib/ransack/translate.rb +6 -6
  96. data/lib/ransack/version.rb +1 -1
  97. data/lib/ransack/visitor.rb +38 -2
  98. data/lib/ransack.rb +6 -10
  99. data/ransack.gemspec +9 -24
  100. data/spec/blueprints/articles.rb +1 -1
  101. data/spec/blueprints/comments.rb +1 -1
  102. data/spec/blueprints/notes.rb +1 -1
  103. data/spec/blueprints/tags.rb +1 -1
  104. data/spec/console.rb +5 -5
  105. data/spec/helpers/polyamorous_helper.rb +2 -17
  106. data/spec/helpers/ransack_helper.rb +1 -1
  107. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  108. data/spec/{ransack → polyamorous}/join_association_spec.rb +3 -1
  109. data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -16
  110. data/spec/ransack/adapters/active_record/base_spec.rb +109 -16
  111. data/spec/ransack/adapters/active_record/context_spec.rb +19 -18
  112. data/spec/ransack/configuration_spec.rb +33 -9
  113. data/spec/ransack/helpers/form_builder_spec.rb +8 -8
  114. data/spec/ransack/helpers/form_helper_spec.rb +109 -20
  115. data/spec/ransack/nodes/condition_spec.rb +37 -0
  116. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  117. data/spec/ransack/nodes/value_spec.rb +115 -0
  118. data/spec/ransack/predicate_spec.rb +75 -2
  119. data/spec/ransack/search_spec.rb +239 -38
  120. data/spec/ransack/translate_spec.rb +1 -1
  121. data/spec/spec_helper.rb +9 -5
  122. data/spec/support/schema.rb +83 -12
  123. metadata +105 -195
  124. data/.travis.yml +0 -49
  125. data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -116
  126. data/lib/ransack/adapters/active_record/ransack/context.rb +0 -60
  127. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
  128. data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
  129. data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
  130. data/lib/ransack/adapters.rb +0 -64
  131. data/lib/ransack/nodes.rb +0 -8
  132. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
  133. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
  134. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -31
  135. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
  136. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -31
  137. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -112
  138. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +0 -12
  139. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +0 -22
  140. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +0 -81
  141. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +0 -2
  142. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -2
  143. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -2
  144. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
  145. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
  146. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -2
  147. data/polyamorous/lib/polyamorous/version.rb +0 -3
  148. data/polyamorous/polyamorous.gemspec +0 -35
  149. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  150. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  151. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  152. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  153. /data/{logo → docs/static/logo}/ransack.png +0 -0
  154. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  155. /data/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
  156. /data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
  157. /data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
  158. /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
  159. /data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
@@ -36,7 +36,7 @@ module Ransack
36
36
  'lt'.freeze, 'lteq'.freeze,
37
37
  'gt'.freeze, 'gteq'.freeze,
38
38
  'in'.freeze, 'not_in'.freeze
39
- ].freeze
39
+ ].freeze
40
40
  A_S_I = ['a'.freeze, 's'.freeze, 'i'.freeze].freeze
41
41
 
42
42
  EQ = 'eq'.freeze
@@ -45,13 +45,132 @@ module Ransack
45
45
  NOT_EQ_ALL = 'not_eq_all'.freeze
46
46
  CONT = 'cont'.freeze
47
47
 
48
- RAILS_5_1 = '5.1'.freeze
49
- RAILS_5_2 = '5.2'.freeze
50
- RAILS_5_2_0 = '5.2.0'.freeze
51
- RAILS_6_0 = '6.0.0'.freeze
52
-
53
48
  RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
54
49
  RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze
50
+
51
+ DISTINCT = 'DISTINCT '.freeze
52
+
53
+ DERIVED_PREDICATES = [
54
+ [CONT, {
55
+ arel_predicate: 'matches'.freeze,
56
+ formatter: proc { |v| "%#{escape_wildcards(v)}%" }
57
+ }
58
+ ],
59
+ ['not_cont'.freeze, {
60
+ arel_predicate: 'does_not_match'.freeze,
61
+ formatter: proc { |v| "%#{escape_wildcards(v)}%" }
62
+ }
63
+ ],
64
+ ['i_cont'.freeze, {
65
+ arel_predicate: 'matches'.freeze,
66
+ formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
67
+ case_insensitive: true
68
+ }
69
+ ],
70
+ ['not_i_cont'.freeze, {
71
+ arel_predicate: 'does_not_match'.freeze,
72
+ formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
73
+ case_insensitive: true
74
+ }
75
+ ],
76
+ ['start'.freeze, {
77
+ arel_predicate: 'matches'.freeze,
78
+ formatter: proc { |v| "#{escape_wildcards(v)}%" }
79
+ }
80
+ ],
81
+ ['not_start'.freeze, {
82
+ arel_predicate: 'does_not_match'.freeze,
83
+ formatter: proc { |v| "#{escape_wildcards(v)}%" }
84
+ }
85
+ ],
86
+ ['end'.freeze, {
87
+ arel_predicate: 'matches'.freeze,
88
+ formatter: proc { |v| "%#{escape_wildcards(v)}" }
89
+ }
90
+ ],
91
+ ['not_end'.freeze, {
92
+ arel_predicate: 'does_not_match'.freeze,
93
+ formatter: proc { |v| "%#{escape_wildcards(v)}" }
94
+ }
95
+ ],
96
+ ['true'.freeze, {
97
+ arel_predicate: proc { |v| v ? EQ : NOT_EQ },
98
+ compounds: false,
99
+ type: :boolean,
100
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
101
+ formatter: proc { |v| true }
102
+ }
103
+ ],
104
+ ['not_true'.freeze, {
105
+ arel_predicate: proc { |v| v ? NOT_EQ : EQ },
106
+ compounds: false,
107
+ type: :boolean,
108
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
109
+ formatter: proc { |v| true }
110
+ }
111
+ ],
112
+ ['false'.freeze, {
113
+ arel_predicate: proc { |v| v ? EQ : NOT_EQ },
114
+ compounds: false,
115
+ type: :boolean,
116
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
117
+ formatter: proc { |v| false }
118
+ }
119
+ ],
120
+ ['not_false'.freeze, {
121
+ arel_predicate: proc { |v| v ? NOT_EQ : EQ },
122
+ compounds: false,
123
+ type: :boolean,
124
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
125
+ formatter: proc { |v| false }
126
+ }
127
+ ],
128
+ ['present'.freeze, {
129
+ arel_predicate: proc { |v| v ? NOT_EQ_ALL : EQ_ANY },
130
+ compounds: false,
131
+ type: :boolean,
132
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
133
+ formatter: proc { |v| [nil, ''.freeze].freeze }
134
+ }
135
+ ],
136
+ ['blank'.freeze, {
137
+ arel_predicate: proc { |v| v ? EQ_ANY : NOT_EQ_ALL },
138
+ compounds: false,
139
+ type: :boolean,
140
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
141
+ formatter: proc { |v| [nil, ''.freeze].freeze }
142
+ }
143
+ ],
144
+ ['null'.freeze, {
145
+ arel_predicate: proc { |v| v ? EQ : NOT_EQ },
146
+ compounds: false,
147
+ type: :boolean,
148
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
149
+ formatter: proc { |v| nil }
150
+ }
151
+ ],
152
+ ['not_null'.freeze, {
153
+ arel_predicate: proc { |v| v ? NOT_EQ : EQ },
154
+ compounds: false,
155
+ type: :boolean,
156
+ validator: proc { |v| BOOLEAN_VALUES.include?(v) },
157
+ formatter: proc { |v| nil } }
158
+ ]
159
+ ].freeze
160
+
161
+ module_function
162
+ # replace % \ to \% \\
163
+ def escape_wildcards(unescaped)
164
+ case ActiveRecord::Base.connection.adapter_name
165
+ when "Mysql2".freeze
166
+ # Necessary for MySQL
167
+ unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1')
168
+ when "PostgreSQL".freeze
169
+ # Necessary for PostgreSQL
170
+ unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1')
171
+ else
172
+ unescaped
173
+ end
174
+ end
55
175
  end
56
176
  end
57
-
@@ -1,19 +1,24 @@
1
1
  require 'ransack/visitor'
2
- Ransack::Adapters.object_mapper.require_context
3
2
 
4
3
  module Ransack
5
4
  class Context
6
5
  attr_reader :search, :object, :klass, :base, :engine, :arel_visitor
7
6
  attr_accessor :auth_object, :search_key
7
+ attr_reader :arel_visitor
8
8
 
9
9
  class << self
10
10
 
11
11
  def for_class(klass, options = {})
12
- raise "not implemented"
12
+ if klass < ActiveRecord::Base
13
+ Adapters::ActiveRecord::Context.new(klass, options)
14
+ end
13
15
  end
14
16
 
15
17
  def for_object(object, options = {})
16
- raise "not implemented"
18
+ case object
19
+ when ActiveRecord::Relation
20
+ Adapters::ActiveRecord::Context.new(object.klass, options)
21
+ end
17
22
  end
18
23
 
19
24
  def for(object, options = {})
@@ -30,11 +35,35 @@ module Ransack
30
35
  end # << self
31
36
 
32
37
  def initialize(object, options = {})
33
- raise "not implemented"
38
+ @object = relation_for(object)
39
+ @klass = @object.klass
40
+ @join_dependency = join_dependency(@object)
41
+ @join_type = options[:join_type] || Polyamorous::OuterJoin
42
+ @search_key = options[:search_key] || Ransack.options[:search_key]
43
+ @associations_pot = {}
44
+ @tables_pot = {}
45
+ @lock_associations = []
46
+
47
+ @base = @join_dependency.instance_variable_get(:@join_root)
48
+ end
49
+
50
+ def bind_pair_for(key)
51
+ @bind_pairs ||= {}
52
+
53
+ @bind_pairs[key] ||= begin
54
+ parent, attr_name = get_parent_and_attribute_name(key.to_s)
55
+ [parent, attr_name] if parent && attr_name
56
+ end
34
57
  end
35
58
 
36
59
  def klassify(obj)
37
- raise "not implemented"
60
+ if Class === obj && ::ActiveRecord::Base > obj
61
+ obj
62
+ elsif obj.respond_to? :klass
63
+ obj.klass
64
+ else
65
+ raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
66
+ end
38
67
  end
39
68
 
40
69
  # Convert a string representing a chain of associations and an attribute
@@ -7,17 +7,11 @@ module ActionView::Helpers::Tags
7
7
  class Base
8
8
  private
9
9
  if defined? ::ActiveRecord
10
- if ::ActiveRecord::VERSION::STRING < '5.2'
11
- def value(object)
12
- object.send @method_name if object # use send instead of public_send
13
- end
14
- else # rails/rails#29791
15
- def value
16
- if @allow_method_names_outside_object
17
- object.send @method_name if object && object.respond_to?(@method_name, true)
18
- else
19
- object.send @method_name if object
20
- end
10
+ def value
11
+ if @allow_method_names_outside_object
12
+ object.send @method_name if object && object.respond_to?(@method_name, true)
13
+ else
14
+ object.send @method_name if object
21
15
  end
22
16
  end
23
17
  end
@@ -39,7 +33,7 @@ module Ransack
39
33
  text = args.first
40
34
  i18n = options[:i18n] || {}
41
35
  text ||= object.translate(
42
- method, i18n.reverse_merge(:include_associations => true)
36
+ method, i18n.reverse_merge(include_associations: true)
43
37
  ) if object.respond_to? :translate
44
38
  super(method, text, options, &block)
45
39
  end
@@ -51,9 +45,9 @@ module Ransack
51
45
  end
52
46
 
53
47
  def attribute_select(options = nil, html_options = nil, action = nil)
54
- options = options || {}
55
- html_options = html_options || {}
56
- action = action || Constants::SEARCH
48
+ options ||= {}
49
+ html_options ||= {}
50
+ action ||= Constants::SEARCH
57
51
  default = options.delete(:default)
58
52
  raise ArgumentError, formbuilder_error_message(
59
53
  "#{action}_select") unless object.respond_to?(:context)
@@ -246,7 +240,7 @@ module Ransack
246
240
  def get_attribute_element(action, base)
247
241
  begin
248
242
  [
249
- Translate.association(base, :context => object.context),
243
+ Translate.association(base, context: object.context),
250
244
  collection_for_base(action, base)
251
245
  ]
252
246
  rescue UntraversableAssociationError
@@ -259,7 +253,7 @@ module Ransack
259
253
  [
260
254
  attr_from_base_and_column(base, c),
261
255
  Translate.attribute(
262
- attr_from_base_and_column(base, c), :context => object.context
256
+ attr_from_base_and_column(base, c), context: object.context
263
257
  )
264
258
  ]
265
259
  end
@@ -129,13 +129,21 @@ module Ransack
129
129
  end
130
130
 
131
131
  def url_options
132
- @params.merge(
133
- @options.merge(
132
+ @params.except(:host).merge(
133
+ @options.except(:class, :data, :host).merge(
134
134
  @search.context.search_key => search_and_sort_params))
135
135
  end
136
136
 
137
137
  def html_options(args)
138
- html_options = extract_options_and_mutate_args!(args)
138
+ if args.empty?
139
+ html_options = @options
140
+ else
141
+ deprecation_message = "Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one."
142
+ caller_location = caller_locations(2, 2).first
143
+ warn "#{deprecation_message} (called at #{caller_location.path}:#{caller_location.lineno})"
144
+ html_options = extract_options_and_mutate_args!(args)
145
+ end
146
+
139
147
  html_options.merge(
140
148
  class: [['sort_link'.freeze, @current_dir], html_options[:class]]
141
149
  .compact.join(' '.freeze)
@@ -145,7 +153,7 @@ module Ransack
145
153
  private
146
154
 
147
155
  def parameters_hash(params)
148
- if ::ActiveRecord::VERSION::MAJOR >= 5 && params.respond_to?(:to_unsafe_h)
156
+ if params.respond_to?(:to_unsafe_h)
149
157
  params.to_unsafe_h
150
158
  else
151
159
  params
@@ -172,7 +180,8 @@ module Ransack
172
180
  end
173
181
 
174
182
  def search_params
175
- @params[@search.context.search_key].presence || {}
183
+ query_params = @params[@search.context.search_key]
184
+ query_params.is_a?(Hash) ? query_params : {}
176
185
  end
177
186
 
178
187
  def sort_params
@@ -1,2 +1,2 @@
1
1
  require 'ransack/helpers/form_builder'
2
- require 'ransack/helpers/form_helper'
2
+ require 'ransack/helpers/form_helper'
@@ -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"
@@ -0,0 +1,70 @@
1
+ sv:
2
+ ransack:
3
+ search: "sök"
4
+ predicate: "predikat"
5
+ and: "och"
6
+ or: "eller"
7
+ any: "vilken som"
8
+ all: "alla"
9
+ combinator: "kombinator"
10
+ attribute: "attribut"
11
+ value: "värde"
12
+ condition: "villkor"
13
+ sort: "sortera"
14
+ asc: "stigande"
15
+ desc: "fallande"
16
+ predicates:
17
+ eq: "lika med"
18
+ eq_any: "lika med vilket som"
19
+ eq_all: "lika med alla"
20
+ not_eq: "inte lika med"
21
+ not_eq_any: "inte lika med någon"
22
+ not_eq_all: "inte lika med alla"
23
+ matches: "matchar"
24
+ matches_any: "matchar någon"
25
+ matches_all: "matchar alla"
26
+ does_not_match: "matchar inte"
27
+ does_not_match_any: "matchar inte någon"
28
+ does_not_match_all: "matchar inte alla"
29
+ lt: "mindre än"
30
+ lt_any: "mindre än någon"
31
+ lt_all: "mindre än alla"
32
+ lteq: "mindre än eller lika med"
33
+ lteq_any: "mindre än eller lika med någon"
34
+ lteq_all: "mindre än eller lika med alla"
35
+ gt: "större än"
36
+ gt_any: "större än någon"
37
+ gt_all: "större än alla"
38
+ gteq: "större än eller lika med"
39
+ gteq_any: "större än eller lika med någon"
40
+ gteq_all: "större än eller lika med alla"
41
+ in: "i"
42
+ in_any: "i någon"
43
+ in_all: "i alla"
44
+ not_in: "inte i"
45
+ not_in_any: "inte i någon"
46
+ not_in_all: "inte i alla"
47
+ cont: "innehåller"
48
+ cont_any: "innehåller någon"
49
+ cont_all: "innehåller alla"
50
+ not_cont: "innehåller inte"
51
+ not_cont_any: "innehåller inte någon"
52
+ not_cont_all: "innehåller inte alla"
53
+ start: "börjar med"
54
+ start_any: "börjar med någon"
55
+ start_all: "börjar med alla"
56
+ not_start: "börjar inte med"
57
+ not_start_any: "börjar inte med någon"
58
+ not_start_all: "börjar inte med alla"
59
+ end: "slutar med"
60
+ end_any: "slutar med någon"
61
+ end_all: "slutar med alla"
62
+ not_end: "slutar inte med"
63
+ not_end_any: "slutar inte med någon"
64
+ not_end_all: "slutar inte med alla"
65
+ 'true': "är sant"
66
+ 'false': "är falskt"
67
+ present: "existerar"
68
+ blank: "är tom"
69
+ 'null': "är null"
70
+ not_null: "är inte null"
@@ -5,8 +5,8 @@ module Ransack
5
5
 
6
6
  attr_reader :name, :ransacker_args
7
7
 
8
- delegate :blank?, :present?, :to => :name
9
- delegate :engine, :to => :context
8
+ delegate :blank?, :present?, to: :name
9
+ delegate :engine, to: :context
10
10
 
11
11
  def initialize(context, name = nil, ransacker_args = [])
12
12
  super(context)
@@ -30,7 +30,7 @@ module Ransack
30
30
 
31
31
  def type
32
32
  if ransacker
33
- return ransacker.type
33
+ ransacker.type
34
34
  else
35
35
  context.type_for(self)
36
36
  end
@@ -2,8 +2,8 @@ module Ransack
2
2
  module Nodes
3
3
  class Condition < Node
4
4
  i18n_word :attribute, :predicate, :combinator, :value
5
- i18n_alias :a => :attribute, :p => :predicate,
6
- :m => :combinator, :v => :value
5
+ i18n_alias a: :attribute, p: :predicate,
6
+ m: :combinator, v: :value
7
7
 
8
8
  attr_accessor :predicate
9
9
 
@@ -15,10 +15,10 @@ module Ransack
15
15
  if attributes.size > 0 && predicate
16
16
  condition = self.new(context)
17
17
  condition.build(
18
- :a => attributes,
19
- :p => predicate.name,
20
- :m => combinator,
21
- :v => predicate.wants_array ? Array(values) : [values]
18
+ a: attributes,
19
+ p: predicate.name,
20
+ m: combinator,
21
+ v: predicate.wants_array ? Array(values) : [values]
22
22
  )
23
23
  # TODO: Figure out what to do with multiple types of attributes,
24
24
  # if anything. Tempted to go with "garbage in, garbage out" here.
@@ -127,7 +127,6 @@ module Ransack
127
127
  alias :m= :combinator=
128
128
  alias :m :combinator
129
129
 
130
-
131
130
  # == build_attribute
132
131
  #
133
132
  # This method was originally called from Nodes::Grouping#new_condition
@@ -255,6 +254,13 @@ module Ransack
255
254
  end
256
255
  end
257
256
 
257
+ def attr_value_for_attribute(attr)
258
+ return attr.attr if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
259
+
260
+ predicate.case_insensitive ? attr.attr.lower : attr.attr
261
+ rescue
262
+ attr.attr
263
+ end
258
264
 
259
265
  def default_type
260
266
  predicate.type || (attributes.first && attributes.first.type)
@@ -277,12 +283,85 @@ module Ransack
277
283
  predicate.negative?
278
284
  end
279
285
 
286
+ def arel_predicate
287
+ predicate = attributes.map { |attribute|
288
+ association = attribute.parent
289
+ if negative? && attribute.associated_collection?
290
+ query = context.build_correlated_subquery(association)
291
+ context.remove_association(association)
292
+ if self.predicate_name == 'not_null' && self.value
293
+ query.where(format_predicate(attribute))
294
+ Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
295
+ else
296
+ query.where(format_predicate(attribute).not)
297
+ Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
298
+ end
299
+ else
300
+ format_predicate(attribute)
301
+ end
302
+ }.reduce(combinator_method)
303
+
304
+ if replace_right_node?(predicate)
305
+ # Replace right node object to plain integer value in order to avoid
306
+ # ActiveModel::RangeError from Arel::Node::Casted.
307
+ # The error can be ignored here because RDBMSs accept large numbers
308
+ # in condition clauses.
309
+ plain_value = predicate.right.value
310
+ predicate.right = plain_value
311
+ end
312
+
313
+ predicate
314
+ end
315
+
280
316
  private
281
317
 
318
+ def combinator_method
319
+ combinator === Constants::OR ? :or : :and
320
+ end
321
+
322
+ def format_predicate(attribute)
323
+ arel_pred = arel_predicate_for_attribute(attribute)
324
+ arel_values = formatted_values_for_attribute(attribute)
325
+ predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
326
+
327
+ if in_predicate?(predicate)
328
+ predicate.right = predicate.right.map do |pr|
329
+ casted_array?(pr) ? format_values_for(pr) : pr
330
+ end
331
+ end
332
+
333
+ predicate
334
+ end
335
+
336
+ def in_predicate?(predicate)
337
+ return unless defined?(Arel::Nodes::Casted)
338
+ predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
339
+ end
340
+
341
+ def casted_array?(predicate)
342
+ predicate.value.is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
343
+ end
344
+
345
+ def format_values_for(predicate)
346
+ predicate.value.map do |val|
347
+ val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
348
+ end
349
+ end
350
+
351
+ def replace_right_node?(predicate)
352
+ return false unless predicate.is_a?(Arel::Nodes::Binary)
353
+
354
+ arel_node = predicate.right
355
+ return false unless arel_node.is_a?(Arel::Nodes::Casted)
356
+
357
+ relation, name = arel_node.attribute.values
358
+ attribute_type = relation.type_for_attribute(name).type
359
+ attribute_type == :integer && arel_node.value.is_a?(Integer)
360
+ end
361
+
282
362
  def valid_combinator?
283
363
  attributes.size < 2 || Constants::AND_OR.include?(combinator)
284
364
  end
285
-
286
365
  end
287
366
  end
288
367
  end
@@ -7,9 +7,9 @@ module Ransack
7
7
  alias :m= :combinator=
8
8
 
9
9
  i18n_word :condition, :and, :or
10
- i18n_alias :c => :condition, :n => :and, :o => :or
10
+ i18n_alias c: :condition, n: :and, o: :or
11
11
 
12
- delegate :each, :to => :values
12
+ delegate :each, to: :values
13
13
 
14
14
  def initialize(context, combinator = nil)
15
15
  super(context)
@@ -22,7 +22,7 @@ module Ransack
22
22
 
23
23
  def translate(key, options = {})
24
24
  super or Translate.attribute(
25
- key.to_s, options.merge(:context => context)
25
+ key.to_s, options.merge(context: context)
26
26
  )
27
27
  end
28
28
 
@@ -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
@@ -2,7 +2,7 @@ module Ransack
2
2
  module Nodes
3
3
  class Node
4
4
  attr_reader :context
5
- delegate :contextualize, :to => :context
5
+ delegate :contextualize, to: :context
6
6
  class_attribute :i18n_words
7
7
  class_attribute :i18n_aliases
8
8
  self.i18n_words = []
@@ -9,7 +9,7 @@ module Ransack
9
9
  class << self
10
10
  def extract(context, str)
11
11
  return unless str
12
- attr, direction = str.split(/\s+/,2)
12
+ attr, direction = str.split(/\s+/, 2)
13
13
  self.new(context).build(name: attr, dir: direction)
14
14
  end
15
15
  end
@@ -31,8 +31,8 @@ module Ransack
31
31
  end
32
32
 
33
33
  def name=(name)
34
- @name = name
35
- context.bind(self, name)
34
+ @name = context.ransackable_alias(name) || name
35
+ context.bind(self, @name)
36
36
  end
37
37
 
38
38
  def dir=(dir)