ransack 2.3.2 → 4.1.1

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 (145) 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 +99 -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 +131 -0
  10. data/.nojekyll +0 -0
  11. data/.rubocop.yml +50 -0
  12. data/CHANGELOG.md +251 -1
  13. data/CONTRIBUTING.md +51 -29
  14. data/Gemfile +12 -10
  15. data/README.md +45 -907
  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 +71 -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 +46 -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 +8879 -0
  66. data/lib/polyamorous/activerecord/join_association.rb +70 -0
  67. data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord}/join_dependency.rb +33 -12
  68. data/lib/polyamorous/activerecord/reflection.rb +11 -0
  69. data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -4
  70. data/lib/ransack/adapters/active_record/base.rb +83 -10
  71. data/lib/ransack/adapters/active_record/context.rb +56 -44
  72. data/lib/ransack/configuration.rb +53 -10
  73. data/lib/ransack/constants.rb +126 -4
  74. data/lib/ransack/context.rb +34 -5
  75. data/lib/ransack/helpers/form_builder.rb +6 -6
  76. data/lib/ransack/helpers/form_helper.rb +14 -5
  77. data/lib/ransack/helpers.rb +1 -1
  78. data/lib/ransack/locale/sv.yml +70 -0
  79. data/lib/ransack/nodes/attribute.rb +3 -3
  80. data/lib/ransack/nodes/condition.rb +80 -9
  81. data/lib/ransack/nodes/grouping.rb +4 -4
  82. data/lib/ransack/nodes/node.rb +1 -1
  83. data/lib/ransack/nodes/sort.rb +3 -3
  84. data/lib/ransack/nodes/value.rb +3 -3
  85. data/lib/ransack/predicate.rb +1 -1
  86. data/lib/ransack/ransacker.rb +1 -1
  87. data/lib/ransack/search.rb +15 -7
  88. data/lib/ransack/translate.rb +6 -6
  89. data/lib/ransack/version.rb +1 -1
  90. data/lib/ransack/visitor.rb +38 -2
  91. data/lib/ransack.rb +5 -8
  92. data/ransack.gemspec +9 -15
  93. data/spec/blueprints/articles.rb +1 -1
  94. data/spec/blueprints/comments.rb +1 -1
  95. data/spec/blueprints/notes.rb +1 -1
  96. data/spec/blueprints/tags.rb +1 -1
  97. data/spec/console.rb +5 -5
  98. data/spec/helpers/polyamorous_helper.rb +2 -8
  99. data/spec/helpers/ransack_helper.rb +1 -1
  100. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  101. data/spec/{ransack → polyamorous}/join_association_spec.rb +3 -1
  102. data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -16
  103. data/spec/ransack/adapters/active_record/base_spec.rb +125 -16
  104. data/spec/ransack/adapters/active_record/context_spec.rb +19 -18
  105. data/spec/ransack/configuration_spec.rb +33 -9
  106. data/spec/ransack/helpers/form_builder_spec.rb +8 -8
  107. data/spec/ransack/helpers/form_helper_spec.rb +109 -20
  108. data/spec/ransack/nodes/condition_spec.rb +37 -0
  109. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  110. data/spec/ransack/nodes/value_spec.rb +115 -0
  111. data/spec/ransack/predicate_spec.rb +37 -2
  112. data/spec/ransack/search_spec.rb +238 -30
  113. data/spec/ransack/translate_spec.rb +1 -1
  114. data/spec/spec_helper.rb +7 -5
  115. data/spec/support/schema.rb +108 -11
  116. metadata +98 -62
  117. data/.travis.yml +0 -47
  118. data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
  119. data/lib/ransack/adapters/active_record/ransack/context.rb +0 -55
  120. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
  121. data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
  122. data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
  123. data/lib/ransack/adapters.rb +0 -64
  124. data/lib/ransack/nodes.rb +0 -8
  125. data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -20
  126. data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
  127. data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -12
  128. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -2
  129. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -2
  130. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
  131. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
  132. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -2
  133. data/polyamorous/lib/polyamorous/version.rb +0 -3
  134. data/polyamorous/polyamorous.gemspec +0 -27
  135. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  136. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  137. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  138. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  139. /data/{logo → docs/static/logo}/ransack.png +0 -0
  140. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  141. /data/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
  142. /data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
  143. /data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
  144. /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
  145. /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,10 +45,132 @@ module Ransack
45
45
  NOT_EQ_ALL = 'not_eq_all'.freeze
46
46
  CONT = 'cont'.freeze
47
47
 
48
- RAILS_6_0 = '6.0.0'.freeze
49
-
50
48
  RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
51
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
52
175
  end
53
176
  end
54
-
@@ -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
@@ -33,7 +33,7 @@ module Ransack
33
33
  text = args.first
34
34
  i18n = options[:i18n] || {}
35
35
  text ||= object.translate(
36
- method, i18n.reverse_merge(:include_associations => true)
36
+ method, i18n.reverse_merge(include_associations: true)
37
37
  ) if object.respond_to? :translate
38
38
  super(method, text, options, &block)
39
39
  end
@@ -45,9 +45,9 @@ module Ransack
45
45
  end
46
46
 
47
47
  def attribute_select(options = nil, html_options = nil, action = nil)
48
- options = options || {}
49
- html_options = html_options || {}
50
- action = action || Constants::SEARCH
48
+ options ||= {}
49
+ html_options ||= {}
50
+ action ||= Constants::SEARCH
51
51
  default = options.delete(:default)
52
52
  raise ArgumentError, formbuilder_error_message(
53
53
  "#{action}_select") unless object.respond_to?(:context)
@@ -240,7 +240,7 @@ module Ransack
240
240
  def get_attribute_element(action, base)
241
241
  begin
242
242
  [
243
- Translate.association(base, :context => object.context),
243
+ Translate.association(base, context: object.context),
244
244
  collection_for_base(action, base)
245
245
  ]
246
246
  rescue UntraversableAssociationError
@@ -253,7 +253,7 @@ module Ransack
253
253
  [
254
254
  attr_from_base_and_column(base, c),
255
255
  Translate.attribute(
256
- attr_from_base_and_column(base, c), :context => object.context
256
+ attr_from_base_and_column(base, c), context: object.context
257
257
  )
258
258
  ]
259
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
+ 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
@@ -263,7 +262,6 @@ module Ransack
263
262
  attr.attr
264
263
  end
265
264
 
266
-
267
265
  def default_type
268
266
  predicate.type || (attributes.first && attributes.first.type)
269
267
  end
@@ -285,12 +283,85 @@ module Ransack
285
283
  predicate.negative?
286
284
  end
287
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
+
288
316
  private
289
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
+
290
362
  def valid_combinator?
291
363
  attributes.size < 2 || Constants::AND_OR.include?(combinator)
292
364
  end
293
-
294
365
  end
295
366
  end
296
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)
@@ -2,7 +2,7 @@ module Ransack
2
2
  module Nodes
3
3
  class Value < Node
4
4
  attr_accessor :value
5
- delegate :present?, :blank?, :to => :value
5
+ delegate :present?, :blank?, to: :value
6
6
 
7
7
  def initialize(context, value = nil)
8
8
  super(context)
@@ -26,7 +26,7 @@ module Ransack
26
26
  case type
27
27
  when :date
28
28
  cast_to_date(value)
29
- when :datetime, :timestamp, :time
29
+ when :datetime, :timestamp, :time, :timestamptz
30
30
  cast_to_time(value)
31
31
  when :boolean
32
32
  cast_to_boolean(value)
@@ -50,7 +50,7 @@ module Ransack
50
50
  y, m, d = *[val].flatten
51
51
  m ||= 1
52
52
  d ||= 1
53
- Date.new(y,m,d) rescue nil
53
+ Date.new(y, m, d) rescue nil
54
54
  end
55
55
  end
56
56
 
@@ -10,7 +10,7 @@ module Ransack
10
10
  end
11
11
 
12
12
  def named(name)
13
- Ransack.predicates[name.to_s]
13
+ Ransack.predicates[(name || Ransack.options[:default_predicate]).to_s]
14
14
  end
15
15
 
16
16
  def detect_and_strip_from_string!(str)
@@ -3,7 +3,7 @@ module Ransack
3
3
 
4
4
  attr_reader :name, :type, :formatter, :args
5
5
 
6
- delegate :call, :to => :@callable
6
+ delegate :call, to: :@callable
7
7
 
8
8
  def initialize(klass, name, opts = {}, &block)
9
9
  @klass, @name = klass, name
@@ -1,6 +1,11 @@
1
- require 'ransack/nodes'
1
+ require 'ransack/nodes/bindable'
2
+ require 'ransack/nodes/node'
3
+ require 'ransack/nodes/attribute'
4
+ require 'ransack/nodes/value'
5
+ require 'ransack/nodes/condition'
6
+ require 'ransack/nodes/sort'
7
+ require 'ransack/nodes/grouping'
2
8
  require 'ransack/context'
3
- Ransack::Adapters.object_mapper.require_search
4
9
  require 'ransack/naming'
5
10
 
6
11
  module Ransack
@@ -9,15 +14,17 @@ module Ransack
9
14
 
10
15
  attr_reader :base, :context
11
16
 
12
- delegate :object, :klass, :to => :context
17
+ delegate :object, :klass, to: :context
13
18
  delegate :new_grouping, :new_condition,
14
19
  :build_grouping, :build_condition,
15
- :translate, :to => :base
20
+ :translate, to: :base
16
21
 
17
22
  def initialize(object, params = {}, options = {})
23
+ strip_whitespace = options.fetch(:strip_whitespace, Ransack.options[:strip_whitespace])
18
24
  params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
19
25
  if params.is_a? Hash
20
26
  params = params.dup
27
+ params = params.transform_values { |v| v.is_a?(String) && strip_whitespace ? v.strip : v }
21
28
  params.delete_if { |k, v| [*v].all?{ |i| i.blank? && i != false } }
22
29
  else
23
30
  params = {}
@@ -29,6 +36,7 @@ module Ransack
29
36
  )
30
37
  @scope_args = {}
31
38
  @sorts ||= []
39
+ @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true
32
40
  build(params.with_indifferent_access)
33
41
  end
34
42
 
@@ -40,11 +48,11 @@ module Ransack
40
48
  collapse_multiparameter_attributes!(params).each do |key, value|
41
49
  if ['s'.freeze, 'sorts'.freeze].freeze.include?(key)
42
50
  send("#{key}=", value)
43
- elsif base.attribute_method?(key)
44
- base.send("#{key}=", value)
45
51
  elsif @context.ransackable_scope?(key, @context.object)
46
52
  add_scope(key, value)
47
- elsif !Ransack.options[:ignore_unknown_conditions]
53
+ elsif base.attribute_method?(key)
54
+ base.send("#{key}=", value)
55
+ elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
48
56
  raise ArgumentError, "Invalid search term #{key}"
49
57
  end
50
58
  end