sequel 5.19.0 → 5.20.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 +24 -0
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +5 -7
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model/associations.rb +35 -9
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +61 -32
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +32 -0
- data/spec/core/database_spec.rb +73 -2
- data/spec/core/schema_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +8 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/integration/plugin_test.rb +15 -0
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -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: e092b5439a78eec1bb50a0bbc8a62c1d84ee5b27b9c5f034c250b632d03e17af
|
4
|
+
data.tar.gz: 231708d52405407b11113c55d5fdcd95e7d6155b0a8283f736876569ced6e4b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c2d7e19056e53f2d780f57f5da3c4675531f9678c6a54e922e0d59895bd2e485f12ee0b2644ed801c832a53653f75531d849cb7d60dcd9fb6a4edbfa8cb4c0b
|
7
|
+
data.tar.gz: 701b041185cc57c1966ea3b4be040e26a9e0aabfd6df45f0192f5b0b51133bfb94629f0634fa6edfc513a1c9193d455cb6222b388683d638e474b344e663ca50
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
=== 5.20.0 (2019-05-01)
|
2
|
+
|
3
|
+
* Fix reversing of alter_table add_foreign_key when :type option is used (jeremyevans) (#1615)
|
4
|
+
|
5
|
+
* Switch from using instance_exec to define_method for model associations and in some plugins (jeremyevans)
|
6
|
+
|
7
|
+
* Fix Database#server_version when using mysql2 adapter with mysql driver on MariaDB 10+ database (v-kolesnikov) (#1614)
|
8
|
+
|
9
|
+
* Make one_to_one setter method handle models that use joined datasets (jeremyevans) (#1612)
|
10
|
+
|
11
|
+
* Make auto_validations plugin work with the class_table_inheritance plugin (jeremyevans) (#1611)
|
12
|
+
|
13
|
+
* Avoid use of instance_exec for PlaceholderLiteralString#with_dataset (jeremyevans)
|
14
|
+
|
15
|
+
* Recognize float unsigned database types as float (keeguon, jeremyevans) (#1609)
|
16
|
+
|
17
|
+
* Support :savepoint options to Database#{after_commit,after_rollback} for making the hooks handle savepoints (jeremyevans)
|
18
|
+
|
19
|
+
* Avoid use of instance_exec in association_dependencies plugin (jeremyevans)
|
20
|
+
|
21
|
+
* Add pg_auto_constraint_validation_override to the pg_auto_constraint_validations plugin, for customizing columns and error message per constraint (jeremyevans)
|
22
|
+
|
23
|
+
* Make Database#check_constraints on PostgreSQL also include constraints where the related columns are not known (jeremyevans)
|
24
|
+
|
1
25
|
=== 5.19.0 (2019-04-02)
|
2
26
|
|
3
27
|
* Use more optimized approach to merging hashes in ruby 2.5+ (jeremyevans)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Database#after_commit and #after_rollback transaction hook methods
|
4
|
+
now support a :savepoint option. Using the :savepoint option makes
|
5
|
+
the hooks savepoint-aware, so after_commit will only be called if
|
6
|
+
all enclosing savepoints and the transaction are committed, and
|
7
|
+
after_rollback will be called when any of the enclosing savepoints
|
8
|
+
are rolled back (which may be before transaction commit/rollback).
|
9
|
+
Examples:
|
10
|
+
|
11
|
+
x = nil
|
12
|
+
DB.transaction do # BEGIN
|
13
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
14
|
+
DB.after_commit(savepoint: true){x = 1}
|
15
|
+
DB.after_rollback(savepoint: true){x = 2}
|
16
|
+
x # nil
|
17
|
+
end # RELEASE SAVEPOINT
|
18
|
+
x # nil
|
19
|
+
end # COMMIT
|
20
|
+
x # 1
|
21
|
+
|
22
|
+
x = nil
|
23
|
+
DB.transaction do # BEGIN
|
24
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
25
|
+
DB.after_commit(savepoint: true){x = 1}
|
26
|
+
DB.after_rollback(savepoint: true){x = 2}
|
27
|
+
x # nil
|
28
|
+
raise Sequel::Rollback
|
29
|
+
end # ROLLBACK TO SAVEPOINT
|
30
|
+
x # 2
|
31
|
+
end # COMMIT
|
32
|
+
x # 2
|
33
|
+
|
34
|
+
x = nil
|
35
|
+
DB.transaction do # BEGIN
|
36
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
37
|
+
DB.after_commit(savepoint: true){x = 1}
|
38
|
+
DB.after_rollback(savepoint: true){x = 2}
|
39
|
+
end # RELEASE SAVEPOINT
|
40
|
+
x # nil
|
41
|
+
raise Sequel::Rollback
|
42
|
+
end
|
43
|
+
x # 2
|
44
|
+
|
45
|
+
* The pg_auto_constraint_validations plugin now supports a
|
46
|
+
pg_auto_constraint_validation_override method for overriding
|
47
|
+
the columns and message for a specific constraint. This is
|
48
|
+
useful if the database cannot determine the columns (due
|
49
|
+
to the constraint containing a database function call), or
|
50
|
+
if you would like to customize the message per constraint.
|
51
|
+
|
52
|
+
= Other Improvements
|
53
|
+
|
54
|
+
* The one_to_one association setter now works with models that use
|
55
|
+
joined datasets, such as child models when using the
|
56
|
+
class_table_inheritance plugin.
|
57
|
+
|
58
|
+
* Database#check_constraints on PostgreSQL now also includes CHECK
|
59
|
+
constraints where the related columns are not known. The :columns
|
60
|
+
entry in the hash will be an empty array in such cases. The
|
61
|
+
exclusion of such constraints in previous versions was not
|
62
|
+
intentional, and the documentation implied that all CHECK
|
63
|
+
constraints were returned.
|
64
|
+
|
65
|
+
* Many cases where instance_exec was previously used on model
|
66
|
+
instances have been changed so that instance methods are defined
|
67
|
+
and called instead. This avoids the creation of singleton classes
|
68
|
+
for model instances, and can significantly improve performance in
|
69
|
+
some cases. This affects all associations as well as the
|
70
|
+
following plugins:
|
71
|
+
|
72
|
+
* composition
|
73
|
+
* hook_class_methods
|
74
|
+
* validation_class_methods
|
75
|
+
|
76
|
+
Other cases where instance_exec is now avoided and a different
|
77
|
+
approach is used:
|
78
|
+
|
79
|
+
* association_dependencies plugin
|
80
|
+
* PlaceholderLiteralString#with_dataset
|
81
|
+
|
82
|
+
* The auto_validations plugin now works with child models when using
|
83
|
+
the class_table_inheritance plugin.
|
84
|
+
|
85
|
+
* Database#server_version now works correctly in the mysql2 adapter
|
86
|
+
when using the MySQL driver with MariaDB 10+.
|
87
|
+
|
88
|
+
* The float unsigned type is now recognized and supported in the
|
89
|
+
schema parser and schema_dumper extension.
|
data/doc/transactions.rdoc
CHANGED
@@ -169,6 +169,44 @@ If you want the current savepoint and potentially enclosing savepoints to be rol
|
|
169
169
|
end # ROLLBACK TO SAVEPOINT
|
170
170
|
end # ROLLBACK
|
171
171
|
|
172
|
+
=== Savepoint Hooks
|
173
|
+
|
174
|
+
When using savepoints, you can use the +:savepoint+ option to +after_commit+ or +after_rollback+ to use a savepoint hook. For +after_commit+, this will only run the hook after transaction commit if all enclosing savepoints are released (not rolled back). For +after_rollback+, this will run the hook after any enclosing savepoint is rolled back (before transaction commit), or after the transaction is rolled back if all enclosing savepoints are released:
|
175
|
+
|
176
|
+
x = nil
|
177
|
+
DB.transaction do # BEGIN
|
178
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
179
|
+
DB.after_commit(savepoint: true){x = 1}
|
180
|
+
DB.after_rollback(savepoint: true){x = 2}
|
181
|
+
x # nil
|
182
|
+
end # RELEASE SAVEPOINT
|
183
|
+
x # nil
|
184
|
+
end # COMMIT
|
185
|
+
x # 1
|
186
|
+
|
187
|
+
x = nil
|
188
|
+
DB.transaction do # BEGIN
|
189
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
190
|
+
DB.after_commit(savepoint: true){x = 1}
|
191
|
+
DB.after_rollback(savepoint: true){x = 2}
|
192
|
+
x # nil
|
193
|
+
raise Sequel::Rollback
|
194
|
+
end # ROLLBACK TO SAVEPOINT
|
195
|
+
x # 2
|
196
|
+
end # COMMIT
|
197
|
+
x # 2
|
198
|
+
|
199
|
+
x = nil
|
200
|
+
DB.transaction do # BEGIN
|
201
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
202
|
+
DB.after_commit(savepoint: true){x = 1}
|
203
|
+
DB.after_rollback(savepoint: true){x = 2}
|
204
|
+
end # RELEASE SAVEPOINT
|
205
|
+
x # nil
|
206
|
+
raise Sequel::Rollback
|
207
|
+
end
|
208
|
+
x # 2
|
209
|
+
|
172
210
|
== Prepared Transactions / Two-Phase Commit
|
173
211
|
|
174
212
|
Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
|
@@ -78,8 +78,8 @@ module Sequel
|
|
78
78
|
end
|
79
79
|
|
80
80
|
# Return the version of the MySQL server to which we are connecting.
|
81
|
-
def server_version(
|
82
|
-
@server_version ||= (
|
81
|
+
def server_version(_server=nil)
|
82
|
+
@server_version ||= super()
|
83
83
|
end
|
84
84
|
|
85
85
|
private
|
@@ -220,24 +220,22 @@ module Sequel
|
|
220
220
|
# A hash of metadata for CHECK constraints on the table.
|
221
221
|
# Keys are CHECK constraint name symbols. Values are hashes with the following keys:
|
222
222
|
# :definition :: An SQL fragment for the definition of the constraint
|
223
|
-
# :columns :: An array of column symbols for the columns referenced in the constraint
|
223
|
+
# :columns :: An array of column symbols for the columns referenced in the constraint,
|
224
|
+
# can be an empty array if the database cannot deteremine the column symbols.
|
224
225
|
def check_constraints(table)
|
225
226
|
m = output_identifier_meth
|
226
227
|
|
227
228
|
rows = metadata_dataset.
|
228
229
|
from{pg_constraint.as(:co)}.
|
229
|
-
|
230
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
230
231
|
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
231
232
|
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
232
233
|
|
233
234
|
hash = {}
|
234
235
|
rows.each do |row|
|
235
236
|
constraint = m.call(row[:constraint])
|
236
|
-
|
237
|
-
|
238
|
-
else
|
239
|
-
hash[constraint] = {:definition=>row[:definition], :columns=>[m.call(row[:column])]}
|
240
|
-
end
|
237
|
+
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
238
|
+
entry[:columns] << m.call(row[:column]) if row[:column]
|
241
239
|
end
|
242
240
|
|
243
241
|
hash
|
@@ -331,7 +331,7 @@ module Sequel
|
|
331
331
|
:time
|
332
332
|
when /\A(bool(ean)?)\z/io
|
333
333
|
:boolean
|
334
|
-
when /\A(real|float
|
334
|
+
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
|
335
335
|
:float
|
336
336
|
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?))\z/io
|
337
337
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
@@ -634,7 +634,7 @@ module Sequel
|
|
634
634
|
|
635
635
|
# Drop a composite foreign key constraint
|
636
636
|
def drop_composite_foreign_key(columns, opts)
|
637
|
-
@operations <<
|
637
|
+
@operations << opts.merge(:op => :drop_constraint, :type => :foreign_key, :columns => columns)
|
638
638
|
nil
|
639
639
|
end
|
640
640
|
end
|
@@ -25,13 +25,19 @@ module Sequel
|
|
25
25
|
# Otherwise, add the block to the list of blocks to call after the currently
|
26
26
|
# in progress transaction commits (and only if it commits).
|
27
27
|
# Options:
|
28
|
+
# :savepoint :: If currently inside a savepoint, only run this hook on transaction
|
29
|
+
# commit if all enclosing savepoints have been released.
|
28
30
|
# :server :: The server/shard to use.
|
29
31
|
def after_commit(opts=OPTS, &block)
|
30
32
|
raise Error, "must provide block to after_commit" unless block
|
31
33
|
synchronize(opts[:server]) do |conn|
|
32
34
|
if h = _trans(conn)
|
33
35
|
raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
|
34
|
-
|
36
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
37
|
+
add_savepoint_hook(conn, :after_commit, block)
|
38
|
+
else
|
39
|
+
add_transaction_hook(conn, :after_commit, block)
|
40
|
+
end
|
35
41
|
else
|
36
42
|
yield
|
37
43
|
end
|
@@ -42,13 +48,20 @@ module Sequel
|
|
42
48
|
# Otherwise, add the block to the list of the blocks to call after the currently
|
43
49
|
# in progress transaction rolls back (and only if it rolls back).
|
44
50
|
# Options:
|
51
|
+
# :savepoint :: If currently inside a savepoint, run this hook immediately when
|
52
|
+
# any enclosing savepoint is rolled back, which may be before the transaction
|
53
|
+
# commits or rollsback.
|
45
54
|
# :server :: The server/shard to use.
|
46
55
|
def after_rollback(opts=OPTS, &block)
|
47
56
|
raise Error, "must provide block to after_rollback" unless block
|
48
57
|
synchronize(opts[:server]) do |conn|
|
49
58
|
if h = _trans(conn)
|
50
59
|
raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
|
51
|
-
|
60
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
61
|
+
add_savepoint_hook(conn, :after_rollback, block)
|
62
|
+
else
|
63
|
+
add_transaction_hook(conn, :after_rollback, block)
|
64
|
+
end
|
52
65
|
end
|
53
66
|
end
|
54
67
|
end
|
@@ -298,6 +311,13 @@ module Sequel
|
|
298
311
|
Sequel.synchronize{@transactions[conn] = hash}
|
299
312
|
end
|
300
313
|
|
314
|
+
# Set the given callable as a hook to be called. Type should be either
|
315
|
+
# :after_commit or :after_rollback.
|
316
|
+
def add_savepoint_hook(conn, type, block)
|
317
|
+
savepoint = _trans(conn)[:savepoints].last
|
318
|
+
(savepoint[type] ||= []) << block
|
319
|
+
end
|
320
|
+
|
301
321
|
# Set the given callable as a hook to be called. Type should be either
|
302
322
|
# :after_commit or :after_rollback.
|
303
323
|
def add_transaction_hook(conn, type, block)
|
@@ -401,6 +421,14 @@ module Sequel
|
|
401
421
|
supports_savepoints? && savepoint_level(conn) > 1
|
402
422
|
end
|
403
423
|
|
424
|
+
# Retrieve the savepoint hooks that should be run for the given
|
425
|
+
# connection and commit status.
|
426
|
+
def savepoint_hooks(conn, committed)
|
427
|
+
if in_savepoint?(conn)
|
428
|
+
_trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
404
432
|
# Retrieve the transaction hooks that should be run for the given
|
405
433
|
# connection and commit status.
|
406
434
|
def transaction_hooks(conn, committed)
|
@@ -411,16 +439,40 @@ module Sequel
|
|
411
439
|
|
412
440
|
# Remove the current thread from the list of active transactions
|
413
441
|
def remove_transaction(conn, committed)
|
414
|
-
|
442
|
+
if in_savepoint?(conn)
|
443
|
+
savepoint_callbacks = savepoint_hooks(conn, committed)
|
444
|
+
if committed
|
445
|
+
savepoint_rollback_callbacks = savepoint_hooks(conn, false)
|
446
|
+
end
|
447
|
+
else
|
448
|
+
callbacks = transaction_hooks(conn, committed)
|
449
|
+
end
|
415
450
|
|
416
451
|
if transaction_finished?(conn)
|
417
452
|
h = _trans(conn)
|
418
453
|
rolled_back = !committed
|
419
454
|
Sequel.synchronize{h[:rolled_back] = rolled_back}
|
420
455
|
Sequel.synchronize{@transactions.delete(conn)}
|
456
|
+
callbacks.each(&:call) if callbacks
|
457
|
+
elsif savepoint_callbacks || savepoint_rollback_callbacks
|
458
|
+
if committed
|
459
|
+
meth = in_savepoint?(conn) ? :add_savepoint_hook : :add_transaction_hook
|
460
|
+
|
461
|
+
if savepoint_callbacks
|
462
|
+
savepoint_callbacks.each do |block|
|
463
|
+
send(meth, conn, :after_commit, block)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
if savepoint_rollback_callbacks
|
468
|
+
savepoint_rollback_callbacks.each do |block|
|
469
|
+
send(meth, conn, :after_rollback, block)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
else
|
473
|
+
savepoint_callbacks.each(&:call)
|
474
|
+
end
|
421
475
|
end
|
422
|
-
|
423
|
-
callbacks.each(&:call) if callbacks
|
424
476
|
end
|
425
477
|
|
426
478
|
# SQL to rollback to a savepoint
|
@@ -170,7 +170,10 @@ module Sequel
|
|
170
170
|
# receiver's dataset to the block, and the block should return the new dataset
|
171
171
|
# to use.
|
172
172
|
def with_dataset
|
173
|
-
|
173
|
+
dataset = yield @dataset
|
174
|
+
other = dup
|
175
|
+
other.instance_variable_set(:@dataset, dataset)
|
176
|
+
other.freeze
|
174
177
|
end
|
175
178
|
|
176
179
|
# Return an array of all objects by running the SQL query for the given arguments.
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -1091,7 +1091,7 @@ module Sequel
|
|
1091
1091
|
# # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
|
1092
1092
|
# # ) SELECT * FROM t
|
1093
1093
|
def with_recursive(name, nonrecursive, recursive, opts=OPTS)
|
1094
|
-
raise(Error, 'This
|
1094
|
+
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
1095
1095
|
if hoist_cte?(nonrecursive)
|
1096
1096
|
s, ds = hoist_cte(nonrecursive)
|
1097
1097
|
s.with_recursive(name, ds, recursive, opts)
|
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
38
38
|
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/
|
39
39
|
{:type=>:Bignum}
|
40
|
-
when /\A(?:real|float
|
40
|
+
when /\A(?:real|float(?: unsigned)?|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/
|
41
41
|
{:type=>Float}
|
42
42
|
when 'boolean', 'bit', 'bool'
|
43
43
|
{:type=>TrueClass}
|
@@ -1617,9 +1617,10 @@ module Sequel
|
|
1617
1617
|
# is hash or array of two element arrays. Consider also specifying the :graph_block
|
1618
1618
|
# option if the value for this option is not a hash or array of two element arrays
|
1619
1619
|
# and you plan to use this association in eager_graph or association_join.
|
1620
|
-
# :dataset :: A proc that is
|
1620
|
+
# :dataset :: A proc that is used to define the method to get the base dataset to use (before the other
|
1621
1621
|
# options are applied). If the proc accepts an argument, it is passed the related
|
1622
|
-
# association reflection.
|
1622
|
+
# association reflection. It is a best practice to always have the dataset accept an argument
|
1623
|
+
# and use the argument to return the appropriate dataset.
|
1623
1624
|
# :distinct :: Use the DISTINCT clause when selecting associating object, both when
|
1624
1625
|
# lazy loading and eager loading via .eager (but not when using .eager_graph).
|
1625
1626
|
# :eager :: The associations to eagerly load via +eager+ when loading the associated object(s).
|
@@ -1909,7 +1910,7 @@ module Sequel
|
|
1909
1910
|
# can be easily overridden in the class itself while allowing for
|
1910
1911
|
# super to be called.
|
1911
1912
|
def association_module_def(name, opts=OPTS, &block)
|
1912
|
-
association_module(opts).
|
1913
|
+
association_module(opts).send(:define_method, name, &block)
|
1913
1914
|
end
|
1914
1915
|
|
1915
1916
|
# Add a private method to the module included in the class.
|
@@ -1944,6 +1945,13 @@ module Sequel
|
|
1944
1945
|
end
|
1945
1946
|
|
1946
1947
|
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
1948
|
+
if opts[:block]
|
1949
|
+
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1950
|
+
end
|
1951
|
+
if opts[:dataset]
|
1952
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1953
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1954
|
+
end
|
1947
1955
|
def_association_method(opts)
|
1948
1956
|
|
1949
1957
|
return if opts[:read_only]
|
@@ -2204,12 +2212,28 @@ module Sequel
|
|
2204
2212
|
if one_to_one
|
2205
2213
|
opts[:setter] ||= proc do |o|
|
2206
2214
|
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2215
|
+
|
2216
|
+
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)))
|
2217
|
+
if old = up_ds.first
|
2218
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2219
|
+
end
|
2220
|
+
save_old = true
|
2221
|
+
end
|
2222
|
+
|
2207
2223
|
if o
|
2208
|
-
|
2224
|
+
if !o.new? && !save_old
|
2225
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2226
|
+
end
|
2209
2227
|
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2210
2228
|
end
|
2229
|
+
|
2211
2230
|
checked_transaction do
|
2212
|
-
|
2231
|
+
if save_old
|
2232
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2233
|
+
else
|
2234
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2235
|
+
end
|
2236
|
+
|
2213
2237
|
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2214
2238
|
end
|
2215
2239
|
end
|
@@ -2287,7 +2311,8 @@ module Sequel
|
|
2287
2311
|
end
|
2288
2312
|
ds = ds.clone(:model_object => self)
|
2289
2313
|
ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
|
2290
|
-
|
2314
|
+
# block method is private
|
2315
|
+
ds = send(opts[:block_method], ds) if opts[:block_method]
|
2291
2316
|
ds
|
2292
2317
|
end
|
2293
2318
|
|
@@ -2310,10 +2335,11 @@ module Sequel
|
|
2310
2335
|
# Return an association dataset for the given association reflection
|
2311
2336
|
def _dataset(opts)
|
2312
2337
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2313
|
-
ds = if opts[:
|
2314
|
-
|
2338
|
+
ds = if opts[:dataset_opt_arity] == 1
|
2339
|
+
# dataset_opt_method is private
|
2340
|
+
send(opts[:dataset_opt_method], opts)
|
2315
2341
|
else
|
2316
|
-
|
2342
|
+
send(opts[:dataset_opt_method])
|
2317
2343
|
end
|
2318
2344
|
_apply_association_options(opts, ds)
|
2319
2345
|
end
|