mini-fast-sys 0.0.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 (94) hide show
  1. checksums.yaml +7 -0
  2. data/mini-fast-sys.gemspec +12 -0
  3. data/ransack-4.4.1/LICENSE +20 -0
  4. data/ransack-4.4.1/README.md +101 -0
  5. data/ransack-4.4.1/lib/polyamorous/activerecord/join_association.rb +70 -0
  6. data/ransack-4.4.1/lib/polyamorous/activerecord/join_association_7_2.rb +55 -0
  7. data/ransack-4.4.1/lib/polyamorous/activerecord/join_dependency.rb +102 -0
  8. data/ransack-4.4.1/lib/polyamorous/activerecord/reflection.rb +11 -0
  9. data/ransack-4.4.1/lib/polyamorous/join.rb +70 -0
  10. data/ransack-4.4.1/lib/polyamorous/polyamorous.rb +27 -0
  11. data/ransack-4.4.1/lib/polyamorous/swapping_reflection_class.rb +11 -0
  12. data/ransack-4.4.1/lib/polyamorous/tree_node.rb +7 -0
  13. data/ransack-4.4.1/lib/ransack/active_record.rb +14 -0
  14. data/ransack-4.4.1/lib/ransack/adapters/active_record/base.rb +152 -0
  15. data/ransack-4.4.1/lib/ransack/adapters/active_record/context.rb +373 -0
  16. data/ransack-4.4.1/lib/ransack/configuration.rb +209 -0
  17. data/ransack-4.4.1/lib/ransack/constants.rb +176 -0
  18. data/ransack-4.4.1/lib/ransack/context.rb +190 -0
  19. data/ransack-4.4.1/lib/ransack/helpers/form_builder.rb +277 -0
  20. data/ransack-4.4.1/lib/ransack/helpers/form_helper.rb +311 -0
  21. data/ransack-4.4.1/lib/ransack/helpers.rb +2 -0
  22. data/ransack-4.4.1/lib/ransack/invalid_search_error.rb +3 -0
  23. data/ransack-4.4.1/lib/ransack/locale/ar.yml +70 -0
  24. data/ransack-4.4.1/lib/ransack/locale/az.yml +70 -0
  25. data/ransack-4.4.1/lib/ransack/locale/bg.yml +70 -0
  26. data/ransack-4.4.1/lib/ransack/locale/ca.yml +70 -0
  27. data/ransack-4.4.1/lib/ransack/locale/cs.yml +70 -0
  28. data/ransack-4.4.1/lib/ransack/locale/da.yml +70 -0
  29. data/ransack-4.4.1/lib/ransack/locale/de.yml +70 -0
  30. data/ransack-4.4.1/lib/ransack/locale/el.yml +70 -0
  31. data/ransack-4.4.1/lib/ransack/locale/en.yml +70 -0
  32. data/ransack-4.4.1/lib/ransack/locale/es.yml +70 -0
  33. data/ransack-4.4.1/lib/ransack/locale/fa.yml +70 -0
  34. data/ransack-4.4.1/lib/ransack/locale/fi.yml +71 -0
  35. data/ransack-4.4.1/lib/ransack/locale/fr.yml +70 -0
  36. data/ransack-4.4.1/lib/ransack/locale/hu.yml +70 -0
  37. data/ransack-4.4.1/lib/ransack/locale/id.yml +70 -0
  38. data/ransack-4.4.1/lib/ransack/locale/it.yml +70 -0
  39. data/ransack-4.4.1/lib/ransack/locale/ja.yml +70 -0
  40. data/ransack-4.4.1/lib/ransack/locale/ko.yml +70 -0
  41. data/ransack-4.4.1/lib/ransack/locale/nl.yml +70 -0
  42. data/ransack-4.4.1/lib/ransack/locale/pt-BR.yml +70 -0
  43. data/ransack-4.4.1/lib/ransack/locale/ro.yml +70 -0
  44. data/ransack-4.4.1/lib/ransack/locale/ru.yml +70 -0
  45. data/ransack-4.4.1/lib/ransack/locale/sk.yml +70 -0
  46. data/ransack-4.4.1/lib/ransack/locale/sv.yml +70 -0
  47. data/ransack-4.4.1/lib/ransack/locale/tr.yml +70 -0
  48. data/ransack-4.4.1/lib/ransack/locale/uk.yml +72 -0
  49. data/ransack-4.4.1/lib/ransack/locale/zh-CN.yml +70 -0
  50. data/ransack-4.4.1/lib/ransack/locale/zh-TW.yml +70 -0
  51. data/ransack-4.4.1/lib/ransack/naming.rb +58 -0
  52. data/ransack-4.4.1/lib/ransack/nodes/attribute.rb +59 -0
  53. data/ransack-4.4.1/lib/ransack/nodes/bindable.rb +52 -0
  54. data/ransack-4.4.1/lib/ransack/nodes/condition.rb +399 -0
  55. data/ransack-4.4.1/lib/ransack/nodes/grouping.rb +206 -0
  56. data/ransack-4.4.1/lib/ransack/nodes/node.rb +34 -0
  57. data/ransack-4.4.1/lib/ransack/nodes/sort.rb +54 -0
  58. data/ransack-4.4.1/lib/ransack/nodes/value.rb +115 -0
  59. data/ransack-4.4.1/lib/ransack/predicate.rb +75 -0
  60. data/ransack-4.4.1/lib/ransack/ransacker.rb +24 -0
  61. data/ransack-4.4.1/lib/ransack/search.rb +195 -0
  62. data/ransack-4.4.1/lib/ransack/translate.rb +156 -0
  63. data/ransack-4.4.1/lib/ransack/version.rb +3 -0
  64. data/ransack-4.4.1/lib/ransack/visitor.rb +87 -0
  65. data/ransack-4.4.1/lib/ransack.rb +33 -0
  66. data/ransack-4.4.1/spec/console.rb +13 -0
  67. data/ransack-4.4.1/spec/factories/articles.rb +7 -0
  68. data/ransack-4.4.1/spec/factories/comments.rb +7 -0
  69. data/ransack-4.4.1/spec/factories/notes.rb +13 -0
  70. data/ransack-4.4.1/spec/factories/people.rb +10 -0
  71. data/ransack-4.4.1/spec/factories/tags.rb +5 -0
  72. data/ransack-4.4.1/spec/helpers/polyamorous_helper.rb +13 -0
  73. data/ransack-4.4.1/spec/helpers/ransack_helper.rb +9 -0
  74. data/ransack-4.4.1/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  75. data/ransack-4.4.1/spec/polyamorous/join_association_spec.rb +29 -0
  76. data/ransack-4.4.1/spec/polyamorous/join_dependency_spec.rb +80 -0
  77. data/ransack-4.4.1/spec/polyamorous/join_spec.rb +19 -0
  78. data/ransack-4.4.1/spec/ransack/adapters/active_record/base_spec.rb +943 -0
  79. data/ransack-4.4.1/spec/ransack/adapters/active_record/context_spec.rb +219 -0
  80. data/ransack-4.4.1/spec/ransack/configuration_spec.rb +201 -0
  81. data/ransack-4.4.1/spec/ransack/helpers/form_builder_spec.rb +169 -0
  82. data/ransack-4.4.1/spec/ransack/helpers/form_helper_spec.rb +1075 -0
  83. data/ransack-4.4.1/spec/ransack/invalid_search_error_spec.rb +27 -0
  84. data/ransack-4.4.1/spec/ransack/nodes/condition_spec.rb +333 -0
  85. data/ransack-4.4.1/spec/ransack/nodes/grouping_spec.rb +111 -0
  86. data/ransack-4.4.1/spec/ransack/nodes/value_spec.rb +126 -0
  87. data/ransack-4.4.1/spec/ransack/predicate_spec.rb +504 -0
  88. data/ransack-4.4.1/spec/ransack/ransacker_spec.rb +69 -0
  89. data/ransack-4.4.1/spec/ransack/search_spec.rb +821 -0
  90. data/ransack-4.4.1/spec/ransack/translate_spec.rb +16 -0
  91. data/ransack-4.4.1/spec/spec_helper.rb +52 -0
  92. data/ransack-4.4.1/spec/support/en.yml +18 -0
  93. data/ransack-4.4.1/spec/support/schema.rb +419 -0
  94. metadata +133 -0
@@ -0,0 +1,219 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Adapters
5
+ module ActiveRecord
6
+ version = ::ActiveRecord::VERSION
7
+ AR_version = "#{version::MAJOR}.#{version::MINOR}"
8
+
9
+ describe Context do
10
+ subject { Context.new(Person) }
11
+
12
+ it 'has an Active Record alias tracker method' do
13
+ expect(subject.alias_tracker)
14
+ .to be_an ::ActiveRecord::Associations::AliasTracker
15
+ end
16
+
17
+ describe '#relation_for' do
18
+ it 'returns relation for given object' do
19
+ expect(subject.object).to be_an ::ActiveRecord::Relation
20
+ end
21
+ end
22
+
23
+ describe '#evaluate' do
24
+ it 'evaluates search objects' do
25
+ s = Search.new(Person, name_eq: 'Joe Blow')
26
+ result = subject.evaluate(s)
27
+
28
+ expect(result).to be_an ::ActiveRecord::Relation
29
+ expect(result.to_sql)
30
+ .to match /#{quote_column_name("name")} = 'Joe Blow'/
31
+ end
32
+
33
+ it 'SELECTs DISTINCT when distinct: true' do
34
+ s = Search.new(Person, name_eq: 'Joe Blow')
35
+ result = subject.evaluate(s, distinct: true)
36
+
37
+ expect(result).to be_an ::ActiveRecord::Relation
38
+ expect(result.to_sql).to match /SELECT DISTINCT/
39
+ end
40
+ end
41
+
42
+ describe '#build_correlated_subquery' do
43
+ it 'build correlated subquery for Root STI model' do
44
+ search = Search.new(Person, { articles_title_not_eq: 'some_title' }, context: subject)
45
+ attribute = search.conditions.first.attributes.first
46
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
47
+ constraint = constraints.first
48
+
49
+ expect(constraints.length).to eql 1
50
+ expect(constraint.left.name).to eql 'person_id'
51
+ expect(constraint.left.relation.name).to eql 'articles'
52
+ expect(constraint.right.name).to eql 'id'
53
+ expect(constraint.right.relation.name).to eql 'people'
54
+ end
55
+
56
+ it 'build correlated subquery for Child STI model when predicate is not_eq' do
57
+ search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject)
58
+ attribute = search.conditions.first.attributes.first
59
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
60
+ constraint = constraints.first
61
+
62
+ expect(constraints.length).to eql 1
63
+ expect(constraint.left.relation.name).to eql 'articles'
64
+ expect(constraint.left.name).to eql 'person_id'
65
+ expect(constraint.right.relation.name).to eql 'people'
66
+ expect(constraint.right.name).to eql 'id'
67
+ end
68
+
69
+ it 'build correlated subquery for Child STI model when predicate is eq' do
70
+ search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject)
71
+ attribute = search.conditions.first.attributes.first
72
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
73
+ constraint = constraints.first
74
+
75
+ expect(constraints.length).to eql 1
76
+ expect(constraint.left.relation.name).to eql 'articles'
77
+ expect(constraint.left.name).to eql 'person_id'
78
+ expect(constraint.right.relation.name).to eql 'people'
79
+ expect(constraint.right.name).to eql 'id'
80
+ end
81
+
82
+ it 'build correlated subquery for multiple conditions (default scope)' do
83
+ search = Search.new(Person, { comments_body_not_eq: 'some_title' })
84
+
85
+ # Was
86
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
87
+ # SELECT "comments"."disabled" FROM "comments"
88
+ # WHERE "comments"."disabled" = "people"."id"
89
+ # AND NOT ("comments"."body" != 'some_title')
90
+ # ) ORDER BY "people"."id" DESC
91
+ # Should Be
92
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
93
+ # SELECT "comments"."person_id" FROM "comments"
94
+ # WHERE "comments"."person_id" = "people"."id"
95
+ # AND NOT ("comments"."body" != 'some_title')
96
+ # ) ORDER BY "people"."id" DESC
97
+
98
+ expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./
99
+ end
100
+
101
+ it 'handles Arel::Nodes::And with children' do
102
+ # Create a mock Arel::Nodes::And with children for testing
103
+ search = Search.new(Person, { articles_title_not_eq: 'some_title', articles_body_not_eq: 'some_body' }, context: subject)
104
+ attribute = search.conditions.first.attributes.first
105
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
106
+ constraint = constraints.first
107
+
108
+ expect(constraints.length).to eql 1
109
+ expect(constraint.left.name).to eql 'person_id'
110
+ expect(constraint.left.relation.name).to eql 'articles'
111
+ expect(constraint.right.name).to eql 'id'
112
+ expect(constraint.right.relation.name).to eql 'people'
113
+ end
114
+
115
+ it 'correctly extracts correlated key from complex AND conditions' do
116
+ # Test with multiple nested conditions to ensure the children traversal works
117
+ search = Search.new(
118
+ Person,
119
+ {
120
+ articles_title_not_eq: 'title',
121
+ articles_body_not_eq: 'body',
122
+ articles_published_eq: true
123
+ },
124
+ context: subject
125
+ )
126
+
127
+ attribute = search.conditions.first.attributes.first
128
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
129
+ constraint = constraints.first
130
+
131
+ expect(constraints.length).to eql 1
132
+ expect(constraint.left.relation.name).to eql 'articles'
133
+ expect(constraint.left.name).to eql 'person_id'
134
+ expect(constraint.right.relation.name).to eql 'people'
135
+ expect(constraint.right.name).to eql 'id'
136
+ end
137
+
138
+ it 'build correlated subquery for polymorphic & default_scope when predicate is not_cont_all' do
139
+ search = Search.new(Article,
140
+ g: [
141
+ {
142
+ m: "and",
143
+ c: [
144
+ {
145
+ a: ["recent_notes_note"],
146
+ p: "not_eq",
147
+ v: ["some_note"],
148
+ }
149
+ ]
150
+ }
151
+ ],
152
+ )
153
+
154
+ expect(search.result.to_sql).to match /(.notes.\..note. != \'some_note\')/
155
+ end
156
+ end
157
+
158
+ describe 'sharing context across searches' do
159
+ let(:shared_context) { Context.for(Person) }
160
+
161
+ before do
162
+ Search.new(Person, { parent_name_eq: 'A' },
163
+ context: shared_context)
164
+ Search.new(Person, { children_name_eq: 'B' },
165
+ context: shared_context)
166
+ end
167
+
168
+ describe '#join_sources' do
169
+ it 'returns dependent arel join nodes for all searches run against
170
+ the context' do
171
+ parents, children = shared_context.join_sources
172
+ expect(children.left.name).to eq "children_people"
173
+ expect(parents.left.name).to eq "parents_people"
174
+ end
175
+
176
+ it 'can be rejoined to execute a valid query' do
177
+ parents, children = shared_context.join_sources
178
+
179
+ expect { Person.joins(parents).joins(children).to_a }
180
+ .to_not raise_error
181
+ end
182
+ end
183
+ end
184
+
185
+ it 'contextualizes strings to attributes' do
186
+ attribute = subject.contextualize 'children_children_parent_name'
187
+ expect(attribute).to be_a Arel::Attributes::Attribute
188
+ expect(attribute.name.to_s).to eq 'name'
189
+ expect(attribute.relation.table_alias).to eq 'parents_people'
190
+ end
191
+
192
+ it 'builds new associations if not yet built' do
193
+ attribute = subject.contextualize 'children_articles_title'
194
+ expect(attribute).to be_a Arel::Attributes::Attribute
195
+ expect(attribute.name.to_s).to eq 'title'
196
+ expect(attribute.relation.name).to eq 'articles'
197
+ expect(attribute.relation.table_alias).to be_nil
198
+ end
199
+
200
+ describe '#type_for' do
201
+ it 'returns nil when column does not exist instead of raising NoMethodError' do
202
+ # Create a mock attribute that references a non-existent column
203
+ mock_attr = double('attribute')
204
+ allow(mock_attr).to receive(:valid?).and_return(true)
205
+
206
+ mock_arel_attr = double('arel_attribute')
207
+ allow(mock_arel_attr).to receive(:relation).and_return(Person.arel_table)
208
+ allow(mock_arel_attr).to receive(:name).and_return('nonexistent_column')
209
+ allow(mock_attr).to receive(:arel_attribute).and_return(mock_arel_attr)
210
+ allow(mock_attr).to receive(:klass).and_return(Person)
211
+
212
+ # This should return nil instead of raising an error
213
+ expect(subject.type_for(mock_attr)).to be_nil
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,201 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ describe Configuration do
5
+ it 'yields Ransack on configure' do
6
+ Ransack.configure { |config| expect(config).to eq Ransack }
7
+ end
8
+
9
+ it 'adds predicates' do
10
+ Ransack.configure do |config|
11
+ config.add_predicate :test_predicate
12
+ end
13
+
14
+ expect(Ransack.predicates).to have_key 'test_predicate'
15
+ expect(Ransack.predicates).to have_key 'test_predicate_any'
16
+ expect(Ransack.predicates).to have_key 'test_predicate_all'
17
+ end
18
+
19
+ it 'avoids creating compound predicates if compounds: false' do
20
+ Ransack.configure do |config|
21
+ config.add_predicate(
22
+ :test_predicate_without_compound,
23
+ compounds: false
24
+ )
25
+ end
26
+ expect(Ransack.predicates)
27
+ .to have_key 'test_predicate_without_compound'
28
+ expect(Ransack.predicates)
29
+ .not_to have_key 'test_predicate_without_compound_any'
30
+ expect(Ransack.predicates)
31
+ .not_to have_key 'test_predicate_without_compound_all'
32
+ end
33
+
34
+ it 'should have default value for search key' do
35
+ expect(Ransack.options[:search_key]).to eq :q
36
+ end
37
+
38
+ it 'changes default search key parameter' do
39
+ default = Ransack.options.clone
40
+
41
+ Ransack.configure { |c| c.search_key = :query }
42
+
43
+ expect(Ransack.options[:search_key]).to eq :query
44
+
45
+ Ransack.options = default
46
+ end
47
+
48
+ it 'should have default value for strip_whitespace' do
49
+ expect(Ransack.options[:strip_whitespace]).to eq true
50
+ end
51
+
52
+ it 'changes default search key parameter' do
53
+ default = Ransack.options.clone
54
+
55
+ Ransack.configure { |c| c.strip_whitespace = false }
56
+
57
+ expect(Ransack.options[:strip_whitespace]).to eq false
58
+
59
+ Ransack.options = default
60
+ end
61
+
62
+ it 'should have default values for arrows' do
63
+ expect(Ransack.options[:up_arrow]).to eq '▼'
64
+ expect(Ransack.options[:down_arrow]).to eq '▲'
65
+ expect(Ransack.options[:default_arrow]).to eq nil
66
+ end
67
+
68
+ it 'changes the default value for the up arrow only' do
69
+ default, new_up_arrow = Ransack.options.clone, 'U+02191'
70
+
71
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: new_up_arrow } }
72
+
73
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
74
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
75
+
76
+ Ransack.options = default
77
+ end
78
+
79
+ it 'changes the default value for the down arrow only' do
80
+ default, new_down_arrow = Ransack.options.clone, '<i class="down"></i>'
81
+
82
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: new_down_arrow } }
83
+
84
+ expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
85
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
86
+
87
+ Ransack.options = default
88
+ end
89
+
90
+ it 'changes the default value for the default arrow only' do
91
+ default, new_default_arrow = Ransack.options.clone, '<i class="default"></i>'
92
+
93
+ Ransack.configure { |c| c.custom_arrows = { default_arrow: new_default_arrow } }
94
+
95
+ expect(Ransack.options[:up_arrow]).to eq default[:up_arrow]
96
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
97
+ expect(Ransack.options[:default_arrow]).to eq new_default_arrow
98
+
99
+ Ransack.options = default
100
+ end
101
+
102
+ it 'changes the default value for all arrows' do
103
+ default = Ransack.options.clone
104
+ new_up_arrow = '<i class="fa fa-long-arrow-up"></i>'
105
+ new_down_arrow = 'U+02193'
106
+ new_default_arrow = 'defaultarrow'
107
+
108
+ Ransack.configure do |c|
109
+ c.custom_arrows = { up_arrow: new_up_arrow, down_arrow: new_down_arrow, default_arrow: new_default_arrow }
110
+ end
111
+
112
+ expect(Ransack.options[:up_arrow]).to eq new_up_arrow
113
+ expect(Ransack.options[:down_arrow]).to eq new_down_arrow
114
+ expect(Ransack.options[:default_arrow]).to eq new_default_arrow
115
+
116
+ Ransack.options = default
117
+ end
118
+
119
+ it 'consecutive arrow customizations respect previous customizations' do
120
+ default = Ransack.options.clone
121
+
122
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: 'up' } }
123
+ expect(Ransack.options[:down_arrow]).to eq default[:down_arrow]
124
+
125
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'DOWN' } }
126
+ expect(Ransack.options[:up_arrow]).to eq 'up'
127
+
128
+ Ransack.configure { |c| c.custom_arrows = { up_arrow: '<i>U-Arrow</i>' } }
129
+ expect(Ransack.options[:down_arrow]).to eq 'DOWN'
130
+
131
+ Ransack.configure { |c| c.custom_arrows = { down_arrow: 'down arrow-2' } }
132
+ expect(Ransack.options[:up_arrow]).to eq '<i>U-Arrow</i>'
133
+
134
+ Ransack.options = default
135
+ end
136
+
137
+ it 'adds predicates that take arrays, overriding compounds' do
138
+ Ransack.configure do |config|
139
+ config.add_predicate(
140
+ :test_array_predicate,
141
+ wants_array: true,
142
+ compounds: true
143
+ )
144
+ end
145
+
146
+ expect(Ransack.predicates['test_array_predicate'].wants_array).to eq true
147
+ expect(Ransack.predicates).not_to have_key 'test_array_predicate_any'
148
+ expect(Ransack.predicates).not_to have_key 'test_array_predicate_all'
149
+ end
150
+
151
+ describe '`wants_array` option takes precedence over Arel predicate' do
152
+ it 'implicitly wants an array for in/not in predicates' do
153
+ Ransack.configure do |config|
154
+ config.add_predicate(
155
+ :test_in_predicate,
156
+ arel_predicate: 'in'
157
+ )
158
+ config.add_predicate(
159
+ :test_not_in_predicate,
160
+ arel_predicate: 'not_in'
161
+ )
162
+ end
163
+
164
+ expect(Ransack.predicates['test_in_predicate'].wants_array)
165
+ .to eq true
166
+ expect(Ransack.predicates['test_not_in_predicate'].wants_array)
167
+ .to eq true
168
+ end
169
+
170
+ it 'explicitly does not want array for in/not_in predicates' do
171
+ Ransack.configure do |config|
172
+ config.add_predicate(
173
+ :test_in_predicate_no_array,
174
+ arel_predicate: 'in',
175
+ wants_array: false
176
+ )
177
+ config.add_predicate(
178
+ :test_not_in_predicate_no_array,
179
+ arel_predicate: 'not_in',
180
+ wants_array: false
181
+ )
182
+ end
183
+
184
+ expect(Ransack.predicates['test_in_predicate_no_array'].wants_array)
185
+ .to eq false
186
+ expect(Ransack.predicates['test_not_in_predicate_no_array'].wants_array)
187
+ .to eq false
188
+ end
189
+ end
190
+
191
+ it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
192
+ default = Ransack.options.clone
193
+
194
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
195
+
196
+ expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first
197
+
198
+ Ransack.options = default
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Helpers
5
+ describe FormBuilder do
6
+ router = ActionDispatch::Routing::RouteSet.new
7
+ router.draw do
8
+ resources :people, :comments, :notes
9
+ end
10
+
11
+ include router.url_helpers
12
+
13
+ # FIXME: figure out a cleaner way to get this behavior
14
+ before do
15
+ @controller = ActionView::TestCase::TestController.new
16
+ @controller.instance_variable_set(:@_routes, router)
17
+ @controller.class_eval { include router.url_helpers }
18
+ @controller.view_context_class.class_eval { include router.url_helpers }
19
+ @s = Person.ransack
20
+ @controller.view_context.search_form_for(@s) { |f| @f = f }
21
+ end
22
+
23
+ it 'selects previously-entered time values with datetime_select' do
24
+ date_values = %w(2011 1 2 03 04 05)
25
+ # @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x
26
+ @s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this
27
+ html = @f.datetime_select(
28
+ :created_at_eq, use_month_numbers: true, include_seconds: true
29
+ )
30
+ date_values.each { |val| expect(html).to include date_select_html(val) }
31
+ end
32
+
33
+ describe '#label' do
34
+ context 'with direct model attributes' do
35
+ it 'localizes attribute names' do
36
+ test_label(@f, :name_cont, /Full Name contains/)
37
+ test_label(@f, :only_admin_start, /admin uSer Only starts with/)
38
+ test_label(@f, :salary_gt, /wages greater than/)
39
+ test_label(@f, :awesome_true, /ransack is really awesome is true/)
40
+ end
41
+ it 'falls back to `attribute_name.capitalize` when no translation' do
42
+ test_label(@f, :email_cont, /Email contains/)
43
+ test_label(@f, :only_sort_start, /Only sort starts with/)
44
+ test_label(@f, :only_search_eq, /Only search equals/)
45
+ end
46
+ end
47
+ context 'with `has_many` association attributes' do
48
+ it 'localizes :"#{pluralized model}_#{attribute name}_#{predicate}"' do
49
+ test_label(@f, :articles_body_start, /Article maiN BoDy starts with/)
50
+ end
51
+ it 'falls back to `model_name.capitalize + attribute_name.capitalize` when no translation' do
52
+ test_label(@f, :articles_title_cont, /Article Title contains/)
53
+ test_label(@f, :articles_subject_header_start, /Article Subject header starts with/)
54
+ end
55
+ end
56
+ context 'with `belongs_to` association attributes' do
57
+ before do
58
+ @controller.view_context.search_form_for(Comment.ransack) { |f| @f = f }
59
+ end
60
+ it 'localizes :"#{singularized model}_#{attribute name}_#{predicate}"' do
61
+ test_label(@f, :article_body_start, /Article maiN BoDy starts with/)
62
+ end
63
+ it 'falls back to `model_name.capitalize + attribute_name.capitalize` when no translation' do
64
+ test_label(@f, :article_title_eq, /Article Title equals/)
65
+ test_label(@f, :article_subject_header_end, /Article Subject header ends with/)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe '#sort_link' do
71
+ it 'sort_link for ransack attribute' do
72
+ sort_link = @f.sort_link :name, controller: 'people'
73
+ expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
74
+ expect(sort_link).to match /sort_link/
75
+ expect(sort_link).to match /Full Name<\/a>/
76
+ end
77
+ it 'sort_link for common attribute' do
78
+ sort_link = @f.sort_link :id, controller: 'people'
79
+ expect(sort_link).to match /id<\/a>/
80
+ end
81
+ end
82
+
83
+ describe '#submit' do
84
+ it 'localizes :search when no default value given' do
85
+ html = @f.submit
86
+ expect(html).to match /"Search"/
87
+ end
88
+ end
89
+
90
+ describe '#attribute_select' do
91
+ it 'returns ransackable attributes' do
92
+ html = @f.attribute_select
93
+ expect(html.split(/\n/).size).to eq(Person.ransackable_attributes.size + 1)
94
+ Person.ransackable_attributes.each do |attribute|
95
+ expect(html).to match /<option value="#{attribute}">/
96
+ end
97
+ end
98
+ it 'returns ransackable attributes for associations with :associations' do
99
+ attributes = Person.ransackable_attributes +
100
+ Article.ransackable_attributes.map { |a| "articles_#{a}" }
101
+ html = @f.attribute_select(associations: ['articles'])
102
+ expect(html.split(/\n/).size).to eq(attributes.size)
103
+ attributes.each do |attribute|
104
+ expect(html).to match /<option value="#{attribute}">/
105
+ end
106
+ end
107
+ it 'returns option groups for base and associations with :associations' do
108
+ html = @f.attribute_select(associations: ['articles'])
109
+ [Person, Article].each do |model|
110
+ expect(html).to match /<optgroup label="#{model}">/
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#predicate_select' do
116
+ it 'returns predicates with predicate_select' do
117
+ html = @f.predicate_select
118
+ Predicate.names.each do |key|
119
+ expect(html).to match /<option value="#{key}">/
120
+ end
121
+ end
122
+ it 'filters predicates with single-value :only' do
123
+ html = @f.predicate_select only: 'eq'
124
+ Predicate.names.reject { |k| k =~ /^eq/ }.each do |key|
125
+ expect(html).not_to match /<option value="#{key}">/
126
+ end
127
+ end
128
+ it 'filters predicates with multi-value :only' do
129
+ html = @f.predicate_select only: [:eq, :lt]
130
+ Predicate.names.reject { |k| k =~ /^(eq|lt)/ }.each do |key|
131
+ expect(html).not_to match /<option value="#{key}">/
132
+ end
133
+ end
134
+ it 'excludes compounds when compounds: false' do
135
+ html = @f.predicate_select compounds: false
136
+ Predicate.names.select { |k| k =~ /_(any|all)$/ }.each do |key|
137
+ expect(html).not_to match /<option value="#{key}">/
138
+ end
139
+ end
140
+ end
141
+
142
+ context 'fields used in polymorphic relations as search attributes in form' do
143
+ before do
144
+ @controller.view_context.search_form_for(Note.ransack) { |f| @f = f }
145
+ end
146
+ it 'accepts poly_id field' do
147
+ html = @f.text_field(:notable_id_eq)
148
+ expect(html).to match /id=\"q_notable_id_eq\"/
149
+ end
150
+ it 'accepts poly_type field' do
151
+ html = @f.text_field(:notable_type_eq)
152
+ expect(html).to match /id=\"q_notable_type_eq\"/
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def test_label(f, query, expected)
159
+ expect(f.label query).to match expected
160
+ end
161
+
162
+ # Starting from Rails 4.2, the date_select html attributes are no longer
163
+ # `sort`ed (for a speed gain), so the tests have to be different:
164
+ def date_select_html(val)
165
+ %(<option value="#{val}" selected="selected">#{val}</option>)
166
+ end
167
+ end
168
+ end
169
+ end