ransack 1.7.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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