deferring 0.6.2 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b06d2014bec77285df7d494e04b1aceae86e4ce9b147f0916002db02e1e79cba
4
- data.tar.gz: d89af52998b5d67de9f29cf971a5f68f1ce9339e340f3170c9c0cbd6c4df1713
3
+ metadata.gz: 35b9b80d6c94a8b980f80cd953565bcf7cf2f8a207de3880904286e617ed30a0
4
+ data.tar.gz: f78c5caa4d9c1a5a446ac504c839bebf21e29a059e57e5739de4ef5842106b63
5
5
  SHA512:
6
- metadata.gz: 3fd88c4d8a0c1be57f6b4ac674faf39bc1a152117a86d719aa6e2727cd93b916d547404e66c5e67f636aeb209918a0dcbcf78db256704ff3e45c0f9951c592ca
7
- data.tar.gz: 15ac8fa0e41d23af97a1ac35ac0e29c429e1287bc83fa6cf7148437070a57a6bd8a36fa49910bc5d8ee4288d2ca20c091ac7289b63b210e2c13d4db4997453a8
6
+ metadata.gz: af8785f36ce8551ba95a9f7dab9d28fdfc8cd46bc6a427bddc70007985225ce3edd23daf1c1cdd21f5102d1c3bef0d2a0ae2598752b0fa38318225235db8a9e4
7
+ data.tar.gz: 549c445412012809bdbe613dd028395acc22ef4dc5e26fc803fc2db5734a2f7b26fc4c134693c6fae5147e6bb7d83d7b4afde8eed25719ef57ea47af3232c4d5
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- deferring (0.6.1)
4
+ deferring (0.7.0)
5
5
  activerecord (>= 4.2)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- deferring (0.6.1)
4
+ deferring (0.7.0)
5
5
  activerecord (>= 4.2)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- deferring (0.6.1)
4
+ deferring (0.7.0)
5
5
  activerecord (>= 4.2)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- deferring (0.6.1)
4
+ deferring (0.7.0)
5
5
  activerecord (>= 4.2)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- deferring (0.6.1)
4
+ deferring (0.7.0)
5
5
  activerecord (>= 4.2)
6
6
 
7
7
  GEM
@@ -26,14 +26,14 @@ module Deferring
26
26
  alias_method :pretty_inspect, :inspect
27
27
 
28
28
  delegate :to_s, :to_a, :inspect, :==, # methods undefined by SimpleDelegator
29
- :is_a?, :as_json, to: :objects
29
+ :as_json, to: :objects
30
30
 
31
31
  def each(&block)
32
32
  objects.each(&block)
33
33
  end
34
34
 
35
35
  # TODO: Add explanation about :first/:last loaded? problem.
36
- [:first, :last].each do |method|
36
+ [:first, :last, :empty?, :size].each do |method|
37
37
  define_method method do
38
38
  unless objects_loaded?
39
39
  original_association.send(method)
@@ -46,7 +46,7 @@ module Deferring
46
46
  # Delegates methods from Ruby's Array module to the object in the deferred
47
47
  # association.
48
48
  delegate :[]=, :[], :clear, :select!, :reject!, :flatten, :flatten!, :sort!,
49
- :keep_if, :delete_if, :sort_by!, :empty?, :size, :length,
49
+ :keep_if, :delete_if, :sort_by!, :length,
50
50
  :each_index,
51
51
  to: :objects
52
52
 
@@ -58,6 +58,14 @@ module Deferring
58
58
  original_association.find(*args)
59
59
  end
60
60
 
61
+ # Delegates Ruby's Enumerable#count method to the original association.
62
+ #
63
+ # The delegation has to be explicit in this case, because the inclusion of
64
+ # Enumerable also defines the count-method on DeferredAssociation.
65
+ def count(*args)
66
+ original_association.count(*args)
67
+ end
68
+
61
69
  # Delegates Ruby's Enumerable#select method to the original association when
62
70
  # no block has been given. Rails' select-method does not accept a block, so
63
71
  # we know that in that case the select-method has to be called on our
@@ -1,3 +1,3 @@
1
1
  module Deferring
2
- VERSION = '0.6.2'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -359,6 +359,58 @@ RSpec.describe 'deferred has_many associations' do
359
359
  people = Person.all
360
360
  expect(people[1].issues.loaded?).to be_falsey
361
361
  end
362
+
363
+ describe 'count' do
364
+ it 'should not load the association' do
365
+ person = Person.where(name: 'Bob').first
366
+ expect(person.issues.count).to eq(2)
367
+ expect(person.issues.loaded?).to be_falsey
368
+ end
369
+
370
+ it 'should execute a count(*) query, even if the association has already been loaded' do
371
+ person = Person.where(name: 'Bob').first
372
+ expect { person.issues.count }.to query(1) { |sql| expect(sql).to include('SELECT COUNT(*) FROM "issues"') }
373
+ expect { person.issues.to_a }.to query(1)
374
+ expect(person.issues.loaded?).to be_truthy
375
+ expect { person.issues.to_a }.to query(0)
376
+ expect { person.issues.count }.to query(1) { |sql| expect(sql).to include('SELECT COUNT(*) FROM "issues"') }
377
+ end
378
+ end
379
+
380
+ describe 'size' do
381
+ it 'should not load the association' do
382
+ person = Person.where(name: 'Bob').first
383
+ expect(person.issues.size).to eq(2)
384
+ expect { person.issues.size }.to query(1) { |sql| expect(sql).to include('SELECT COUNT(*) FROM "issues"') }
385
+ expect(person.issues.loaded?).to be_falsey
386
+ end
387
+
388
+ it 'should not execute query when the association has already been loaded' do
389
+ person = Person.where(name: 'Bob').first
390
+ expect { person.issues.size }.to query(1) { |sql| expect(sql).to include('SELECT COUNT(*) FROM "issues"') }
391
+ expect { person.issues.to_a }.to query(1)
392
+ expect(person.issues.loaded?).to be_truthy
393
+ expect { person.issues.to_a }.to query(0)
394
+ expect { person.issues.size }.to query(0)
395
+ end
396
+ end
397
+
398
+ describe 'empty?' do
399
+ it 'should not load the association' do
400
+ person = Person.where(name: 'Bob').first
401
+ expect(person.issues.empty?).to be_falsey
402
+ expect(person.issues.loaded?).to be_falsey
403
+ end
404
+
405
+ it 'should not execute query when the association has already been loaded' do
406
+ person = Person.where(name: 'Bob').first
407
+ expect { person.issues.empty? }.to query(1) { |sql| expect(sql).to include('SELECT 1') }
408
+ expect { person.issues.to_a }.to query(1)
409
+ expect(person.issues.loaded?).to be_truthy
410
+ expect { person.issues.to_a }.to query(0)
411
+ expect { person.issues.empty? }.to query(0)
412
+ end
413
+ end
362
414
  end # preloading associations
363
415
 
364
416
  describe 'active record api' do
@@ -9,7 +9,6 @@ RSpec.describe 'deferred accepts_nested_attributes' do
9
9
  Team.create!(name: 'Database Administration')
10
10
  Team.create!(name: 'End-User Support')
11
11
  Team.create!(name: 'Operations')
12
-
13
12
  end
14
13
 
15
14
  let(:bob) { Person.where(name: 'Bob').first }
@@ -48,21 +47,4 @@ RSpec.describe 'deferred accepts_nested_attributes' do
48
47
  expect(p.team_ids.sort).to eq([1])
49
48
  end
50
49
 
51
- it '' do
52
- issue = Issue.create!(subject: 'Foo', person: bob)
53
-
54
- p = Person.where(name: 'Alice').take
55
- p.attributes = {
56
- issues_attributes: [
57
- { id: issue.id },
58
- ]
59
- }
60
- p.save!
61
-
62
- p.reload
63
- expect(p.issues.size).to eq(1)
64
-
65
- bob.reload
66
- expect(bob.issues.size).to eq(0)
67
- end
68
50
  end
@@ -5,6 +5,7 @@ require 'support/models/team'
5
5
  require 'support/models/issue'
6
6
  require 'support/models/address'
7
7
  require 'support/models/non_validated_issue'
8
+ require 'support/matchers/query_matcher'
8
9
 
9
10
  RSpec.configure do |config|
10
11
  config.disable_monkey_patching!
@@ -18,6 +19,8 @@ RSpec.configure do |config|
18
19
  # --seed 1234
19
20
  config.order = 'random'
20
21
 
22
+ config.include(Deferring::Matchers)
23
+
21
24
  # Rollback all the database changes after each spec, poor man's
22
25
  # DatabaseCleaner :-)
23
26
  config.around do |example|
@@ -0,0 +1,127 @@
1
+ module Deferring
2
+ module Matchers
3
+
4
+ class ArQuery #:nodoc:
5
+ cattr_accessor :executed
6
+
7
+ @@recording_queries = false
8
+ def self.recording_queries?
9
+ @@recording_queries
10
+ end
11
+
12
+ def initialize(expected, &block)
13
+ @expected = expected
14
+ @block = block
15
+ end
16
+
17
+ def matches?(given_proc)
18
+ @eval_block = false
19
+ @eval_error = nil
20
+ ArQuery.executed = []
21
+ @@recording_queries = true
22
+
23
+ given_proc.call
24
+
25
+ if @expected.is_a?(Integer)
26
+ @actual = ArQuery.executed.length
27
+ @matched = @actual == @expected
28
+ else
29
+ @actual = ArQuery.executed.detect { |sql| @expected === sql }
30
+ @matched = !@actual.nil?
31
+ end
32
+
33
+ eval_block if @block && @matched && !negative_expectation?
34
+
35
+ ensure
36
+ ArQuery.executed = nil
37
+ @@recording_queries = false
38
+ return @matched && @eval_error.nil?
39
+ end
40
+
41
+ def eval_block
42
+ @eval_block = true
43
+ begin
44
+ @block[ArQuery.executed]
45
+ rescue => err
46
+ @eval_error = err
47
+ end
48
+ end
49
+
50
+ def supports_block_expectations?
51
+ true
52
+ end
53
+
54
+ def failure_message
55
+ if @eval_error
56
+ @eval_error.message
57
+ elsif @expected.is_a?(Integer)
58
+ "expected #{@expected}, got #{@actual}"
59
+ else
60
+ "expected to execute a query with pattern #{@expected.inspect}, but it wasn't"
61
+ end
62
+ end
63
+
64
+ def failure_message_when_negated
65
+ if @expected.is_a?(Integer)
66
+ "did not expect #{@expected}"
67
+ else
68
+ "did not expect to execute a query with pattern #{@expected.inspect}, but it was executed"
69
+ end
70
+ end
71
+
72
+ def description
73
+ if @expected.is_a?(Integer)
74
+ @expected == 1 ? 'execute 1 query' : "execute #{@expected} queries"
75
+ else
76
+ "execute query with pattern #{@expected.inspect}"
77
+ end
78
+ end
79
+
80
+ def negative_expectation?
81
+ @negative_expectation ||= !caller.first(3).find { |s| s =~ /should_not/ }.nil?
82
+ end
83
+ end
84
+
85
+ def query(expected = 1, &block)
86
+ ArQuery.new(expected, &block)
87
+ end
88
+ end
89
+ end
90
+
91
+ # For Rails 6.0
92
+ module ActiveRecord
93
+ module ConnectionAdapters
94
+ module SQLite3
95
+ module DatabaseStatements
96
+ [:exec_query, :exec, :execute].each do |method|
97
+ if respond_to?(method)
98
+ define_method("#{method}_with_query_record") do |sql, *args|
99
+ Deferring::Matchers::ArQuery.executed << sql if Deferring::Matchers::ArQuery.recording_queries?
100
+ send("#{method}_without_query_record", sql, *args)
101
+ end
102
+
103
+ alias_method :"#{method}_without_query_record", method
104
+ alias_method method, :"#{method}_with_query_record"
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ # For previous versions of Rails
113
+ module ActiveRecord
114
+ module ConnectionAdapters
115
+ class SQLite3Adapter
116
+ [:exec_query, :exec, :execute].each do |method|
117
+ define_method("#{method}_with_query_record") do |sql, *args|
118
+ Deferring::Matchers::ArQuery.executed << sql if Deferring::Matchers::ArQuery.recording_queries?
119
+ send("#{method}_without_query_record", sql, *args)
120
+ end
121
+
122
+ alias_method :"#{method}_without_query_record", method
123
+ alias_method method, :"#{method}_with_query_record"
124
+ end
125
+ end
126
+ end
127
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deferring
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robin Roestenburg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-08 00:00:00.000000000 Z
11
+ date: 2020-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -116,6 +116,7 @@ files:
116
116
  - spec/lib/deferring_nested_attributes_spec.rb
117
117
  - spec/spec_helper.rb
118
118
  - spec/support/active_record.rb
119
+ - spec/support/matchers/query_matcher.rb
119
120
  - spec/support/models/address.rb
120
121
  - spec/support/models/issue.rb
121
122
  - spec/support/models/non_validated_issue.rb
@@ -151,6 +152,7 @@ test_files:
151
152
  - spec/lib/deferring_nested_attributes_spec.rb
152
153
  - spec/spec_helper.rb
153
154
  - spec/support/active_record.rb
155
+ - spec/support/matchers/query_matcher.rb
154
156
  - spec/support/models/address.rb
155
157
  - spec/support/models/issue.rb
156
158
  - spec/support/models/non_validated_issue.rb