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: eebfe24a7a4bc0831e9b5d983f7d49c46f1af66b531b137cbad11d65195c02dd
4
- data.tar.gz: 812a28ac539eab752717576a44cd63f4e95d306c9ceeb3830f044227ec6c09db
3
+ metadata.gz: 04015d9861a0451c0612228e83590c6b503d39b57d5821a12bb624997710bdee
4
+ data.tar.gz: 79b9cb458dd9e10953e2d711d85706311dd76e27d7c30577d07019c2943d2dad
5
5
  SHA512:
6
- metadata.gz: d87c8e29c99c922be6411c7f74589b3685781502a38131f7f832465a334ab62cbee3125ba0fe4c844b98d496d1c0ef49cba6091c1f96c0268a0b46bc75f665d6
7
- data.tar.gz: 00155ca49d9f3f01d8b4cf7385f81bb8e72e0acb8ec2db436ee3ae74468cb606cab343ac52e62120a65b4f5c129eaa3cb3a1ea6212fdd9f30520372cc3173087
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
- at_least: nil,
28
- at_most: nil,
29
- exactly: nil
27
+ **extra
30
28
  )
31
- case [at_least, at_most, exactly].compact.length
29
+ having_args = extra.slice(:at_least, :at_most, :exactly)
30
+
31
+ case having_args.length
32
32
  when 0
33
- filtering_by_count = false
33
+ # No-op
34
34
  when 1
35
- filtering_by_count = true
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: filtering_by_count)
47
+ ds = _association_filter_dataset(reflection, group_by_remote: !!having_arg)
46
48
  ds = yield(ds) if block_given?
47
49
 
48
- if filtering_by_count
49
- ds =
50
- ds.having(
51
- case
52
- when at_least then COUNT_STAR >= at_least
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
- cond = ds.exists
61
- cond = Sequel.~(cond) if invert
62
- where(cond)
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: (:group_by_remote if group_by_remote)
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
- result.group_by(*remote_keys)
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
 
@@ -3,7 +3,7 @@
3
3
  module Sequel
4
4
  module Plugins
5
5
  module AssociationFiltering
6
- VERSION = '0.0.5'
6
+ VERSION = '0.0.6'
7
7
  end
8
8
  end
9
9
  end
@@ -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
- DB.drop_table? :artists
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, at_most: 4, exactly: 5)
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.5
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-05-29 00:00:00.000000000 Z
11
+ date: 2018-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler