sequel-association-filtering 0.0.5 → 0.0.6
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: 04015d9861a0451c0612228e83590c6b503d39b57d5821a12bb624997710bdee
|
|
4
|
+
data.tar.gz: 79b9cb458dd9e10953e2d711d85706311dd76e27d7c30577d07019c2943d2dad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 80bdf2c8f73d0a705c14b133b477a5cff075d81f576871fa1fb50d43336149c91d2e0d1e2a189b8acc68d7471c6cff924f1de13a604448fe750ffc622f6119b9
|
|
7
|
+
data.tar.gz: e33f89d68b1425958ea34941d9eae7c5a0b245b02a0c154b47f80e2c8621eebbb15e7937c4cb9f6eb20e664c179c79bd8d5b97e70dd3cdc1ef847d60afb76611
|
|
@@ -24,15 +24,17 @@ module Sequel
|
|
|
24
24
|
def association_filter(
|
|
25
25
|
association_name,
|
|
26
26
|
invert: false,
|
|
27
|
-
|
|
28
|
-
at_most: nil,
|
|
29
|
-
exactly: nil
|
|
27
|
+
**extra
|
|
30
28
|
)
|
|
31
|
-
|
|
29
|
+
having_args = extra.slice(:at_least, :at_most, :exactly)
|
|
30
|
+
|
|
31
|
+
case having_args.length
|
|
32
32
|
when 0
|
|
33
|
-
|
|
33
|
+
# No-op
|
|
34
34
|
when 1
|
|
35
|
-
|
|
35
|
+
having_arg = having_args.keys[0]
|
|
36
|
+
having_value = having_args.values[0]
|
|
37
|
+
raise Error, ":#{having_arg} must be an Integer if present" unless having_value.is_a?(Integer)
|
|
36
38
|
else
|
|
37
39
|
raise Error, "cannot pass more than one of :at_least, :at_most, and :exactly"
|
|
38
40
|
end
|
|
@@ -42,24 +44,30 @@ module Sequel
|
|
|
42
44
|
raise Error, "association #{association_name} not found on model #{model}"
|
|
43
45
|
end
|
|
44
46
|
|
|
45
|
-
ds = _association_filter_dataset(reflection, group_by_remote:
|
|
47
|
+
ds = _association_filter_dataset(reflection, group_by_remote: !!having_arg)
|
|
46
48
|
ds = yield(ds) if block_given?
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
when at_most then COUNT_STAR <= at_most
|
|
54
|
-
when exactly then COUNT_STAR =~ exactly
|
|
55
|
-
else raise Error, ""
|
|
56
|
-
end
|
|
57
|
-
)
|
|
58
|
-
end
|
|
50
|
+
cache_key =
|
|
51
|
+
_association_filter_cache_key(
|
|
52
|
+
reflection: reflection,
|
|
53
|
+
extra: :"#{invert}_#{having_arg}_#{having_value}",
|
|
54
|
+
)
|
|
59
55
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
ds.send :cached_dataset, cache_key do
|
|
57
|
+
having_condition =
|
|
58
|
+
case having_arg
|
|
59
|
+
when :at_least then COUNT_STAR >= having_value
|
|
60
|
+
when :at_most then COUNT_STAR <= having_value
|
|
61
|
+
when :exactly then COUNT_STAR =~ having_value
|
|
62
|
+
when nil then nil
|
|
63
|
+
else raise Error, "Unexpected argument: #{having_arg.inspect}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
ds = ds.having(having_condition) if having_condition
|
|
67
|
+
cond = ds.exists
|
|
68
|
+
cond = Sequel.~(cond) if invert
|
|
69
|
+
where(cond)
|
|
70
|
+
end
|
|
63
71
|
end
|
|
64
72
|
|
|
65
73
|
def association_exclude(association_name, &block)
|
|
@@ -72,7 +80,7 @@ module Sequel
|
|
|
72
80
|
cache_key =
|
|
73
81
|
_association_filter_cache_key(
|
|
74
82
|
reflection: reflection,
|
|
75
|
-
extra:
|
|
83
|
+
extra: :"association_#{group_by_remote}"
|
|
76
84
|
)
|
|
77
85
|
|
|
78
86
|
ds = reflection.associated_dataset
|
|
@@ -103,11 +111,8 @@ module Sequel
|
|
|
103
111
|
inject{|a,b| Sequel.&(a, b)}
|
|
104
112
|
).select(1)
|
|
105
113
|
|
|
106
|
-
if group_by_remote
|
|
107
|
-
|
|
108
|
-
else
|
|
109
|
-
result
|
|
110
|
-
end
|
|
114
|
+
result = result.group_by(*remote_keys) if group_by_remote
|
|
115
|
+
result
|
|
111
116
|
end
|
|
112
117
|
end
|
|
113
118
|
|
data/spec/basic_spec.rb
CHANGED
|
@@ -4,20 +4,43 @@ require 'spec_helper'
|
|
|
4
4
|
|
|
5
5
|
class BasicSpec < AssociationFilteringSpecs
|
|
6
6
|
before do
|
|
7
|
+
drop_tables
|
|
8
|
+
|
|
7
9
|
DB.create_table :artists do
|
|
8
10
|
primary_key :id
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
DB.create_table :albums do
|
|
14
|
+
primary_key :id
|
|
15
|
+
foreign_key :artist_id, :artists
|
|
16
|
+
end
|
|
17
|
+
|
|
11
18
|
DB.run <<-SQL
|
|
12
19
|
INSERT INTO artists SELECT i FROM generate_series(1, 10) i;
|
|
20
|
+
INSERT INTO albums (artist_id) SELECT (i % 10) + 1 FROM generate_series(1, 100) i;
|
|
13
21
|
SQL
|
|
14
22
|
|
|
15
23
|
class Artist < Sequel::Model
|
|
24
|
+
one_to_many :albums, class: 'BasicSpec::Album'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Album < Sequel::Model
|
|
28
|
+
many_to_one :artist, class: 'BasicSpec::Artist'
|
|
29
|
+
|
|
30
|
+
dataset_module do
|
|
31
|
+
subset :even_id, Sequel.lit('id % ? = 0', 2)
|
|
32
|
+
end
|
|
16
33
|
end
|
|
17
34
|
end
|
|
18
35
|
|
|
19
36
|
after do
|
|
20
|
-
|
|
37
|
+
BasicSpec.send(:remove_const, :Artist)
|
|
38
|
+
BasicSpec.send(:remove_const, :Album)
|
|
39
|
+
drop_tables
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def drop_tables
|
|
43
|
+
DB.drop_table? :albums, :artists
|
|
21
44
|
end
|
|
22
45
|
|
|
23
46
|
describe "association_filter" do
|
|
@@ -31,12 +54,62 @@ class BasicSpec < AssociationFilteringSpecs
|
|
|
31
54
|
end
|
|
32
55
|
|
|
33
56
|
it "with more than one of at_least, at_most, or exactly should throw an error" do
|
|
57
|
+
a, b = [:at_most, :exactly, :at_least].sample(2)
|
|
58
|
+
|
|
34
59
|
error =
|
|
35
60
|
assert_raises(Sequel::Plugins::AssociationFiltering::Error) do
|
|
36
|
-
Artist.association_filter(:widgets,
|
|
61
|
+
Artist.association_filter(:widgets, a => 4, b => 5)
|
|
37
62
|
end
|
|
38
63
|
|
|
39
64
|
assert_equal "cannot pass more than one of :at_least, :at_most, and :exactly", error.message
|
|
40
65
|
end
|
|
66
|
+
|
|
67
|
+
it "with an at_least/at_most/exactly that is not an integer should raise an error" do
|
|
68
|
+
a = [:at_most, :exactly, :at_least].sample
|
|
69
|
+
|
|
70
|
+
error =
|
|
71
|
+
assert_raises(Sequel::Plugins::AssociationFiltering::Error) do
|
|
72
|
+
Artist.association_filter(:widgets, a => Object.new)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
assert_equal ":#{a} must be an Integer if present", error.message
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe "cached datasets" do
|
|
79
|
+
let :seen_object_ids do
|
|
80
|
+
Set.new
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def assert_cached(*args, &block)
|
|
84
|
+
ds1, ds2 = Array.new(2, Artist.association_filter(:albums, *args, &block))
|
|
85
|
+
assert_equal ds1.object_id, ds2.object_id, ds1.sql
|
|
86
|
+
assert seen_object_ids.add?(ds1.object_id), ds1.sql
|
|
87
|
+
|
|
88
|
+
ds1, ds2 = Array.new(2, Artist.association_exclude(:albums, *args, &block))
|
|
89
|
+
assert_equal ds1.object_id, ds2.object_id, ds1.sql
|
|
90
|
+
assert seen_object_ids.add?(ds1.object_id), ds1.sql
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def refute_cached(&block)
|
|
94
|
+
ds1, ds2 = Array.new(2, &block)
|
|
95
|
+
refute_equal ds1.object_id, ds2.object_id, ds1.sql
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should be able to return a cached dataset" do
|
|
99
|
+
assert_cached { Artist.association_filter(:albums) }
|
|
100
|
+
assert_cached { Artist.association_filter(:albums, &:even_id) }
|
|
101
|
+
|
|
102
|
+
assert_cached { Artist.association_filter(:albums, at_least: 2) }
|
|
103
|
+
assert_cached { Artist.association_filter(:albums, at_least: 3) }
|
|
104
|
+
assert_cached { Artist.association_filter(:albums, exactly: 2) }
|
|
105
|
+
assert_cached { Artist.association_filter(:albums, at_most: 2) }
|
|
106
|
+
|
|
107
|
+
assert_cached { Artist.association_filter(:albums, at_least: 2, &:even_id) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should not cache potentially dynamic datasets" do
|
|
111
|
+
refute_cached { Artist.association_filter(:albums){|a| a.where(artist_id: 2)} }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
41
114
|
end
|
|
42
115
|
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.6
|
|
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-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|