sequel 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/CHANGELOG +64 -0
  2. data/Rakefile +1 -1
  3. data/lib/sequel_core/adapters/jdbc.rb +6 -2
  4. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  5. data/lib/sequel_core/adapters/oracle.rb +4 -77
  6. data/lib/sequel_core/adapters/postgres.rb +39 -26
  7. data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
  8. data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
  9. data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
  10. data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
  11. data/lib/sequel_core/core_ext.rb +10 -0
  12. data/lib/sequel_core/core_sql.rb +7 -0
  13. data/lib/sequel_core/database.rb +22 -0
  14. data/lib/sequel_core/database/schema.rb +1 -1
  15. data/lib/sequel_core/dataset.rb +29 -11
  16. data/lib/sequel_core/dataset/sql.rb +27 -7
  17. data/lib/sequel_core/migration.rb +20 -2
  18. data/lib/sequel_core/object_graph.rb +24 -10
  19. data/lib/sequel_core/schema/generator.rb +22 -9
  20. data/lib/sequel_core/schema/sql.rb +13 -9
  21. data/lib/sequel_core/sql.rb +27 -2
  22. data/lib/sequel_model/association_reflection.rb +251 -141
  23. data/lib/sequel_model/associations.rb +114 -61
  24. data/lib/sequel_model/base.rb +25 -21
  25. data/lib/sequel_model/eager_loading.rb +17 -40
  26. data/lib/sequel_model/hooks.rb +25 -24
  27. data/lib/sequel_model/record.rb +29 -51
  28. data/lib/sequel_model/schema.rb +1 -1
  29. data/lib/sequel_model/validations.rb +13 -3
  30. data/spec/adapters/postgres_spec.rb +104 -18
  31. data/spec/adapters/spec_helper.rb +4 -1
  32. data/spec/integration/eager_loader_test.rb +5 -4
  33. data/spec/integration/spec_helper.rb +4 -1
  34. data/spec/sequel_core/connection_pool_spec.rb +24 -24
  35. data/spec/sequel_core/core_sql_spec.rb +12 -0
  36. data/spec/sequel_core/dataset_spec.rb +77 -2
  37. data/spec/sequel_core/expression_filters_spec.rb +6 -0
  38. data/spec/sequel_core/object_graph_spec.rb +40 -2
  39. data/spec/sequel_core/schema_spec.rb +13 -0
  40. data/spec/sequel_model/association_reflection_spec.rb +8 -8
  41. data/spec/sequel_model/associations_spec.rb +164 -3
  42. data/spec/sequel_model/caching_spec.rb +2 -1
  43. data/spec/sequel_model/eager_loading_spec.rb +107 -3
  44. data/spec/sequel_model/hooks_spec.rb +38 -22
  45. data/spec/sequel_model/model_spec.rb +11 -35
  46. data/spec/sequel_model/plugins_spec.rb +4 -2
  47. data/spec/sequel_model/record_spec.rb +8 -5
  48. data/spec/sequel_model/validations_spec.rb +25 -0
  49. data/spec/spec_config.rb +4 -3
  50. metadata +21 -19
@@ -88,7 +88,7 @@ module Sequel::Model::Associations
88
88
  # after a new item is added to the association.
89
89
  # - :after_load - Symbol, Proc, or array of both/either specifying a callback to call
90
90
  # after the associated record(s) have been retrieved from the database. Not called
91
- # when eager loading (see the :eager_loader option to accomplish it when eager loading).
91
+ # when eager loading via eager_graph, but called when eager loading via eager.
92
92
  # - :after_remove - Symbol, Proc, or array of both/either specifying a callback to call
93
93
  # after an item is removed from the association.
94
94
  # - :allow_eager - If set to false, you cannot load the association eagerly
@@ -113,6 +113,10 @@ module Sequel::Model::Associations
113
113
  # For many_to_one associations, this is ignored unless this association is
114
114
  # being eagerly loaded, as it doesn't save queries unless multiple objects
115
115
  # can be loaded at once.
116
+ # - :eager_grapher - A proc to use to implement eager loading via eager graph, overriding the default.
117
+ # Takes three arguments, a dataset, an alias to use for the table to graph for this association,
118
+ # and the alias that was used for the current table (since you can cascade associations),
119
+ # Should return a copy of the dataset with the association graphed into it.
116
120
  # - :eager_loader - A proc to use to implement eager loading, overriding the default. Takes three arguments,
117
121
  # a key hash (used solely to enhance performance), an array of records,
118
122
  # and a hash of dependent associations. The associated records should
@@ -152,6 +156,8 @@ module Sequel::Model::Associations
152
156
  # * :many_to_one:
153
157
  # - :key - foreign_key in current model's table that references
154
158
  # associated model's primary key, as a symbol. Defaults to :"#{name}_id".
159
+ # - :primary_key - column in the associated table that :key option references, as a symbol.
160
+ # Defaults to the primary key of the associated table.
155
161
  # * :one_to_many:
156
162
  # - :key - foreign key in associated model's table that references
157
163
  # current model's primary key, as a symbol. Defaults to
@@ -164,15 +170,9 @@ module Sequel::Model::Associations
164
170
  # so using this is similar to using many_to_one, in terms of the methods
165
171
  # it adds, the main difference is that the foreign key is in the associated
166
172
  # table instead of the current table.
173
+ # - :primary_key - column in the current table that :key option references, as a symbol.
174
+ # Defaults to primary key of the current table.
167
175
  # * :many_to_many:
168
- # - :join_table - name of table that includes the foreign keys to both
169
- # the current model and the associated model, as a symbol. Defaults to the name
170
- # of current model and name of associated model, pluralized,
171
- # underscored, sorted, and joined with '_'.
172
- # - :left_key - foreign key in join table that points to current model's
173
- # primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
174
- # - :right_key - foreign key in join table that points to associated
175
- # model's primary key, as a symbol. Defaults to Defaults to :"#{name.to_s.singularize}_id".
176
176
  # - :graph_join_table_block - The block to pass to join_table for
177
177
  # the join table when eagerly loading the association via eager_graph.
178
178
  # - :graph_join_table_conditions - The additional conditions to use on the SQL join for
@@ -185,13 +185,26 @@ module Sequel::Model::Associations
185
185
  # table when eagerly loading the association via eager_graph, instead of the default
186
186
  # conditions specified by the foreign/primary keys. This option causes the
187
187
  # :graph_join_table_conditions option to be ignored.
188
+ # - :join_table - name of table that includes the foreign keys to both
189
+ # the current model and the associated model, as a symbol. Defaults to the name
190
+ # of current model and name of associated model, pluralized,
191
+ # underscored, sorted, and joined with '_'.
192
+ # - :left_key - foreign key in join table that points to current model's
193
+ # primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
194
+ # - :left_primary_key - column in current table that :left_key points to, as a symbol.
195
+ # Defaults to primary key of current table.
196
+ # - :right_key - foreign key in join table that points to associated
197
+ # model's primary key, as a symbol. Defaults to Defaults to :"#{name.to_s.singularize}_id".
198
+ # - :right_primary_key - column in associated table that :right_key points to, as a symbol.
199
+ # Defaults to primary key of the associated table.
200
+ # - :uniq - Adds a after_load callback that makes the array of objects unique.
188
201
  def associate(type, name, opts = {}, &block)
189
- raise(Error, 'invalid association type') unless AssociationReflection::ASSOCIATION_TYPES.include?(type)
202
+ raise(Error, 'invalid association type') unless assoc_class = ASSOCIATION_TYPES[type]
190
203
  raise(Error, 'Model.associate name argument must be a symbol') unless Symbol === name
191
204
 
192
205
  # merge early so we don't modify opts
193
206
  opts = opts.merge(:type => type, :name => name, :block => block, :cache => true, :model => self)
194
- opts = AssociationReflection.new.merge!(opts)
207
+ opts = assoc_class.new.merge!(opts)
195
208
  opts[:eager_block] = block unless opts.include?(:eager_block)
196
209
  opts[:graph_join_type] ||= :left_outer
197
210
  opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
@@ -246,29 +259,30 @@ module Sequel::Model::Associations
246
259
 
247
260
  private
248
261
 
249
- # Hash storing the association reflections. Keys are association name
250
- # symbols, values are association reflection hashes.
251
- def association_reflections
252
- @association_reflections ||= {}
262
+ # Add a method to the association module
263
+ def association_module_def(name, &block)
264
+ overridable_methods_module.class_def(name, &block)
253
265
  end
254
-
266
+
267
+ # Add a method to the association module
268
+ def association_module_private_def(name, &block)
269
+ association_module_def(name, &block)
270
+ overridable_methods_module.send(:private, name)
271
+ end
272
+
255
273
  # Add the add_ instance method
256
274
  def def_add_method(opts)
257
- class_def(opts.add_method){|o| add_associated_object(opts, o)}
275
+ association_module_def(opts.add_method){|o| add_associated_object(opts, o)}
258
276
  end
259
277
 
260
278
  # Adds association methods to the model for *_to_many associations.
261
279
  def def_association_dataset_methods(opts)
262
280
  # If a block is given, define a helper method for it, because it takes
263
281
  # an argument. This is unnecessary in Ruby 1.9, as that has instance_exec.
264
- if opts[:block]
265
- class_def(opts.dataset_helper_method, &opts[:block])
266
- private opts.dataset_helper_method
267
- end
268
- class_def(opts._dataset_method, &opts[:dataset])
269
- private opts._dataset_method
270
- class_def(opts.dataset_method){_dataset(opts)}
271
- class_def(opts.association_method){|*reload| load_associated_objects(opts, reload[0])}
282
+ association_module_private_def(opts.dataset_helper_method, &opts[:block]) if opts[:block]
283
+ association_module_private_def(opts._dataset_method, &opts[:dataset])
284
+ association_module_def(opts.dataset_method){_dataset(opts)}
285
+ association_module_def(opts.association_method){|*reload| load_associated_objects(opts, reload[0])}
272
286
  end
273
287
 
274
288
  # Adds many_to_many association instance methods
@@ -277,38 +291,54 @@ module Sequel::Model::Associations
277
291
  model = self
278
292
  left = (opts[:left_key] ||= opts.default_left_key)
279
293
  right = (opts[:right_key] ||= opts.default_right_key)
294
+ left_pk = (opts[:left_primary_key] ||= self.primary_key)
280
295
  opts[:class_name] ||= name.to_s.singularize.camelize
281
296
  join_table = (opts[:join_table] ||= opts.default_join_table)
282
297
  left_key_alias = opts[:left_key_alias] ||= :x_foreign_key_x
283
298
  left_key_select = opts[:left_key_select] ||= left.qualify(join_table).as(opts[:left_key_alias])
284
- opts[:graph_join_table_conditions] = opts[:graph_join_table_conditions] ? opts[:graph_join_table_conditions].to_a : []
299
+ graph_jt_conds = opts[:graph_join_table_conditions] = opts[:graph_join_table_conditions] ? opts[:graph_join_table_conditions].to_a : []
285
300
  opts[:graph_join_table_join_type] ||= opts[:graph_join_type]
286
- opts[:dataset] ||= proc{opts.associated_class.inner_join(join_table, [[right, opts.associated_primary_key], [left, pk]])}
301
+ opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
302
+ opts[:dataset] ||= proc{opts.associated_class.inner_join(join_table, [[right, opts.right_primary_key], [left, send(left_pk)]])}
287
303
  database = db
288
304
 
289
305
  opts[:eager_loader] ||= proc do |key_hash, records, associations|
290
- h = key_hash[model.primary_key]
306
+ h = key_hash[left_pk]
291
307
  records.each{|object| object.associations[name] = []}
292
- model.eager_loading_dataset(opts, opts.associated_class.inner_join(join_table, [[right, opts.associated_primary_key], [left, h.keys]]), Array(opts.select) + Array(left_key_select), associations).all do |assoc_record|
308
+ model.eager_loading_dataset(opts, opts.associated_class.inner_join(join_table, [[right, opts.right_primary_key], [left, h.keys]]), Array(opts.select) + Array(left_key_select), associations).all do |assoc_record|
293
309
  next unless objects = h[assoc_record.values.delete(left_key_alias)]
294
310
  objects.each{|object| object.associations[name].push(assoc_record)}
295
311
  end
296
312
  end
297
313
 
314
+ join_type = opts[:graph_join_type]
315
+ select = opts[:graph_select]
316
+ use_only_conditions = opts.include?(:graph_only_conditions)
317
+ only_conditions = opts[:graph_only_conditions]
318
+ conditions = opts[:graph_conditions]
319
+ graph_block = opts[:graph_block]
320
+ use_jt_only_conditions = opts.include?(:graph_join_table_only_conditions)
321
+ jt_only_conditions = opts[:graph_join_table_only_conditions]
322
+ jt_join_type = opts[:graph_join_table_join_type]
323
+ jt_graph_block = opts[:graph_join_table_block]
324
+ opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
325
+ ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : [[left, left_pk]] + graph_jt_conds, :select=>false, :table_alias=>ds.send(:eager_unique_table_alias, ds, join_table), :join_type=>jt_join_type, :implicit_qualifier=>table_alias, &jt_graph_block)
326
+ ds.graph(opts.associated_class, use_only_conditions ? only_conditions : [[opts.right_primary_key, right]] + conditions, :select=>select, :table_alias=>assoc_alias, :join_type=>join_type, &graph_block)
327
+ end
328
+
298
329
  def_association_dataset_methods(opts)
299
330
 
300
331
  return if opts[:read_only]
301
332
 
302
- class_def(opts._add_method) do |o|
303
- database.dataset.from(join_table).insert(left=>pk, right=>o.pk)
333
+ association_module_private_def(opts._add_method) do |o|
334
+ database.dataset.from(join_table).insert(left=>send(left_pk), right=>o.send(opts.right_primary_key))
304
335
  end
305
- class_def(opts._remove_method) do |o|
306
- database.dataset.from(join_table).filter([[left, pk], [right, o.pk]]).delete
336
+ association_module_private_def(opts._remove_method) do |o|
337
+ database.dataset.from(join_table).filter([[left, send(left_pk)], [right, o.send(opts.right_primary_key)]]).delete
307
338
  end
308
- class_def(opts._remove_all_method) do
309
- database.dataset.from(join_table).filter(left=>pk).delete
339
+ association_module_private_def(opts._remove_all_method) do
340
+ database.dataset.from(join_table).filter(left=>send(left_pk)).delete
310
341
  end
311
- private opts._add_method, opts._remove_method, opts._remove_all_method
312
342
 
313
343
  def_add_method(opts)
314
344
  def_remove_methods(opts)
@@ -318,12 +348,12 @@ module Sequel::Model::Associations
318
348
  def def_many_to_one(opts)
319
349
  name = opts[:name]
320
350
  model = self
321
- opts[:key] = opts.default_right_key unless opts.include?(:key)
351
+ opts[:key] = opts.default_key unless opts.include?(:key)
322
352
  key = opts[:key]
323
353
  opts[:class_name] ||= name.to_s.camelize
324
354
  opts[:dataset] ||= proc do
325
355
  klass = opts.associated_class
326
- klass.filter(opts.associated_primary_key.qualify(klass.table_name)=>send(key))
356
+ klass.filter(opts.primary_key.qualify(klass.table_name)=>send(key))
327
357
  end
328
358
  opts[:eager_loader] ||= proc do |key_hash, records, associations|
329
359
  h = key_hash[key]
@@ -333,21 +363,31 @@ module Sequel::Model::Associations
333
363
  records.each{|object| object.associations[name] = nil}
334
364
  # Skip eager loading if no objects have a foreign key for this association
335
365
  unless keys.empty?
336
- model.eager_loading_dataset(opts, opts.associated_class.filter(opts.associated_primary_key.qualify(opts.associated_class.table_name)=>keys), opts.select, associations).all do |assoc_record|
337
- next unless objects = h[assoc_record.pk]
366
+ klass = opts.associated_class
367
+ model.eager_loading_dataset(opts, klass.filter(opts.primary_key.qualify(klass.table_name)=>keys), opts.select, associations).all do |assoc_record|
368
+ next unless objects = h[assoc_record.send(opts.primary_key)]
338
369
  objects.each{|object| object.associations[name] = assoc_record}
339
370
  end
340
371
  end
341
372
  end
342
373
 
374
+ join_type = opts[:graph_join_type]
375
+ select = opts[:graph_select]
376
+ use_only_conditions = opts.include?(:graph_only_conditions)
377
+ only_conditions = opts[:graph_only_conditions]
378
+ conditions = opts[:graph_conditions]
379
+ graph_block = opts[:graph_block]
380
+ opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
381
+ ds.graph(opts.associated_class, use_only_conditions ? only_conditions : [[opts.primary_key, key]] + conditions, :select=>select, :table_alias=>assoc_alias, :join_type=>join_type, :implicit_qualifier=>table_alias, &graph_block)
382
+ end
383
+
343
384
  def_association_dataset_methods(opts)
344
385
 
345
386
  return if opts[:read_only]
346
387
 
347
- class_def(opts._setter_method){|o| send(:"#{key}=", (o.pk if o))}
348
- private opts._setter_method
388
+ association_module_private_def(opts._setter_method){|o| send(:"#{key}=", (o.send(opts.primary_key) if o))}
349
389
 
350
- class_def(opts.setter_method) do |o|
390
+ association_module_def(opts.setter_method) do |o|
351
391
  raise(Sequel::Error, 'model object does not have a primary key') if o && !o.pk
352
392
  old_val = send(opts.association_method)
353
393
  return o if old_val == o
@@ -367,17 +407,19 @@ module Sequel::Model::Associations
367
407
  def def_one_to_many(opts)
368
408
  name = opts[:name]
369
409
  model = self
370
- key = (opts[:key] ||= opts.default_left_key)
410
+ key = (opts[:key] ||= opts.default_key)
411
+ primary_key = (opts[:primary_key] ||= self.primary_key)
371
412
  opts[:class_name] ||= name.to_s.singularize.camelize
372
413
  opts[:dataset] ||= proc do
373
414
  klass = opts.associated_class
374
- klass.filter(key.qualify(klass.table_name) => pk)
415
+ klass.filter(key.qualify(klass.table_name) => send(primary_key))
375
416
  end
376
417
  opts[:eager_loader] ||= proc do |key_hash, records, associations|
377
- h = key_hash[model.primary_key]
418
+ h = key_hash[primary_key]
378
419
  records.each{|object| object.associations[name] = []}
379
420
  reciprocal = opts.reciprocal
380
- model.eager_loading_dataset(opts, opts.associated_class.filter(key.qualify(opts.associated_class.table_name)=>h.keys), opts.select, associations).all do |assoc_record|
421
+ klass = opts.associated_class
422
+ model.eager_loading_dataset(opts, klass.filter(key.qualify(klass.table_name)=>h.keys), opts.select, associations).all do |assoc_record|
381
423
  next unless objects = h[assoc_record[key]]
382
424
  objects.each do |object|
383
425
  object.associations[name].push(assoc_record)
@@ -386,44 +428,55 @@ module Sequel::Model::Associations
386
428
  end
387
429
  end
388
430
 
431
+ join_type = opts[:graph_join_type]
432
+ select = opts[:graph_select]
433
+ use_only_conditions = opts.include?(:graph_only_conditions)
434
+ only_conditions = opts[:graph_only_conditions]
435
+ conditions = opts[:graph_conditions]
436
+ graph_block = opts[:graph_block]
437
+ opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
438
+ ds = ds.graph(opts.associated_class, use_only_conditions ? only_conditions : [[key, primary_key]] + conditions, :select=>select, :table_alias=>assoc_alias, :join_type=>join_type, :implicit_qualifier=>table_alias, &graph_block)
439
+ # We only load reciprocals for one_to_many associations, as other reciprocals don't make sense
440
+ ds.opts[:eager_graph][:reciprocals][assoc_alias] = opts.reciprocal
441
+ ds
442
+ end
443
+
389
444
  def_association_dataset_methods(opts)
390
445
 
391
446
  unless opts[:read_only]
392
- class_def(opts._add_method) do |o|
393
- o.send(:"#{key}=", pk)
447
+ association_module_private_def(opts._add_method) do |o|
448
+ o.send(:"#{key}=", send(primary_key))
394
449
  o.save || raise(Sequel::Error, "invalid associated object, cannot save")
395
450
  end
396
- private opts._add_method
397
451
  def_add_method(opts)
398
452
 
399
453
  unless opts[:one_to_one]
400
- class_def(opts._remove_method) do |o|
454
+ association_module_private_def(opts._remove_method) do |o|
401
455
  o.send(:"#{key}=", nil)
402
456
  o.save || raise(Sequel::Error, "invalid associated object, cannot save")
403
457
  end
404
- class_def(opts._remove_all_method) do
405
- opts.associated_class.filter(key=>pk).update(key=>nil)
458
+ association_module_private_def(opts._remove_all_method) do
459
+ opts.associated_class.filter(key=>send(primary_key)).update(key=>nil)
406
460
  end
407
- private opts._remove_method, opts._remove_all_method
408
461
  def_remove_methods(opts)
409
462
  end
410
463
  end
411
464
  if opts[:one_to_one]
412
- private opts.association_method, opts.dataset_method
465
+ overridable_methods_module.send(:private, opts.association_method, opts.dataset_method)
413
466
  n = name.to_s.singularize.to_sym
414
467
  raise(Sequel::Error, "one_to_many association names should still be plural even when using the :one_to_one option") if n == name
415
- class_def(n) do |*o|
468
+ association_module_def(n) do |*o|
416
469
  objs = send(name, *o)
417
470
  raise(Sequel::Error, "multiple values found for a one-to-one relationship") if objs.length > 1
418
471
  objs.first
419
472
  end
420
473
  unless opts[:read_only]
421
- private opts.add_method
422
- class_def(:"#{n}=") do |o|
474
+ overridable_methods_module.send(:private, opts.add_method)
475
+ association_module_def(:"#{n}=") do |o|
423
476
  klass = opts.associated_class
424
477
  model.db.transaction do
425
478
  send(opts.add_method, o)
426
- klass.filter(Sequel::SQL::BooleanExpression.new(:AND, {key=>pk}, ~{klass.primary_key=>o.pk}.sql_expr)).update(key=>nil)
479
+ klass.filter(Sequel::SQL::BooleanExpression.new(:AND, {key=>send(primary_key)}, ~{klass.primary_key=>o.pk}.sql_expr)).update(key=>nil)
427
480
  end
428
481
  end
429
482
  end
@@ -432,7 +485,7 @@ module Sequel::Model::Associations
432
485
 
433
486
  # Add the remove_ and remove_all instance methods
434
487
  def def_remove_methods(opts)
435
- class_def(opts.remove_method){|o| remove_associated_object(opts, o)}
436
- class_def(opts.remove_all_method){remove_all_associated_objects(opts)}
488
+ association_module_def(opts.remove_method){|o| remove_associated_object(opts, o)}
489
+ association_module_def(opts.remove_all_method){remove_all_associated_objects(opts)}
437
490
  end
438
491
  end
@@ -1,12 +1,9 @@
1
1
  module Sequel
2
2
  class Model
3
- # Whether to lazily load the schema for future subclasses. Unless turned
4
- # off, checks the database for the table schema whenever a subclass is
5
- # created
6
- @@lazy_load_schema = false
7
-
8
3
  @allowed_columns = nil
4
+ @association_reflections = {}
9
5
  @dataset_methods = {}
6
+ @hooks = {}
10
7
  @primary_key = :id
11
8
  @raise_on_save_failure = true
12
9
  @raise_on_typecast_failure = true
@@ -22,6 +19,9 @@ module Sequel
22
19
  # (default: all columns).
23
20
  metaattr_reader :allowed_columns
24
21
 
22
+ # All association reflections defined for this model (default: none).
23
+ metaattr_reader :association_reflections
24
+
25
25
  # Hash of dataset methods to add to this class and subclasses when
26
26
  # set_dataset is called.
27
27
  metaattr_reader :dataset_methods
@@ -76,7 +76,7 @@ module Sequel
76
76
  :@raise_on_save_failure=>nil, :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
77
77
  :@sti_dataset=>nil, :@sti_key=>nil, :@strict_param_setting=>nil,
78
78
  :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
79
- :@raise_on_typecast_failure=>nil}
79
+ :@raise_on_typecast_failure=>nil, :@association_reflections=>:dup}
80
80
 
81
81
  # Returns the first record from the database matching the conditions.
82
82
  # If a hash is given, it is used as the conditions. If another
@@ -218,6 +218,8 @@ module Sequel
218
218
  nil
219
219
  end
220
220
  end
221
+ hooks = subclass.instance_variable_set(:@hooks, {})
222
+ sup_class.instance_variable_get(:@hooks).each{|k,v| hooks[k] = v.dup}
221
223
  end
222
224
 
223
225
  # Returns the implicit table name for the model class.
@@ -225,14 +227,6 @@ module Sequel
225
227
  name.demodulize.underscore.pluralize.to_sym
226
228
  end
227
229
 
228
- # Set whether to lazily load the schema for future model classes.
229
- # When the schema is lazy loaded, the schema information is grabbed
230
- # during the first instantiation of the class instead of
231
- # when the class is created.
232
- def self.lazy_load_schema=(value)
233
- @@lazy_load_schema = value
234
- end
235
-
236
230
  # Initializes a model instance as an existing record. This constructor is
237
231
  # used by Sequel to initialize model instances when fetching records.
238
232
  # #load requires that values be a hash where all keys are symbols. It
@@ -290,6 +284,11 @@ module Sequel
290
284
  end
291
285
  @dataset.transform(@transform) if @dataset
292
286
  end
287
+
288
+ # Whether or not the given column is serialized for this model.
289
+ def self.serialized?(column)
290
+ @transform ? @transform.include?(column) : false
291
+ end
293
292
 
294
293
  # Set the columns to allow in new/set/update. Using this means that
295
294
  # any columns not listed here will not be modified. If you have any virtual
@@ -312,8 +311,7 @@ module Sequel
312
311
  # and adds a destroy method to it. It also extends the dataset with
313
312
  # the Associations::EagerLoading methods, and assigns a transform to it
314
313
  # if there is one associated with the model. Finally, it attempts to
315
- # determine the database schema based on the given/created dataset unless
316
- # lazy_load_schema is set.
314
+ # determine the database schema based on the given/created dataset.
317
315
  def self.set_dataset(ds, opts={})
318
316
  inherited = opts[:inherited]
319
317
  @dataset = case ds
@@ -328,13 +326,13 @@ module Sequel
328
326
  @dataset.set_model(self)
329
327
  @dataset.transform(@transform) if @transform
330
328
  if inherited
331
- ((@columns = @dataset.columns) rescue nil) unless @@lazy_load_schema
329
+ @columns = @dataset.columns rescue nil
332
330
  else
333
331
  @dataset.extend(DatasetMethods)
334
332
  @dataset.extend(Associations::EagerLoading)
335
333
  @dataset_methods.each{|meth, block| @dataset.meta_def(meth, &block)} if @dataset_methods
336
334
  end
337
- ((@db_schema = inherited ? superclass.db_schema : get_db_schema) rescue nil) unless @@lazy_load_schema
335
+ @db_schema = (inherited ? superclass.db_schema : get_db_schema) rescue nil
338
336
  self
339
337
  end
340
338
  metaalias :dataset=, :set_dataset
@@ -434,11 +432,10 @@ module Sequel
434
432
 
435
433
  # Create the column accessors
436
434
  def self.def_column_accessor(*columns) # :nodoc:
437
- include(@column_accessors_module = Module.new) unless @column_accessors_module
438
435
  columns.each do |column|
439
436
  im = instance_methods.collect{|x| x.to_s}
440
437
  meth = "#{column}="
441
- @column_accessors_module.module_eval do
438
+ overridable_methods_module.module_eval do
442
439
  define_method(column){self[column]} unless im.include?(column.to_s)
443
440
  unless im.include?(meth)
444
441
  define_method(meth) do |*v|
@@ -492,6 +489,13 @@ module Sequel
492
489
  schema_hash
493
490
  end
494
491
 
492
+ # Module that the class includes that holds methods the class adds for column accessors and
493
+ # associations so that the methods can be overridden with super
494
+ def self.overridable_methods_module
495
+ include(@overridable_methods_module = Module.new) unless @overridable_methods_module
496
+ @overridable_methods_module
497
+ end
498
+
495
499
  # Set the columns for this model, reset the str_columns,
496
500
  # and create accessor methods for each column.
497
501
  def self.set_columns(new_columns) # :nodoc:
@@ -501,6 +505,6 @@ module Sequel
501
505
  @columns
502
506
  end
503
507
 
504
- private_class_method :def_column_accessor, :get_db_schema, :set_columns
508
+ private_class_method :def_column_accessor, :get_db_schema, :overridable_methods_module, :set_columns
505
509
  end
506
510
  end