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 +4 -4
- data/gemfiles/rails_42.gemfile.lock +1 -1
- data/gemfiles/rails_50.gemfile.lock +1 -1
- data/gemfiles/rails_51.gemfile.lock +1 -1
- data/gemfiles/rails_52.gemfile.lock +1 -1
- data/gemfiles/rails_60.gemfile.lock +1 -1
- data/lib/deferring/deferred_association.rb +11 -3
- data/lib/deferring/version.rb +1 -1
- data/spec/lib/deferring_has_many_spec.rb +52 -0
- data/spec/lib/deferring_nested_attributes_spec.rb +0 -18
- data/spec/spec_helper.rb +3 -0
- data/spec/support/matchers/query_matcher.rb +127 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35b9b80d6c94a8b980f80cd953565bcf7cf2f8a207de3880904286e617ed30a0
|
4
|
+
data.tar.gz: f78c5caa4d9c1a5a446ac504c839bebf21e29a059e57e5739de4ef5842106b63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af8785f36ce8551ba95a9f7dab9d28fdfc8cd46bc6a427bddc70007985225ce3edd23daf1c1cdd21f5102d1c3bef0d2a0ae2598752b0fa38318225235db8a9e4
|
7
|
+
data.tar.gz: 549c445412012809bdbe613dd028395acc22ef4dc5e26fc803fc2db5734a2f7b26fc4c134693c6fae5147e6bb7d83d7b4afde8eed25719ef57ea47af3232c4d5
|
@@ -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
|
-
:
|
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!, :
|
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
|
data/lib/deferring/version.rb
CHANGED
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -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.
|
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-
|
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
|