sequel-association-filtering 0.0.2 → 0.0.3
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b96fa93edf655b190d3d510e871ab7666218719f0c07e8c5034d47cab3b47c0
|
4
|
+
data.tar.gz: 65f1b9fc8f9ed8d0c9fd499eb5bb32477857bd1c4fb9ad0b1e7185ba44fb0a51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0642b74414f320c1152478c25cf34694c152b1f15c96204fc1e17929df7b6671e60fe31f46367cedac198df3a59b7fe11cb117602e7937c4f7bf5d8864a179f
|
7
|
+
data.tar.gz: d70a6cc39248e252a46d7d0c14dd31bbbbd5838531267a0a5d1e8c6b7746b039c4e0e4da440aced09b204c02faba98b6185b6ffbcd8bb2f3d11f67e132ca7894
|
@@ -7,30 +7,47 @@ module Sequel
|
|
7
7
|
class Error < StandardError; end
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
-
Plugins.def_dataset_methods(
|
10
|
+
Plugins.def_dataset_methods(
|
11
|
+
self,
|
12
|
+
[
|
13
|
+
:association_filter,
|
14
|
+
:association_exclude,
|
15
|
+
]
|
16
|
+
)
|
11
17
|
end
|
12
18
|
|
13
19
|
module DatasetMethods
|
14
|
-
def association_filter(association_name)
|
20
|
+
def association_filter(association_name, invert: false)
|
15
21
|
reflection =
|
16
22
|
model.association_reflections.fetch(association_name) do
|
17
23
|
raise Error, "association #{association_name} not found on model #{model}"
|
18
24
|
end
|
19
25
|
|
20
26
|
if block_given?
|
21
|
-
|
22
|
-
where(
|
27
|
+
cond = yield(_association_filter_dataset(reflection)).exists
|
28
|
+
invert ? exclude(cond) : where(cond)
|
23
29
|
else
|
24
|
-
|
25
|
-
|
30
|
+
cache_key =
|
31
|
+
_association_filter_cache_key(
|
32
|
+
reflection: reflection,
|
33
|
+
extra: :"bare_#{invert}",
|
34
|
+
)
|
35
|
+
|
36
|
+
cached_dataset(cache_key) do
|
37
|
+
cond = _association_filter_dataset(reflection).exists
|
38
|
+
invert ? exclude(cond) : where(cond)
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|
29
42
|
|
43
|
+
def association_exclude(association_name, &block)
|
44
|
+
association_filter(association_name, invert: true, &block)
|
45
|
+
end
|
46
|
+
|
30
47
|
private
|
31
48
|
|
32
49
|
def _association_filter_dataset(reflection)
|
33
|
-
cache_key = _association_filter_cache_key(reflection)
|
50
|
+
cache_key = _association_filter_cache_key(reflection: reflection)
|
34
51
|
|
35
52
|
ds = reflection.associated_dataset
|
36
53
|
|
@@ -61,8 +78,8 @@ module Sequel
|
|
61
78
|
end
|
62
79
|
end
|
63
80
|
|
64
|
-
def _association_filter_cache_key(reflection
|
65
|
-
:"_association_filter_#{reflection[:model]}_#{reflection[:name]}_#{
|
81
|
+
def _association_filter_cache_key(reflection:, extra: nil)
|
82
|
+
:"_association_filter_#{reflection[:model]}_#{reflection[:name]}_#{extra}"
|
66
83
|
end
|
67
84
|
end
|
68
85
|
end
|
data/spec/many_to_many_spec.rb
CHANGED
@@ -70,4 +70,30 @@ class ManyToManySpec < AssociationFilteringSpecs
|
|
70
70
|
assert_includes record.genres.map(&:id), 5
|
71
71
|
end
|
72
72
|
end
|
73
|
+
|
74
|
+
describe "association_exclude through a many_to_many_association" do
|
75
|
+
it "should support an empty filter that checks for existence" do
|
76
|
+
expected_count = 100 - DB[:album_genres].distinct(:album_id).count
|
77
|
+
|
78
|
+
ds = Album.association_exclude(:genres)
|
79
|
+
assert_equal %(SELECT * FROM "albums" WHERE NOT (EXISTS (SELECT 1 FROM "genres" INNER JOIN "album_genres" ON ("album_genres"."genre_id" = "genres"."id") WHERE ("album_genres"."album_id" = "albums"."id")))), ds.sql
|
80
|
+
assert_equal expected_count, ds.count
|
81
|
+
|
82
|
+
album_id_to_delete = DB[:album_genres].order_by{random.function}.get(:album_id)
|
83
|
+
|
84
|
+
DB[:album_genres].where(album_id: album_id_to_delete).delete
|
85
|
+
assert_equal expected_count + 1, ds.count
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should support a simple filter" do
|
89
|
+
expected_count = 100 - DB[:album_genres].where(genre_id: 5).distinct(:album_id).count
|
90
|
+
|
91
|
+
ds = Album.association_exclude(:genres){|t| t.where(Sequel[:genres][:id] =~ 5)}
|
92
|
+
assert_equal expected_count, ds.count
|
93
|
+
assert_equal %(SELECT * FROM \"albums\" WHERE NOT (EXISTS (SELECT 1 FROM "genres" INNER JOIN "album_genres" ON ("album_genres"."genre_id" = "genres"."id") WHERE (("album_genres"."album_id" = "albums"."id") AND ("genres"."id" = 5))))), ds.sql
|
94
|
+
|
95
|
+
record = ds.first!
|
96
|
+
refute_includes record.genres.map(&:id), 5
|
97
|
+
end
|
98
|
+
end
|
73
99
|
end
|
data/spec/many_to_one_spec.rb
CHANGED
@@ -45,4 +45,15 @@ class ManyToOneSpec < AssociationFilteringSpecs
|
|
45
45
|
assert_includes [5, 10], record.artist_id
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
describe "association_exclude through a many_to_one association" do
|
50
|
+
it "should support a simple filter" do
|
51
|
+
ds = Album.association_exclude(:artist){|a| a.where{mod(id, 5) =~ 0}}
|
52
|
+
assert_equal 80, ds.count
|
53
|
+
assert_equal %(SELECT * FROM "albums" WHERE NOT (EXISTS (SELECT 1 FROM "artists" WHERE (("artists"."id" = "albums"."artist_id") AND (mod("id", 5) = 0)) LIMIT 1))), ds.sql
|
54
|
+
|
55
|
+
record = ds.order_by{random.function}.first
|
56
|
+
refute_includes [5, 10], record.artist_id
|
57
|
+
end
|
58
|
+
end
|
48
59
|
end
|
data/spec/one_to_many_spec.rb
CHANGED
@@ -129,5 +129,26 @@ class OneToManySpec < AssociationFilteringSpecs
|
|
129
129
|
assert_equal 99, ds.count
|
130
130
|
end
|
131
131
|
end
|
132
|
+
|
133
|
+
describe "association_exclude through a one_to_many association" do
|
134
|
+
it "should support a simple filter" do
|
135
|
+
ds = Artist.association_exclude(:albums){|t| t.where(serial_column: 40)}
|
136
|
+
|
137
|
+
assert_equal 99, ds.count
|
138
|
+
assert_equal %(SELECT * FROM "artists" WHERE NOT (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."id_1" = "artists"."id_1") AND ("albums"."id_2" = "artists"."id_2") AND ("serial_column" = 40))))), ds.sql
|
139
|
+
|
140
|
+
artists = ds.all
|
141
|
+
refute_includes artists.flat_map(&:albums).map(&:serial_column), 40
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should support an empty filter that checks for existence" do
|
145
|
+
ds = Artist.association_exclude(:albums)
|
146
|
+
assert_equal 0, ds.count
|
147
|
+
assert_equal %(SELECT * FROM "artists" WHERE NOT (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."id_1" = "artists"."id_1") AND ("albums"."id_2" = "artists"."id_2"))))), ds.sql
|
148
|
+
|
149
|
+
Album.where(id_1: 5, id_2: 6).delete
|
150
|
+
assert_equal 1, ds.count
|
151
|
+
end
|
152
|
+
end
|
132
153
|
end
|
133
154
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-association-filtering
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hanks
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|