sequel 5.41.0 → 5.46.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 +46 -0
- data/README.rdoc +1 -2
- data/doc/association_basics.rdoc +22 -3
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/testing.rdoc +3 -0
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado.rb +16 -16
- data/lib/sequel/adapters/odbc.rb +5 -1
- data/lib/sequel/adapters/shared/postgres.rb +0 -12
- data/lib/sequel/adapters/shared/sqlite.rb +8 -4
- data/lib/sequel/core.rb +11 -0
- data/lib/sequel/database/misc.rb +1 -2
- data/lib/sequel/database/schema_generator.rb +35 -47
- data/lib/sequel/database/schema_methods.rb +4 -0
- data/lib/sequel/dataset/query.rb +1 -3
- data/lib/sequel/dataset/sql.rb +7 -0
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/date_arithmetic.rb +29 -16
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/model/associations.rb +146 -75
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +2 -1
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/json_serializer.rb +37 -22
- data/lib/sequel/plugins/nested_attributes.rb +5 -2
- data/lib/sequel/plugins/pg_array_associations.rb +52 -38
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/serialization.rb +8 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +500 -0
- data/lib/sequel/version.rb +1 -1
- metadata +19 -3
@@ -8,9 +8,10 @@
|
|
8
8
|
# DB.extension :date_arithmetic
|
9
9
|
#
|
10
10
|
# Then you can use the Sequel.date_add and Sequel.date_sub methods
|
11
|
-
# to return Sequel expressions
|
11
|
+
# to return Sequel expressions (this example shows the only supported
|
12
|
+
# keys for the second argument):
|
12
13
|
#
|
13
|
-
# add = Sequel.date_add(:date_column, years: 1, months: 2, days:
|
14
|
+
# add = Sequel.date_add(:date_column, years: 1, months: 2, weeks: 2, days: 1)
|
14
15
|
# sub = Sequel.date_sub(:date_column, hours: 1, minutes: 2, seconds: 3)
|
15
16
|
#
|
16
17
|
# In addition to specifying the interval as a hash, there is also
|
@@ -54,7 +55,6 @@ module Sequel
|
|
54
55
|
end
|
55
56
|
parts = {}
|
56
57
|
interval.each{|k,v| parts[k] = -v unless v.nil?}
|
57
|
-
parts
|
58
58
|
DateAdd.new(expr, parts, opts)
|
59
59
|
end
|
60
60
|
end
|
@@ -185,22 +185,35 @@ module Sequel
|
|
185
185
|
# ActiveSupport::Duration :: Converted to a hash using the interval's parts.
|
186
186
|
def initialize(expr, interval, opts=OPTS)
|
187
187
|
@expr = expr
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
188
|
+
|
189
|
+
h = Hash.new(0)
|
190
|
+
interval = interval.parts unless interval.is_a?(Hash)
|
191
|
+
interval.each do |unit, value|
|
192
|
+
# skip nil values
|
193
|
+
next unless value
|
194
|
+
|
195
|
+
# Convert weeks to days, as ActiveSupport::Duration can use weeks,
|
196
|
+
# but the database-specific literalizers only support days.
|
197
|
+
if unit == :weeks
|
198
|
+
unit = :days
|
199
|
+
value *= 7
|
200
|
+
end
|
201
|
+
|
202
|
+
unless DatasetMethods::DURATION_UNITS.include?(unit)
|
203
|
+
raise Sequel::Error, "Invalid key used in DateAdd interval hash: #{unit.inspect}"
|
195
204
|
end
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
205
|
+
|
206
|
+
# Attempt to prevent SQL injection by users who pass untrusted strings
|
207
|
+
# as interval values. It doesn't make sense to support literal strings,
|
208
|
+
# due to the numeric adding below.
|
209
|
+
if value.is_a?(String)
|
210
|
+
raise Sequel::InvalidValue, "cannot provide String value as interval part: #{value.inspect}"
|
211
|
+
end
|
212
|
+
|
213
|
+
h[unit] += value
|
201
214
|
end
|
202
215
|
|
203
|
-
@interval.freeze
|
216
|
+
@interval = Hash[h].freeze
|
204
217
|
@cast_type = opts[:cast] if opts[:cast]
|
205
218
|
freeze
|
206
219
|
end
|
@@ -12,7 +12,9 @@
|
|
12
12
|
#
|
13
13
|
# How accurate this count is depends on the number of rows
|
14
14
|
# added/deleted from the table since the last time it was
|
15
|
-
# analyzed.
|
15
|
+
# analyzed. If the table has not been vacuumed or analyzed
|
16
|
+
# yet, this can return 0 or -1 depending on the PostgreSQL
|
17
|
+
# version in use.
|
16
18
|
#
|
17
19
|
# To load the extension into the database:
|
18
20
|
#
|
@@ -263,7 +263,9 @@ module Sequel
|
|
263
263
|
# yielding each row to the block.
|
264
264
|
def eager_load_results(eo, &block)
|
265
265
|
rows = eo[:rows]
|
266
|
-
|
266
|
+
unless eo[:initialize_rows] == false
|
267
|
+
Sequel.synchronize_with(eo[:mutex]){initialize_association_cache(rows)}
|
268
|
+
end
|
267
269
|
if eo[:id_map]
|
268
270
|
ids = eo[:id_map].keys
|
269
271
|
return ids if ids.empty?
|
@@ -311,7 +313,8 @@ module Sequel
|
|
311
313
|
objects = loader.all(ids)
|
312
314
|
end
|
313
315
|
|
314
|
-
objects.each(&block)
|
316
|
+
Sequel.synchronize_with(eo[:mutex]){objects.each(&block)}
|
317
|
+
|
315
318
|
if strategy == :ruby
|
316
319
|
apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
|
317
320
|
end
|
@@ -1592,6 +1595,7 @@ module Sequel
|
|
1592
1595
|
# === Multiple Types
|
1593
1596
|
# :adder :: Proc used to define the private _add_* method for doing the database work
|
1594
1597
|
# to associate the given object to the current object (*_to_many assocations).
|
1598
|
+
# Set to nil to not define a add_* method for the association.
|
1595
1599
|
# :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1596
1600
|
# after a new item is added to the association.
|
1597
1601
|
# :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1620,6 +1624,7 @@ module Sequel
|
|
1620
1624
|
# the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
|
1621
1625
|
# :clearer :: Proc used to define the private _remove_all_* method for doing the database work
|
1622
1626
|
# to remove all objects associated to the current object (*_to_many assocations).
|
1627
|
+
# Set to nil to not define a remove_all_* method for the association.
|
1623
1628
|
# :clone :: Merge the current options and block into the options and block used in defining
|
1624
1629
|
# the given association. Can be used to DRY up a bunch of similar associations that
|
1625
1630
|
# all share the same options such as :class and :key, while changing the order and block used.
|
@@ -1674,7 +1679,7 @@ module Sequel
|
|
1674
1679
|
# :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
|
1675
1680
|
# the association via +eager_graph+, instead of the default conditions specified by the
|
1676
1681
|
# foreign/primary keys. This option causes the :graph_conditions option to be ignored.
|
1677
|
-
# :graph_order ::
|
1682
|
+
# :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
|
1678
1683
|
# in the case where :order contains an identifier qualified by the table's name, which may not match
|
1679
1684
|
# the alias used when eager graphing. By setting this to the unqualified identifier, it will be
|
1680
1685
|
# automatically qualified when using eager_graph.
|
@@ -1686,6 +1691,10 @@ module Sequel
|
|
1686
1691
|
# limit (first element) and an offset (second element).
|
1687
1692
|
# :methods_module :: The module that methods the association creates will be placed into. Defaults
|
1688
1693
|
# to the module containing the model's columns.
|
1694
|
+
# :no_association_method :: Do not add a method for the association. This can save memory if the association
|
1695
|
+
# method is never used.
|
1696
|
+
# :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
|
1697
|
+
# method is never used.
|
1689
1698
|
# :order :: the column(s) by which to order the association dataset. Can be a
|
1690
1699
|
# singular column symbol or an array of column symbols.
|
1691
1700
|
# :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
|
@@ -1698,6 +1707,7 @@ module Sequel
|
|
1698
1707
|
# the current association's key(s). Set to nil to not use a reciprocal.
|
1699
1708
|
# :remover :: Proc used to define the private _remove_* method for doing the database work
|
1700
1709
|
# to remove the association between the given object and the current object (*_to_many assocations).
|
1710
|
+
# Set to nil to not define a remove_* method for the association.
|
1701
1711
|
# :select :: the columns to select. Defaults to the associated class's table_name.* in an association
|
1702
1712
|
# that uses joins, which means it doesn't include the attributes from the
|
1703
1713
|
# join table. If you want to include the join table attributes, you can
|
@@ -1706,6 +1716,7 @@ module Sequel
|
|
1706
1716
|
# the same name in both the join table and the associated table.
|
1707
1717
|
# :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
|
1708
1718
|
# between the given object and the current object (*_to_one associations).
|
1719
|
+
# Set to nil to not define a setter method for the association.
|
1709
1720
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1710
1721
|
# loading limited associations using the default :union strategy.
|
1711
1722
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
@@ -1838,8 +1849,7 @@ module Sequel
|
|
1838
1849
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
1839
1850
|
opts.delete(:class) unless opts[:class]
|
1840
1851
|
|
1841
|
-
|
1842
|
-
def_association_instance_methods(opts)
|
1852
|
+
def_association(opts)
|
1843
1853
|
|
1844
1854
|
orig_opts.delete(:clone)
|
1845
1855
|
opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
|
@@ -1953,6 +1963,13 @@ module Sequel
|
|
1953
1963
|
association_module(opts).send(:private, name)
|
1954
1964
|
end
|
1955
1965
|
|
1966
|
+
# Delegate to the type-specific association method to setup the
|
1967
|
+
# association, and define the association instance methods.
|
1968
|
+
def def_association(opts)
|
1969
|
+
send(:"def_#{opts[:type]}", opts)
|
1970
|
+
def_association_instance_methods(opts)
|
1971
|
+
end
|
1972
|
+
|
1956
1973
|
# Adds the association method to the association methods module.
|
1957
1974
|
def def_association_method(opts)
|
1958
1975
|
association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
|
@@ -1978,13 +1995,13 @@ module Sequel
|
|
1978
1995
|
opts[:setter_method] = :"#{opts[:name]}="
|
1979
1996
|
end
|
1980
1997
|
|
1981
|
-
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
1998
|
+
association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
|
1982
1999
|
if opts[:block]
|
1983
2000
|
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1984
2001
|
end
|
1985
2002
|
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1986
2003
|
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1987
|
-
def_association_method(opts)
|
2004
|
+
def_association_method(opts) unless opts[:no_association_method]
|
1988
2005
|
|
1989
2006
|
return if opts[:read_only]
|
1990
2007
|
|
@@ -2072,50 +2089,60 @@ module Sequel
|
|
2072
2089
|
return if opts[:read_only]
|
2073
2090
|
|
2074
2091
|
if one_through_one
|
2075
|
-
opts
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2092
|
+
unless opts.has_key?(:setter)
|
2093
|
+
opts[:setter] = proc do |o|
|
2094
|
+
h = {}
|
2095
|
+
lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
|
2096
|
+
jtds = _join_table_dataset(opts).where(lh)
|
2079
2097
|
|
2080
|
-
|
2081
|
-
|
2098
|
+
checked_transaction do
|
2099
|
+
current = jtds.first
|
2082
2100
|
|
2083
|
-
if o
|
2084
|
-
new_values = []
|
2085
|
-
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2086
|
-
end
|
2087
|
-
|
2088
|
-
if current
|
2089
|
-
current_values = rcks.map{|k| current[k]}
|
2090
|
-
jtds = jtds.where(rcks.zip(current_values))
|
2091
2101
|
if o
|
2092
|
-
|
2093
|
-
|
2102
|
+
new_values = []
|
2103
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2104
|
+
end
|
2105
|
+
|
2106
|
+
if current
|
2107
|
+
current_values = rcks.map{|k| current[k]}
|
2108
|
+
jtds = jtds.where(rcks.zip(current_values))
|
2109
|
+
if o
|
2110
|
+
if current_values != new_values
|
2111
|
+
jtds.update(h)
|
2112
|
+
end
|
2113
|
+
else
|
2114
|
+
jtds.delete
|
2094
2115
|
end
|
2095
|
-
|
2096
|
-
|
2116
|
+
elsif o
|
2117
|
+
lh.each{|k,v| h[k] = v}
|
2118
|
+
jtds.insert(h)
|
2097
2119
|
end
|
2098
|
-
elsif o
|
2099
|
-
lh.each{|k,v| h[k] = v}
|
2100
|
-
jtds.insert(h)
|
2101
2120
|
end
|
2102
2121
|
end
|
2103
2122
|
end
|
2104
|
-
opts
|
2123
|
+
if opts.fetch(:setter, true)
|
2124
|
+
opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
|
2125
|
+
end
|
2105
2126
|
else
|
2106
|
-
opts
|
2107
|
-
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2127
|
+
unless opts.has_key?(:adder)
|
2128
|
+
opts[:adder] = proc do |o|
|
2129
|
+
h = {}
|
2130
|
+
lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
|
2131
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
|
2132
|
+
_join_table_dataset(opts).insert(h)
|
2133
|
+
end
|
2111
2134
|
end
|
2112
2135
|
|
2113
|
-
opts
|
2114
|
-
|
2136
|
+
unless opts.has_key?(:remover)
|
2137
|
+
opts[:remover] = proc do |o|
|
2138
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)}) + rcks.zip(opts.right_primary_key_methods.map{|k| o.get_column_value(k)})).delete
|
2139
|
+
end
|
2115
2140
|
end
|
2116
2141
|
|
2117
|
-
opts
|
2118
|
-
|
2142
|
+
unless opts.has_key?(:clearer)
|
2143
|
+
opts[:clearer] = proc do
|
2144
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
|
2145
|
+
end
|
2119
2146
|
end
|
2120
2147
|
end
|
2121
2148
|
end
|
@@ -2172,8 +2199,12 @@ module Sequel
|
|
2172
2199
|
|
2173
2200
|
return if opts[:read_only]
|
2174
2201
|
|
2175
|
-
|
2176
|
-
|
2202
|
+
unless opts.has_key?(:setter)
|
2203
|
+
opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
|
2204
|
+
end
|
2205
|
+
if opts.fetch(:setter, true)
|
2206
|
+
opts[:_setter] = proc{|o| set_associated_object(opts, o)}
|
2207
|
+
end
|
2177
2208
|
end
|
2178
2209
|
|
2179
2210
|
# Configures one_to_many and one_to_one association reflections and adds the related association methods
|
@@ -2240,49 +2271,59 @@ module Sequel
|
|
2240
2271
|
cks.each{|k| ck_nil_hash[k] = nil}
|
2241
2272
|
|
2242
2273
|
if one_to_one
|
2243
|
-
opts
|
2244
|
-
|
2274
|
+
unless opts.has_key?(:setter)
|
2275
|
+
opts[:setter] = proc do |o|
|
2276
|
+
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2245
2277
|
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2278
|
+
if (froms = up_ds.opts[:from]) && (from = froms[0]) && (from.is_a?(Sequel::Dataset) || (from.is_a?(Sequel::SQL::AliasedExpression) && from.expression.is_a?(Sequel::Dataset)))
|
2279
|
+
if old = up_ds.first
|
2280
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2281
|
+
end
|
2282
|
+
save_old = true
|
2249
2283
|
end
|
2250
|
-
save_old = true
|
2251
|
-
end
|
2252
2284
|
|
2253
|
-
|
2254
|
-
|
2255
|
-
|
2285
|
+
if o
|
2286
|
+
if !o.new? && !save_old
|
2287
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2288
|
+
end
|
2289
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2256
2290
|
end
|
2257
|
-
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2258
|
-
end
|
2259
2291
|
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2292
|
+
checked_transaction do
|
2293
|
+
if save_old
|
2294
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2295
|
+
else
|
2296
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2297
|
+
end
|
2266
2298
|
|
2267
|
-
|
2299
|
+
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2300
|
+
end
|
2268
2301
|
end
|
2269
2302
|
end
|
2270
|
-
opts
|
2303
|
+
if opts.fetch(:setter, true)
|
2304
|
+
opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
|
2305
|
+
end
|
2271
2306
|
else
|
2272
2307
|
save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
|
2273
2308
|
|
2274
|
-
opts
|
2275
|
-
|
2276
|
-
|
2309
|
+
unless opts.has_key?(:adder)
|
2310
|
+
opts[:adder] = proc do |o|
|
2311
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2312
|
+
o.save(save_opts)
|
2313
|
+
end
|
2277
2314
|
end
|
2278
2315
|
|
2279
|
-
opts
|
2280
|
-
|
2281
|
-
|
2316
|
+
unless opts.has_key?(:remover)
|
2317
|
+
opts[:remover] = proc do |o|
|
2318
|
+
cks.each{|k| o.set_column_value(:"#{k}=", nil)}
|
2319
|
+
o.save(save_opts)
|
2320
|
+
end
|
2282
2321
|
end
|
2283
2322
|
|
2284
|
-
opts
|
2285
|
-
|
2323
|
+
unless opts.has_key?(:clearer)
|
2324
|
+
opts[:clearer] = proc do
|
2325
|
+
_apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
|
2326
|
+
end
|
2286
2327
|
end
|
2287
2328
|
end
|
2288
2329
|
end
|
@@ -3008,6 +3049,8 @@ module Sequel
|
|
3008
3049
|
# You can specify an custom alias and/or join type on a per-association basis by providing an
|
3009
3050
|
# Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
|
3010
3051
|
#
|
3052
|
+
# You cannot mix calls to +eager_graph+ and +graph+ on the same dataset.
|
3053
|
+
#
|
3011
3054
|
# Examples:
|
3012
3055
|
#
|
3013
3056
|
# # For each album, eager_graph load the artist
|
@@ -3374,15 +3417,30 @@ module Sequel
|
|
3374
3417
|
egl.dup
|
3375
3418
|
end
|
3376
3419
|
|
3377
|
-
# Eagerly load all specified associations
|
3420
|
+
# Eagerly load all specified associations.
|
3378
3421
|
def eager_load(a, eager_assoc=@opts[:eager])
|
3379
3422
|
return if a.empty?
|
3423
|
+
|
3424
|
+
# Reflections for all associations to eager load
|
3425
|
+
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3426
|
+
|
3427
|
+
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
3428
|
+
|
3429
|
+
reflections.each do |r|
|
3430
|
+
a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
|
3431
|
+
end
|
3432
|
+
|
3433
|
+
nil
|
3434
|
+
end
|
3435
|
+
|
3436
|
+
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
3437
|
+
def prepare_eager_load(a, reflections, eager_assoc)
|
3438
|
+
eager_load_data = {}
|
3439
|
+
|
3380
3440
|
# Key is foreign/primary key name symbol.
|
3381
3441
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
3382
3442
|
# and values being an array of current model objects with that specific foreign/primary key
|
3383
3443
|
key_hash = {}
|
3384
|
-
# Reflections for all associations to eager load
|
3385
|
-
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3386
3444
|
|
3387
3445
|
# Populate the key_hash entry for each association being eagerly loaded
|
3388
3446
|
reflections.each do |r|
|
@@ -3413,7 +3471,6 @@ module Sequel
|
|
3413
3471
|
id_map = nil
|
3414
3472
|
end
|
3415
3473
|
|
3416
|
-
loader = r[:eager_loader]
|
3417
3474
|
associations = eager_assoc[r[:name]]
|
3418
3475
|
if associations.respond_to?(:call)
|
3419
3476
|
eager_block = associations
|
@@ -3421,9 +3478,23 @@ module Sequel
|
|
3421
3478
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
3422
3479
|
eager_block, associations = pr_assoc
|
3423
3480
|
end
|
3424
|
-
|
3425
|
-
|
3426
|
-
end
|
3481
|
+
|
3482
|
+
eager_load_data[r[:eager_loader]] = {:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map}
|
3483
|
+
end
|
3484
|
+
|
3485
|
+
eager_load_data
|
3486
|
+
end
|
3487
|
+
|
3488
|
+
# Using the hash of loaders and eager options, perform the eager loading.
|
3489
|
+
def perform_eager_loads(eager_load_data)
|
3490
|
+
eager_load_data.map do |loader, eo|
|
3491
|
+
perform_eager_load(loader, eo)
|
3492
|
+
end
|
3493
|
+
end
|
3494
|
+
|
3495
|
+
# Perform eager loading for a single association using the loader and eager options.
|
3496
|
+
def perform_eager_load(loader, eo)
|
3497
|
+
loader.call(eo)
|
3427
3498
|
end
|
3428
3499
|
|
3429
3500
|
# Return a subquery expression for filering by a many_to_many association
|