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.
- checksums.yaml +7 -0
- data/mini-fast-sys.gemspec +12 -0
- data/ransack-4.4.1/LICENSE +20 -0
- data/ransack-4.4.1/README.md +101 -0
- data/ransack-4.4.1/lib/polyamorous/activerecord/join_association.rb +70 -0
- data/ransack-4.4.1/lib/polyamorous/activerecord/join_association_7_2.rb +55 -0
- data/ransack-4.4.1/lib/polyamorous/activerecord/join_dependency.rb +102 -0
- data/ransack-4.4.1/lib/polyamorous/activerecord/reflection.rb +11 -0
- data/ransack-4.4.1/lib/polyamorous/join.rb +70 -0
- data/ransack-4.4.1/lib/polyamorous/polyamorous.rb +27 -0
- data/ransack-4.4.1/lib/polyamorous/swapping_reflection_class.rb +11 -0
- data/ransack-4.4.1/lib/polyamorous/tree_node.rb +7 -0
- data/ransack-4.4.1/lib/ransack/active_record.rb +14 -0
- data/ransack-4.4.1/lib/ransack/adapters/active_record/base.rb +152 -0
- data/ransack-4.4.1/lib/ransack/adapters/active_record/context.rb +373 -0
- data/ransack-4.4.1/lib/ransack/configuration.rb +209 -0
- data/ransack-4.4.1/lib/ransack/constants.rb +176 -0
- data/ransack-4.4.1/lib/ransack/context.rb +190 -0
- data/ransack-4.4.1/lib/ransack/helpers/form_builder.rb +277 -0
- data/ransack-4.4.1/lib/ransack/helpers/form_helper.rb +311 -0
- data/ransack-4.4.1/lib/ransack/helpers.rb +2 -0
- data/ransack-4.4.1/lib/ransack/invalid_search_error.rb +3 -0
- data/ransack-4.4.1/lib/ransack/locale/ar.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/az.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/bg.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/ca.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/cs.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/da.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/de.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/el.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/en.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/es.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/fa.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/fi.yml +71 -0
- data/ransack-4.4.1/lib/ransack/locale/fr.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/hu.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/id.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/it.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/ja.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/ko.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/nl.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/pt-BR.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/ro.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/ru.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/sk.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/sv.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/tr.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/uk.yml +72 -0
- data/ransack-4.4.1/lib/ransack/locale/zh-CN.yml +70 -0
- data/ransack-4.4.1/lib/ransack/locale/zh-TW.yml +70 -0
- data/ransack-4.4.1/lib/ransack/naming.rb +58 -0
- data/ransack-4.4.1/lib/ransack/nodes/attribute.rb +59 -0
- data/ransack-4.4.1/lib/ransack/nodes/bindable.rb +52 -0
- data/ransack-4.4.1/lib/ransack/nodes/condition.rb +399 -0
- data/ransack-4.4.1/lib/ransack/nodes/grouping.rb +206 -0
- data/ransack-4.4.1/lib/ransack/nodes/node.rb +34 -0
- data/ransack-4.4.1/lib/ransack/nodes/sort.rb +54 -0
- data/ransack-4.4.1/lib/ransack/nodes/value.rb +115 -0
- data/ransack-4.4.1/lib/ransack/predicate.rb +75 -0
- data/ransack-4.4.1/lib/ransack/ransacker.rb +24 -0
- data/ransack-4.4.1/lib/ransack/search.rb +195 -0
- data/ransack-4.4.1/lib/ransack/translate.rb +156 -0
- data/ransack-4.4.1/lib/ransack/version.rb +3 -0
- data/ransack-4.4.1/lib/ransack/visitor.rb +87 -0
- data/ransack-4.4.1/lib/ransack.rb +33 -0
- data/ransack-4.4.1/spec/console.rb +13 -0
- data/ransack-4.4.1/spec/factories/articles.rb +7 -0
- data/ransack-4.4.1/spec/factories/comments.rb +7 -0
- data/ransack-4.4.1/spec/factories/notes.rb +13 -0
- data/ransack-4.4.1/spec/factories/people.rb +10 -0
- data/ransack-4.4.1/spec/factories/tags.rb +5 -0
- data/ransack-4.4.1/spec/helpers/polyamorous_helper.rb +13 -0
- data/ransack-4.4.1/spec/helpers/ransack_helper.rb +9 -0
- data/ransack-4.4.1/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
- data/ransack-4.4.1/spec/polyamorous/join_association_spec.rb +29 -0
- data/ransack-4.4.1/spec/polyamorous/join_dependency_spec.rb +80 -0
- data/ransack-4.4.1/spec/polyamorous/join_spec.rb +19 -0
- data/ransack-4.4.1/spec/ransack/adapters/active_record/base_spec.rb +943 -0
- data/ransack-4.4.1/spec/ransack/adapters/active_record/context_spec.rb +219 -0
- data/ransack-4.4.1/spec/ransack/configuration_spec.rb +201 -0
- data/ransack-4.4.1/spec/ransack/helpers/form_builder_spec.rb +169 -0
- data/ransack-4.4.1/spec/ransack/helpers/form_helper_spec.rb +1075 -0
- data/ransack-4.4.1/spec/ransack/invalid_search_error_spec.rb +27 -0
- data/ransack-4.4.1/spec/ransack/nodes/condition_spec.rb +333 -0
- data/ransack-4.4.1/spec/ransack/nodes/grouping_spec.rb +111 -0
- data/ransack-4.4.1/spec/ransack/nodes/value_spec.rb +126 -0
- data/ransack-4.4.1/spec/ransack/predicate_spec.rb +504 -0
- data/ransack-4.4.1/spec/ransack/ransacker_spec.rb +69 -0
- data/ransack-4.4.1/spec/ransack/search_spec.rb +821 -0
- data/ransack-4.4.1/spec/ransack/translate_spec.rb +16 -0
- data/ransack-4.4.1/spec/spec_helper.rb +52 -0
- data/ransack-4.4.1/spec/support/en.yml +18 -0
- data/ransack-4.4.1/spec/support/schema.rb +419 -0
- 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
|