trailblazer-finder 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +22 -11
- data/.travis.yml +0 -2
- data/CHANGES.md +4 -0
- data/README.md +70 -58
- data/lib/trailblazer/finder/adapters/active_record/sorting.rb +6 -2
- data/lib/trailblazer/finder/adapters/data_mapper.rb +2 -2
- data/lib/trailblazer/finder/adapters/data_mapper/sorting.rb +25 -25
- data/lib/trailblazer/finder/adapters/sequel/sorting.rb +9 -7
- data/lib/trailblazer/finder/base.rb +0 -2
- data/lib/trailblazer/finder/features/predicate.rb +4 -9
- data/lib/trailblazer/finder/features/sorting.rb +70 -28
- data/lib/trailblazer/finder/utils/deep_locate.rb +4 -19
- data/lib/trailblazer/finder/utils/parse.rb +6 -6
- data/lib/trailblazer/finder/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/spec_helper_active_record.rb +0 -1
- data/spec/support/sorting_shared_example.rb +67 -37
- data/spec/trailblazer/finder/adapters/active_record/predicates_spec.rb +5 -2
- data/spec/trailblazer/finder/adapters/active_record/sorting_spec.rb +93 -80
- data/spec/trailblazer/finder/adapters/data_mapper/base_spec.rb +1 -1
- data/spec/trailblazer/finder/adapters/data_mapper/sorting_spec.rb +85 -85
- data/spec/trailblazer/finder/adapters/sequel/predicates_spec.rb +2 -2
- data/spec/trailblazer/finder/adapters/sequel/sorting_spec.rb +101 -80
- data/spec/trailblazer/finder/features/sorting_spec.rb +59 -62
- data/trailblazer-finder.gemspec +1 -1
- metadata +2 -2
@@ -2,6 +2,8 @@ module Trailblazer
|
|
2
2
|
class Finder
|
3
3
|
module Features
|
4
4
|
module Predicate
|
5
|
+
PREDICATES = %w[eq not_eq blank not_blank lt lte gt gte].freeze
|
6
|
+
|
5
7
|
def self.included(base)
|
6
8
|
base.extend ClassMethods
|
7
9
|
base.instance_eval do
|
@@ -10,7 +12,7 @@ module Trailblazer
|
|
10
12
|
end
|
11
13
|
|
12
14
|
module ClassMethods
|
13
|
-
def
|
15
|
+
def predicate_filter(attribute, predicate)
|
14
16
|
filter_by "#{attribute}_#{predicate}" do |entity_type, value|
|
15
17
|
splitter = Utils::Splitter.new "#{attribute}_#{predicate}", value
|
16
18
|
splitter.split_key predicate.to_sym
|
@@ -20,14 +22,7 @@ module Trailblazer
|
|
20
22
|
|
21
23
|
def predicates_for(*attributes)
|
22
24
|
attributes.each do |attribute|
|
23
|
-
|
24
|
-
do_filters(attribute, "not_eq")
|
25
|
-
do_filters(attribute, "blank")
|
26
|
-
do_filters(attribute, "not_blank")
|
27
|
-
do_filters(attribute, "gt")
|
28
|
-
do_filters(attribute, "gte")
|
29
|
-
do_filters(attribute, "lt")
|
30
|
-
do_filters(attribute, "lte")
|
25
|
+
PREDICATES.each { |predicate| predicate_filter(attribute, predicate) }
|
31
26
|
end
|
32
27
|
end
|
33
28
|
end
|
@@ -5,55 +5,97 @@ module Trailblazer
|
|
5
5
|
def self.included(base)
|
6
6
|
base.extend ClassMethods
|
7
7
|
base.instance_eval do
|
8
|
-
filter_by :sort do |entity_type,
|
9
|
-
sort_it(entity_type, sort_attribute, sort_direction)
|
8
|
+
filter_by :sort do |entity_type, value|
|
9
|
+
next sort_it(entity_type, [sort_orders(sort_attribute(value), sort_direction(value))]) if value.nil?
|
10
|
+
sort_attributes = value.split(',')
|
11
|
+
sorters = []
|
12
|
+
sort_attributes.each do |sort_attr|
|
13
|
+
sorters << sort_orders(sort_attribute(sort_attr), sort_direction(sort_attr))
|
14
|
+
end
|
15
|
+
sort_it(entity_type, sorters)
|
10
16
|
end
|
11
17
|
end
|
12
18
|
end
|
13
19
|
|
14
|
-
# Adapters will overwite this method
|
15
|
-
def sort_it(entity_type, sort_attribute, sort_direction)
|
16
|
-
case sort_direction
|
17
|
-
when 'asc', 'ascending'
|
18
|
-
entity_type.sort_by { |a| a[sort_attribute.to_sym] }
|
19
|
-
when 'desc', 'descending'
|
20
|
-
entity_type.sort_by { |a| a[sort_attribute.to_sym] }.reverse
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
20
|
def sort?(attribute)
|
25
|
-
|
21
|
+
sort.include?(attribute.to_s)
|
26
22
|
end
|
27
23
|
|
28
|
-
def
|
29
|
-
|
24
|
+
def sort_direction_for(attribute)
|
25
|
+
return 'asc' if !sort.nil? && sort.include?("#{attribute} asc")
|
26
|
+
'desc'
|
30
27
|
end
|
31
28
|
|
32
|
-
def
|
33
|
-
|
29
|
+
def reverse_sort_direction_for(attribute)
|
30
|
+
return 'desc' if sort_direction_for(attribute) == 'asc'
|
31
|
+
'asc'
|
34
32
|
end
|
35
33
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
def sort_params_for(attribute, options = {})
|
35
|
+
options['sort'] = if sort.nil?
|
36
|
+
"#{attribute} #{sort_direction_for(attribute)}"
|
37
|
+
elsif sort.include?(attribute.to_s)
|
38
|
+
sort
|
39
|
+
else
|
40
|
+
"#{attribute} #{sort_direction_for(attribute)}"
|
41
|
+
end
|
42
|
+
params options
|
42
43
|
end
|
43
44
|
|
44
|
-
def
|
45
|
+
def new_sort_params_for(attribute, options = {})
|
45
46
|
options['sort'] = "#{attribute} #{sort_direction_for(attribute)}"
|
46
47
|
params options
|
47
48
|
end
|
48
49
|
|
49
|
-
def
|
50
|
-
|
50
|
+
def add_sort_params_for(attribute, options = {})
|
51
|
+
options['sort'] = if sort.nil?
|
52
|
+
"#{attribute} #{sort_direction_for(attribute)}"
|
53
|
+
elsif sort.include?(attribute.to_s)
|
54
|
+
sort.gsub(/#{attribute} #{sort_direction_for(attribute)}/, "#{attribute} #{reverse_sort_direction_for(attribute)}")
|
55
|
+
else
|
56
|
+
"#{sort}, #{attribute} #{sort_direction_for(attribute)}"
|
57
|
+
end
|
58
|
+
params options
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# ORM Adapters will overwite this method
|
64
|
+
def sort_it(entity_type, sort_attributes)
|
65
|
+
entity_type.sort do |this, that|
|
66
|
+
sort_attributes.reduce(0) do |diff, order|
|
67
|
+
next diff if diff != 0 # this and that have differed at an earlier order entry
|
68
|
+
key, direction = order
|
69
|
+
# deal with nil cases
|
70
|
+
next 0 if this[key].nil? && that[key].nil?
|
71
|
+
next 1 if this[key].nil?
|
72
|
+
next -1 if that[key].nil?
|
73
|
+
# do the actual comparison
|
74
|
+
comparison = this[key] <=> that[key]
|
75
|
+
comparison * direction
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# ORM Adapters will overwite this method
|
81
|
+
def sort_orders(sort_attr, sort_dir)
|
82
|
+
[sort_attr.to_sym, (sort_dir.to_sym == :asc ? 1 : -1)]
|
83
|
+
end
|
84
|
+
|
85
|
+
def sort_attribute(attribute)
|
86
|
+
result = Utils::Extra.ensure_included attribute.to_s.split(' ', 2).first, self.class.sort_attributes
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
def sort_direction(attribute)
|
91
|
+
return Utils::Extra.ensure_included attribute.to_s.split(' ', 2).last, %w[desc asc] unless attribute.nil?
|
92
|
+
'desc'
|
51
93
|
end
|
52
94
|
|
53
95
|
module ClassMethods
|
96
|
+
attr_accessor :sorted_attributes
|
54
97
|
def sortable_by(*attributes)
|
55
|
-
config[:sort_attributes]
|
56
|
-
config[:defaults]['sort'] = "#{config[:sort_attributes].first} desc"
|
98
|
+
config[:sort_attributes] = attributes.map(&:to_s)
|
57
99
|
end
|
58
100
|
|
59
101
|
def sort_attributes
|
@@ -2,35 +2,20 @@ module Trailblazer
|
|
2
2
|
class Finder
|
3
3
|
module Utils
|
4
4
|
module DeepLocate
|
5
|
-
def self.deep_locate(comparator, object)
|
6
|
-
comparator = _construct_key_comparator(comparator, object) unless comparator.respond_to?(:call)
|
7
|
-
|
8
|
-
_deep_locate(comparator, object)
|
9
|
-
end
|
10
|
-
|
11
|
-
def self._construct_key_comparator(search_key, object)
|
12
|
-
search_key = search_key.to_s if defined?(::ActiveSupport::HashWithIndifferentAccess) && object.is_a?(::ActiveSupport::HashWithIndifferentAccess)
|
13
|
-
search_key = search_key.to_s if object.respond_to?(:indifferent_access?) && object.indifferent_access?
|
14
|
-
|
15
|
-
lambda do |non_callable_object|
|
16
|
-
->(key, _, _) { key == non_callable_object }
|
17
|
-
end.call(search_key)
|
18
|
-
end
|
19
|
-
|
20
|
-
def self._deep_locate(comparator, object, result = [])
|
5
|
+
def self.deep_locate(comparator, object, result = [])
|
21
6
|
if object.is_a?(::Enumerable)
|
22
|
-
if object.any? { |value|
|
7
|
+
if object.any? { |value| match_comparator?(value, comparator, object) }
|
23
8
|
result.push object
|
24
9
|
end
|
25
10
|
(object.respond_to?(:values) ? object.values : object.entries).each do |value|
|
26
|
-
|
11
|
+
deep_locate(comparator, value, result)
|
27
12
|
end
|
28
13
|
end
|
29
14
|
|
30
15
|
result
|
31
16
|
end
|
32
17
|
|
33
|
-
def self.
|
18
|
+
def self.match_comparator?(value, comparator, object)
|
34
19
|
if object.is_a?(::Hash)
|
35
20
|
key, value = value
|
36
21
|
else
|
@@ -7,13 +7,13 @@ module Trailblazer
|
|
7
7
|
class Parse
|
8
8
|
# Need a replacement for this
|
9
9
|
def self.date(value)
|
10
|
-
return
|
11
|
-
return if [true, false].include? value
|
12
|
-
return if value.is_a? Integer
|
13
|
-
return if value =~ /[[:alpha:]]/
|
10
|
+
return unless valid_date(value)
|
14
11
|
Date.parse(value).strftime('%Y-%m-%d')
|
15
|
-
|
16
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.valid_date(date)
|
15
|
+
date_hash = Date._parse(date.to_s)
|
16
|
+
Date.valid_date?(date_hash[:year].to_i, date_hash[:mon].to_i, date_hash[:mday].to_i)
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.term(value)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,71 +1,90 @@
|
|
1
1
|
shared_examples_for 'a sorting feature' do
|
2
2
|
describe '#sort?' do
|
3
|
-
it '
|
3
|
+
it 'says if the requested item is sorted on, for single sort' do
|
4
4
|
finder = finder_with_sort 'price desc'
|
5
5
|
|
6
6
|
expect(finder).to be_sort :price
|
7
7
|
expect(finder).not_to be_sort :name
|
8
8
|
end
|
9
9
|
|
10
|
-
it '
|
10
|
+
it 'says if the requested item is sorted on for multiple sorts' do
|
11
|
+
finder = finder_with_sort 'price desc, name asc, id desc'
|
12
|
+
|
13
|
+
expect(finder).to be_sort :price
|
14
|
+
expect(finder).to be_sort :name
|
15
|
+
expect(finder).to be_sort :id
|
16
|
+
expect(finder).not_to be_sort :created_at
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'matches string also, for single sort' do
|
11
20
|
finder = finder_with_sort 'price desc'
|
12
21
|
|
13
22
|
expect(finder).to be_sort 'price'
|
14
23
|
expect(finder).not_to be_sort 'name'
|
15
24
|
end
|
16
25
|
|
17
|
-
it 'matches
|
18
|
-
finder = finder_with_sort 'price desc'
|
26
|
+
it 'matches string also, for multiple sorts' do
|
27
|
+
finder = finder_with_sort 'price desc, name asc, id desc'
|
19
28
|
|
20
|
-
expect(finder).to be_sort 'price
|
21
|
-
expect(finder).
|
29
|
+
expect(finder).to be_sort 'price'
|
30
|
+
expect(finder).to be_sort 'name'
|
31
|
+
expect(finder).to be_sort 'id'
|
32
|
+
expect(finder).not_to be_sort 'created_at'
|
22
33
|
end
|
23
|
-
end
|
24
34
|
|
25
|
-
|
26
|
-
it 'returns sort option attribute' do
|
35
|
+
it 'matches exact strings, for single sort' do
|
27
36
|
finder = finder_with_sort 'price desc'
|
28
|
-
expect(finder.sort_attribute).to eq 'price'
|
29
|
-
end
|
30
37
|
|
31
|
-
|
32
|
-
finder
|
33
|
-
expect(finder.sort_attribute).to eq 'name'
|
38
|
+
expect(finder).to be_sort 'price desc'
|
39
|
+
expect(finder).not_to be_sort 'price asc'
|
34
40
|
end
|
35
41
|
|
36
|
-
it '
|
37
|
-
finder = finder_with_sort '
|
38
|
-
|
42
|
+
it 'matches exact strings, for multiple sort' do
|
43
|
+
finder = finder_with_sort 'price desc, name asc'
|
44
|
+
|
45
|
+
expect(finder).to be_sort 'price desc'
|
46
|
+
expect(finder).to be_sort 'name asc'
|
47
|
+
expect(finder).not_to be_sort 'name desc'
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
42
|
-
describe '#
|
43
|
-
it 'returns
|
44
|
-
expect(finder_with_sort('price desc').
|
45
|
-
expect(finder_with_sort('price asc').sort_direction).to eq 'asc'
|
51
|
+
describe '#sort_direction_for' do
|
52
|
+
it 'returns desc if current sort attribute is not the given attribute' do
|
53
|
+
expect(finder_with_sort('price desc').sort_direction_for('name')).to eq 'desc'
|
46
54
|
end
|
47
55
|
|
48
|
-
it '
|
49
|
-
expect(finder_with_sort.
|
50
|
-
expect(finder_with_sort('price').sort_direction).to eq 'desc'
|
56
|
+
it 'returns asc if current sort attribute is the given attribute' do
|
57
|
+
expect(finder_with_sort('name desc').sort_direction_for('name')).to eq 'desc'
|
51
58
|
end
|
52
59
|
|
53
|
-
it '
|
54
|
-
expect(finder_with_sort('
|
60
|
+
it 'returns desc if current sort attribute is the given attribute, but asc with direction' do
|
61
|
+
expect(finder_with_sort('name asc').sort_direction_for('name')).to eq 'asc'
|
55
62
|
end
|
56
63
|
end
|
57
64
|
|
58
|
-
describe '#
|
65
|
+
describe '#reverse_sort_direction_for' do
|
59
66
|
it 'returns desc if current sort attribute is not the given attribute' do
|
60
|
-
expect(finder_with_sort('price desc').
|
67
|
+
expect(finder_with_sort('price desc').reverse_sort_direction_for('name')).to eq 'asc'
|
61
68
|
end
|
62
69
|
|
63
70
|
it 'returns asc if current sort attribute is the given attribute' do
|
64
|
-
expect(finder_with_sort('name desc').
|
71
|
+
expect(finder_with_sort('name desc').reverse_sort_direction_for('name')).to eq 'asc'
|
65
72
|
end
|
66
73
|
|
67
74
|
it 'returns desc if current sort attribute is the given attribute, but asc with direction' do
|
68
|
-
expect(finder_with_sort('name asc').
|
75
|
+
expect(finder_with_sort('name asc').reverse_sort_direction_for('name')).to eq 'desc'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#new_sort_params_for' do
|
80
|
+
it 'adds new sort parmas when none are present' do
|
81
|
+
finder = finder_with_sort
|
82
|
+
expect(finder.new_sort_params_for(:price)).to eq 'sort' => 'price desc'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'adds new sort params when multiple ones exists (replaces them all)' do
|
86
|
+
finder = finder_with_sort 'name desc, price desc, id asc', name: 'test'
|
87
|
+
expect(finder.new_sort_params_for(:name)).to eq 'sort' => 'name desc', 'name' => 'test'
|
69
88
|
end
|
70
89
|
end
|
71
90
|
|
@@ -75,9 +94,10 @@ shared_examples_for 'a sorting feature' do
|
|
75
94
|
expect(finder.sort_params_for(:price)).to eq 'sort' => 'price desc', 'name' => 'test'
|
76
95
|
end
|
77
96
|
|
78
|
-
it '
|
79
|
-
finder = finder_with_sort 'name desc', name: 'test'
|
80
|
-
expect(finder.sort_params_for(:name)).to eq 'sort' => 'name asc', 'name' => 'test'
|
97
|
+
it 'finds params for multiple sorts' do
|
98
|
+
finder = finder_with_sort 'name desc, price desc, id asc', name: 'test'
|
99
|
+
expect(finder.sort_params_for(:name)).to eq 'sort' => 'name desc, price desc, id asc', 'name' => 'test'
|
100
|
+
expect(finder.sort_params_for(:price)).to eq 'sort' => 'name desc, price desc, id asc', 'name' => 'test'
|
81
101
|
end
|
82
102
|
|
83
103
|
it 'accepts additional options' do
|
@@ -86,10 +106,20 @@ shared_examples_for 'a sorting feature' do
|
|
86
106
|
end
|
87
107
|
end
|
88
108
|
|
89
|
-
describe '#
|
90
|
-
it '
|
91
|
-
|
92
|
-
expect(
|
109
|
+
describe '#add_sort_params_for' do
|
110
|
+
it 'adds sort params when none exists' do
|
111
|
+
finder = finder_with_sort
|
112
|
+
expect(finder.add_sort_params_for(:price)).to eq 'sort' => 'price desc'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'adds sort params when one already exists' do
|
116
|
+
finder = finder_with_sort 'name', name: 'test'
|
117
|
+
expect(finder.add_sort_params_for(:price)).to eq 'sort' => 'name, price desc', 'name' => 'test'
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'adds sort params when current attribute already exists, but change it' do
|
121
|
+
finder = finder_with_sort 'name asc, price asc', name: 'test'
|
122
|
+
expect(finder.add_sort_params_for(:price)).to eq 'sort' => 'name asc, price desc', 'name' => 'test'
|
93
123
|
end
|
94
124
|
end
|
95
125
|
end
|
@@ -6,6 +6,11 @@ module Trailblazer
|
|
6
6
|
module Adapters
|
7
7
|
module ActiveRecord
|
8
8
|
describe Predicates do
|
9
|
+
after do
|
10
|
+
Product.delete_all
|
11
|
+
Product.reset_pk_sequence
|
12
|
+
end
|
13
|
+
|
9
14
|
class TestFinder < Trailblazer::Finder
|
10
15
|
features Predicate
|
11
16
|
adapters ActiveRecord
|
@@ -23,8 +28,6 @@ module Trailblazer
|
|
23
28
|
|
24
29
|
describe 'equals' do
|
25
30
|
before do
|
26
|
-
Product.delete_all
|
27
|
-
Product.reset_pk_sequence
|
28
31
|
10.times do |i|
|
29
32
|
next Product.create name: "", price: "1#{i}" if i == 7
|
30
33
|
next Product.create name: nil, price: "1#{i}" if i == 8
|
@@ -1,86 +1,99 @@
|
|
1
1
|
require 'spec_helper_active_record'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
finder = finder_with_sort 'created_at desc', category: ''
|
76
|
-
|
77
|
-
expect(finder.results.map(&:name)).to eq [product_of_older_category.name, product_of_newer_category.name]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
it_behaves_like 'a sorting feature'
|
82
|
-
end
|
3
|
+
describe "Trailblazer::Finder::Adapters::ActiveRecord::Sorting", :sorting do
|
4
|
+
after do
|
5
|
+
Product.delete_all
|
6
|
+
Product.reset_pk_sequence
|
7
|
+
end
|
8
|
+
|
9
|
+
before do
|
10
|
+
Product.delete_all
|
11
|
+
Product.reset_pk_sequence
|
12
|
+
end
|
13
|
+
|
14
|
+
class TestARFinder < Trailblazer::Finder
|
15
|
+
features Sorting
|
16
|
+
adapters ActiveRecord
|
17
|
+
|
18
|
+
entity_type { Product.all }
|
19
|
+
|
20
|
+
sortable_by :name, :price, :created_at
|
21
|
+
|
22
|
+
filter_by :name
|
23
|
+
filter_by :price
|
24
|
+
end
|
25
|
+
|
26
|
+
def finder_with_sort(sort = nil, filters = {})
|
27
|
+
TestARFinder.new filter: (sort.nil? ? {} : { sort: sort }).merge(filters)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'sorting' do
|
31
|
+
|
32
|
+
it 'sorts results based on the sort option desc' do
|
33
|
+
5.times { |i| Product.create! price: i }
|
34
|
+
|
35
|
+
finder = finder_with_sort 'price desc'
|
36
|
+
expect(finder.results.map(&:price)).to eq [4, 3, 2, 1, 0]
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'sorts results based on the sort option asc' do
|
40
|
+
5.times { |i| Product.create! price: i }
|
41
|
+
|
42
|
+
finder = finder_with_sort 'price asc'
|
43
|
+
expect(finder.results.map(&:price)).to eq [0, 1, 2, 3, 4]
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'defaults to defaulted database sort if none is set' do
|
47
|
+
5.times { |i| Product.create! name: "Name#{i}" }
|
48
|
+
|
49
|
+
finder = finder_with_sort
|
50
|
+
expect(finder.results.map(&:name)).to eq %w[Name0 Name1 Name2 Name3 Name4]
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'ignores invalid sort values' do
|
54
|
+
finder = finder_with_sort 'invalid attribute'
|
55
|
+
expect { finder.results.to_a }.not_to raise_error
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'can handle renames of sorting in joins' do
|
59
|
+
older_category = Category.create! title: 'older'
|
60
|
+
newer_category = Category.create! title: 'newer'
|
61
|
+
|
62
|
+
product_of_newer_category = Product.create! name: 'older product', category: newer_category
|
63
|
+
product_of_older_category = Product.create! name: 'newer product', category: older_category
|
64
|
+
|
65
|
+
finder = finder_with_sort 'created_at desc', category: ''
|
66
|
+
|
67
|
+
expect(finder.results.map(&:name)).to eq [product_of_older_category.name, product_of_newer_category.name]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'sorting by multiple' do
|
72
|
+
before do
|
73
|
+
5.times do |i|
|
74
|
+
Product.create name: "Name#{i}", price: "1#{i}"
|
83
75
|
end
|
76
|
+
Product.create name: 'Name3', price: '8'
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'sorts by multiple columns name asc and price asc' do
|
80
|
+
finder = finder_with_sort 'name asc, price asc'
|
81
|
+
expect(finder.results.map(&:name)).to eq %w[Name0 Name1 Name2 Name3 Name3 Name4]
|
82
|
+
expect(finder.results.map(&:price)).to eq [10, 11, 12, 8, 13, 14]
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'sorts by multiple columns name asc and price desc' do
|
86
|
+
finder = finder_with_sort 'name asc, price desc'
|
87
|
+
expect(finder.results.map(&:name)).to eq %w[Name0 Name1 Name2 Name3 Name3 Name4]
|
88
|
+
expect(finder.results.map(&:price)).to eq [10, 11, 12, 13, 8, 14]
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'sorts by multiple columns name desc and price desc' do
|
92
|
+
finder = finder_with_sort 'name desc, price desc'
|
93
|
+
expect(finder.results.map(&:name)).to eq %w[Name4 Name3 Name3 Name2 Name1 Name0]
|
94
|
+
expect(finder.results.map(&:price)).to eq [14, 13, 8, 12, 11, 10]
|
84
95
|
end
|
85
96
|
end
|
97
|
+
|
98
|
+
it_behaves_like 'a sorting feature'
|
86
99
|
end
|