search_object 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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +283 -0
- data/Rakefile +6 -0
- data/example/.gitignore +16 -0
- data/example/.rspec +1 -0
- data/example/Gemfile +11 -0
- data/example/README.md +34 -0
- data/example/Rakefile +6 -0
- data/example/app/assets/javascripts/application.js +5 -0
- data/example/app/assets/stylesheets/application.css.scss +40 -0
- data/example/app/assets/stylesheets/reset.css +43 -0
- data/example/app/controllers/application_controller.rb +3 -0
- data/example/app/controllers/posts_controller.rb +5 -0
- data/example/app/models/.keep +0 -0
- data/example/app/models/post.rb +13 -0
- data/example/app/models/post_search.rb +44 -0
- data/example/app/models/user.rb +5 -0
- data/example/app/views/layouts/application.html.slim +12 -0
- data/example/app/views/posts/index.html.slim +48 -0
- data/example/bin/bundle +3 -0
- data/example/bin/rails +4 -0
- data/example/bin/rake +4 -0
- data/example/config.ru +4 -0
- data/example/config/application.rb +27 -0
- data/example/config/boot.rb +4 -0
- data/example/config/database.yml +12 -0
- data/example/config/environment.rb +5 -0
- data/example/config/environments/development.rb +29 -0
- data/example/config/environments/test.rb +37 -0
- data/example/config/initializers/filter_parameter_logging.rb +4 -0
- data/example/config/initializers/secret_token.rb +12 -0
- data/example/config/initializers/session_store.rb +3 -0
- data/example/config/initializers/wrap_parameters.rb +14 -0
- data/example/config/routes.rb +3 -0
- data/example/db/migrate/20131102130117_create_users.rb +10 -0
- data/example/db/migrate/20131102130413_create_posts.rb +18 -0
- data/example/db/schema.rb +40 -0
- data/example/db/seeds.rb +37 -0
- data/example/log/.keep +0 -0
- data/example/screenshot.png +0 -0
- data/example/spec/models/post_search_spec.rb +81 -0
- data/example/spec/spec_helper.rb +19 -0
- data/lib/search_object.rb +20 -0
- data/lib/search_object/base.rb +64 -0
- data/lib/search_object/helper.rb +36 -0
- data/lib/search_object/plugin/kaminari.rb +18 -0
- data/lib/search_object/plugin/model.rb +16 -0
- data/lib/search_object/plugin/paging.rb +42 -0
- data/lib/search_object/plugin/sorting.rb +54 -0
- data/lib/search_object/plugin/will_paginate.rb +17 -0
- data/lib/search_object/search.rb +26 -0
- data/lib/search_object/version.rb +3 -0
- data/search_object.gemspec +31 -0
- data/spec/search_object/base_spec.rb +237 -0
- data/spec/search_object/helper_spec.rb +30 -0
- data/spec/search_object/plugin/kaminari_spec.rb +50 -0
- data/spec/search_object/plugin/model_spec.rb +22 -0
- data/spec/search_object/plugin/paging_spec.rb +43 -0
- data/spec/search_object/plugin/sorting_spec.rb +139 -0
- data/spec/search_object/plugin/will_paginate_spec.rb +51 -0
- data/spec/search_object/search_spec.rb +72 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/spec_helper_active_record.rb +19 -0
- data/spec/support/kaminari_setup.rb +7 -0
- metadata +292 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'search_object/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "search_object"
|
8
|
+
spec.version = SearchObject::VERSION
|
9
|
+
spec.authors = ["Radoslav Stankov"]
|
10
|
+
spec.email = ["rstankov@gmail.com"]
|
11
|
+
spec.description = %q{Search object DSL}
|
12
|
+
spec.summary = %q{Provides DSL for creating search objects}
|
13
|
+
spec.homepage = "https://github.com/RStankov/SearchObject"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency 'rspec', '~> 2.14'
|
24
|
+
spec.add_development_dependency 'rspec-mocks', '>= 2.12.3'
|
25
|
+
spec.add_development_dependency 'activerecord', '>= 3.0.0'
|
26
|
+
spec.add_development_dependency 'sqlite3'
|
27
|
+
spec.add_development_dependency 'coveralls'
|
28
|
+
spec.add_development_dependency 'active_model_lint-rspec'
|
29
|
+
spec.add_development_dependency 'will_paginate'
|
30
|
+
spec.add_development_dependency 'kaminari'
|
31
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module SearchObject
|
4
|
+
describe Base do
|
5
|
+
def new_search(default_scope = [], filters = {}, &block)
|
6
|
+
search_class = Class.new do
|
7
|
+
include SearchObject.module
|
8
|
+
|
9
|
+
scope { default_scope }
|
10
|
+
|
11
|
+
if block.nil?
|
12
|
+
option :value do |scope, value|
|
13
|
+
scope.find_all { |v| v == value }
|
14
|
+
end
|
15
|
+
else
|
16
|
+
class_eval &block
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
search_class.new filters
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can had its #initialize method overwritten" do
|
24
|
+
search = new_search do
|
25
|
+
def initialize(filters = {})
|
26
|
+
@initialized = true
|
27
|
+
super filters
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialized?
|
31
|
+
@initialized
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(search).to be_initialized
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can have multiple subclasses" do
|
39
|
+
search1 = new_search [1, 2, 3], filter: 1 do
|
40
|
+
option :filter do |scope, value|
|
41
|
+
scope.select { |v| v == value }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
search2 = new_search [1, 2, 3], filter: 1 do
|
46
|
+
option :filter, 2 do |scope, value|
|
47
|
+
scope.reject { |v| v == value }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(search1.results).not_to eq search2.results
|
52
|
+
end
|
53
|
+
|
54
|
+
context "no scope" do
|
55
|
+
def search_class
|
56
|
+
Class.new do
|
57
|
+
include SearchObject.module
|
58
|
+
|
59
|
+
option :name do
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "treats first argument as scope" do
|
65
|
+
expect(search_class.new('scope').results).to eq 'scope'
|
66
|
+
end
|
67
|
+
|
68
|
+
it "treats second argument as filters" do
|
69
|
+
expect(search_class.new('scope', name: 'name').params).to eq 'name' => 'name'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "option" do
|
74
|
+
it "has default filter" do
|
75
|
+
scope = [1, 2, 3]
|
76
|
+
expect(scope).to receive(:where).with('value' => 1) { 'results' }
|
77
|
+
|
78
|
+
search = new_search scope, value: 1 do
|
79
|
+
option :value
|
80
|
+
end
|
81
|
+
|
82
|
+
expect(search.results).to eq 'results'
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns the scope if nil returned" do
|
86
|
+
scope = [1, 2, 3]
|
87
|
+
search = new_search scope, value: 'some' do
|
88
|
+
option :value do
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
expect(search.results).to eq scope
|
94
|
+
end
|
95
|
+
|
96
|
+
it "can use methods from the object" do
|
97
|
+
search1 = new_search [1, 2, 3], filter: 1 do
|
98
|
+
option :filter do |scope, value|
|
99
|
+
some_instance_method(scope, value)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def some_instance_method(scope, value)
|
105
|
+
scope.select { |v| v == value }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
expect(search1.results).to eq [1]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "option attributes" do
|
114
|
+
it "access option values" do
|
115
|
+
search = new_search [], value: 1
|
116
|
+
expect(search.value).to eq 1
|
117
|
+
end
|
118
|
+
|
119
|
+
it "returns default option value if option is not specified" do
|
120
|
+
search = new_search do
|
121
|
+
option :value, 1
|
122
|
+
end
|
123
|
+
expect(search.value).to eq 1
|
124
|
+
end
|
125
|
+
|
126
|
+
it "does not include invalid options" do
|
127
|
+
search = new_search [], invalid: 'option'
|
128
|
+
expect { search.invalid }.to raise_error NoMethodError
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#results" do
|
133
|
+
it "returns only the filtered search results" do
|
134
|
+
search = new_search [1 ,2 ,3], value: 1
|
135
|
+
expect(search.results).to eq [1]
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can apply several options" do
|
139
|
+
scope = [1, 2, 3, 4, 5, 6, 7]
|
140
|
+
search = new_search scope, bigger_than: 3, odd: true do
|
141
|
+
option :bigger_than do |scope, value|
|
142
|
+
scope.find_all { |v| v > value }
|
143
|
+
end
|
144
|
+
|
145
|
+
option :odd do |scope, value|
|
146
|
+
scope.find_all(&:odd?) if value
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
expect(search.results).to eq [5, 7]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "ignores invalid filters" do
|
154
|
+
search = new_search [1, 2, 3], invalid: 'option'
|
155
|
+
expect(search.results).to eq [1, 2, 3]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "can be overwritten by overwriting #fetch_results" do
|
159
|
+
search = new_search [1, 2, 3], value: 1 do
|
160
|
+
option :value do |scope, value|
|
161
|
+
scope.find_all { |v| v == value }
|
162
|
+
end
|
163
|
+
|
164
|
+
def fetch_results
|
165
|
+
super.map { |v| "~#{v}~" }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
expect(search.results).to eq ['~1~']
|
170
|
+
end
|
171
|
+
|
172
|
+
it "applies to default options" do
|
173
|
+
search = new_search [1,2,3] do
|
174
|
+
option :value, 1 do |scope, value|
|
175
|
+
scope.select { |v| v == value }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
expect(search.results).to eq [1]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "#results?" do
|
183
|
+
it "returns true if there are results" do
|
184
|
+
expect(new_search([1,2,3], value: 1).results?).to be_true
|
185
|
+
end
|
186
|
+
|
187
|
+
it "returns false if there aren't any results" do
|
188
|
+
expect(new_search([1,2,3], value: 4).results?).to be_false
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "#count" do
|
193
|
+
it "counts the number of results" do
|
194
|
+
expect(new_search([1,2,3], value: 1).count).to eq 1
|
195
|
+
end
|
196
|
+
|
197
|
+
it "can't be bypassed by plug ins" do
|
198
|
+
search = new_search [1,2,3] do
|
199
|
+
def fetch_results
|
200
|
+
[]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
expect(search.count).to eq 3
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "#params" do
|
209
|
+
it "exports options as params" do
|
210
|
+
search = new_search [], value: 1
|
211
|
+
expect(search.params).to eq 'value' => 1
|
212
|
+
end
|
213
|
+
|
214
|
+
it "can overwrite options (mainly used for url handers)" do
|
215
|
+
search = new_search [], value: 1
|
216
|
+
expect(search.params(value: 2)).to eq 'value' => 2
|
217
|
+
end
|
218
|
+
|
219
|
+
it "ignores missing options" do
|
220
|
+
search = new_search
|
221
|
+
expect(search.params).to eq({})
|
222
|
+
end
|
223
|
+
|
224
|
+
it "ignores invalid options" do
|
225
|
+
search = new_search [], invalid: 'option'
|
226
|
+
expect(search.params).to eq({})
|
227
|
+
end
|
228
|
+
|
229
|
+
it "includes default options" do
|
230
|
+
search = new_search do
|
231
|
+
option :value, 1
|
232
|
+
end
|
233
|
+
expect(search.params).to eq 'value' => 1
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module SearchObject
|
4
|
+
describe Helper do
|
5
|
+
describe ".stringify_keys" do
|
6
|
+
it "converts hash keys to strings" do
|
7
|
+
hash = Helper.stringify_keys a: 1, b: nil, c: false
|
8
|
+
expect(hash).to eq 'a' => 1, 'b' => nil, 'c' => false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".select_keys" do
|
13
|
+
it "selects only given keys" do
|
14
|
+
hash = Helper.select_keys({a: 1, b: 2, c:3}, [:a, :b])
|
15
|
+
expect(hash).to eq a: 1, b: 2
|
16
|
+
end
|
17
|
+
|
18
|
+
it "ignores not existing keys" do
|
19
|
+
hash = Helper.select_keys({}, [:a, :b])
|
20
|
+
expect(hash).to eq({})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "camelize" do
|
25
|
+
it "transforms :paging to 'Paging'" do
|
26
|
+
expect(Helper.camelize(:paging)).to eq 'Paging'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper_active_record'
|
2
|
+
|
3
|
+
require_relative '../../support/kaminari_setup'
|
4
|
+
|
5
|
+
module SearchObject
|
6
|
+
module Plugin
|
7
|
+
describe Paging do
|
8
|
+
def search_class
|
9
|
+
Class.new do
|
10
|
+
include SearchObject.module(:kaminari)
|
11
|
+
|
12
|
+
scope { Product }
|
13
|
+
|
14
|
+
per_page 2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
Product.delete_all
|
20
|
+
end
|
21
|
+
|
22
|
+
it "paginates" do
|
23
|
+
10.times { |i| Product.create name: "product_#{i}" }
|
24
|
+
search = search_class.new({}, 3)
|
25
|
+
expect(search.results.map(&:name)).to eq %w(product_4 product_5)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "uses will paginate" do
|
29
|
+
search = search_class.new
|
30
|
+
expect(search.results.respond_to? :total_pages).to be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "treats nil page as 0" do
|
34
|
+
search = search_class.new({}, nil)
|
35
|
+
expect(search.page).to eq 0
|
36
|
+
end
|
37
|
+
|
38
|
+
it "treats negative page numbers as positive" do
|
39
|
+
search = search_class.new({}, -1)
|
40
|
+
expect(search.page).to eq 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it "gives the real count" do
|
44
|
+
10.times { |i| Product.create name: "product_#{i}" }
|
45
|
+
search = search_class.new({}, 1)
|
46
|
+
expect(search.count).to eq 10
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'active_model_lint-rspec'
|
4
|
+
|
5
|
+
module SearchObject
|
6
|
+
module Plugin
|
7
|
+
class ExtendedModel
|
8
|
+
include SearchObject.module(:model)
|
9
|
+
|
10
|
+
# Fake errors
|
11
|
+
# Since SearchObject is focused to plain search forms,
|
12
|
+
# which don't have validation most of the time
|
13
|
+
def errors
|
14
|
+
Hash.new([])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe ExtendedModel do
|
19
|
+
it_behaves_like 'an ActiveModel'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper_active_record'
|
2
|
+
|
3
|
+
module SearchObject
|
4
|
+
module Plugin
|
5
|
+
describe Paging do
|
6
|
+
def search_class
|
7
|
+
Class.new do
|
8
|
+
include SearchObject.module(:paging)
|
9
|
+
|
10
|
+
scope { Product }
|
11
|
+
|
12
|
+
per_page 2
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
Product.delete_all
|
18
|
+
end
|
19
|
+
|
20
|
+
it "paginates results (by offset and limit)" do
|
21
|
+
10.times { |i| Product.create name: "product_#{i}" }
|
22
|
+
search = search_class.new({}, 1)
|
23
|
+
expect(search.results.map(&:name)).to eq %w(product_2 product_3)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "treats nil page as 0" do
|
27
|
+
search = search_class.new({}, nil)
|
28
|
+
expect(search.page).to eq 0
|
29
|
+
end
|
30
|
+
|
31
|
+
it "treats negative page numbers as positive" do
|
32
|
+
search = search_class.new({}, -1)
|
33
|
+
expect(search.page).to eq 1
|
34
|
+
end
|
35
|
+
|
36
|
+
it "gives the real count" do
|
37
|
+
10.times { |i| Product.create name: "product_#{i}" }
|
38
|
+
search = search_class.new({}, 1)
|
39
|
+
expect(search.count).to eq 10
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'spec_helper_active_record'
|
2
|
+
|
3
|
+
module SearchObject
|
4
|
+
module Plugin
|
5
|
+
describe Sorting do
|
6
|
+
def search_class
|
7
|
+
Class.new do
|
8
|
+
include SearchObject.module(:sorting)
|
9
|
+
|
10
|
+
scope { Product.all }
|
11
|
+
|
12
|
+
sort_by :name, :price
|
13
|
+
|
14
|
+
option :name
|
15
|
+
option :price
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "sorting" do
|
20
|
+
after do
|
21
|
+
Product.delete_all
|
22
|
+
end
|
23
|
+
|
24
|
+
it "sorts results based on the sort option" do
|
25
|
+
5.times { |i| Product.create! price: i }
|
26
|
+
|
27
|
+
search = search_class.new sort: 'price desc'
|
28
|
+
expect(search.results.map(&:price)).to eq [4, 3, 2, 1, 0]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "defaults to first sort by option" do
|
32
|
+
5.times { |i| Product.create! name: "Name#{i}" }
|
33
|
+
|
34
|
+
search = search_class.new
|
35
|
+
expect(search.results.map(&:name)).to eq %w(Name4 Name3 Name2 Name1 Name0)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "ignores invalid sort values" do
|
39
|
+
search = search_class.new sort: 'invalid attribute'
|
40
|
+
expect { search.results.to_a }.not_to raise_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#sort?" do
|
45
|
+
it "matches the sort option" do
|
46
|
+
search = search_class.new sort: 'price desc'
|
47
|
+
|
48
|
+
expect(search.sort?(:price)).to be_true
|
49
|
+
expect(search.sort?(:name)).to be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it "matches string also" do
|
53
|
+
search = search_class.new sort: 'price desc'
|
54
|
+
|
55
|
+
expect(search.sort?('price')).to be_true
|
56
|
+
expect(search.sort?('name')).to be_false
|
57
|
+
end
|
58
|
+
|
59
|
+
it "matches exact strings" do
|
60
|
+
search = search_class.new sort: 'price desc'
|
61
|
+
|
62
|
+
expect(search.sort?('price desc')).to be_true
|
63
|
+
expect(search.sort?('price asc')).to be_false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#sort_attribute" do
|
68
|
+
it "returns sort option attribute" do
|
69
|
+
search = search_class.new sort: 'price desc'
|
70
|
+
expect(search.sort_attribute).to eq 'price'
|
71
|
+
end
|
72
|
+
|
73
|
+
it "defaults to the first sort by option" do
|
74
|
+
search = search_class.new
|
75
|
+
expect(search.sort_attribute).to eq 'name'
|
76
|
+
end
|
77
|
+
|
78
|
+
it "rejects invalid sort options, uses defaults" do
|
79
|
+
search = search_class.new sort: 'invalid'
|
80
|
+
expect(search.sort_attribute).to eq 'name'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#sort_direction" do
|
85
|
+
it "returns asc or desc" do
|
86
|
+
expect(search_class.new(sort: 'price desc').sort_direction).to eq 'desc'
|
87
|
+
expect(search_class.new(sort: 'price asc').sort_direction).to eq 'asc'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "defaults to desc" do
|
91
|
+
expect(search_class.new.sort_direction).to eq 'desc'
|
92
|
+
expect(search_class.new(sort: 'price').sort_direction).to eq 'desc'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "rejects invalid sort options, uses desc" do
|
96
|
+
expect(search_class.new(sort: 'price foo').sort_direction).to eq 'desc'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#sort_direction_for" do
|
101
|
+
it "returns desc if current sort attribute is not the given attribute" do
|
102
|
+
expect(search_class.new(sort: 'price desc').sort_direction_for('name')).to eq 'desc'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "returns asc if current sort attribute is the given attribute" do
|
106
|
+
expect(search_class.new(sort: 'name desc').sort_direction_for('name')).to eq 'asc'
|
107
|
+
end
|
108
|
+
|
109
|
+
it "returns desc if current sort attribute is the given attribute, but asc with direction" do
|
110
|
+
expect(search_class.new(sort: 'name asc').sort_direction_for('name')).to eq 'desc'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#sort_params_for" do
|
115
|
+
it "adds sort direction" do
|
116
|
+
search = search_class.new sort: 'name', name: 'test'
|
117
|
+
expect(search.sort_params_for(:price)).to eq 'sort' => 'price desc', 'name' => 'test'
|
118
|
+
end
|
119
|
+
|
120
|
+
it "reverses sort direction if this is the current sort attribute" do
|
121
|
+
search = search_class.new sort: 'name desc', name: 'test'
|
122
|
+
expect(search.sort_params_for(:name)).to eq 'sort' => 'name asc', 'name' => 'test'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "accepts additional options" do
|
126
|
+
search = search_class.new
|
127
|
+
expect(search.sort_params_for(:price, name: 'value')).to eq 'sort' => 'price desc', 'name' => 'value'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#reverted_sort_direction" do
|
132
|
+
it "reverts sorting direction" do
|
133
|
+
expect(search_class.new(sort: 'price desc').reverted_sort_direction).to eq 'asc'
|
134
|
+
expect(search_class.new(sort: 'price asc').reverted_sort_direction).to eq 'desc'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|