ransack 1.7.0 → 2.4.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 (127) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/SECURITY.md +12 -0
  4. data/.github/workflows/test.yml +120 -0
  5. data/.gitignore +3 -0
  6. data/CHANGELOG.md +463 -27
  7. data/CONTRIBUTING.md +52 -22
  8. data/Gemfile +24 -24
  9. data/README.md +453 -126
  10. data/Rakefile +6 -25
  11. data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +24 -0
  12. data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +79 -0
  13. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
  14. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
  15. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
  16. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
  17. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
  18. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
  19. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
  20. data/lib/polyamorous/join.rb +70 -0
  21. data/lib/polyamorous/polyamorous.rb +24 -0
  22. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  23. data/lib/polyamorous/tree_node.rb +7 -0
  24. data/lib/ransack/adapters/active_record/base.rb +27 -2
  25. data/lib/ransack/adapters/active_record/context.rb +213 -139
  26. data/lib/ransack/adapters/active_record/ransack/constants.rb +70 -55
  27. data/lib/ransack/adapters/active_record/ransack/context.rb +10 -18
  28. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +42 -32
  29. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  30. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  31. data/lib/ransack/adapters/active_record.rb +11 -10
  32. data/lib/ransack/adapters.rb +45 -23
  33. data/lib/ransack/configuration.rb +107 -4
  34. data/lib/ransack/constants.rb +13 -26
  35. data/lib/ransack/context.rb +45 -33
  36. data/lib/ransack/helpers/form_builder.rb +21 -12
  37. data/lib/ransack/helpers/form_helper.rb +75 -70
  38. data/lib/ransack/locale/ar.yml +70 -0
  39. data/lib/ransack/locale/az.yml +70 -0
  40. data/lib/ransack/locale/bg.yml +70 -0
  41. data/lib/ransack/locale/ca.yml +70 -0
  42. data/lib/ransack/locale/da.yml +70 -0
  43. data/lib/ransack/locale/el.yml +70 -0
  44. data/lib/ransack/locale/es.yml +22 -22
  45. data/lib/ransack/locale/fa.yml +70 -0
  46. data/lib/ransack/locale/fi.yml +71 -0
  47. data/lib/ransack/locale/id.yml +70 -0
  48. data/lib/ransack/locale/it.yml +70 -0
  49. data/lib/ransack/locale/ja.yml +70 -0
  50. data/lib/ransack/locale/nl.yml +4 -4
  51. data/lib/ransack/locale/pt-BR.yml +70 -0
  52. data/lib/ransack/locale/ru.yml +70 -0
  53. data/lib/ransack/locale/sk.yml +70 -0
  54. data/lib/ransack/locale/tr.yml +70 -0
  55. data/lib/ransack/locale/{zh.yml → zh-CN.yml} +13 -13
  56. data/lib/ransack/locale/zh-TW.yml +70 -0
  57. data/lib/ransack/nodes/attribute.rb +5 -2
  58. data/lib/ransack/nodes/bindable.rb +18 -6
  59. data/lib/ransack/nodes/condition.rb +85 -28
  60. data/lib/ransack/nodes/grouping.rb +17 -11
  61. data/lib/ransack/nodes/sort.rb +9 -5
  62. data/lib/ransack/nodes/value.rb +74 -68
  63. data/lib/ransack/nodes.rb +1 -1
  64. data/lib/ransack/predicate.rb +17 -20
  65. data/lib/ransack/search.rb +17 -8
  66. data/lib/ransack/translate.rb +115 -115
  67. data/lib/ransack/version.rb +1 -1
  68. data/lib/ransack/visitor.rb +1 -12
  69. data/lib/ransack.rb +9 -9
  70. data/logo/ransack-h.png +0 -0
  71. data/logo/ransack-h.svg +34 -0
  72. data/logo/ransack-v.png +0 -0
  73. data/logo/ransack-v.svg +34 -0
  74. data/logo/ransack.png +0 -0
  75. data/logo/ransack.svg +21 -0
  76. data/ransack.gemspec +7 -24
  77. data/spec/console.rb +4 -0
  78. data/spec/helpers/polyamorous_helper.rb +19 -0
  79. data/spec/polyamorous/join_association_spec.rb +35 -0
  80. data/spec/polyamorous/join_dependency_spec.rb +97 -0
  81. data/spec/polyamorous/join_spec.rb +19 -0
  82. data/spec/ransack/adapters/active_record/base_spec.rb +370 -75
  83. data/spec/ransack/adapters/active_record/context_spec.rb +72 -34
  84. data/spec/ransack/configuration_spec.rb +97 -14
  85. data/spec/ransack/helpers/form_builder_spec.rb +2 -11
  86. data/spec/ransack/helpers/form_helper_spec.rb +481 -113
  87. data/spec/ransack/nodes/condition_spec.rb +24 -0
  88. data/spec/ransack/nodes/grouping_spec.rb +56 -0
  89. data/spec/ransack/predicate_spec.rb +79 -5
  90. data/spec/ransack/search_spec.rb +207 -81
  91. data/spec/spec_helper.rb +8 -0
  92. data/spec/support/schema.rb +100 -42
  93. metadata +57 -184
  94. data/.travis.yml +0 -69
  95. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  96. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -201
  97. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -215
  98. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  99. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  100. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  101. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  102. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  103. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  104. data/lib/ransack/adapters/mongoid/base.rb +0 -130
  105. data/lib/ransack/adapters/mongoid/context.rb +0 -208
  106. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  107. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  108. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  109. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  110. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  111. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  112. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  113. data/lib/ransack/adapters/mongoid.rb +0 -13
  114. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -276
  115. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  116. data/spec/mongoid/configuration_spec.rb +0 -102
  117. data/spec/mongoid/dependencies_spec.rb +0 -8
  118. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  119. data/spec/mongoid/nodes/condition_spec.rb +0 -34
  120. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  121. data/spec/mongoid/predicate_spec.rb +0 -155
  122. data/spec/mongoid/search_spec.rb +0 -446
  123. data/spec/mongoid/support/mongoid.yml +0 -6
  124. data/spec/mongoid/support/schema.rb +0 -128
  125. data/spec/mongoid/translate_spec.rb +0 -14
  126. data/spec/mongoid_spec_helper.rb +0 -59
  127. data/spec/ransack/dependencies_spec.rb +0 -12
@@ -9,11 +9,10 @@ module Ransack
9
9
  describe Context do
10
10
  subject { Context.new(Person) }
11
11
 
12
- if AR_version >= '3.1'
13
- it 'has an Active Record alias tracker method' do
14
- expect(subject.alias_tracker)
15
- .to be_an ::ActiveRecord::Associations::AliasTracker
16
- end
12
+
13
+ it 'has an Active Record alias tracker method' do
14
+ expect(subject.alias_tracker)
15
+ .to be_an ::ActiveRecord::Associations::AliasTracker
17
16
  end
18
17
 
19
18
  describe '#relation_for' do
@@ -24,8 +23,8 @@ module Ransack
24
23
 
25
24
  describe '#evaluate' do
26
25
  it 'evaluates search objects' do
27
- search = Search.new(Person, :name_eq => 'Joe Blow')
28
- result = subject.evaluate(search)
26
+ s = Search.new(Person, name_eq: 'Joe Blow')
27
+ result = subject.evaluate(s)
29
28
 
30
29
  expect(result).to be_an ::ActiveRecord::Relation
31
30
  expect(result.to_sql)
@@ -33,54 +32,93 @@ module Ransack
33
32
  end
34
33
 
35
34
  it 'SELECTs DISTINCT when distinct: true' do
36
- search = Search.new(Person, :name_eq => 'Joe Blow')
37
- result = subject.evaluate(search, :distinct => true)
35
+ s = Search.new(Person, name_eq: 'Joe Blow')
36
+ result = subject.evaluate(s, distinct: true)
38
37
 
39
38
  expect(result).to be_an ::ActiveRecord::Relation
40
39
  expect(result.to_sql).to match /SELECT DISTINCT/
41
40
  end
42
41
  end
43
42
 
44
- describe "sharing context across searches" do
45
- let(:shared_context) { Context.for(Person) }
43
+ describe '#build_correlated_subquery' do
44
+ it 'build correlated subquery for Root STI model' do
45
+ search = Search.new(Person, { articles_title_not_eq: 'some_title' }, context: subject)
46
+ attribute = search.conditions.first.attributes.first
47
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
48
+ constraint = constraints.first
49
+
50
+ expect(constraints.length).to eql 1
51
+ expect(constraint.left.name).to eql 'person_id'
52
+ expect(constraint.left.relation.name).to eql 'articles'
53
+ expect(constraint.right.name).to eql 'id'
54
+ expect(constraint.right.relation.name).to eql 'people'
55
+ end
46
56
 
47
- before do
48
- Search.new(Person, { :parent_name_eq => 'A' },
49
- context: shared_context)
50
- Search.new(Person, { :children_name_eq => 'B' },
51
- context: shared_context)
57
+ it 'build correlated subquery for Child STI model when predicate is not_eq' do
58
+ search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject)
59
+ attribute = search.conditions.first.attributes.first
60
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
61
+ constraint = constraints.first
62
+
63
+ expect(constraints.length).to eql 1
64
+ expect(constraint.left.relation.name).to eql 'articles'
65
+ expect(constraint.left.name).to eql 'person_id'
66
+ expect(constraint.right.relation.name).to eql 'people'
67
+ expect(constraint.right.name).to eql 'id'
52
68
  end
53
69
 
54
- describe '#join_associations', :if => AR_version <= '4.0' do
55
- it 'returns dependent join associations for all searches run
56
- against the context' do
57
- parents, children = shared_context.join_associations
70
+ it 'build correlated subquery for Child STI model when predicate is eq' do
71
+ search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject)
72
+ attribute = search.conditions.first.attributes.first
73
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
74
+ constraint = constraints.first
75
+
76
+ expect(constraints.length).to eql 1
77
+ expect(constraint.left.relation.name).to eql 'articles'
78
+ expect(constraint.left.name).to eql 'person_id'
79
+ expect(constraint.right.relation.name).to eql 'people'
80
+ expect(constraint.right.name).to eql 'id'
81
+ end
58
82
 
59
- expect(children.aliased_table_name).to eq "children_people"
60
- expect(parents.aliased_table_name).to eq "parents_people"
61
- end
83
+ it 'build correlated subquery for multiple conditions (default scope)' do
84
+ search = Search.new(Person, { comments_body_not_eq: 'some_title'})
85
+
86
+ # Was
87
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
88
+ # SELECT "comments"."disabled" FROM "comments"
89
+ # WHERE "comments"."disabled" = "people"."id"
90
+ # AND NOT ("comments"."body" != 'some_title')
91
+ # ) ORDER BY "people"."id" DESC
92
+ # Should Be
93
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
94
+ # SELECT "comments"."person_id" FROM "comments"
95
+ # WHERE "comments"."person_id" = "people"."id"
96
+ # AND NOT ("comments"."body" != 'some_title')
97
+ # ) ORDER BY "people"."id" DESC
98
+
99
+ expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./
100
+ end
101
+ end
62
102
 
63
- it 'can be rejoined to execute a valid query' do
64
- parents, children = shared_context.join_associations
103
+ describe 'sharing context across searches' do
104
+ let(:shared_context) { Context.for(Person) }
65
105
 
66
- expect { Person.joins(parents).joins(children).to_a }
67
- .to_not raise_error
68
- end
106
+ before do
107
+ Search.new(Person, { parent_name_eq: 'A' },
108
+ context: shared_context)
109
+ Search.new(Person, { children_name_eq: 'B' },
110
+ context: shared_context)
69
111
  end
70
112
 
71
113
  describe '#join_sources' do
72
- # FIXME: fix this test for Rails 4.2.
73
114
  it 'returns dependent arel join nodes for all searches run against
74
- the context',
75
- :if => %w(3.1 3.2 4.0 4.1).include?(AR_version) do
115
+ the context' do
76
116
  parents, children = shared_context.join_sources
77
-
78
117
  expect(children.left.name).to eq "children_people"
79
118
  expect(parents.left.name).to eq "parents_people"
80
119
  end
81
120
 
82
- it 'can be rejoined to execute a valid query',
83
- :if => AR_version >= '3.1' do
121
+ it 'can be rejoined to execute a valid query' do
84
122
  parents, children = shared_context.join_sources
85
123
 
86
124
  expect { Person.joins(parents).joins(children).to_a }
@@ -3,9 +3,7 @@ require 'spec_helper'
3
3
  module Ransack
4
4
  describe Configuration do
5
5
  it 'yields Ransack on configure' do
6
- Ransack.configure do |config|
7
- expect(config).to eq Ransack
8
- end
6
+ Ransack.configure { |config| expect(config).to eq Ransack }
9
7
  end
10
8
 
11
9
  it 'adds predicates' do
@@ -38,17 +36,88 @@ module Ransack
38
36
  end
39
37
 
40
38
  it 'changes default search key parameter' do
41
- # store original state so we can restore it later
42
- before = Ransack.options.clone
39
+ default = Ransack.options.clone
43
40
 
44
- Ransack.configure do |config|
45
- config.search_key = :query
46
- end
41
+ Ransack.configure { |c| c.search_key = :query }
47
42
 
48
43
  expect(Ransack.options[:search_key]).to eq :query
49
44
 
50
- # restore original state so we don't break other tests
51
- Ransack.options = before
45
+ Ransack.options = default
46
+ end
47
+
48
+ it 'should have default values for arrows' do
49
+ expect(Ransack.options[:up_arrow]).to eq '&#9660;'
50
+ expect(Ransack.options[:down_arrow]).to eq '&#9650;'
51
+ expect(Ransack.options[:default_arrow]).to eq nil
52
+ end
53
+
54
+ it 'changes the default value for the up arrow only' do
55
+ default, new_up_arrow = Ransack.options.clone, 'U+02191'
56
+
57
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: new_up_arrow } }
58
+
59
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
60
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
61
+
62
+ Ransack.options = default
63
+ end
64
+
65
+ it 'changes the default value for the down arrow only' do
66
+ default, new_down_arrow = Ransack.options.clone, '<i class="down"></i>'
67
+
68
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: new_down_arrow } }
69
+
70
+ expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
71
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
72
+
73
+ Ransack.options = default
74
+ end
75
+
76
+ it 'changes the default value for the default arrow only' do
77
+ default, new_default_arrow = Ransack.options.clone, '<i class="default"></i>'
78
+
79
+ Ransack.configure { |c| c.custom_arrows = { default_arrow: new_default_arrow } }
80
+
81
+ expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
82
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
83
+ expect(Ransack.options[:default_arrow]).to eq new_default_arrow
84
+
85
+ Ransack.options = default
86
+ end
87
+
88
+ it 'changes the default value for all arrows' do
89
+ default = Ransack.options.clone
90
+ new_up_arrow = '<i class="fa fa-long-arrow-up"></i>'
91
+ new_down_arrow = 'U+02193'
92
+ new_default_arrow = 'defaultarrow'
93
+
94
+ Ransack.configure do |c|
95
+ c.custom_arrows = { up_arrow: new_up_arrow, down_arrow: new_down_arrow, default_arrow: new_default_arrow }
96
+ end
97
+
98
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
99
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
100
+ expect(Ransack.options[:default_arrow]).to eq new_default_arrow
101
+
102
+ Ransack.options = default
103
+ end
104
+
105
+ it 'consecutive arrow customizations respect previous customizations' do
106
+ default = Ransack.options.clone
107
+
108
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: 'up' } }
109
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
110
+
111
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'DOWN' } }
112
+ expect(Ransack.options[:up_arrow]).to eq 'up'
113
+
114
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: '<i>U-Arrow</i>' } }
115
+ expect(Ransack.options[:down_arrow]).to eq 'DOWN'
116
+
117
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'down arrow-2' } }
118
+ expect(Ransack.options[:up_arrow]).to eq '<i>U-Arrow</i>'
119
+
120
+ Ransack.options = default
52
121
  end
53
122
 
54
123
  it 'adds predicates that take arrays, overriding compounds' do
@@ -78,8 +147,10 @@ module Ransack
78
147
  )
79
148
  end
80
149
 
81
- expect(Ransack.predicates['test_in_predicate'].wants_array).to eq true
82
- expect(Ransack.predicates['test_not_in_predicate'].wants_array).to eq true
150
+ expect(Ransack.predicates['test_in_predicate'].wants_array)
151
+ .to eq true
152
+ expect(Ransack.predicates['test_not_in_predicate'].wants_array)
153
+ .to eq true
83
154
  end
84
155
 
85
156
  it 'explicitly does not want array for in/not_in predicates' do
@@ -96,9 +167,21 @@ module Ransack
96
167
  )
97
168
  end
98
169
 
99
- expect(Ransack.predicates['test_in_predicate_no_array'].wants_array).to eq false
100
- expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array).to eq false
170
+ expect(Ransack.predicates['test_in_predicate_no_array'].wants_array)
171
+ .to eq false
172
+ expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array)
173
+ .to eq false
101
174
  end
102
175
  end
176
+
177
+ it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
178
+ default = Ransack.options.clone
179
+
180
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
181
+
182
+ expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first
183
+
184
+ Ransack.options = default
185
+ end
103
186
  end
104
187
  end
@@ -7,7 +7,6 @@ module Ransack
7
7
  router = ActionDispatch::Routing::RouteSet.new
8
8
  router.draw do
9
9
  resources :people, :comments, :notes
10
- get ':controller(/:action(/:id(.:format)))'
11
10
  end
12
11
 
13
12
  include router.url_helpers
@@ -72,11 +71,7 @@ module Ransack
72
71
  describe '#sort_link' do
73
72
  it 'sort_link for ransack attribute' do
74
73
  sort_link = @f.sort_link :name, :controller => 'people'
75
- if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
76
- expect(sort_link).to match /people\?q%5Bs%5D=name\+asc/
77
- else
78
- expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
79
- end
74
+ expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
80
75
  expect(sort_link).to match /sort_link/
81
76
  expect(sort_link).to match /Full Name<\/a>/
82
77
  end
@@ -168,11 +163,7 @@ module Ransack
168
163
  # Starting from Rails 4.2, the date_select html attributes are no longer
169
164
  # `sort`ed (for a speed gain), so the tests have to be different:
170
165
  def date_select_html(val)
171
- if ::ActiveRecord::VERSION::STRING >= '4.2'
172
- %(<option value="#{val}" selected="selected">#{val}</option>)
173
- else
174
- %(<option selected="selected" value="#{val}">#{val}</option>)
175
- end
166
+ %(<option value="#{val}" selected="selected">#{val}</option>)
176
167
  end
177
168
 
178
169
  end