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