sequel 3.23.0 → 3.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG +64 -0
  2. data/doc/association_basics.rdoc +43 -5
  3. data/doc/model_hooks.rdoc +64 -27
  4. data/doc/prepared_statements.rdoc +8 -4
  5. data/doc/reflection.rdoc +8 -2
  6. data/doc/release_notes/3.23.0.txt +1 -1
  7. data/doc/release_notes/3.24.0.txt +420 -0
  8. data/lib/sequel/adapters/db2.rb +8 -1
  9. data/lib/sequel/adapters/firebird.rb +25 -9
  10. data/lib/sequel/adapters/informix.rb +4 -19
  11. data/lib/sequel/adapters/jdbc.rb +34 -17
  12. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  13. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  14. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  15. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  16. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  18. data/lib/sequel/adapters/postgres.rb +30 -1
  19. data/lib/sequel/adapters/shared/access.rb +10 -0
  20. data/lib/sequel/adapters/shared/informix.rb +45 -0
  21. data/lib/sequel/adapters/shared/mssql.rb +82 -8
  22. data/lib/sequel/adapters/shared/mysql.rb +25 -7
  23. data/lib/sequel/adapters/shared/postgres.rb +39 -6
  24. data/lib/sequel/adapters/shared/sqlite.rb +57 -5
  25. data/lib/sequel/adapters/sqlite.rb +8 -3
  26. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  27. data/lib/sequel/ast_transformer.rb +190 -0
  28. data/lib/sequel/core.rb +1 -1
  29. data/lib/sequel/database/misc.rb +6 -0
  30. data/lib/sequel/database/query.rb +33 -3
  31. data/lib/sequel/database/schema_methods.rb +6 -2
  32. data/lib/sequel/dataset/features.rb +6 -0
  33. data/lib/sequel/dataset/prepared_statements.rb +17 -2
  34. data/lib/sequel/dataset/query.rb +17 -0
  35. data/lib/sequel/dataset/sql.rb +2 -53
  36. data/lib/sequel/exceptions.rb +4 -0
  37. data/lib/sequel/extensions/to_dot.rb +95 -83
  38. data/lib/sequel/model.rb +5 -0
  39. data/lib/sequel/model/associations.rb +80 -14
  40. data/lib/sequel/model/base.rb +182 -55
  41. data/lib/sequel/model/exceptions.rb +3 -1
  42. data/lib/sequel/plugins/association_pks.rb +6 -4
  43. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  44. data/lib/sequel/plugins/many_through_many.rb +8 -3
  45. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  46. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  47. data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
  48. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  49. data/lib/sequel/sql.rb +8 -0
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +43 -18
  52. data/spec/core/connection_pool_spec.rb +56 -77
  53. data/spec/core/database_spec.rb +25 -0
  54. data/spec/core/dataset_spec.rb +127 -16
  55. data/spec/core/expression_filters_spec.rb +13 -0
  56. data/spec/core/schema_spec.rb +6 -1
  57. data/spec/extensions/association_pks_spec.rb +7 -0
  58. data/spec/extensions/defaults_setter_spec.rb +64 -0
  59. data/spec/extensions/many_through_many_spec.rb +60 -4
  60. data/spec/extensions/nested_attributes_spec.rb +1 -0
  61. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  62. data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
  63. data/spec/extensions/prepared_statements_spec.rb +72 -0
  64. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  65. data/spec/extensions/to_dot_spec.rb +3 -5
  66. data/spec/integration/associations_test.rb +155 -1
  67. data/spec/integration/dataset_test.rb +8 -1
  68. data/spec/integration/plugin_test.rb +119 -0
  69. data/spec/integration/prepared_statement_test.rb +72 -1
  70. data/spec/integration/schema_test.rb +66 -8
  71. data/spec/integration/transaction_test.rb +40 -0
  72. data/spec/model/associations_spec.rb +349 -8
  73. data/spec/model/base_spec.rb +59 -0
  74. data/spec/model/hooks_spec.rb +161 -0
  75. data/spec/model/record_spec.rb +24 -0
  76. metadata +21 -4
@@ -271,7 +271,8 @@ module Sequel
271
271
  db
272
272
  begin
273
273
  if self == Model || !@dataset
274
- subclass.set_dataset(subclass.implicit_table_name) unless subclass.name.empty?
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 the destroy,
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
- set(values)
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 BeforeHookFailed. Otherwise it returns nil.
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 and !valid?(opts)
1037
- raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
1038
- return
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
- errors.clear
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
- # Actually do the deletion of the object's dataset.
1197
+ # Do the deletion of the object's dataset, and check that the row
1198
+ # was actually deleted.
1201
1199
  def _delete
1202
- n = _delete_dataset.delete
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
- raise_hook_failure(:destroy) if before_destroy == false
1217
- _destroy_delete
1218
- after_destroy
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.respond_to?(:insert_select) and h = ds.insert_select(@values)
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.insert(@values)
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.first || raise(Error, "Record not found"))
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
- raise_hook_failure(:save) if before_save == false
1265
- if new?
1266
- raise_hook_failure(:create) if before_create == false
1267
- pk = _insert
1268
- @this = nil
1269
- @new = false
1270
- @was_new = true
1271
- after_create
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 = _update_dataset.update(columns)
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
- # If not raising on failure, check for BeforeHookFailed
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 BeforeHookFailed
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 BeforeHookFailed, "one of the before_#{type} hooks returned false"
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
- class BeforeHookFailed < Error; end
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 disassociated from primary keys not provided to the method.
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[:primary_key])
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
- ds.unfiltered.filter(opts[:primary_key]=>pks).update(opts[:key]=>pk)
80
- ds.exclude(opts[:primary_key]=>pks).update(opts[:key]=>nil)
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