trailblazer-finder 0.1.0
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/.gitignore +19 -0
- data/.rubocop.yml +45 -0
- data/.rubocop_todo.yml +52 -0
- data/.travis.yml +15 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +23 -0
- data/README.md +494 -0
- data/Rakefile +29 -0
- data/lib/trailblazer/finder/adapters/active_record/paging.rb +20 -0
- data/lib/trailblazer/finder/adapters/active_record/sorting.rb +20 -0
- data/lib/trailblazer/finder/adapters/active_record.rb +30 -0
- data/lib/trailblazer/finder/adapters/data_mapper/paging.rb +20 -0
- data/lib/trailblazer/finder/adapters/data_mapper/sorting.rb +25 -0
- data/lib/trailblazer/finder/adapters/data_mapper.rb +30 -0
- data/lib/trailblazer/finder/adapters/friendly_id.rb +33 -0
- data/lib/trailblazer/finder/adapters/kaminari.rb +18 -0
- data/lib/trailblazer/finder/adapters/sequel/paging.rb +20 -0
- data/lib/trailblazer/finder/adapters/sequel/sorting.rb +25 -0
- data/lib/trailblazer/finder/adapters/sequel.rb +30 -0
- data/lib/trailblazer/finder/adapters/will_paginate.rb +18 -0
- data/lib/trailblazer/finder/adapters.rb +26 -0
- data/lib/trailblazer/finder/base.rb +98 -0
- data/lib/trailblazer/finder/errors/block_ignored.rb +11 -0
- data/lib/trailblazer/finder/errors/invalid_defined_by_value.rb +11 -0
- data/lib/trailblazer/finder/errors/invalid_number.rb +16 -0
- data/lib/trailblazer/finder/errors/missing_entity_type.rb +11 -0
- data/lib/trailblazer/finder/errors/with_ignored.rb +11 -0
- data/lib/trailblazer/finder/features/paging.rb +55 -0
- data/lib/trailblazer/finder/features/sorting.rb +66 -0
- data/lib/trailblazer/finder/features.rb +22 -0
- data/lib/trailblazer/finder/filter.rb +66 -0
- data/lib/trailblazer/finder/find.rb +29 -0
- data/lib/trailblazer/finder/utils/extra.rb +31 -0
- data/lib/trailblazer/finder/utils/params.rb +28 -0
- data/lib/trailblazer/finder/utils/parse.rb +25 -0
- data/lib/trailblazer/finder/utils/string.rb +35 -0
- data/lib/trailblazer/finder/version.rb +5 -0
- data/lib/trailblazer/finder.rb +29 -0
- data/lib/trailblazer/operation/finder.rb +61 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/spec_helper_active_record.rb +50 -0
- data/spec/spec_helper_data_mapper.rb +35 -0
- data/spec/spec_helper_sequel.rb +32 -0
- data/spec/support/paging_shared_example.rb +65 -0
- data/spec/support/sorting_shared_example.rb +95 -0
- data/spec/trailblazer/finder/adapters/active_record/base_spec.rb +112 -0
- data/spec/trailblazer/finder/adapters/active_record/paging_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/active_record/sorting_spec.rb +82 -0
- data/spec/trailblazer/finder/adapters/data_mapper/base_spec.rb +112 -0
- data/spec/trailblazer/finder/adapters/data_mapper/paging_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/data_mapper/sorting_spec.rb +85 -0
- data/spec/trailblazer/finder/adapters/friendly_id_spec.rb +46 -0
- data/spec/trailblazer/finder/adapters/kaminari_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/sequel/base_spec.rb +112 -0
- data/spec/trailblazer/finder/adapters/sequel/paging_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/sequel/sorting_spec.rb +82 -0
- data/spec/trailblazer/finder/adapters/will_paginate_spec.rb +71 -0
- data/spec/trailblazer/finder/adapters_spec.rb +110 -0
- data/spec/trailblazer/finder/base_spec.rb +329 -0
- data/spec/trailblazer/finder/features/paging_spec.rb +104 -0
- data/spec/trailblazer/finder/features/sorting_spec.rb +100 -0
- data/spec/trailblazer/finder/features_spec.rb +55 -0
- data/spec/trailblazer/finder/filter_spec.rb +133 -0
- data/spec/trailblazer/finder/find_spec.rb +72 -0
- data/spec/trailblazer/finder/utils/extra_spec.rb +41 -0
- data/spec/trailblazer/finder/utils/params_spec.rb +39 -0
- data/spec/trailblazer/finder/utils/parse_spec.rb +33 -0
- data/spec/trailblazer/finder/utils/string_spec.rb +25 -0
- data/spec/trailblazer/operation/finder_spec.rb +103 -0
- data/spec/trailblazer/operation/paging_spec.rb +68 -0
- data/spec/trailblazer/operation/sorting_spec.rb +80 -0
- data/trailblazer-finder.gemspec +41 -0
- metadata +402 -0
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
puts 'AR is loaded' if Gem.loaded_specs.key?('active_record')
|
3
|
+
module Trailblazer
|
4
|
+
class Finder # rubocop:disable Metrics/ClassLength
|
5
|
+
describe Base do
|
6
|
+
def define_finder_class(&block)
|
7
|
+
Class.new do
|
8
|
+
include Finder::Base
|
9
|
+
|
10
|
+
class_eval(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def finder_class(default_entity_type = [], &block)
|
15
|
+
define_finder_class do
|
16
|
+
entity_type { default_entity_type }
|
17
|
+
|
18
|
+
if block.nil?
|
19
|
+
filter_by :value do |entity_type, value|
|
20
|
+
entity_type.select { |v| v == value }
|
21
|
+
end
|
22
|
+
else
|
23
|
+
class_eval(&block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def new_finder(default_entity_type = [], filter = {}, &block)
|
29
|
+
finder_class(default_entity_type, &block).new filter: filter
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can have its #initialize method overwritten' do
|
33
|
+
finder = new_finder do
|
34
|
+
attr_reader :initialized
|
35
|
+
|
36
|
+
alias_method :initialized?, :initialized
|
37
|
+
|
38
|
+
def initialize(filter = {})
|
39
|
+
@initialized = true
|
40
|
+
super filter
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
expect(finder).to be_initialized
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can have multiple subclasses' do
|
48
|
+
finder1 = new_finder [1, 2, 3], filter: 1 do
|
49
|
+
filter_by :filter do |entity_type, value|
|
50
|
+
entity_type.select { |v| v == value }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
finder2 = new_finder [1, 2, 3], filter: 1 do
|
55
|
+
filter_by :filter, 2 do |entity_type, value|
|
56
|
+
entity_type.reject { |v| v == value }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(finder1.results).not_to eq finder2.results
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can be inherited' do
|
64
|
+
equality_finder = Class.new(finder_class([1, 2, 3])) do
|
65
|
+
filter_by :value do |entity_type, value|
|
66
|
+
entity_type.select { |v| v == value }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
inequality_finder = Class.new(finder_class([1, 2, 3])) do
|
71
|
+
filter_by :value do |entity_type, value|
|
72
|
+
entity_type.select { |v| v > value }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
expect(equality_finder.new(filter: { value: 1 }).results).to eq [1]
|
77
|
+
expect(inequality_finder.new(filter: { value: 1 }).results).to eq [2, 3]
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'entity_type' do
|
81
|
+
def finder_class
|
82
|
+
define_finder_class do
|
83
|
+
filter_by :name do
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'accepts entity_type as argument' do
|
89
|
+
expect(finder_class.new(entity_type: 'entity_type').results).to eq 'entity_type'
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'raises an error if no entity_type is provided' do
|
93
|
+
expect { finder_class.new }.to raise_error Finder::Error::MissingEntityType
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'can overwrite the finder entity_type' do
|
97
|
+
finder_class = define_finder_class do
|
98
|
+
entity_type { 'entity_type' }
|
99
|
+
end
|
100
|
+
|
101
|
+
expect(finder_class.new(entity_type: 'other entity_type').results).to eq 'other entity_type'
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'accepts a block in context of finder object' do
|
105
|
+
finder_class = define_finder_class do
|
106
|
+
entity_type { inner_entity_type }
|
107
|
+
|
108
|
+
attr_reader :inner_entity_type
|
109
|
+
|
110
|
+
def initialize
|
111
|
+
@inner_entity_type = 'entity_type'
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
expect(finder_class.new.results).to eq 'entity_type'
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'passing nil as entity_type in constructor, falls back to default entity_type' do
|
120
|
+
finder_class = define_finder_class do
|
121
|
+
entity_type { 'entity_type' }
|
122
|
+
end
|
123
|
+
|
124
|
+
expect(finder_class.new(entity_type: nil).results).to eq 'entity_type'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'filter_by' do
|
129
|
+
it 'has a default filter' do
|
130
|
+
entity_type = [{ id: 1, value: 'Test 1' }, { id: 2, value: 'Test 2' }, { id: 3, value: 'Test 3' }]
|
131
|
+
finder = new_finder entity_type, value: 'Test 1' do
|
132
|
+
filter_by :value
|
133
|
+
end
|
134
|
+
|
135
|
+
expect(finder.results.map { |n| n[:value] }).to eq ['Test 1']
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'has a default filter working when it\'s nested' do
|
139
|
+
entity_type = [{ id: 1, value: [id: 4, value: 'Test 1'] }, { id: 2, value: 'Test 2' }, { id: 3, value: 'Test 3' }]
|
140
|
+
finder = new_finder entity_type, value: 'Test 1' do
|
141
|
+
filter_by :value
|
142
|
+
end
|
143
|
+
|
144
|
+
expect(finder.results.map { |n| n[:value] }).to eq ['Test 1']
|
145
|
+
expect(finder.results.map { |n| n[:id] }).to eq [4]
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'has another default filter' do
|
149
|
+
entity_type = [{ id: 1, value: 'Test 1' }, { id: 2, value: 'Test 2' }, { id: 3, value: 'Test 3' }]
|
150
|
+
finder = new_finder entity_type, id: 2 do
|
151
|
+
filter_by :id
|
152
|
+
end
|
153
|
+
|
154
|
+
expect(finder.results.map { |n| n[:value] }).to eq ['Test 2']
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'returns the entity_type if nil is returned' do
|
158
|
+
entity_type = [1, 2, 3]
|
159
|
+
finder = new_finder entity_type, value: 'some' do
|
160
|
+
filter_by :value do
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
expect(finder.results).to eq entity_type
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'can use methods from the object' do
|
169
|
+
finder1 = new_finder [1, 2, 3], filter: 1 do
|
170
|
+
filter_by :filter do |entity_type, value|
|
171
|
+
some_instance_method(entity_type, value)
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def some_instance_method(entity_type, value)
|
177
|
+
entity_type.select { |v| v == value }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
expect(finder1.results).to eq [1]
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'can dispatch with instance methods' do
|
185
|
+
finder = new_finder [1, 2, 3], filter: 1 do
|
186
|
+
filter_by :filter, with: :some_instance_method
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def some_instance_method(entity_type, value)
|
191
|
+
entity_type.select { |v| v == value }
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
expect(finder.results).to eq [1]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe 'filter_by attributes' do
|
200
|
+
it 'accesses filter values' do
|
201
|
+
finder = new_finder [], value: 1
|
202
|
+
expect(finder.value).to eq 1
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'returns default filter value if filter_by is not specified' do
|
206
|
+
finder = new_finder do
|
207
|
+
filter_by :value, 1
|
208
|
+
end
|
209
|
+
expect(finder.value).to eq 1
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'does not include invalid filters' do
|
213
|
+
finder = new_finder [], invalid: 'option'
|
214
|
+
expect { finder.invalid }.to raise_error NoMethodError
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe '.results' do
|
219
|
+
it 'shortcut for creating new finder and immediately returning results' do
|
220
|
+
klass = finder_class [1, 2, 3]
|
221
|
+
expect(klass.results(filter: { value: 1 })).to eq [1]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe '#results' do
|
226
|
+
it 'returns only the filtered finder results' do
|
227
|
+
finder = new_finder [1, 2, 3], value: 1
|
228
|
+
expect(finder.results).to eq [1]
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'can apply several filters' do
|
232
|
+
values = [1, 2, 3, 4, 5, 6, 7]
|
233
|
+
finder = new_finder values, bigger_than: 3, odd: true do
|
234
|
+
filter_by :bigger_than do |entity_type, value|
|
235
|
+
entity_type.select { |v| v > value }
|
236
|
+
end
|
237
|
+
|
238
|
+
filter_by :odd do |entity_type, value|
|
239
|
+
entity_type.select(&:odd?) if value
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
expect(finder.results).to eq [5, 7]
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'ignores invalid filters' do
|
247
|
+
finder = new_finder [1, 2, 3], invalid: 'filter_by'
|
248
|
+
expect(finder.results).to eq [1, 2, 3]
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'can be overwritten by overwriting #fetch_results' do
|
252
|
+
finder = new_finder [1, 2, 3], value: 1 do
|
253
|
+
filter_by :value do |entity_type, value|
|
254
|
+
entity_type.select { |v| v == value }
|
255
|
+
end
|
256
|
+
|
257
|
+
def fetch_results
|
258
|
+
super.map { |v| "~#{v}~" }
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
expect(finder.results).to eq ['~1~']
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'applies to default filters' do
|
266
|
+
finder = new_finder [1, 2, 3] do
|
267
|
+
filter_by :value, 1 do |entity_type, value|
|
268
|
+
entity_type.select { |v| v == value }
|
269
|
+
end
|
270
|
+
end
|
271
|
+
expect(finder.results).to eq [1]
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
describe '#results?' do
|
276
|
+
it 'returns true if there are results' do
|
277
|
+
expect(new_finder([1, 2, 3], value: 1)).to be_results
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'returns false if there are no results' do
|
281
|
+
expect(new_finder([1, 2, 3], value: 4)).not_to be_results
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe '#count' do
|
286
|
+
it 'counts the number of results' do
|
287
|
+
expect(new_finder([1, 2, 3], value: 1).count).to eq 1
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'can not be bypassed by features' do
|
291
|
+
finder = new_finder [1, 2, 3] do
|
292
|
+
def fetch_results; end
|
293
|
+
end
|
294
|
+
|
295
|
+
expect(finder.count).to eq 3
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe '#params' do
|
300
|
+
it 'exports filters as params' do
|
301
|
+
finder = new_finder [], value: 1
|
302
|
+
expect(finder.params).to eq 'value' => 1
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'can overwrite filters (mainly used for url handers)' do
|
306
|
+
finder = new_finder [], value: 1
|
307
|
+
expect(finder.params(value: 2)).to eq 'value' => 2
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'ignores missing filters' do
|
311
|
+
finder = new_finder
|
312
|
+
expect(finder.params).to eq({})
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'ignores invalid filters' do
|
316
|
+
finder = new_finder [], invalid: 'option'
|
317
|
+
expect(finder.params).to eq({})
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'includes default filters' do
|
321
|
+
finder = new_finder do
|
322
|
+
filter_by :value, 1
|
323
|
+
end
|
324
|
+
expect(finder.params).to eq 'value' => 1
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/paging_shared_example'
|
3
|
+
|
4
|
+
module Trailblazer
|
5
|
+
class Finder
|
6
|
+
module Features
|
7
|
+
describe Paging do
|
8
|
+
it_behaves_like 'a paging feature'
|
9
|
+
|
10
|
+
it 'uses a pre-defined Hash entity_type instead of any ORM' do
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def define_finder_class(&block)
|
15
|
+
Class.new do
|
16
|
+
include Trailblazer::Finder::Base
|
17
|
+
include Trailblazer::Finder::Features::Paging
|
18
|
+
|
19
|
+
instance_eval(&block) if block_given?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def finder_class # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
24
|
+
define_finder_class do
|
25
|
+
entity_type do
|
26
|
+
[
|
27
|
+
{
|
28
|
+
id: 1,
|
29
|
+
name: 'product_1',
|
30
|
+
price: '11',
|
31
|
+
created_at: Time.now,
|
32
|
+
updated_at: Time.now
|
33
|
+
},
|
34
|
+
{
|
35
|
+
id: 2,
|
36
|
+
name: 'product_2',
|
37
|
+
price: '12',
|
38
|
+
created_at: Time.now,
|
39
|
+
updated_at: Time.now
|
40
|
+
},
|
41
|
+
{
|
42
|
+
id: 3,
|
43
|
+
name: 'product_3',
|
44
|
+
price: '13',
|
45
|
+
created_at: Time.now,
|
46
|
+
updated_at: Time.now
|
47
|
+
},
|
48
|
+
{
|
49
|
+
id: 4,
|
50
|
+
name: 'product_4',
|
51
|
+
price: '14',
|
52
|
+
created_at: Time.now,
|
53
|
+
updated_at: Time.now
|
54
|
+
},
|
55
|
+
{
|
56
|
+
id: 5,
|
57
|
+
name: 'product_5',
|
58
|
+
price: '15',
|
59
|
+
created_at: Time.now,
|
60
|
+
updated_at: Time.now
|
61
|
+
},
|
62
|
+
{
|
63
|
+
id: 6,
|
64
|
+
name: 'product_6',
|
65
|
+
price: '15',
|
66
|
+
created_at: Time.now,
|
67
|
+
updated_at: Time.now
|
68
|
+
}
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
per_page 2
|
73
|
+
|
74
|
+
min_per_page 2
|
75
|
+
max_per_page 10
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def finder_with_page(page = nil, per_page = nil)
|
80
|
+
finder_class.new page: page, per_page: per_page
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can be inherited' do
|
84
|
+
child_class = Class.new(finder_class)
|
85
|
+
expect(child_class.new.per_page).to eq 2
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#results' do
|
89
|
+
it 'paginates results' do
|
90
|
+
finder = finder_with_page 2, 2
|
91
|
+
expect(finder.results.map { |n| n[:name] }).to eq %w[product_3 product_4]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#count' do
|
96
|
+
it 'gives the real count' do
|
97
|
+
finder = finder_with_page 1
|
98
|
+
expect(finder.count).to eq 6
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/sorting_shared_example'
|
3
|
+
|
4
|
+
module Trailblazer
|
5
|
+
class Finder
|
6
|
+
module Features
|
7
|
+
describe Sorting do
|
8
|
+
it 'uses a pre-defined Hash entity_type instead of any ORM' do
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def finder_class # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
13
|
+
Class.new do
|
14
|
+
include Trailblazer::Finder::Base
|
15
|
+
include Trailblazer::Finder::Features::Sorting
|
16
|
+
|
17
|
+
entity_type do
|
18
|
+
[
|
19
|
+
{
|
20
|
+
id: 1,
|
21
|
+
name: 'product_1',
|
22
|
+
price: '11',
|
23
|
+
created_at: Time.now,
|
24
|
+
updated_at: Time.now
|
25
|
+
},
|
26
|
+
{
|
27
|
+
id: 2,
|
28
|
+
name: 'product_2',
|
29
|
+
price: '12',
|
30
|
+
created_at: Time.now,
|
31
|
+
updated_at: Time.now
|
32
|
+
},
|
33
|
+
{
|
34
|
+
id: 3,
|
35
|
+
name: 'product_3',
|
36
|
+
price: '13',
|
37
|
+
created_at: Time.now,
|
38
|
+
updated_at: Time.now
|
39
|
+
},
|
40
|
+
{
|
41
|
+
id: 4,
|
42
|
+
name: 'product_4',
|
43
|
+
price: '14',
|
44
|
+
created_at: Time.now,
|
45
|
+
updated_at: Time.now
|
46
|
+
},
|
47
|
+
{
|
48
|
+
id: 5,
|
49
|
+
name: 'product_5',
|
50
|
+
price: '15',
|
51
|
+
created_at: Time.now,
|
52
|
+
updated_at: Time.now
|
53
|
+
}
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
sortable_by :name, :price, :created_at
|
58
|
+
|
59
|
+
filter_by :name
|
60
|
+
filter_by :price
|
61
|
+
filter_by(:category) { |entity_type, _| entity_type.joins(:category) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def finder_with_sort(sort = nil, filters = {})
|
66
|
+
finder_class.new filter: { sort: sort }.merge(filters)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'can be inherited' do
|
70
|
+
child_class = Class.new(finder_class)
|
71
|
+
expect(child_class.new.sort_attribute).to eq 'name'
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'sorting' do
|
75
|
+
it 'sorts results based on the sort option desc' do
|
76
|
+
finder = finder_with_sort 'price desc'
|
77
|
+
expect(finder.results.map { |n| n[:price] }).to eq %w[15 14 13 12 11]
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sorts results based on the sort option asc' do
|
81
|
+
finder = finder_with_sort 'price asc'
|
82
|
+
expect(finder.results.map { |n| n[:price] }).to eq %w[11 12 13 14 15]
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'defaults to first sort by option' do
|
86
|
+
finder = finder_with_sort
|
87
|
+
expect(finder.results.map { |n| n[:name] }).to eq %w[product_5 product_4 product_3 product_2 product_1]
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'ignores invalid sort values' do
|
91
|
+
finder = finder_with_sort 'invalid attribute'
|
92
|
+
expect { finder.results.to_a }.not_to raise_error
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it_behaves_like 'a sorting feature'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Trailblazer
|
4
|
+
class Finder
|
5
|
+
describe Features do
|
6
|
+
def define_finder_class(&block)
|
7
|
+
Class.new do
|
8
|
+
include Trailblazer::Finder::Base
|
9
|
+
include Trailblazer::Finder::Features
|
10
|
+
|
11
|
+
instance_eval(&block) if block_given?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it '.adapters' do
|
16
|
+
finder_class = define_finder_class do
|
17
|
+
entity_type { 'entity_type' }
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(finder_class).to respond_to(:features)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Paging do
|
24
|
+
it 'can load Paging feature' do
|
25
|
+
expect do
|
26
|
+
define_finder_class do
|
27
|
+
entity_type { 'entity_type' }
|
28
|
+
features Paging
|
29
|
+
end
|
30
|
+
end.not_to raise_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe Sorting do
|
35
|
+
it 'can load Sorting feature' do
|
36
|
+
expect do
|
37
|
+
define_finder_class do
|
38
|
+
entity_type { 'entity_type' }
|
39
|
+
features Sorting
|
40
|
+
end
|
41
|
+
end.not_to raise_error
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'raises an error on non-existing feature' do
|
46
|
+
expect do
|
47
|
+
define_finder_class do
|
48
|
+
entity_type { 'entity_type' }
|
49
|
+
features Unknown
|
50
|
+
end
|
51
|
+
end.to raise_error NameError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|