kiroshi 0.1.1 → 0.3.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 +4 -4
- data/.codacy.yml +13 -0
- data/.markdownlint.json +6 -0
- data/.rubocop_todo.yml +4 -8
- data/README.md +62 -68
- data/lib/kiroshi/filter.rb +46 -24
- data/lib/kiroshi/filter_query/exact.rb +3 -3
- data/lib/kiroshi/filter_query/like.rb +30 -6
- data/lib/kiroshi/filter_query.rb +10 -14
- data/lib/kiroshi/filter_runner.rb +47 -35
- data/lib/kiroshi/filters/class_methods.rb +172 -0
- data/lib/kiroshi/filters.rb +89 -78
- data/lib/kiroshi/version.rb +1 -1
- data/lib/kiroshi.rb +3 -23
- data/spec/lib/kiroshi/filter_query/exact_spec.rb +41 -7
- data/spec/lib/kiroshi/filter_query/like_spec.rb +53 -12
- data/spec/lib/kiroshi/filter_runner_spec.rb +51 -26
- data/spec/lib/kiroshi/filter_spec.rb +9 -12
- data/spec/lib/kiroshi/filters/class_methods_spec.rb +103 -0
- data/spec/lib/kiroshi/filters_spec.rb +315 -9
- data/spec/spec_helper.rb +1 -0
- data/spec/support/db/schema.rb +1 -0
- metadata +6 -2
@@ -6,11 +6,10 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
6
6
|
describe '#apply' do
|
7
7
|
subject(:query) { described_class.new(filter_runner) }
|
8
8
|
|
9
|
-
let(:filter_runner) { Kiroshi::FilterRunner.new(filter: filter, scope: scope,
|
9
|
+
let(:filter_runner) { Kiroshi::FilterRunner.new(filter: filter, scope: scope, value: filter_value) }
|
10
10
|
let(:filter) { Kiroshi::Filter.new(:name, match: :exact) }
|
11
11
|
let(:scope) { Document.all }
|
12
12
|
let(:filter_value) { 'test_document' }
|
13
|
-
let(:filters) { { name: filter_value } }
|
14
13
|
|
15
14
|
let!(:matching_document) { create(:document, name: 'test_document') }
|
16
15
|
let!(:non_matching_document) { create(:document, name: 'other_document') }
|
@@ -36,7 +35,6 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
36
35
|
context 'when filtering by status attribute' do
|
37
36
|
let(:filter) { Kiroshi::Filter.new(:status, match: :exact) }
|
38
37
|
let(:filter_value) { 'published' }
|
39
|
-
let(:filters) { { status: filter_value } }
|
40
38
|
|
41
39
|
let!(:published_document) { create(:document, status: 'published') }
|
42
40
|
let!(:draft_document) { create(:document, status: 'draft') }
|
@@ -63,7 +61,6 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
63
61
|
context 'when filtering with numeric values' do
|
64
62
|
let(:filter) { Kiroshi::Filter.new(:priority, match: :exact) }
|
65
63
|
let(:filter_value) { 1 }
|
66
|
-
let(:filters) { { priority: filter_value } }
|
67
64
|
|
68
65
|
let!(:high_priority_document) { create(:document, priority: 1) }
|
69
66
|
let!(:medium_priority_document) { create(:document, priority: 2) }
|
@@ -90,7 +87,6 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
90
87
|
context 'when filtering with boolean values' do
|
91
88
|
let(:filter) { Kiroshi::Filter.new(:active, match: :exact) }
|
92
89
|
let(:filter_value) { true }
|
93
|
-
let(:filters) { { active: filter_value } }
|
94
90
|
|
95
91
|
let!(:active_document) { create(:document, active: true) }
|
96
92
|
let!(:inactive_document) { create(:document, active: false) }
|
@@ -154,7 +150,6 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
154
150
|
context 'when filter has table configured' do
|
155
151
|
let(:scope) { Document.joins(:tags) }
|
156
152
|
let(:filter_value) { 'ruby' }
|
157
|
-
let(:filters) { { name: filter_value } }
|
158
153
|
|
159
154
|
let!(:first_tag) { Tag.find_or_create_by(name: 'ruby') }
|
160
155
|
let!(:second_tag) { Tag.find_or_create_by(name: 'javascript') }
|
@@ -252,7 +247,6 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
252
247
|
context 'when filtering by different attributes with table qualification' do
|
253
248
|
let(:filter) { Kiroshi::Filter.new(:id, match: :exact, table: :tags) }
|
254
249
|
let(:filter_value) { first_tag.id }
|
255
|
-
let(:filters) { { id: filter_value } }
|
256
250
|
|
257
251
|
let(:expected_sql) do
|
258
252
|
<<~SQL.squish
|
@@ -276,5 +270,45 @@ RSpec.describe Kiroshi::FilterQuery::Exact, type: :model do
|
|
276
270
|
end
|
277
271
|
end
|
278
272
|
end
|
273
|
+
|
274
|
+
context 'when Filter#column is different from filter_key' do
|
275
|
+
let(:filter) { Kiroshi::Filter.new(:user_name, match: :exact, column: :full_name) }
|
276
|
+
let(:filter_value) { 'John Doe' }
|
277
|
+
|
278
|
+
let!(:matching_document) { create(:document, full_name: 'John Doe') }
|
279
|
+
let!(:non_matching_document) { create(:document, full_name: 'Jane Smith') }
|
280
|
+
|
281
|
+
let(:expected_sql) do
|
282
|
+
<<~SQL.squish
|
283
|
+
SELECT "documents".* FROM "documents" WHERE "documents"."full_name" = 'John Doe'
|
284
|
+
SQL
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'uses the column name instead of filter_key in SQL' do
|
288
|
+
expect(query.apply.to_sql).to eq(expected_sql)
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'returns records that match the column value' do
|
292
|
+
expect(query.apply).to include(matching_document)
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'does not return records that do not match the column value' do
|
296
|
+
expect(query.apply).not_to include(non_matching_document)
|
297
|
+
end
|
298
|
+
|
299
|
+
context 'with table qualification' do
|
300
|
+
let(:filter) { Kiroshi::Filter.new(:user_name, match: :exact, table: :documents, column: :full_name) }
|
301
|
+
|
302
|
+
let(:expected_sql) do
|
303
|
+
<<~SQL.squish
|
304
|
+
SELECT "documents".* FROM "documents" WHERE "documents"."full_name" = 'John Doe'
|
305
|
+
SQL
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'generates SQL with proper table and column qualification' do
|
309
|
+
expect(query.apply.to_sql).to eq(expected_sql)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
279
313
|
end
|
280
314
|
end
|
@@ -6,11 +6,10 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
6
6
|
describe '#apply' do
|
7
7
|
subject(:query) { described_class.new(filter_runner) }
|
8
8
|
|
9
|
-
let(:filter_runner) { Kiroshi::FilterRunner.new(filter: filter, scope: scope,
|
9
|
+
let(:filter_runner) { Kiroshi::FilterRunner.new(filter: filter, scope: scope, value: filter_value) }
|
10
10
|
let(:filter) { Kiroshi::Filter.new(:name, match: :like) }
|
11
11
|
let(:scope) { Document.all }
|
12
12
|
let(:filter_value) { 'test' }
|
13
|
-
let(:filters) { { name: filter_value } }
|
14
13
|
|
15
14
|
let!(:matching_document) { create(:document, name: 'test_document') }
|
16
15
|
let!(:another_match) { create(:document, name: 'my_test_file') }
|
@@ -18,7 +17,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
18
17
|
|
19
18
|
let(:expected_sql) do
|
20
19
|
<<~SQL.squish
|
21
|
-
SELECT "documents".* FROM "documents" WHERE (documents.name LIKE '%test%')
|
20
|
+
SELECT "documents".* FROM "documents" WHERE ("documents"."name" LIKE '%test%')
|
22
21
|
SQL
|
23
22
|
end
|
24
23
|
|
@@ -41,7 +40,6 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
41
40
|
context 'when filtering by status attribute' do
|
42
41
|
let(:filter) { Kiroshi::Filter.new(:status, match: :like) }
|
43
42
|
let(:filter_value) { 'pub' }
|
44
|
-
let(:filters) { { status: filter_value } }
|
45
43
|
|
46
44
|
let!(:published_document) { create(:document, status: 'published') }
|
47
45
|
let!(:republished_document) { create(:document, status: 'republished') }
|
@@ -49,7 +47,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
49
47
|
|
50
48
|
let(:expected_sql) do
|
51
49
|
<<~SQL.squish
|
52
|
-
SELECT "documents".* FROM "documents" WHERE (documents.status LIKE '%pub%')
|
50
|
+
SELECT "documents".* FROM "documents" WHERE ("documents"."status" LIKE '%pub%')
|
53
51
|
SQL
|
54
52
|
end
|
55
53
|
|
@@ -73,7 +71,6 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
73
71
|
context 'when filtering with numeric values as strings' do
|
74
72
|
let(:filter) { Kiroshi::Filter.new(:version, match: :like) }
|
75
73
|
let(:filter_value) { '1.2' }
|
76
|
-
let(:filters) { { version: filter_value } }
|
77
74
|
|
78
75
|
let!(:version_match) { create(:document, version: '1.2.3') }
|
79
76
|
let!(:another_version) { create(:document, version: '2.1.2') }
|
@@ -81,7 +78,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
81
78
|
|
82
79
|
let(:expected_sql) do
|
83
80
|
<<~SQL.squish
|
84
|
-
SELECT "documents".* FROM "documents" WHERE (documents.version LIKE '%1.2%')
|
81
|
+
SELECT "documents".* FROM "documents" WHERE ("documents"."version" LIKE '%1.2%')
|
85
82
|
SQL
|
86
83
|
end
|
87
84
|
|
@@ -107,7 +104,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
107
104
|
|
108
105
|
let(:expected_sql) do
|
109
106
|
<<~SQL.squish
|
110
|
-
SELECT "documents".* FROM "documents" WHERE (documents.name LIKE '%nonexistent%')
|
107
|
+
SELECT "documents".* FROM "documents" WHERE ("documents"."name" LIKE '%nonexistent%')
|
111
108
|
SQL
|
112
109
|
end
|
113
110
|
|
@@ -190,7 +187,6 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
190
187
|
context 'when filter has table configured' do
|
191
188
|
let(:scope) { Document.joins(:tags) }
|
192
189
|
let(:filter_value) { 'ruby' }
|
193
|
-
let(:filters) { { name: filter_value } }
|
194
190
|
|
195
191
|
let!(:first_tag) { Tag.find_or_create_by(name: 'ruby') }
|
196
192
|
let!(:second_tag) { Tag.find_or_create_by(name: 'ruby_on_rails') }
|
@@ -222,7 +218,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
222
218
|
|
223
219
|
it 'generates SQL with tags table qualification' do
|
224
220
|
result_sql = query.apply.to_sql
|
225
|
-
expect(result_sql).to include('tags.name LIKE')
|
221
|
+
expect(result_sql).to include('"tags"."name" LIKE')
|
226
222
|
end
|
227
223
|
|
228
224
|
it 'generates SQL with correct LIKE pattern for tag name' do
|
@@ -249,7 +245,7 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
249
245
|
|
250
246
|
it 'generates SQL with documents table qualification' do
|
251
247
|
result_sql = query.apply.to_sql
|
252
|
-
expect(result_sql).to include('documents.name LIKE')
|
248
|
+
expect(result_sql).to include('"documents"."name" LIKE')
|
253
249
|
end
|
254
250
|
|
255
251
|
it 'generates SQL with correct LIKE pattern for document name' do
|
@@ -267,7 +263,52 @@ RSpec.describe Kiroshi::FilterQuery::Like, type: :model do
|
|
267
263
|
|
268
264
|
it 'generates SQL with string table qualification' do
|
269
265
|
result_sql = query.apply.to_sql
|
270
|
-
expect(result_sql).to include('tags.name LIKE')
|
266
|
+
expect(result_sql).to include('"tags"."name" LIKE')
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context 'when Filter#column is different from filter_key' do
|
272
|
+
let(:filter) { Kiroshi::Filter.new(:user_name, match: :like, column: :full_name) }
|
273
|
+
let(:filter_value) { 'John' }
|
274
|
+
|
275
|
+
let!(:matching_document) { create(:document, full_name: 'John Doe') }
|
276
|
+
let!(:another_match) { create(:document, full_name: 'Johnny Smith') }
|
277
|
+
let!(:non_matching_document) { create(:document, full_name: 'Jane Wilson') }
|
278
|
+
|
279
|
+
let(:expected_sql) do
|
280
|
+
<<~SQL.squish
|
281
|
+
SELECT "documents".* FROM "documents" WHERE ("documents"."full_name" LIKE '%John%')
|
282
|
+
SQL
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'uses the column name instead of filter_key in SQL' do
|
286
|
+
expect(query.apply.to_sql).to eq(expected_sql)
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'returns records that partially match the column value' do
|
290
|
+
expect(query.apply).to include(matching_document)
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'returns multiple records that contain the column value' do
|
294
|
+
expect(query.apply).to include(another_match)
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'does not return records that do not contain the column value' do
|
298
|
+
expect(query.apply).not_to include(non_matching_document)
|
299
|
+
end
|
300
|
+
|
301
|
+
context 'with table qualification' do
|
302
|
+
let(:filter) { Kiroshi::Filter.new(:user_name, match: :like, table: :documents, column: :full_name) }
|
303
|
+
|
304
|
+
let(:expected_sql) do
|
305
|
+
<<~SQL.squish
|
306
|
+
SELECT "documents".* FROM "documents" WHERE ("documents"."full_name" LIKE '%John%')
|
307
|
+
SQL
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'generates SQL with proper table and column qualification' do
|
311
|
+
expect(query.apply.to_sql).to eq(expected_sql)
|
271
312
|
end
|
272
313
|
end
|
273
314
|
end
|
@@ -4,12 +4,12 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
RSpec.describe Kiroshi::FilterRunner, type: :model do
|
6
6
|
describe '#apply' do
|
7
|
-
subject(:runner) { described_class.new(filter: filter, scope: scope,
|
7
|
+
subject(:runner) { described_class.new(filter: filter, scope: scope, value: filter_value) }
|
8
8
|
|
9
9
|
let(:scope) { Document.all }
|
10
10
|
let(:filter_value) { 'test_value' }
|
11
|
-
let(:
|
12
|
-
let!(:matching_document) { create(:document, name:
|
11
|
+
let(:document_name) { filter_value }
|
12
|
+
let!(:matching_document) { create(:document, name: document_name) }
|
13
13
|
let!(:non_matching_document) { create(:document, name: 'other_value') }
|
14
14
|
|
15
15
|
context 'when filter match is :exact' do
|
@@ -39,7 +39,7 @@ RSpec.describe Kiroshi::FilterRunner, type: :model do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'generates correct SQL with table name prefix' do
|
42
|
-
expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE (documents
|
42
|
+
expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE (\"documents\".\"name\" LIKE '%test%')"
|
43
43
|
expect(runner.apply.to_sql).to eq(expected_sql)
|
44
44
|
end
|
45
45
|
end
|
@@ -57,8 +57,9 @@ RSpec.describe Kiroshi::FilterRunner, type: :model do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
context 'when filter value is not present' do
|
60
|
-
let(:
|
61
|
-
let(:
|
60
|
+
let(:document_name) { 'Some name' }
|
61
|
+
let(:filter) { Kiroshi::Filter.new(:name) }
|
62
|
+
let(:filter_value) { nil }
|
62
63
|
|
63
64
|
it 'returns the original scope unchanged' do
|
64
65
|
expect(runner.apply).to eq(scope)
|
@@ -66,45 +67,69 @@ RSpec.describe Kiroshi::FilterRunner, type: :model do
|
|
66
67
|
end
|
67
68
|
|
68
69
|
context 'when filter value is empty string' do
|
69
|
-
let(:
|
70
|
-
let(:
|
70
|
+
let(:document_name) { 'Some name' }
|
71
|
+
let(:filter) { Kiroshi::Filter.new(:name) }
|
72
|
+
let(:filter_value) { '' }
|
71
73
|
|
72
74
|
it 'returns the original scope unchanged' do
|
73
75
|
expect(runner.apply).to eq(scope)
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
|
-
context '
|
78
|
-
let(:filter)
|
79
|
-
let(:
|
79
|
+
context 'with status filter' do
|
80
|
+
let(:filter) { Kiroshi::Filter.new(:status, match: :exact) }
|
81
|
+
let(:filter_value) { 'finished' }
|
82
|
+
let!(:matching_document) { create(:document, name: 'test_name', status: 'finished') }
|
83
|
+
let!(:non_matching_document) { create(:document, name: 'other_name', status: 'processing') }
|
80
84
|
|
81
|
-
it '
|
82
|
-
expect(runner.apply).to
|
85
|
+
it 'filters by the configured attribute only returning the matched' do
|
86
|
+
expect(runner.apply).to include(matching_document)
|
83
87
|
end
|
84
|
-
end
|
85
|
-
|
86
|
-
context 'when filters hash is empty' do
|
87
|
-
let(:filter) { Kiroshi::Filter.new(:name) }
|
88
|
-
let(:filters) { {} }
|
89
88
|
|
90
|
-
it '
|
91
|
-
expect(runner.apply).
|
89
|
+
it 'does not return non-matching records' do
|
90
|
+
expect(runner.apply).not_to include(non_matching_document)
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
context '
|
96
|
-
let(:filter) { Kiroshi::Filter.new(:
|
97
|
-
let(:
|
98
|
-
let!(:matching_document) { create(:document, name: 'test_name', status: 'finished') }
|
99
|
-
let!(:non_matching_document) { create(:document, name: 'other_name', status: 'processing') }
|
94
|
+
context 'when Filter#column is different from filter_key' do
|
95
|
+
let(:filter) { Kiroshi::Filter.new(:user_name, match: :exact, column: :full_name) }
|
96
|
+
let(:filter_value) { 'John Doe' }
|
100
97
|
|
101
|
-
|
98
|
+
let!(:matching_document) { create(:document, full_name: 'John Doe') }
|
99
|
+
let!(:non_matching_document) { create(:document, full_name: 'Jane Smith') }
|
100
|
+
|
101
|
+
it 'filters using the column name instead of filter_key' do
|
102
102
|
expect(runner.apply).to include(matching_document)
|
103
103
|
end
|
104
104
|
|
105
105
|
it 'does not return non-matching records' do
|
106
106
|
expect(runner.apply).not_to include(non_matching_document)
|
107
107
|
end
|
108
|
+
|
109
|
+
it 'generates correct SQL using the column name' do
|
110
|
+
expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE \"documents\".\"full_name\" = 'John Doe'"
|
111
|
+
expect(runner.apply.to_sql).to eq(expected_sql)
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'with LIKE match' do
|
115
|
+
let(:filter) { Kiroshi::Filter.new(:user_name, match: :like, column: :full_name) }
|
116
|
+
let(:filter_value) { 'John' }
|
117
|
+
|
118
|
+
let!(:partial_match) { create(:document, full_name: 'Johnny Smith') }
|
119
|
+
|
120
|
+
it 'performs LIKE filtering using the column name' do
|
121
|
+
expect(runner.apply).to include(matching_document)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'includes partial matches using the column name' do
|
125
|
+
expect(runner.apply).to include(partial_match)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'generates correct LIKE SQL using the column name' do
|
129
|
+
expected_sql = "SELECT \"documents\".* FROM \"documents\" WHERE (\"documents\".\"full_name\" LIKE '%John%')"
|
130
|
+
expect(runner.apply.to_sql).to eq(expected_sql)
|
131
|
+
end
|
132
|
+
end
|
108
133
|
end
|
109
134
|
end
|
110
135
|
end
|
@@ -6,19 +6,18 @@ RSpec.describe Kiroshi::Filter, type: :model do
|
|
6
6
|
describe '#apply' do
|
7
7
|
let(:scope) { Document.all }
|
8
8
|
let(:filter_value) { 'test_value' }
|
9
|
-
let(:filters) { { name: filter_value } }
|
10
9
|
let!(:matching_document) { create(:document, name: filter_value) }
|
11
10
|
let!(:non_matching_document) { create(:document, name: 'other_value') }
|
12
11
|
|
13
12
|
context 'when match is :exact' do
|
14
13
|
subject(:filter) { described_class.new(:name, match: :exact) }
|
15
14
|
|
16
|
-
it 'returns
|
17
|
-
expect(filter.apply(scope,
|
15
|
+
it 'returns documents matching the filter' do
|
16
|
+
expect(filter.apply(scope: scope, value: filter_value)).to include(matching_document)
|
18
17
|
end
|
19
18
|
|
20
|
-
it 'does not return
|
21
|
-
expect(filter.apply(scope,
|
19
|
+
it 'does not return documents not matching the filter' do
|
20
|
+
expect(filter.apply(scope: scope, value: filter_value)).not_to include(non_matching_document)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
@@ -30,11 +29,11 @@ RSpec.describe Kiroshi::Filter, type: :model do
|
|
30
29
|
let!(:non_matching_document) { create(:document, name: 'other_value') }
|
31
30
|
|
32
31
|
it 'returns partial matches' do
|
33
|
-
expect(filter.apply(scope,
|
32
|
+
expect(filter.apply(scope: scope, value: filter_value)).to include(matching_document)
|
34
33
|
end
|
35
34
|
|
36
35
|
it 'does not return non-matching records' do
|
37
|
-
expect(filter.apply(scope,
|
36
|
+
expect(filter.apply(scope: scope, value: filter_value)).not_to include(non_matching_document)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
@@ -42,21 +41,19 @@ RSpec.describe Kiroshi::Filter, type: :model do
|
|
42
41
|
subject(:filter) { described_class.new(:name) }
|
43
42
|
|
44
43
|
it 'defaults to exact match returning only exact matches' do
|
45
|
-
expect(filter.apply(scope,
|
44
|
+
expect(filter.apply(scope: scope, value: filter_value)).to include(matching_document)
|
46
45
|
end
|
47
46
|
|
48
47
|
it 'defaults to exact match returning not returning when filtering by a non-matching value' do
|
49
|
-
expect(filter.apply(scope,
|
48
|
+
expect(filter.apply(scope: scope, value: filter_value)).not_to include(non_matching_document)
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
52
|
context 'when filter value is not present' do
|
54
53
|
subject(:filter) { described_class.new(:name) }
|
55
54
|
|
56
|
-
let(:filters) { { name: nil } }
|
57
|
-
|
58
55
|
it 'returns the original scope unchanged' do
|
59
|
-
expect(filter.apply(scope,
|
56
|
+
expect(filter.apply(scope: scope, value: nil)).to eq(scope)
|
60
57
|
end
|
61
58
|
end
|
62
59
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Kiroshi::Filters::ClassMethods, type: :model do
|
6
|
+
subject(:filters_class) { Class.new(Kiroshi::Filters) }
|
7
|
+
|
8
|
+
let(:filter_instance) { filters_class.new(filters) }
|
9
|
+
let(:scope) { Document.all }
|
10
|
+
let(:filters) { {} }
|
11
|
+
|
12
|
+
describe '.filter_by' do
|
13
|
+
let(:scope) { Document.all }
|
14
|
+
let(:filters) { { name: name } }
|
15
|
+
let(:name) { 'test_name' }
|
16
|
+
|
17
|
+
context 'when adding a new filter' do
|
18
|
+
it do
|
19
|
+
expect { filters_class.filter_by :name }
|
20
|
+
.to change { filter_instance.apply(scope) }
|
21
|
+
.from(scope).to(scope.where(name: name))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when adding a filter with table qualification' do
|
26
|
+
let(:scope) { Document.joins(:tags) }
|
27
|
+
|
28
|
+
it do
|
29
|
+
expect { filters_class.filter_by :name, table: :documents }
|
30
|
+
.to change { filter_instance.apply(scope) }
|
31
|
+
.from(scope).to(scope.where(documents: { name: name }))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when adding a filter with different table' do
|
36
|
+
let(:scope) { Document.joins(:tags) }
|
37
|
+
let(:filters) { { name: 'ruby' } }
|
38
|
+
let(:name) { 'ruby' }
|
39
|
+
|
40
|
+
it do
|
41
|
+
expect { filters_class.filter_by :name, table: :tags }
|
42
|
+
.to change { filter_instance.apply(scope) }
|
43
|
+
.from(scope).to(scope.where(tags: { name: name }))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when adding a like filter with table qualification' do
|
48
|
+
let(:scope) { Document.joins(:tags) }
|
49
|
+
let(:filters) { { name: 'test' } }
|
50
|
+
let(:name) { 'test' }
|
51
|
+
|
52
|
+
it do
|
53
|
+
expect { filters_class.filter_by :name, match: :like, table: :documents }
|
54
|
+
.to change { filter_instance.apply(scope) }
|
55
|
+
.from(scope).to(scope.where('"documents"."name" LIKE ?', '%test%'))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when column is different from filter_key' do
|
60
|
+
let(:filters) { { user_name: 'John Doe' } }
|
61
|
+
|
62
|
+
context 'with exact match' do
|
63
|
+
it do
|
64
|
+
expect { filters_class.filter_by :user_name, column: :full_name }
|
65
|
+
.to change { filter_instance.apply(scope) }
|
66
|
+
.from(scope).to(scope.where(full_name: 'John Doe'))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with like match' do
|
71
|
+
let(:filters) { { user_name: 'John' } }
|
72
|
+
|
73
|
+
it do
|
74
|
+
expect { filters_class.filter_by :user_name, match: :like, column: :full_name }
|
75
|
+
.to change { filter_instance.apply(scope) }
|
76
|
+
.from(scope).to(scope.where('"documents"."full_name" LIKE ?', '%John%'))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with table qualification and different column' do
|
81
|
+
let(:scope) { Document.joins(:tags) }
|
82
|
+
let(:filters) { { tag_identifier: 'ruby' } }
|
83
|
+
|
84
|
+
it do
|
85
|
+
expect { filters_class.filter_by :tag_identifier, table: :tags, column: :name }
|
86
|
+
.to change { filter_instance.apply(scope) }
|
87
|
+
.from(scope).to(scope.where(tags: { name: 'ruby' }))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'with like match, table qualification and different column' do
|
92
|
+
let(:scope) { Document.joins(:tags) }
|
93
|
+
let(:filters) { { tag_identifier: 'rub' } }
|
94
|
+
|
95
|
+
it do
|
96
|
+
expect { filters_class.filter_by :tag_identifier, match: :like, table: :tags, column: :name }
|
97
|
+
.to change { filter_instance.apply(scope) }
|
98
|
+
.from(scope).to(scope.where('"tags"."name" LIKE ?', '%rub%'))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|