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