sequel 3.23.0 → 3.24.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.
- data/CHANGELOG +64 -0
- data/doc/association_basics.rdoc +43 -5
- data/doc/model_hooks.rdoc +64 -27
- data/doc/prepared_statements.rdoc +8 -4
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.23.0.txt +1 -1
- data/doc/release_notes/3.24.0.txt +420 -0
- data/lib/sequel/adapters/db2.rb +8 -1
- data/lib/sequel/adapters/firebird.rb +25 -9
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc.rb +34 -17
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/postgres.rb +30 -1
- data/lib/sequel/adapters/shared/access.rb +10 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +82 -8
- data/lib/sequel/adapters/shared/mysql.rb +25 -7
- data/lib/sequel/adapters/shared/postgres.rb +39 -6
- data/lib/sequel/adapters/shared/sqlite.rb +57 -5
- data/lib/sequel/adapters/sqlite.rb +8 -3
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +33 -3
- data/lib/sequel/database/schema_methods.rb +6 -2
- data/lib/sequel/dataset/features.rb +6 -0
- data/lib/sequel/dataset/prepared_statements.rb +17 -2
- data/lib/sequel/dataset/query.rb +17 -0
- data/lib/sequel/dataset/sql.rb +2 -53
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model.rb +5 -0
- data/lib/sequel/model/associations.rb +80 -14
- data/lib/sequel/model/base.rb +182 -55
- data/lib/sequel/model/exceptions.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +6 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/many_through_many.rb +8 -3
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/sql.rb +8 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +43 -18
- data/spec/core/connection_pool_spec.rb +56 -77
- data/spec/core/database_spec.rb +25 -0
- data/spec/core/dataset_spec.rb +127 -16
- data/spec/core/expression_filters_spec.rb +13 -0
- data/spec/core/schema_spec.rb +6 -1
- data/spec/extensions/association_pks_spec.rb +7 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/many_through_many_spec.rb +60 -4
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/integration/associations_test.rb +155 -1
- data/spec/integration/dataset_test.rb +8 -1
- data/spec/integration/plugin_test.rb +119 -0
- data/spec/integration/prepared_statement_test.rb +72 -1
- data/spec/integration/schema_test.rb +66 -8
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/model/associations_spec.rb +349 -8
- data/spec/model/base_spec.rb +59 -0
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/record_spec.rb +24 -0
- metadata +21 -4
data/lib/sequel/model/base.rb
CHANGED
@@ -271,7 +271,8 @@ module Sequel
|
|
271
271
|
db
|
272
272
|
begin
|
273
273
|
if self == Model || !@dataset
|
274
|
-
|
274
|
+
n = subclass.name
|
275
|
+
subclass.set_dataset(subclass.implicit_table_name) unless n.nil? || n.empty?
|
275
276
|
elsif @dataset
|
276
277
|
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
277
278
|
end
|
@@ -686,10 +687,10 @@ module Sequel
|
|
686
687
|
|
687
688
|
# Sequel::Model instance methods that implement basic model functionality.
|
688
689
|
#
|
689
|
-
# * All of the methods in +HOOKS+ create instance methods that are called
|
690
|
+
# * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
|
690
691
|
# by Sequel when the appropriate action occurs. For example, when destroying
|
691
|
-
# a model object, Sequel will call +before_destroy+, do
|
692
|
-
# and then call +after_destroy+.
|
692
|
+
# a model object, Sequel will call +around_destory+, which will call +before_destroy+, do
|
693
|
+
# the destroy, and then call +after_destroy+.
|
693
694
|
# * The following instance_methods all call the class method of the same
|
694
695
|
# name: columns, dataset, db, primary_key, db_schema.
|
695
696
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
@@ -698,6 +699,7 @@ module Sequel
|
|
698
699
|
# gets the default value from the class by calling the class method of the same name.
|
699
700
|
module InstanceMethods
|
700
701
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
702
|
+
AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
701
703
|
|
702
704
|
# Define instance method(s) that calls class method(s) of the
|
703
705
|
# same name, caching the result in an instance variable. Define
|
@@ -750,7 +752,7 @@ module Sequel
|
|
750
752
|
@values = {}
|
751
753
|
@new = true
|
752
754
|
@modified = true
|
753
|
-
|
755
|
+
initialize_set(values)
|
754
756
|
changed_columns.clear
|
755
757
|
yield self if block_given?
|
756
758
|
end
|
@@ -1016,7 +1018,7 @@ module Sequel
|
|
1016
1018
|
#
|
1017
1019
|
# If +save+ fails and either raise_on_save_failure or the
|
1018
1020
|
# :raise_on_failure option is true, it raises ValidationFailed
|
1019
|
-
# or
|
1021
|
+
# or HookFailed. Otherwise it returns nil.
|
1020
1022
|
#
|
1021
1023
|
# If it succeeds, it returns self.
|
1022
1024
|
#
|
@@ -1033,9 +1035,11 @@ module Sequel
|
|
1033
1035
|
# raise_on_save_failure setting
|
1034
1036
|
def save(*columns)
|
1035
1037
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
1036
|
-
if opts[:validate] != false
|
1037
|
-
|
1038
|
-
|
1038
|
+
if opts[:validate] != false
|
1039
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
1040
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
1041
|
+
return
|
1042
|
+
end
|
1039
1043
|
end
|
1040
1044
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
1041
1045
|
end
|
@@ -1185,21 +1189,15 @@ module Sequel
|
|
1185
1189
|
# artist(:name=>'Invalid').valid? # => false
|
1186
1190
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
1187
1191
|
def valid?(opts = {})
|
1188
|
-
|
1189
|
-
if before_validation == false
|
1190
|
-
raise_hook_failure(:validation) if raise_on_failure?(opts)
|
1191
|
-
return false
|
1192
|
-
end
|
1193
|
-
validate
|
1194
|
-
after_validation
|
1195
|
-
errors.empty?
|
1192
|
+
_valid?(false, opts)
|
1196
1193
|
end
|
1197
1194
|
|
1198
1195
|
private
|
1199
1196
|
|
1200
|
-
#
|
1197
|
+
# Do the deletion of the object's dataset, and check that the row
|
1198
|
+
# was actually deleted.
|
1201
1199
|
def _delete
|
1202
|
-
n =
|
1200
|
+
n = _delete_without_checking
|
1203
1201
|
raise(NoExistingObject, "Attempt to delete object did not result in a single row modification (Rows Deleted: #{n}, SQL: #{_delete_dataset.delete_sql})") if require_modification && n != 1
|
1204
1202
|
n
|
1205
1203
|
end
|
@@ -1210,12 +1208,24 @@ module Sequel
|
|
1210
1208
|
this
|
1211
1209
|
end
|
1212
1210
|
|
1211
|
+
# Actually do the deletion of the object's dataset. Return the
|
1212
|
+
# number of rows modified.
|
1213
|
+
def _delete_without_checking
|
1214
|
+
_delete_dataset.delete
|
1215
|
+
end
|
1216
|
+
|
1213
1217
|
# Internal destroy method, separted from destroy to
|
1214
1218
|
# allow running inside a transaction
|
1215
1219
|
def _destroy(opts)
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1220
|
+
called = false
|
1221
|
+
around_destroy do
|
1222
|
+
called = true
|
1223
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
1224
|
+
_destroy_delete
|
1225
|
+
after_destroy
|
1226
|
+
true
|
1227
|
+
end
|
1228
|
+
raise_hook_failure(:destroy) unless called
|
1219
1229
|
self
|
1220
1230
|
end
|
1221
1231
|
|
@@ -1230,11 +1240,11 @@ module Sequel
|
|
1230
1240
|
# the record should be refreshed from the database.
|
1231
1241
|
def _insert
|
1232
1242
|
ds = _insert_dataset
|
1233
|
-
if !ds.opts[:select] and ds.
|
1243
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
1234
1244
|
@values = h
|
1235
1245
|
nil
|
1236
1246
|
else
|
1237
|
-
iid = ds
|
1247
|
+
iid = _insert_raw(ds)
|
1238
1248
|
# if we have a regular primary key and it's not set in @values,
|
1239
1249
|
# we assume it's the last inserted id
|
1240
1250
|
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !@values[pk]
|
@@ -1243,52 +1253,89 @@ module Sequel
|
|
1243
1253
|
pk
|
1244
1254
|
end
|
1245
1255
|
end
|
1246
|
-
|
1256
|
+
|
1247
1257
|
# The dataset to use when inserting a new object. The same as the model's
|
1248
1258
|
# dataset by default.
|
1249
1259
|
def _insert_dataset
|
1250
1260
|
model.dataset
|
1251
1261
|
end
|
1252
1262
|
|
1263
|
+
# Insert into the given dataset and return the primary key created (if any).
|
1264
|
+
def _insert_raw(ds)
|
1265
|
+
ds.insert(@values)
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
# Insert into the given dataset and return the hash of column values.
|
1269
|
+
def _insert_select_raw(ds)
|
1270
|
+
ds.insert_select(@values)
|
1271
|
+
end
|
1272
|
+
|
1253
1273
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
1254
1274
|
# is used for reading newly inserted values from the database
|
1255
1275
|
def _refresh(dataset)
|
1256
|
-
set_values(dataset
|
1276
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
1257
1277
|
changed_columns.clear
|
1258
1278
|
self
|
1259
1279
|
end
|
1280
|
+
|
1281
|
+
# Get the row of column data from the database.
|
1282
|
+
def _refresh_get(dataset)
|
1283
|
+
dataset.first
|
1284
|
+
end
|
1260
1285
|
|
1261
1286
|
# Internal version of save, split from save to allow running inside
|
1262
1287
|
# it's own transaction.
|
1263
1288
|
def _save(columns, opts)
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1289
|
+
was_new = false
|
1290
|
+
pk = nil
|
1291
|
+
called_save = false
|
1292
|
+
called_cu = false
|
1293
|
+
around_save do
|
1294
|
+
called_save = true
|
1295
|
+
raise_hook_failure(:save) if before_save == false
|
1296
|
+
if new?
|
1297
|
+
was_new = true
|
1298
|
+
around_create do
|
1299
|
+
called_cu = true
|
1300
|
+
raise_hook_failure(:create) if before_create == false
|
1301
|
+
pk = _insert
|
1302
|
+
@this = nil
|
1303
|
+
@new = false
|
1304
|
+
@was_new = true
|
1305
|
+
after_create
|
1306
|
+
true
|
1307
|
+
end
|
1308
|
+
raise_hook_failure(:create) unless called_cu
|
1309
|
+
else
|
1310
|
+
around_update do
|
1311
|
+
called_cu = true
|
1312
|
+
raise_hook_failure(:update) if before_update == false
|
1313
|
+
if columns.empty?
|
1314
|
+
@columns_updated = if opts[:changed]
|
1315
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
1316
|
+
else
|
1317
|
+
_save_update_all_columns_hash
|
1318
|
+
end
|
1319
|
+
changed_columns.clear
|
1320
|
+
else # update only the specified columns
|
1321
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
1322
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
1323
|
+
end
|
1324
|
+
_update_columns(@columns_updated)
|
1325
|
+
@this = nil
|
1326
|
+
after_update
|
1327
|
+
true
|
1328
|
+
end
|
1329
|
+
raise_hook_failure(:update) unless called_cu
|
1330
|
+
end
|
1272
1331
|
after_save
|
1332
|
+
true
|
1333
|
+
end
|
1334
|
+
raise_hook_failure(:save) unless called_save
|
1335
|
+
if was_new
|
1273
1336
|
@was_new = nil
|
1274
1337
|
pk ? _save_refresh : changed_columns.clear
|
1275
1338
|
else
|
1276
|
-
raise_hook_failure(:update) if before_update == false
|
1277
|
-
if columns.empty?
|
1278
|
-
@columns_updated = if opts[:changed]
|
1279
|
-
@values.reject{|k,v| !changed_columns.include?(k)}
|
1280
|
-
else
|
1281
|
-
_save_update_all_columns_hash
|
1282
|
-
end
|
1283
|
-
changed_columns.clear
|
1284
|
-
else # update only the specified columns
|
1285
|
-
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
1286
|
-
changed_columns.reject!{|c| columns.include?(c)}
|
1287
|
-
end
|
1288
|
-
_update_columns(@columns_updated)
|
1289
|
-
@this = nil
|
1290
|
-
after_update
|
1291
|
-
after_save
|
1292
1339
|
@columns_updated = nil
|
1293
1340
|
end
|
1294
1341
|
@modified = false
|
@@ -1321,9 +1368,10 @@ module Sequel
|
|
1321
1368
|
_update(columns) unless columns.empty?
|
1322
1369
|
end
|
1323
1370
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
1371
|
+
# Update this instance's dataset with the supplied column hash,
|
1372
|
+
# checking that only a single row was modified.
|
1325
1373
|
def _update(columns)
|
1326
|
-
n =
|
1374
|
+
n = _update_without_checking(columns)
|
1327
1375
|
raise(NoExistingObject, "Attempt to update object did not result in a single row modification (SQL: #{_update_dataset.update_sql(columns)})") if require_modification && n != 1
|
1328
1376
|
n
|
1329
1377
|
end
|
@@ -1334,7 +1382,46 @@ module Sequel
|
|
1334
1382
|
this
|
1335
1383
|
end
|
1336
1384
|
|
1337
|
-
#
|
1385
|
+
# Update this instances dataset with the supplied column hash.
|
1386
|
+
def _update_without_checking(columns)
|
1387
|
+
_update_dataset.update(columns)
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
1391
|
+
# failures will be raised as HookFailure exceptions. If it is
|
1392
|
+
# +false+, +false+ will be returned instead.
|
1393
|
+
def _valid?(raise_errors, opts)
|
1394
|
+
errors.clear
|
1395
|
+
called = false
|
1396
|
+
error = false
|
1397
|
+
around_validation do
|
1398
|
+
called = true
|
1399
|
+
if before_validation == false
|
1400
|
+
if raise_errors
|
1401
|
+
raise_hook_failure(:validation)
|
1402
|
+
else
|
1403
|
+
error = true
|
1404
|
+
end
|
1405
|
+
false
|
1406
|
+
else
|
1407
|
+
validate
|
1408
|
+
after_validation
|
1409
|
+
errors.empty?
|
1410
|
+
end
|
1411
|
+
end
|
1412
|
+
error = true unless called
|
1413
|
+
if error
|
1414
|
+
if raise_errors
|
1415
|
+
raise_hook_failure(:validation)
|
1416
|
+
else
|
1417
|
+
false
|
1418
|
+
end
|
1419
|
+
else
|
1420
|
+
errors.empty?
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
# If not raising on failure, check for HookFailed
|
1338
1425
|
# being raised by yielding and swallow it.
|
1339
1426
|
def checked_save_failure(opts)
|
1340
1427
|
if raise_on_failure?(opts)
|
@@ -1342,7 +1429,7 @@ module Sequel
|
|
1342
1429
|
else
|
1343
1430
|
begin
|
1344
1431
|
yield
|
1345
|
-
rescue
|
1432
|
+
rescue HookFailed
|
1346
1433
|
nil
|
1347
1434
|
end
|
1348
1435
|
end
|
@@ -1353,6 +1440,13 @@ module Sequel
|
|
1353
1440
|
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
1354
1441
|
end
|
1355
1442
|
|
1443
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
1444
|
+
# exists so it can be overridden. This is called only for new records, before
|
1445
|
+
# changed_columns is cleared.
|
1446
|
+
def initialize_set(h)
|
1447
|
+
set(h)
|
1448
|
+
end
|
1449
|
+
|
1356
1450
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
1357
1451
|
def inspect_values
|
1358
1452
|
@values.inspect
|
@@ -1369,7 +1463,7 @@ module Sequel
|
|
1369
1463
|
# Raise an error appropriate to the hook type. May be swallowed by
|
1370
1464
|
# checked_save_failure depending on the raise_on_failure? setting.
|
1371
1465
|
def raise_hook_failure(type)
|
1372
|
-
raise
|
1466
|
+
raise HookFailed, "one of the before_#{type} hooks returned false"
|
1373
1467
|
end
|
1374
1468
|
|
1375
1469
|
# Set the columns, filtered by the only and except arrays.
|
@@ -1461,6 +1555,18 @@ module Sequel
|
|
1461
1555
|
# Artist.dataset.model # => Artist
|
1462
1556
|
attr_accessor :model
|
1463
1557
|
|
1558
|
+
# Assume if a single integer is given that it is a lookup by primary
|
1559
|
+
# key, and call with_pk with the argument.
|
1560
|
+
#
|
1561
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
1562
|
+
def [](*args)
|
1563
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
1564
|
+
with_pk(i)
|
1565
|
+
else
|
1566
|
+
super
|
1567
|
+
end
|
1568
|
+
end
|
1569
|
+
|
1464
1570
|
# Destroy each row in the dataset by instantiating it and then calling
|
1465
1571
|
# destroy on the resulting model object. This isn't as fast as deleting
|
1466
1572
|
# the dataset, which does a single SQL call, but this runs any destroy
|
@@ -1491,6 +1597,27 @@ module Sequel
|
|
1491
1597
|
super(pk, value_column)
|
1492
1598
|
end
|
1493
1599
|
end
|
1600
|
+
|
1601
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
1602
|
+
# value.
|
1603
|
+
#
|
1604
|
+
# # Single primary key
|
1605
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
1606
|
+
#
|
1607
|
+
# # Composite primary key
|
1608
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
1609
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
1610
|
+
def with_pk(pk)
|
1611
|
+
case primary_key = model.primary_key
|
1612
|
+
when Array
|
1613
|
+
raise(Error, "single primary key given (#{pk.inspect}) when a composite primary key is expected (#{primary_key.inspect})") unless pk.is_a?(Array)
|
1614
|
+
raise(Error, "composite primary key given (#{pk.inspect}) does not match composite primary key length (#{primary_key.inspect})") if pk.length != primary_key.length
|
1615
|
+
first(primary_key.zip(pk))
|
1616
|
+
else
|
1617
|
+
raise(Error, "composite primary key given (#{pk.inspect}) when a single primary key is expected (#{primary_key.inspect})") if pk.is_a?(Array)
|
1618
|
+
first(primary_key=>pk)
|
1619
|
+
end
|
1620
|
+
end
|
1494
1621
|
end
|
1495
1622
|
|
1496
1623
|
extend ClassMethods
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Sequel
|
2
2
|
# Exception class raised when +raise_on_save_failure+ is set and a before hook returns false
|
3
|
-
|
3
|
+
# or an around hook doesn't call super or yield.
|
4
|
+
class HookFailed < Error; end
|
5
|
+
BeforeHookFailed = HookFailed
|
4
6
|
|
5
7
|
# Exception class raised when +require_modification+ is set and an UPDATE or DELETE statement to modify the dataset doesn't
|
6
8
|
# modify a single row.
|
@@ -14,7 +14,7 @@ module Sequel
|
|
14
14
|
#
|
15
15
|
# Note that it uses the singular form of the association name. Also note
|
16
16
|
# that the setter both associates to new primary keys not in the assocation
|
17
|
-
# and
|
17
|
+
# and disassociates from primary keys not provided to the method.
|
18
18
|
#
|
19
19
|
# This plugin makes modifications directly to the underlying tables,
|
20
20
|
# it does not create or return any model objects, and therefore does
|
@@ -71,13 +71,15 @@ module Sequel
|
|
71
71
|
super
|
72
72
|
return if opts[:type] == :one_to_one
|
73
73
|
def_association_pks_getter(opts) do
|
74
|
-
send(opts.dataset_method).select_map(opts
|
74
|
+
send(opts.dataset_method).select_map(opts.associated_class.primary_key)
|
75
75
|
end
|
76
76
|
def_association_pks_setter(opts) do |pks|
|
77
77
|
checked_transaction do
|
78
78
|
ds = send(opts.dataset_method)
|
79
|
-
|
80
|
-
|
79
|
+
primary_key = opts.associated_class.primary_key
|
80
|
+
key = opts[:key]
|
81
|
+
ds.unfiltered.filter(primary_key=>pks).update(key=>pk)
|
82
|
+
ds.exclude(primary_key=>pks).update(key=>nil)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
# DefaultsSetter is a simple plugin that sets non-nil/NULL default values upon
|
4
|
+
# initialize:
|
5
|
+
#
|
6
|
+
# # column a default NULL
|
7
|
+
# # column b default 2
|
8
|
+
# album = Album.new.values # {:b => 2}
|
9
|
+
# album = Album.new(:a=>1, :b=>3).values # {:a => 1, :b => 3}
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
#
|
13
|
+
# # Make all model subclass instances set defaults (called before loading subclasses)
|
14
|
+
# Sequel::Model.plugin :defaults_setter
|
15
|
+
#
|
16
|
+
# # Make the Album class set defaults
|
17
|
+
# Album.plugin :defaults_setter
|
18
|
+
module DefaultsSetter
|
19
|
+
# Set the default values based on the model schema
|
20
|
+
def self.configure(model)
|
21
|
+
model.send(:set_default_values)
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# The default values to set in initialize for this model. A hash with column symbol
|
26
|
+
# keys and default values. If the default values respond to +call+, it will be called
|
27
|
+
# to get the value, otherwise the value will be used directly. You can manually modify
|
28
|
+
# this hash to set specific default values, by default the ones will be parsed from the database.
|
29
|
+
attr_reader :default_values
|
30
|
+
|
31
|
+
# Set the default values when loading the dataset.
|
32
|
+
def set_dataset(*)
|
33
|
+
x = super
|
34
|
+
set_default_values
|
35
|
+
x
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def set_default_values
|
41
|
+
h = {}
|
42
|
+
@db_schema.each{|k, v| h[k] = v[:ruby_default] if v[:ruby_default]} if @db_schema
|
43
|
+
@default_values = h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module InstanceMethods
|
48
|
+
private
|
49
|
+
|
50
|
+
# Set default values if they are not already set by the hash provided to initialize.
|
51
|
+
def initialize_set(h)
|
52
|
+
super
|
53
|
+
model.default_values.each{|k,v| self[k] = (v.respond_to?(:call) ? v.call : v) unless values.has_key?(k)}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|