sequel 5.11.0 → 5.12.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/CHANGELOG +32 -0
- data/doc/advanced_associations.rdoc +132 -14
- data/doc/postgresql.rdoc +14 -0
- data/doc/release_notes/5.12.0.txt +141 -0
- data/lib/sequel/adapters/ado/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +5 -6
- data/lib/sequel/adapters/postgres.rb +18 -5
- data/lib/sequel/adapters/shared/mysql.rb +5 -5
- data/lib/sequel/adapters/sqlite.rb +0 -5
- data/lib/sequel/adapters/tinytds.rb +0 -5
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +2 -5
- data/lib/sequel/core.rb +6 -1
- data/lib/sequel/dataset/graph.rb +25 -9
- data/lib/sequel/dataset/placeholder_literalizer.rb +47 -17
- data/lib/sequel/dataset/prepared_statements.rb +86 -18
- data/lib/sequel/dataset/sql.rb +5 -1
- data/lib/sequel/extensions/caller_logging.rb +79 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
- data/lib/sequel/model/associations.rb +56 -23
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
- data/lib/sequel/plugins/static_cache.rb +9 -8
- data/lib/sequel/plugins/tactical_eager_loading.rb +63 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/oracle_spec.rb +44 -0
- data/spec/adapters/postgres_spec.rb +39 -0
- data/spec/core/dataset_spec.rb +23 -9
- data/spec/core/object_graph_spec.rb +314 -284
- data/spec/extensions/caller_logging_spec.rb +52 -0
- data/spec/extensions/eager_graph_eager_spec.rb +100 -0
- data/spec/extensions/finder_spec.rb +1 -1
- data/spec/extensions/prepared_statements_spec.rb +7 -12
- data/spec/extensions/static_cache_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +262 -1
- data/spec/integration/associations_test.rb +72 -0
- data/spec/integration/dataset_test.rb +3 -3
- data/spec/model/eager_loading_spec.rb +90 -0
- metadata +8 -2
@@ -121,7 +121,7 @@ SQL
|
|
121
121
|
|
122
122
|
oid_map = {}
|
123
123
|
models.each do |model|
|
124
|
-
raise Error, "#{model.inspect} does not use the static_cache plugin" unless model.respond_to?(:load_cache
|
124
|
+
raise Error, "#{model.inspect} does not use the static_cache plugin" unless model.respond_to?(:load_cache)
|
125
125
|
oid_map[get(regclass_oid(model.dataset.first_source_table))] = model
|
126
126
|
end
|
127
127
|
|
@@ -129,7 +129,7 @@ SQL
|
|
129
129
|
begin
|
130
130
|
listen(opts[:channel_name]||default_static_cache_update_name, {:loop=>true}.merge!(opts)) do |_, _, oid|
|
131
131
|
if model = oid_map[oid.to_i]
|
132
|
-
model.
|
132
|
+
model.load_cache
|
133
133
|
end
|
134
134
|
end
|
135
135
|
ensure
|
@@ -633,10 +633,15 @@ module Sequel
|
|
633
633
|
ds = ds.eager(associations)
|
634
634
|
end
|
635
635
|
if block = eo[:eager_block]
|
636
|
+
orig_ds = ds
|
636
637
|
ds = block.call(ds)
|
637
638
|
end
|
638
639
|
if eager_loading_use_associated_key?
|
639
|
-
ds = ds.
|
640
|
+
ds = if ds.opts[:eager_graph] && !orig_ds.opts[:eager_graph]
|
641
|
+
block.call(orig_ds.select_append(*associated_key_array))
|
642
|
+
else
|
643
|
+
ds.select_append(*associated_key_array)
|
644
|
+
end
|
640
645
|
end
|
641
646
|
if self[:eager_graph]
|
642
647
|
raise(Error, "cannot eagerly load a #{self[:type]} association that uses :eager_graph") if eager_loading_use_associated_key?
|
@@ -2949,7 +2954,7 @@ module Sequel
|
|
2949
2954
|
# Replace the array of plain hashes with an array of model objects will all eager_graphed
|
2950
2955
|
# associations set in the associations cache for each object.
|
2951
2956
|
def eager_graph_build_associations(hashes)
|
2952
|
-
hashes.replace(
|
2957
|
+
hashes.replace(_eager_graph_build_associations(hashes, eager_graph_loader))
|
2953
2958
|
end
|
2954
2959
|
|
2955
2960
|
private
|
@@ -2960,6 +2965,12 @@ module Sequel
|
|
2960
2965
|
clone(:join=>clone(:graph_from_self=>false).eager_graph_with_options(associations, :join_type=>type, :join_only=>true).opts[:join])
|
2961
2966
|
end
|
2962
2967
|
|
2968
|
+
# Process the array of hashes using the eager graph loader to return an array
|
2969
|
+
# of model objects with the associations set.
|
2970
|
+
def _eager_graph_build_associations(hashes, egl)
|
2971
|
+
egl.load(hashes)
|
2972
|
+
end
|
2973
|
+
|
2963
2974
|
# If the association has conditions itself, then it requires additional filters be
|
2964
2975
|
# added to the current dataset to ensure that the passed in object would also be
|
2965
2976
|
# included by the association's conditions.
|
@@ -3051,6 +3062,14 @@ module Sequel
|
|
3051
3062
|
end
|
3052
3063
|
end
|
3053
3064
|
|
3065
|
+
# The EagerGraphLoader instance used for converting eager_graph results.
|
3066
|
+
def eager_graph_loader
|
3067
|
+
unless egl = cache_get(:_model_eager_graph_loader)
|
3068
|
+
egl = cache_set(:_model_eager_graph_loader, EagerGraphLoader.new(self))
|
3069
|
+
end
|
3070
|
+
egl.dup
|
3071
|
+
end
|
3072
|
+
|
3054
3073
|
# Eagerly load all specified associations
|
3055
3074
|
def eager_load(a, eager_assoc=@opts[:eager])
|
3056
3075
|
return if a.empty?
|
@@ -3238,12 +3257,15 @@ module Sequel
|
|
3238
3257
|
:offset
|
3239
3258
|
end
|
3240
3259
|
end
|
3260
|
+
after_load_map.freeze
|
3261
|
+
alias_map.freeze
|
3262
|
+
type_map.freeze
|
3241
3263
|
|
3242
3264
|
# Make dependency map hash out of requirements array for each association.
|
3243
3265
|
# This builds a tree of dependencies that will be used for recursion
|
3244
3266
|
# to ensure that all parts of the object graph are loaded into the
|
3245
3267
|
# appropriate subordinate association.
|
3246
|
-
@dependency_map = {}
|
3268
|
+
dependency_map = @dependency_map = {}
|
3247
3269
|
# Sort the associations by requirements length, so that
|
3248
3270
|
# requirements are added to the dependency hash before their
|
3249
3271
|
# dependencies.
|
@@ -3259,18 +3281,12 @@ module Sequel
|
|
3259
3281
|
hash[ta] = {}
|
3260
3282
|
end
|
3261
3283
|
end
|
3284
|
+
freezer = lambda do |h|
|
3285
|
+
h.freeze
|
3286
|
+
h.each_value(&freezer)
|
3287
|
+
end
|
3288
|
+
freezer.call(dependency_map)
|
3262
3289
|
|
3263
|
-
# This mapping is used to make sure that duplicate entries in the
|
3264
|
-
# result set are mapped to a single record. For example, using a
|
3265
|
-
# single one_to_many association with 10 associated records,
|
3266
|
-
# the main object column values appear in the object graph 10 times.
|
3267
|
-
# We map by primary key, if available, or by the object's entire values,
|
3268
|
-
# if not. The mapping must be per table, so create sub maps for each table
|
3269
|
-
# alias.
|
3270
|
-
records_map = {@master=>{}}
|
3271
|
-
alias_map.keys.each{|ta| records_map[ta] = {}}
|
3272
|
-
@records_map = records_map
|
3273
|
-
|
3274
3290
|
datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
|
3275
3291
|
column_aliases = opts[:graph][:column_aliases]
|
3276
3292
|
primary_keys = {}
|
@@ -3296,9 +3312,9 @@ module Sequel
|
|
3296
3312
|
h.select{|ca, c| primary_keys[ta] = ca if pk == c}
|
3297
3313
|
end
|
3298
3314
|
end
|
3299
|
-
@column_maps = column_maps
|
3300
|
-
@primary_keys = primary_keys
|
3301
|
-
@row_procs = row_procs
|
3315
|
+
@column_maps = column_maps.freeze
|
3316
|
+
@primary_keys = primary_keys.freeze
|
3317
|
+
@row_procs = row_procs.freeze
|
3302
3318
|
|
3303
3319
|
# For performance, create two special maps for the master table,
|
3304
3320
|
# so you can skip a hash lookup.
|
@@ -3310,22 +3326,35 @@ module Sequel
|
|
3310
3326
|
# used for performance, to get all values in one hash lookup instead of
|
3311
3327
|
# separate hash lookups for each data structure.
|
3312
3328
|
ta_map = {}
|
3313
|
-
alias_map.
|
3314
|
-
ta_map[ta] = [
|
3329
|
+
alias_map.each_key do |ta|
|
3330
|
+
ta_map[ta] = [row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]].freeze
|
3315
3331
|
end
|
3316
|
-
@ta_map = ta_map
|
3332
|
+
@ta_map = ta_map.freeze
|
3333
|
+
freeze
|
3317
3334
|
end
|
3318
3335
|
|
3319
3336
|
# Return an array of primary model instances with the associations cache prepopulated
|
3320
3337
|
# for all model objects (both primary and associated).
|
3321
3338
|
def load(hashes)
|
3339
|
+
# This mapping is used to make sure that duplicate entries in the
|
3340
|
+
# result set are mapped to a single record. For example, using a
|
3341
|
+
# single one_to_many association with 10 associated records,
|
3342
|
+
# the main object column values appear in the object graph 10 times.
|
3343
|
+
# We map by primary key, if available, or by the object's entire values,
|
3344
|
+
# if not. The mapping must be per table, so create sub maps for each table
|
3345
|
+
# alias.
|
3346
|
+
@records_map = records_map = {}
|
3347
|
+
alias_map.keys.each{|ta| records_map[ta] = {}}
|
3348
|
+
|
3322
3349
|
master = master()
|
3323
3350
|
|
3324
3351
|
# Assign to local variables for speed increase
|
3325
3352
|
rp = row_procs[master]
|
3326
|
-
rm = records_map[master]
|
3353
|
+
rm = records_map[master] = {}
|
3327
3354
|
dm = dependency_map
|
3328
3355
|
|
3356
|
+
records_map.freeze
|
3357
|
+
|
3329
3358
|
# This will hold the final record set that we will be replacing the object graph with.
|
3330
3359
|
records = []
|
3331
3360
|
|
@@ -3346,6 +3375,9 @@ module Sequel
|
|
3346
3375
|
# Run after_load procs if there are any
|
3347
3376
|
post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty?
|
3348
3377
|
|
3378
|
+
records_map.each_value(&:freeze)
|
3379
|
+
freeze
|
3380
|
+
|
3349
3381
|
records
|
3350
3382
|
end
|
3351
3383
|
|
@@ -3365,13 +3397,14 @@ module Sequel
|
|
3365
3397
|
end
|
3366
3398
|
key = hkey(ta_h)
|
3367
3399
|
end
|
3368
|
-
|
3400
|
+
rp, assoc_name, tm, rcm = @ta_map[ta]
|
3401
|
+
rm = records_map[ta]
|
3369
3402
|
|
3370
3403
|
# Check type map for all dependencies, and use a unique
|
3371
3404
|
# object if any are dependencies for multiple objects,
|
3372
3405
|
# to prevent duplicate objects from showing up in the case
|
3373
3406
|
# the normal duplicate removal code is not being used.
|
3374
|
-
if !@unique && !deps.empty? && deps.any?{|dep_key,_| @ta_map[dep_key][
|
3407
|
+
if !@unique && !deps.empty? && deps.any?{|dep_key,_| @ta_map[dep_key][2]}
|
3375
3408
|
key = [current.object_id, key]
|
3376
3409
|
end
|
3377
3410
|
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1415,9 +1415,9 @@ module Sequel
|
|
1415
1415
|
# is valid and before hooks execute successfully. Fails if:
|
1416
1416
|
#
|
1417
1417
|
# * the record is not valid, or
|
1418
|
-
# * before_save
|
1419
|
-
# * the record is new and before_create
|
1420
|
-
# * the record is not new and before_update
|
1418
|
+
# * before_save calls cancel_action, or
|
1419
|
+
# * the record is new and before_create calls cancel_action, or
|
1420
|
+
# * the record is not new and before_update calls cancel_action.
|
1421
1421
|
#
|
1422
1422
|
# If +save+ fails and either raise_on_save_failure or the
|
1423
1423
|
# :raise_on_failure option is true, it raises ValidationFailed
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The eager_graph_eager plugin allows for chaining eager loads after eager_graph
|
6
|
+
# loads. Given the following model associations:
|
7
|
+
#
|
8
|
+
# Band.one_to_many :albums
|
9
|
+
# Album.one_to_many :tracks
|
10
|
+
#
|
11
|
+
# Let's say you wanted to return bands ordered by album name, and eagerly load
|
12
|
+
# those albums, you can do that using:
|
13
|
+
#
|
14
|
+
# Band.eager_graph(:albums).order{albums[:name]}
|
15
|
+
#
|
16
|
+
# Let's say you also wanted to eagerly load the tracks for each album. You could
|
17
|
+
# just add them to the eager_graph call:
|
18
|
+
#
|
19
|
+
# Band.eager_graph(albums: :tracks).order{albums[:name]}
|
20
|
+
#
|
21
|
+
# However, the bloats the result set, and you aren't ordering by the track
|
22
|
+
# information, so a join is not required. The eager_graph_eager plugin allows
|
23
|
+
# you to specify that the tracks be eagerly loaded in a separate query after
|
24
|
+
# the eager_graph load of albums:
|
25
|
+
#
|
26
|
+
# Band.eager_graph(:albums).eager_graph_eager([:albums], :tracks).order{albums[:name]}
|
27
|
+
#
|
28
|
+
# <tt>Dataset#eager_graph_eager</tt>'s first argument is a dependency chain, specified
|
29
|
+
# as an array of symbols. This specifies the point at which to perform the eager load.
|
30
|
+
# The remaining arguments are arguments that could be passed to Dataset#eager to specify
|
31
|
+
# what dependent associations should be loaded at that point.
|
32
|
+
#
|
33
|
+
# If you also have the following model association:
|
34
|
+
#
|
35
|
+
# Track.one_to_many :lyrics
|
36
|
+
#
|
37
|
+
# Here's some different ways of performing eager loading:
|
38
|
+
#
|
39
|
+
# # 4 Queries: bands, albums, tracks, lyrics
|
40
|
+
# Band.eager(albums: {tracks: :lyrics})
|
41
|
+
#
|
42
|
+
# # 1 Query: bands+albums+tracks+lyrics
|
43
|
+
# Band.eager_graph(albums: {tracks: :lyrics})
|
44
|
+
#
|
45
|
+
# # 3 Queries: bands+albums, tracks, lyrics
|
46
|
+
# Band.eager_graph(:albums).eager_graph_eager([:albums], tracks: :lyrics)
|
47
|
+
#
|
48
|
+
# # 2 Queries: bands+albums+tracks, lyrics
|
49
|
+
# Band.eager_graph(albums: :tracks).eager_graph_eager([:albums, :tracks], :lyrics)
|
50
|
+
#
|
51
|
+
# # 2 Queries: bands+albums, tracks+lyrics
|
52
|
+
# Band.eager_graph(:albums).eager_graph_eager([:albums], tracks: proc{|ds| ds.eager_graph(:lyrics)})
|
53
|
+
#
|
54
|
+
# Usage:
|
55
|
+
#
|
56
|
+
# # Support eager_graph_eager in all subclass datasets (called before loading subclasses)
|
57
|
+
# Sequel::Model.plugin :eager_graph_eager
|
58
|
+
#
|
59
|
+
# # Support eager_graph_eager in Album class datasets
|
60
|
+
# Album.plugin :eager_graph_eager
|
61
|
+
module EagerGraphEager
|
62
|
+
module DatasetMethods
|
63
|
+
# Specify for the given dependency chain, after loading objects for the
|
64
|
+
# current dataset via eager_graph, eagerly load the given associations at that point in the
|
65
|
+
# dependency chain.
|
66
|
+
#
|
67
|
+
# dependency_chain :: Array of association symbols, with the first association symbol
|
68
|
+
# specifying an association in the dataset's model, the next
|
69
|
+
# association specifying an association in the previous association's
|
70
|
+
# associated model, and so on.
|
71
|
+
# assocs :: Symbols or hashes specifying associations to eagerly load at the point
|
72
|
+
# specified by the dependency chain.
|
73
|
+
def eager_graph_eager(dependency_chain, *assocs)
|
74
|
+
unless dependency_chain.is_a?(Array) && dependency_chain.all?{|s| s.is_a?(Symbol)} && !dependency_chain.empty?
|
75
|
+
raise Error, "eager_graph_eager first argument must be array of symbols"
|
76
|
+
end
|
77
|
+
|
78
|
+
current = model
|
79
|
+
deps = dependency_chain.map do |dep|
|
80
|
+
unless ref = current.association_reflection(dep)
|
81
|
+
raise Error, "invalid association #{dep.inspect} for #{current.inspect}"
|
82
|
+
end
|
83
|
+
current = ref.associated_class
|
84
|
+
[dep, ref.returns_array?]
|
85
|
+
end
|
86
|
+
assocs = current.dataset.send(:eager_options_for_associations, assocs)
|
87
|
+
|
88
|
+
deps.each(&:freeze)
|
89
|
+
deps.unshift(current)
|
90
|
+
deps.freeze
|
91
|
+
|
92
|
+
assocs.freeze
|
93
|
+
|
94
|
+
if h = @opts[:eager_graph_eager]
|
95
|
+
h = Hash[h]
|
96
|
+
h[deps] = assocs
|
97
|
+
else
|
98
|
+
h = {deps => assocs}
|
99
|
+
end
|
100
|
+
|
101
|
+
clone(:eager_graph_eager=>h.freeze)
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
# After building objects from the rows, if eager_graph_eager has been
|
107
|
+
# called on the datasets, for each dependency chain specified, eagerly
|
108
|
+
# load the appropriate associations.
|
109
|
+
def eager_graph_build_associations(rows)
|
110
|
+
objects = super
|
111
|
+
|
112
|
+
if eager_data = @opts[:eager_graph_eager]
|
113
|
+
eager_data.each do |deps, assocs|
|
114
|
+
current = objects
|
115
|
+
|
116
|
+
last_class, *deps = deps
|
117
|
+
deps.each do |dep, is_multiple|
|
118
|
+
current_assocs = current.map(&:associations)
|
119
|
+
|
120
|
+
if is_multiple
|
121
|
+
current = current_assocs.flat_map{|a| a[dep]}
|
122
|
+
else
|
123
|
+
current = current_assocs.map{|a| a[dep]}
|
124
|
+
current.compact!
|
125
|
+
end
|
126
|
+
|
127
|
+
current.uniq!(&:object_id)
|
128
|
+
end
|
129
|
+
|
130
|
+
last_class.dataset.send(:eager_load, current, assocs)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
objects
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -55,6 +55,7 @@ module Sequel
|
|
55
55
|
# Now if you +#dup+ a Model object (the resulting object is not frozen), you
|
56
56
|
# will be able to update and save the duplicate.
|
57
57
|
# Note the caveats around your responsibility to update the cache still applies.
|
58
|
+
# You can update the cache via `.load_cache` method.
|
58
59
|
module StaticCache
|
59
60
|
# Populate the static caches when loading the plugin. Options:
|
60
61
|
# :frozen :: Whether retrieved model objects are frozen. The default is true,
|
@@ -209,14 +210,6 @@ module Sequel
|
|
209
210
|
!@static_cache_frozen
|
210
211
|
end
|
211
212
|
|
212
|
-
private
|
213
|
-
|
214
|
-
# Return the frozen object with the given pk, or nil if no such object exists
|
215
|
-
# in the cache, without issuing a database query.
|
216
|
-
def primary_key_lookup(pk)
|
217
|
-
static_cache_object(cache[pk])
|
218
|
-
end
|
219
|
-
|
220
213
|
# Reload the cache for this model by retrieving all of the instances in the dataset
|
221
214
|
# freezing them, and populating the cached array and hash.
|
222
215
|
def load_cache
|
@@ -230,6 +223,14 @@ module Sequel
|
|
230
223
|
@cache = h.freeze
|
231
224
|
end
|
232
225
|
|
226
|
+
private
|
227
|
+
|
228
|
+
# Return the frozen object with the given pk, or nil if no such object exists
|
229
|
+
# in the cache, without issuing a database query.
|
230
|
+
def primary_key_lookup(pk)
|
231
|
+
static_cache_object(cache[pk])
|
232
|
+
end
|
233
|
+
|
233
234
|
# If frozen: false is not used, just return the argument. Otherwise,
|
234
235
|
# create a new instance with the arguments values if the argument is
|
235
236
|
# not nil.
|
@@ -60,6 +60,46 @@ module Sequel
|
|
60
60
|
# # SELECT * FROM artists WHERE name > 'N' AND id IN (...)
|
61
61
|
# albums.first.artists(eager: lambda{|ds| ds.where(Sequel[:name] > 'N')})
|
62
62
|
#
|
63
|
+
# The tactical_eager_loading plugin also allows transparent eager
|
64
|
+
# loading when calling association methods on associated objects
|
65
|
+
# eagerly loaded via Dataset#eager_graph. This can reduce N queries
|
66
|
+
# to a single query when iterating over all associated objects.
|
67
|
+
# Consider the following code:
|
68
|
+
#
|
69
|
+
# artists = Artist.eager_graph(:albums).all
|
70
|
+
# artists.each do |artist|
|
71
|
+
# artist.albums.each do |album|
|
72
|
+
# album.tracks
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# By default this will issue a single query to load the artists and
|
77
|
+
# albums, and then one query for each album to load the tracks for
|
78
|
+
# the album:
|
79
|
+
#
|
80
|
+
# # SELECT artists.id, ...
|
81
|
+
# albums.id, ...
|
82
|
+
# # FROM artists
|
83
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id);
|
84
|
+
# # SELECT * FROM tracks WHERE album_id = 1;
|
85
|
+
# # SELECT * FROM tracks WHERE album_id = 2;
|
86
|
+
# # SELECT * FROM tracks WHERE album_id = 10;
|
87
|
+
# # ...
|
88
|
+
#
|
89
|
+
# With the tactical_eager_loading plugin, this uses the same
|
90
|
+
# query to load the artists and albums, but then issues a single query
|
91
|
+
# to load the tracks for all albums.
|
92
|
+
#
|
93
|
+
# # SELECT artists.id, ...
|
94
|
+
# albums.id, ...
|
95
|
+
# # FROM artists
|
96
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id);
|
97
|
+
# # SELECT * FROM tracks WHERE (tracks.album_id IN (1, 2, 10, ...));
|
98
|
+
#
|
99
|
+
# Note that transparent eager loading for associated objects
|
100
|
+
# loaded by eager_graph will only take place if the associated classes
|
101
|
+
# also use the tactical_eager_loading plugin.
|
102
|
+
#
|
63
103
|
# Usage:
|
64
104
|
#
|
65
105
|
# # Make all model subclass instances use tactical eager loading (called before loading subclasses)
|
@@ -112,7 +152,29 @@ module Sequel
|
|
112
152
|
module DatasetMethods
|
113
153
|
private
|
114
154
|
|
115
|
-
# Set the retrieved_with and retrieved_by attributes for the
|
155
|
+
# Set the retrieved_with and retrieved_by attributes for each of the associated objects
|
156
|
+
# created by the eager graph loader with the appropriate class dataset and array of objects.
|
157
|
+
def _eager_graph_build_associations(_, egl)
|
158
|
+
objects = super
|
159
|
+
|
160
|
+
master = egl.master
|
161
|
+
egl.records_map.each do |k, v|
|
162
|
+
next if k == master || v.empty?
|
163
|
+
|
164
|
+
by = opts[:graph][:table_aliases][k]
|
165
|
+
values = v.values
|
166
|
+
|
167
|
+
values.each do |o|
|
168
|
+
next unless o.is_a?(TacticalEagerLoading::InstanceMethods) && !o.retrieved_by
|
169
|
+
o.retrieved_by = by
|
170
|
+
o.retrieved_with = values
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
objects
|
175
|
+
end
|
176
|
+
|
177
|
+
# Set the retrieved_with and retrieved_by attributes for each object
|
116
178
|
# with the current dataset and array of all objects.
|
117
179
|
def post_load(objects)
|
118
180
|
super
|