ransack 3.2.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +72 -0
  3. data/.github/workflows/cronjob.yml +6 -9
  4. data/.github/workflows/deploy.yml +1 -1
  5. data/.github/workflows/rubocop.yml +2 -2
  6. data/.github/workflows/test-deploy.yml +1 -1
  7. data/.github/workflows/test.yml +23 -22
  8. data/.rubocop.yml +7 -1
  9. data/CHANGELOG.md +86 -0
  10. data/CONTRIBUTING.md +40 -21
  11. data/Gemfile +10 -10
  12. data/README.md +4 -9
  13. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +1 -1
  14. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +5 -1
  15. data/docs/docs/getting-started/advanced-mode.md +1 -1
  16. data/docs/docs/getting-started/search-matches.md +1 -1
  17. data/docs/docs/getting-started/simple-mode.md +6 -2
  18. data/docs/docs/getting-started/sorting.md +45 -53
  19. data/docs/docs/going-further/acts-as-taggable-on.md +4 -4
  20. data/docs/docs/going-further/form-customisation.md +1 -1
  21. data/docs/docs/going-further/i18n.md +3 -3
  22. data/docs/docs/going-further/other-notes.md +2 -2
  23. data/docs/docs/going-further/polymorphic-search.md +6 -0
  24. data/docs/docs/going-further/saving-queries.md +1 -1
  25. data/docs/docs/going-further/searching-postgres.md +1 -1
  26. data/docs/package.json +7 -3
  27. data/docs/yarn.lock +2371 -1928
  28. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +14 -4
  29. data/lib/ransack/adapters/active_record/base.rb +78 -7
  30. data/lib/ransack/adapters/active_record/context.rb +2 -1
  31. data/lib/ransack/configuration.rb +25 -12
  32. data/lib/ransack/constants.rb +125 -0
  33. data/lib/ransack/context.rb +34 -5
  34. data/lib/ransack/helpers/form_builder.rb +3 -3
  35. data/lib/ransack/helpers/form_helper.rb +3 -2
  36. data/lib/ransack/nodes/attribute.rb +2 -2
  37. data/lib/ransack/nodes/condition.rb +80 -7
  38. data/lib/ransack/nodes/grouping.rb +3 -3
  39. data/lib/ransack/nodes/node.rb +1 -1
  40. data/lib/ransack/nodes/value.rb +1 -1
  41. data/lib/ransack/predicate.rb +1 -1
  42. data/lib/ransack/ransacker.rb +1 -1
  43. data/lib/ransack/search.rb +9 -4
  44. data/lib/ransack/translate.rb +2 -2
  45. data/lib/ransack/version.rb +1 -1
  46. data/lib/ransack/visitor.rb +38 -2
  47. data/lib/ransack.rb +3 -6
  48. data/ransack.gemspec +1 -1
  49. data/spec/ransack/adapters/active_record/base_spec.rb +89 -0
  50. data/spec/ransack/configuration_spec.rb +9 -9
  51. data/spec/ransack/helpers/form_builder_spec.rb +8 -8
  52. data/spec/ransack/helpers/form_helper_spec.rb +36 -2
  53. data/spec/ransack/nodes/condition_spec.rb +24 -0
  54. data/spec/ransack/predicate_spec.rb +36 -1
  55. data/spec/ransack/translate_spec.rb +1 -1
  56. data/spec/support/schema.rb +55 -10
  57. metadata +6 -13
  58. data/lib/polyamorous.rb +0 -1
  59. data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
  60. data/lib/ransack/adapters/active_record/ransack/context.rb +0 -56
  61. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
  62. data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
  63. data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
  64. data/lib/ransack/adapters.rb +0 -64
  65. data/lib/ransack/nodes.rb +0 -8
  66. /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ransack
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ernie Miller
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2022-05-25 00:00:00.000000000 Z
15
+ date: 2023-10-23 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activerecord
@@ -69,6 +69,7 @@ extra_rdoc_files: []
69
69
  files:
70
70
  - ".github/FUNDING.yml"
71
71
  - ".github/SECURITY.md"
72
+ - ".github/workflows/codeql.yml"
72
73
  - ".github/workflows/cronjob.yml"
73
74
  - ".github/workflows/deploy.yml"
74
75
  - ".github/workflows/rubocop.yml"
@@ -139,7 +140,6 @@ files:
139
140
  - docs/static/logo/ransack.png
140
141
  - docs/static/logo/ransack.svg
141
142
  - docs/yarn.lock
142
- - lib/polyamorous.rb
143
143
  - lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb
144
144
  - lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb
145
145
  - lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb
@@ -154,15 +154,9 @@ files:
154
154
  - lib/polyamorous/swapping_reflection_class.rb
155
155
  - lib/polyamorous/tree_node.rb
156
156
  - lib/ransack.rb
157
- - lib/ransack/adapters.rb
158
- - lib/ransack/adapters/active_record.rb
157
+ - lib/ransack/active_record.rb
159
158
  - lib/ransack/adapters/active_record/base.rb
160
159
  - lib/ransack/adapters/active_record/context.rb
161
- - lib/ransack/adapters/active_record/ransack/constants.rb
162
- - lib/ransack/adapters/active_record/ransack/context.rb
163
- - lib/ransack/adapters/active_record/ransack/nodes/condition.rb
164
- - lib/ransack/adapters/active_record/ransack/translate.rb
165
- - lib/ransack/adapters/active_record/ransack/visitor.rb
166
160
  - lib/ransack/configuration.rb
167
161
  - lib/ransack/constants.rb
168
162
  - lib/ransack/context.rb
@@ -196,7 +190,6 @@ files:
196
190
  - lib/ransack/locale/zh-CN.yml
197
191
  - lib/ransack/locale/zh-TW.yml
198
192
  - lib/ransack/naming.rb
199
- - lib/ransack/nodes.rb
200
193
  - lib/ransack/nodes/attribute.rb
201
194
  - lib/ransack/nodes/bindable.rb
202
195
  - lib/ransack/nodes/condition.rb
@@ -249,14 +242,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
249
242
  requirements:
250
243
  - - ">="
251
244
  - !ruby/object:Gem::Version
252
- version: '2.7'
245
+ version: '3.0'
253
246
  required_rubygems_version: !ruby/object:Gem::Requirement
254
247
  requirements:
255
248
  - - ">="
256
249
  - !ruby/object:Gem::Version
257
250
  version: '0'
258
251
  requirements: []
259
- rubygems_version: 3.3.14
252
+ rubygems_version: 3.4.21
260
253
  signing_key:
261
254
  specification_version: 4
262
255
  summary: Object-based searching for Active Record.
data/lib/polyamorous.rb DELETED
@@ -1 +0,0 @@
1
- require 'polyamorous/polyamorous'
@@ -1,128 +0,0 @@
1
- module Ransack
2
- module Constants
3
- DISTINCT = 'DISTINCT '.freeze
4
-
5
- DERIVED_PREDICATES = [
6
- [CONT, {
7
- arel_predicate: 'matches'.freeze,
8
- formatter: proc { |v| "%#{escape_wildcards(v)}%" }
9
- }
10
- ],
11
- ['not_cont'.freeze, {
12
- arel_predicate: 'does_not_match'.freeze,
13
- formatter: proc { |v| "%#{escape_wildcards(v)}%" }
14
- }
15
- ],
16
- ['i_cont'.freeze, {
17
- arel_predicate: 'matches'.freeze,
18
- formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
19
- case_insensitive: true
20
- }
21
- ],
22
- ['not_i_cont'.freeze, {
23
- arel_predicate: 'does_not_match'.freeze,
24
- formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
25
- case_insensitive: true
26
- }
27
- ],
28
- ['start'.freeze, {
29
- arel_predicate: 'matches'.freeze,
30
- formatter: proc { |v| "#{escape_wildcards(v)}%" }
31
- }
32
- ],
33
- ['not_start'.freeze, {
34
- arel_predicate: 'does_not_match'.freeze,
35
- formatter: proc { |v| "#{escape_wildcards(v)}%" }
36
- }
37
- ],
38
- ['end'.freeze, {
39
- arel_predicate: 'matches'.freeze,
40
- formatter: proc { |v| "%#{escape_wildcards(v)}" }
41
- }
42
- ],
43
- ['not_end'.freeze, {
44
- arel_predicate: 'does_not_match'.freeze,
45
- formatter: proc { |v| "%#{escape_wildcards(v)}" }
46
- }
47
- ],
48
- ['true'.freeze, {
49
- arel_predicate: proc { |v| v ? EQ : NOT_EQ },
50
- compounds: false,
51
- type: :boolean,
52
- validator: proc { |v| BOOLEAN_VALUES.include?(v) },
53
- formatter: proc { |v| true }
54
- }
55
- ],
56
- ['not_true'.freeze, {
57
- arel_predicate: proc { |v| v ? NOT_EQ : EQ },
58
- compounds: false,
59
- type: :boolean,
60
- validator: proc { |v| BOOLEAN_VALUES.include?(v) },
61
- formatter: proc { |v| true }
62
- }
63
- ],
64
- ['false'.freeze, {
65
- arel_predicate: proc { |v| v ? EQ : NOT_EQ },
66
- compounds: false,
67
- type: :boolean,
68
- validator: proc { |v| BOOLEAN_VALUES.include?(v) },
69
- formatter: proc { |v| false }
70
- }
71
- ],
72
- ['not_false'.freeze, {
73
- arel_predicate: proc { |v| v ? NOT_EQ : EQ },
74
- compounds: false,
75
- type: :boolean,
76
- validator: proc { |v| BOOLEAN_VALUES.include?(v) },
77
- formatter: proc { |v| false }
78
- }
79
- ],
80
- ['present'.freeze, {
81
- arel_predicate: proc { |v| v ? NOT_EQ_ALL : EQ_ANY },
82
- compounds: false,
83
- type: :boolean,
84
- validator: proc { |v| BOOLEAN_VALUES.include?(v) },
85
- formatter: proc { |v| [nil, ''.freeze].freeze }
86
- }
87
- ],
88
- ['blank'.freeze, {
89
- arel_predicate: proc { |v| v ? EQ_ANY : NOT_EQ_ALL },
90
- compounds: false,
91
- type: :boolean,
92
- validator: proc { |v| BOOLEAN_VALUES.include?(v) },
93
- formatter: proc { |v| [nil, ''.freeze].freeze }
94
- }
95
- ],
96
- ['null'.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| nil }
102
- }
103
- ],
104
- ['not_null'.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| nil } }
110
- ]
111
- ].freeze
112
-
113
- module_function
114
- # replace % \ to \% \\
115
- def escape_wildcards(unescaped)
116
- case ActiveRecord::Base.connection.adapter_name
117
- when "Mysql2".freeze
118
- # Necessary for MySQL
119
- unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1')
120
- when "PostgreSQL".freeze
121
- # Necessary for PostgreSQL
122
- unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1')
123
- else
124
- unescaped
125
- end
126
- end
127
- end
128
- end
@@ -1,56 +0,0 @@
1
- require 'ransack/visitor'
2
-
3
- module Ransack
4
- class Context
5
- attr_reader :arel_visitor
6
-
7
- class << self
8
-
9
- def for_class(klass, options = {})
10
- if klass < ActiveRecord::Base
11
- Adapters::ActiveRecord::Context.new(klass, options)
12
- end
13
- end
14
-
15
- def for_object(object, options = {})
16
- case object
17
- when ActiveRecord::Relation
18
- Adapters::ActiveRecord::Context.new(object.klass, options)
19
- end
20
- end
21
-
22
- end # << self
23
-
24
- def initialize(object, options = {})
25
- @object = relation_for(object)
26
- @klass = @object.klass
27
- @join_dependency = join_dependency(@object)
28
- @join_type = options[:join_type] || Polyamorous::OuterJoin
29
- @search_key = options[:search_key] || Ransack.options[:search_key]
30
- @associations_pot = {}
31
- @tables_pot = {}
32
- @lock_associations = []
33
-
34
- @base = @join_dependency.instance_variable_get(:@join_root)
35
- end
36
-
37
- def bind_pair_for(key)
38
- @bind_pairs ||= {}
39
-
40
- @bind_pairs[key] ||= begin
41
- parent, attr_name = get_parent_and_attribute_name(key.to_s)
42
- [parent, attr_name] if parent && attr_name
43
- end
44
- end
45
-
46
- def klassify(obj)
47
- if Class === obj && ::ActiveRecord::Base > obj
48
- obj
49
- elsif obj.respond_to? :klass
50
- obj.klass
51
- else
52
- raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
53
- end
54
- end
55
- end
56
- end
@@ -1,61 +0,0 @@
1
- module Ransack
2
- module Nodes
3
- class Condition
4
-
5
- def arel_predicate
6
- attributes.map { |attribute|
7
- association = attribute.parent
8
- if negative? && attribute.associated_collection?
9
- query = context.build_correlated_subquery(association)
10
- context.remove_association(association)
11
- if self.predicate_name == 'not_null' && self.value
12
- query.where(format_predicate(attribute))
13
- Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
14
- else
15
- query.where(format_predicate(attribute).not)
16
- Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
17
- end
18
- else
19
- format_predicate(attribute)
20
- end
21
- }.reduce(combinator_method)
22
- end
23
-
24
- private
25
-
26
- def combinator_method
27
- combinator === Constants::OR ? :or : :and
28
- end
29
-
30
- def format_predicate(attribute)
31
- arel_pred = arel_predicate_for_attribute(attribute)
32
- arel_values = formatted_values_for_attribute(attribute)
33
- predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
34
-
35
- if in_predicate?(predicate)
36
- predicate.right = predicate.right.map do |pr|
37
- casted_array?(pr) ? format_values_for(pr) : pr
38
- end
39
- end
40
-
41
- predicate
42
- end
43
-
44
- def in_predicate?(predicate)
45
- return unless defined?(Arel::Nodes::Casted)
46
- predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
47
- end
48
-
49
- def casted_array?(predicate)
50
- predicate.value.is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
51
- end
52
-
53
- def format_values_for(predicate)
54
- predicate.value.map do |val|
55
- val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
56
- end
57
- end
58
-
59
- end
60
- end
61
- end
@@ -1,8 +0,0 @@
1
- module Ransack
2
- module Translate
3
-
4
- def self.i18n_key(klass)
5
- klass.model_name.i18n_key
6
- end
7
- end
8
- end
@@ -1,47 +0,0 @@
1
- module Ransack
2
- class Visitor
3
- def visit_and(object)
4
- nodes = object.values.map { |o| accept(o) }.compact
5
- return nil unless nodes.size > 0
6
-
7
- if nodes.size > 1
8
- Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
9
- else
10
- nodes.first
11
- end
12
- end
13
-
14
- def quoted?(object)
15
- case object
16
- when Arel::Nodes::SqlLiteral, Bignum, Fixnum
17
- false
18
- else
19
- true
20
- end
21
- end
22
-
23
- def visit_Ransack_Nodes_Sort(object)
24
- if object.valid?
25
- if object.attr.is_a?(Arel::Attributes::Attribute)
26
- object.attr.send(object.dir)
27
- else
28
- ordered(object)
29
- end
30
- else
31
- scope_name = :"sort_by_#{object.name}_#{object.dir}"
32
- scope_name if object.context.object.respond_to?(scope_name)
33
- end
34
- end
35
-
36
- private
37
-
38
- def ordered(object)
39
- case object.dir
40
- when 'asc'.freeze
41
- Arel::Nodes::Ascending.new(object.attr)
42
- when 'desc'.freeze
43
- Arel::Nodes::Descending.new(object.attr)
44
- end
45
- end
46
- end
47
- end
@@ -1,64 +0,0 @@
1
- module Ransack
2
- module Adapters
3
-
4
- def self.object_mapper
5
- @object_mapper ||= instantiate_object_mapper
6
- end
7
-
8
- def self.instantiate_object_mapper
9
- if defined?(::ActiveRecord::Base)
10
- ActiveRecordAdapter.new
11
- elsif defined?(::Mongoid)
12
- MongoidAdapter.new
13
- else
14
- raise "Unsupported adapter"
15
- end
16
- end
17
-
18
- class ActiveRecordAdapter
19
- def require_constants
20
- require 'ransack/adapters/active_record/ransack/constants'
21
- end
22
-
23
- def require_adapter
24
- require 'ransack/adapters/active_record/ransack/translate'
25
- require 'ransack/adapters/active_record'
26
- end
27
-
28
- def require_context
29
- require 'ransack/adapters/active_record/ransack/visitor'
30
- end
31
-
32
- def require_nodes
33
- require 'ransack/adapters/active_record/ransack/nodes/condition'
34
- end
35
-
36
- def require_search
37
- require 'ransack/adapters/active_record/ransack/context'
38
- end
39
- end
40
-
41
- class MongoidAdapter
42
- def require_constants
43
- require 'ransack/adapters/mongoid/ransack/constants'
44
- end
45
-
46
- def require_adapter
47
- require 'ransack/adapters/mongoid/ransack/translate'
48
- require 'ransack/adapters/mongoid'
49
- end
50
-
51
- def require_context
52
- require 'ransack/adapters/mongoid/ransack/visitor'
53
- end
54
-
55
- def require_nodes
56
- require 'ransack/adapters/mongoid/ransack/nodes/condition'
57
- end
58
-
59
- def require_search
60
- require 'ransack/adapters/mongoid/ransack/context'
61
- end
62
- end
63
- end
64
- end
data/lib/ransack/nodes.rb DELETED
@@ -1,8 +0,0 @@
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
- Ransack::Adapters.object_mapper.require_nodes
7
- require 'ransack/nodes/sort'
8
- require 'ransack/nodes/grouping'