sequel-association-filtering 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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