sequel 3.11.0 → 3.12.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.
Files changed (136) hide show
  1. data/CHANGELOG +70 -0
  2. data/Rakefile +1 -1
  3. data/doc/active_record.rdoc +896 -0
  4. data/doc/advanced_associations.rdoc +46 -31
  5. data/doc/association_basics.rdoc +14 -9
  6. data/doc/dataset_basics.rdoc +3 -3
  7. data/doc/migration.rdoc +1011 -0
  8. data/doc/model_hooks.rdoc +198 -0
  9. data/doc/querying.rdoc +811 -86
  10. data/doc/release_notes/3.12.0.txt +304 -0
  11. data/doc/sharding.rdoc +17 -0
  12. data/doc/sql.rdoc +537 -0
  13. data/doc/validations.rdoc +501 -0
  14. data/lib/sequel/adapters/jdbc.rb +19 -27
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
  16. data/lib/sequel/adapters/mysql.rb +5 -4
  17. data/lib/sequel/adapters/odbc.rb +3 -2
  18. data/lib/sequel/adapters/shared/mssql.rb +7 -6
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +2 -8
  21. data/lib/sequel/adapters/shared/sqlite.rb +2 -5
  22. data/lib/sequel/adapters/sqlite.rb +4 -4
  23. data/lib/sequel/core.rb +0 -1
  24. data/lib/sequel/database.rb +2 -1060
  25. data/lib/sequel/database/connecting.rb +227 -0
  26. data/lib/sequel/database/dataset.rb +58 -0
  27. data/lib/sequel/database/dataset_defaults.rb +127 -0
  28. data/lib/sequel/database/logging.rb +62 -0
  29. data/lib/sequel/database/misc.rb +246 -0
  30. data/lib/sequel/database/query.rb +390 -0
  31. data/lib/sequel/database/schema_generator.rb +7 -3
  32. data/lib/sequel/database/schema_methods.rb +351 -7
  33. data/lib/sequel/dataset/actions.rb +9 -2
  34. data/lib/sequel/dataset/misc.rb +6 -2
  35. data/lib/sequel/dataset/mutation.rb +3 -11
  36. data/lib/sequel/dataset/query.rb +49 -6
  37. data/lib/sequel/exceptions.rb +3 -0
  38. data/lib/sequel/extensions/migration.rb +395 -113
  39. data/lib/sequel/extensions/schema_dumper.rb +21 -13
  40. data/lib/sequel/model.rb +27 -25
  41. data/lib/sequel/model/associations.rb +72 -34
  42. data/lib/sequel/model/base.rb +74 -18
  43. data/lib/sequel/model/errors.rb +8 -1
  44. data/lib/sequel/plugins/active_model.rb +8 -0
  45. data/lib/sequel/plugins/association_pks.rb +87 -0
  46. data/lib/sequel/plugins/association_proxies.rb +8 -0
  47. data/lib/sequel/plugins/boolean_readers.rb +12 -6
  48. data/lib/sequel/plugins/caching.rb +14 -7
  49. data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
  50. data/lib/sequel/plugins/composition.rb +2 -1
  51. data/lib/sequel/plugins/force_encoding.rb +10 -7
  52. data/lib/sequel/plugins/hook_class_methods.rb +12 -11
  53. data/lib/sequel/plugins/identity_map.rb +9 -0
  54. data/lib/sequel/plugins/instance_hooks.rb +23 -13
  55. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  56. data/lib/sequel/plugins/many_through_many.rb +18 -4
  57. data/lib/sequel/plugins/nested_attributes.rb +1 -0
  58. data/lib/sequel/plugins/optimistic_locking.rb +1 -1
  59. data/lib/sequel/plugins/rcte_tree.rb +9 -8
  60. data/lib/sequel/plugins/schema.rb +8 -0
  61. data/lib/sequel/plugins/serialization.rb +1 -3
  62. data/lib/sequel/plugins/sharding.rb +135 -0
  63. data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
  64. data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
  65. data/lib/sequel/plugins/string_stripper.rb +26 -0
  66. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
  67. data/lib/sequel/plugins/timestamps.rb +15 -2
  68. data/lib/sequel/plugins/touch.rb +13 -0
  69. data/lib/sequel/plugins/update_primary_key.rb +48 -0
  70. data/lib/sequel/plugins/validation_class_methods.rb +8 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  72. data/lib/sequel/sql.rb +17 -20
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/postgres_spec.rb +5 -5
  75. data/spec/core/core_sql_spec.rb +17 -1
  76. data/spec/core/database_spec.rb +17 -5
  77. data/spec/core/dataset_spec.rb +31 -8
  78. data/spec/core/schema_generator_spec.rb +8 -1
  79. data/spec/core/schema_spec.rb +13 -0
  80. data/spec/extensions/association_pks_spec.rb +85 -0
  81. data/spec/extensions/hook_class_methods_spec.rb +9 -9
  82. data/spec/extensions/migration_spec.rb +339 -219
  83. data/spec/extensions/schema_dumper_spec.rb +28 -17
  84. data/spec/extensions/sharding_spec.rb +272 -0
  85. data/spec/extensions/single_table_inheritance_spec.rb +92 -4
  86. data/spec/extensions/skip_create_refresh_spec.rb +17 -0
  87. data/spec/extensions/string_stripper_spec.rb +23 -0
  88. data/spec/extensions/update_primary_key_spec.rb +65 -0
  89. data/spec/extensions/validation_class_methods_spec.rb +5 -5
  90. data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
  91. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
  92. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  93. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  94. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
  95. data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
  96. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
  97. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
  98. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
  99. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
  100. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
  101. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
  102. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
  103. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
  104. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  105. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
  106. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
  107. data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
  108. data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
  109. data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
  110. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  111. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
  112. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  113. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
  114. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  115. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
  116. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
  117. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  118. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  119. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
  120. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
  121. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
  122. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
  123. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
  124. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
  125. data/spec/integration/eager_loader_test.rb +20 -20
  126. data/spec/integration/migrator_test.rb +187 -0
  127. data/spec/integration/plugin_test.rb +150 -0
  128. data/spec/integration/schema_test.rb +13 -2
  129. data/spec/model/associations_spec.rb +41 -14
  130. data/spec/model/base_spec.rb +69 -0
  131. data/spec/model/eager_loading_spec.rb +7 -3
  132. data/spec/model/record_spec.rb +79 -4
  133. data/spec/model/validations_spec.rb +21 -9
  134. metadata +66 -5
  135. data/doc/schema.rdoc +0 -36
  136. data/lib/sequel/database/schema_sql.rb +0 -320
@@ -7,6 +7,7 @@ module Sequel
7
7
  # Nested attributes are created using the nested_attributes method:
8
8
  #
9
9
  # Artist.one_to_many :albums
10
+ # Artist.plugin :nested_attributes
10
11
  # Artist.nested_attributes :albums
11
12
  # a = Artist.new(:name=>'YJM',
12
13
  # :albums_attributes=>[{:name=>'RF'}, {:name=>'MO'}])
@@ -1,5 +1,5 @@
1
1
  module Sequel
2
- require 'plugins/instance_filters'
2
+ tsk_require 'sequel/plugins/instance_filters'
3
3
 
4
4
  module Plugins
5
5
  # This plugin implements a simple database-independent locking mechanism
@@ -146,11 +146,11 @@ module Sequel
146
146
  end
147
147
  end
148
148
  a[:after_load] ||= aal
149
- a[:eager_loader] ||= proc do |key_hash, objects, associations|
150
- id_map = key_hash[key]
149
+ a[:eager_loader] ||= proc do |eo|
150
+ id_map = eo[:key_hash][key]
151
151
  parent_map = {}
152
152
  children_map = {}
153
- objects.each do |obj|
153
+ eo[:rows].each do |obj|
154
154
  parent_map[obj[prkey]] = obj
155
155
  (children_map[obj[key]] ||= []) << obj
156
156
  obj.associations[ancestors] = []
@@ -164,7 +164,7 @@ module Sequel
164
164
  model.join(t, key=>prkey).
165
165
  select(SQL::QualifiedIdentifier.new(t, ka), c_all)),
166
166
  r.select,
167
- associations).all do |obj|
167
+ eo[:associations], eo).all do |obj|
168
168
  opk = obj[prkey]
169
169
  if in_pm = parent_map.has_key?(opk)
170
170
  if idm_obj = parent_map[opk]
@@ -224,11 +224,12 @@ module Sequel
224
224
  end
225
225
  end
226
226
  d[:after_load] = dal
227
- d[:eager_loader] ||= proc do |key_hash, objects, associations|
228
- id_map = key_hash[prkey]
227
+ d[:eager_loader] ||= proc do |eo|
228
+ id_map = eo[:key_hash][prkey]
229
+ associations = eo[:associations]
229
230
  parent_map = {}
230
231
  children_map = {}
231
- objects.each do |obj|
232
+ eo[:rows].each do |obj|
232
233
  parent_map[obj[prkey]] = obj
233
234
  obj.associations[descendants] = []
234
235
  obj.associations[childrena] = []
@@ -248,7 +249,7 @@ module Sequel
248
249
  model.eager_loading_dataset(r,
249
250
  model.from(t).with_recursive(t, base_case, recursive_case),
250
251
  r.select,
251
- associations).all do |obj|
252
+ associations, eo).all do |obj|
252
253
  if level
253
254
  no_cache = no_cache_level == obj.values.delete(la)
254
255
  end
@@ -8,6 +8,14 @@ module Sequel
8
8
  # This plugin is mostly suited to test code. If there is any
9
9
  # chance that your application's schema could change, you should
10
10
  # be using the migration extension instead.
11
+ #
12
+ # Usage:
13
+ #
14
+ # # Add the schema methods to all model subclasses (called before loading subclasses)
15
+ # Sequel::Model.plugin :schema
16
+ #
17
+ # # Add the schema methods to the Album class
18
+ # Album.plugin :schema
11
19
  module Schema
12
20
  module ClassMethods
13
21
  # Creates table, using the column information from set_schema.
@@ -117,10 +117,8 @@ module Sequel
117
117
 
118
118
  # Serialize all deserialized values
119
119
  def before_save
120
+ deserialized_values.each{|k,v| @values[k] = serialize_value(k, v)}
120
121
  super
121
- deserialized_values.each do |k,v|
122
- @values[k] = serialize_value(k, v)
123
- end
124
122
  end
125
123
 
126
124
  # Empty the deserialized values when refreshing.
@@ -0,0 +1,135 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The sharding plugin makes it easy to use Sequel's sharding features
4
+ # with models. It lets you create model objects on specific shards,
5
+ # and any models retrieved from specific shards are automatically
6
+ # saved back to those shards. It also works with associations,
7
+ # so that model objects retrieved from specific shards will only
8
+ # return associated objects from that shard, and using the
9
+ # add/remove/remove_all association methods will only affect
10
+ # that shard.
11
+ #
12
+ # Usage:
13
+ #
14
+ # # Add the sharding support to all model subclasses (called before loading subclasses)
15
+ # Sequel::Model.plugin :sharding
16
+ #
17
+ # # Add the sharding support to the Album class
18
+ # Album.plugin :sharding
19
+ module Sharding
20
+ module ClassMethods
21
+ # Create a new object on the given shard s.
22
+ def create_using_server(s, values={}, &block)
23
+ new_using_server(s, values, &block).save
24
+ end
25
+
26
+ # When eagerly loading, if the current dataset has a defined shard and the
27
+ # dataset that you will be using to get the associated records does not,
28
+ # use the current dataset's shard for the associated dataset.
29
+ def eager_loading_dataset(opts, ds, select, associations, eager_options={})
30
+ ds = super(opts, ds, select, associations, eager_options)
31
+ if !ds.opts[:server] and s = eager_options[:self] and server = s.opts[:server]
32
+ ds = ds.server(server)
33
+ end
34
+ ds
35
+ end
36
+
37
+ # Return a newly instantiated object that is tied to the given
38
+ # shard s. When the object is saved, a record will be inserted
39
+ # on shard s.
40
+ def new_using_server(s, values={}, &block)
41
+ new(values, &block).set_server(s)
42
+ end
43
+ end
44
+
45
+ module InstanceMethods
46
+ # Set the shard that this object is tied to. Returns self.
47
+ def set_server(s)
48
+ @server = s
49
+ self
50
+ end
51
+
52
+ # Set the server that this object is tied to, unless it has
53
+ # already been set. Returns self.
54
+ def set_server?(s)
55
+ @server ||= s
56
+ self
57
+ end
58
+
59
+ # Ensure that the instance dataset is tied to the correct shard.
60
+ def this
61
+ use_server(super)
62
+ end
63
+
64
+ private
65
+
66
+ # Ensure that association datasets are tied to the correct shard.
67
+ def _apply_association_options(*args)
68
+ use_server(super)
69
+ end
70
+
71
+ # Ensure that the object is inserted into the correct shard.
72
+ def _insert_dataset
73
+ use_server(super)
74
+ end
75
+
76
+ # Ensure that the join table for many_to_many associations uses the correct shard.
77
+ def _join_table_dataset(opts)
78
+ use_server(super)
79
+ end
80
+
81
+ # Make sure to use the correct shard when using a transaction
82
+ def checked_transaction(opts={}, &block)
83
+ super(@server ? {:server=>@server}.merge(opts) : opts, &block)
84
+ end
85
+
86
+ # If creating the object by doing <tt>add_association</tt> for a
87
+ # +many_to_many+ association, make sure the associated object is created on the
88
+ # current object's shard, unless the passed object already has an assigned shard.
89
+ def ensure_associated_primary_key(opts, o, *args)
90
+ o.set_server?(@server) if o.respond_to?(:set_server?)
91
+ super
92
+ end
93
+
94
+ # Set the given dataset to use the current object's shard.
95
+ def use_server(ds)
96
+ @server ? ds.server(@server) : ds
97
+ end
98
+ end
99
+
100
+ module DatasetMethods
101
+ # If a row proc exists on the dataset, replace it with one that calls the
102
+ # previous row_proc, but calls set_server on the output of that row_proc,
103
+ # ensuring that objects retrieved by a specific shard know which shard they
104
+ # are tied to.
105
+ def server(s)
106
+ ds = super
107
+ if rp = row_proc
108
+ ds.row_proc = proc{|r| rp.call(r).set_server(s)}
109
+ end
110
+ ds
111
+ end
112
+
113
+ private
114
+
115
+ # Set the shard of all retrieved objects to the shard of
116
+ # the table's dataset, or the current shard if the table's
117
+ # dataset does not have a shard.
118
+ def graph_each
119
+ ta = @opts[:graph][:table_aliases]
120
+ s = @opts[:server]
121
+ super do |r|
122
+ r.each do |k, v|
123
+ if ds = ta[k]
124
+ dss = ds.opts[:server]
125
+ end
126
+ vs = dss || s
127
+ v.set_server(vs) if vs && v.respond_to?(:set_server)
128
+ end
129
+ yield r
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -1,27 +1,76 @@
1
1
  module Sequel
2
2
  module Plugins
3
- # Sequel's built in Single Table Inheritance plugin makes subclasses
4
- # of this model only load rows where the given key field matches the
5
- # subclass's name. If the key given has a NULL value or there are
6
- # any problems looking up the class, uses the current class.
3
+ # The single_table_inheritance plugin allows storing all objects
4
+ # in the same class hierarchy in the same table. It makes it so
5
+ # subclasses of this model only load rows related to the subclass,
6
+ # and when you retrieve rows from the main class, you get instances
7
+ # of the subclasses (if the rows should use the subclasses's class).
8
+ #
9
+ # By default, the plugin assumes that the +sti_key+ column (the first
10
+ # argument to the plugin) holds the class name as a string. However,
11
+ # you can override this by using the <tt>:model_map</tt> option and/or
12
+ # the <tt>:key_map</tt> option.
7
13
  #
8
- # You should only use this in the parent class, not in the subclasses.
14
+ # You should only load this plugin in the parent class, not in the subclasses.
9
15
  #
10
16
  # You shouldn't call set_dataset in the model after applying this
11
- # plugin, otherwise subclasses might use the wrong dataset.
17
+ # plugin, otherwise subclasses might use the wrong dataset. You should
18
+ # make sure this plugin is loaded before the subclasses. Note that since you
19
+ # need to load the plugin before the subclasses are created, you can't use
20
+ # direct class references in the plugin class. You should specify subclasses
21
+ # in the plugin call using class name strings or symbols, see usage below.
12
22
  #
13
- # The filters and row_proc that sti_key sets up in subclasses may not work correctly if
14
- # those subclasses have further subclasses. For those middle subclasses,
15
- # you may need to call set_dataset manually with the correct filter and
16
- # row_proc.
23
+ # Usage:
24
+ #
25
+ # # Use the default of storing the class name in the sti_key
26
+ # # column (:kind in this case)
27
+ # Employee.plugin :single_table_inheritance, :kind
28
+ #
29
+ # # Using integers to store the class type, with a :model_map hash
30
+ # # and an sti_key of :type
31
+ # Employee.plugin :single_table_inheritance, :type,
32
+ # :model_map=>{1=>:Staff, 2=>:Manager}
33
+ #
34
+ # # Using non-class name strings
35
+ # Employee.plugin :single_table_inheritance, :type,
36
+ # :model_map=>{'line staff'=>:Staff, 'supervisor'=>:Manager}
37
+ #
38
+ # # Using custom procs, with :model_map taking column values
39
+ # # and yielding either a class, string, symbol, or nil,
40
+ # # and :key_map taking a class object and returning the column
41
+ # # value to use
42
+ # Employee.plugin :single_table_inheritance, :type,
43
+ # :model_map=>proc{|v| v.reverse},
44
+ # :key_map=>proc{|klass| klass.name.reverse}
45
+ #
46
+ # One minor issue to note is that if you specify the <tt>:key_map</tt>
47
+ # option as a hash, instead of having it inferred from the <tt>:model_map</tt>,
48
+ # you should only use class name strings as keys, you should not use symbols
49
+ # as keys.
17
50
  module SingleTableInheritance
18
- # Set the sti_key and sti_dataset for the model, and change the
19
- # dataset's row_proc so that the dataset yields objects of varying classes,
20
- # where the class used has the same name as the key field.
21
- def self.configure(model, key)
51
+ # Setup the necessary STI variables, see the module RDoc for SingleTableInheritance
52
+ def self.configure(model, key, opts={})
22
53
  model.instance_eval do
54
+ @sti_key_array = nil
23
55
  @sti_key = key
24
56
  @sti_dataset = dataset
57
+ @sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
58
+ @sti_key_map = if km = opts[:key_map]
59
+ if km.is_a?(Hash)
60
+ h = Hash.new{|h,k| h[k.to_s] unless k.is_a?(String)}
61
+ h.merge!(km)
62
+ else
63
+ km
64
+ end
65
+ elsif sti_model_map.is_a?(Hash)
66
+ h = Hash.new{|h,k| h[k.to_s] unless k.is_a?(String)}
67
+ sti_model_map.each do |k,v|
68
+ h[v.to_s] = k
69
+ end
70
+ h
71
+ else
72
+ lambda{|klass| klass.name.to_s}
73
+ end
25
74
  dataset.row_proc = lambda{|r| model.sti_load(r)}
26
75
  end
27
76
  end
@@ -34,17 +83,38 @@ module Sequel
34
83
  # The column name holding the STI key for this model
35
84
  attr_reader :sti_key
36
85
 
37
- # Copy the sti_key and sti_dataset to the subclasses, and filter the
38
- # subclass's dataset so it is restricted to rows where the key column
39
- # matches the subclass's name.
86
+ # Array holding keys for all subclasses of this class, used for the
87
+ # dataset filter in subclasses. Nil in the main class.
88
+ attr_reader :sti_key_array
89
+
90
+ # A hash/proc with class keys and column value values, mapping
91
+ # the the class to a particular value given to the sti_key column.
92
+ # Used to set the column value when creating objects, and for the
93
+ # filter when retrieving objects in subclasses.
94
+ attr_reader :sti_key_map
95
+
96
+ # A hash/proc with column value keys and class values, mapping
97
+ # the value of the sti_key column to the appropriate class to use.
98
+ attr_reader :sti_model_map
99
+
100
+ # Copy the necessary attributes to the subclasses, and filter the
101
+ # subclass's dataset based on the sti_kep_map entry for the class.
40
102
  def inherited(subclass)
41
103
  super
42
104
  sk = sti_key
43
105
  sd = sti_dataset
44
- subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>subclass.name.to_s), :inherited=>true)
106
+ skm = sti_key_map
107
+ smm = sti_model_map
108
+ key = skm[subclass]
109
+ sti_subclass_added(key)
110
+ ska = [key]
111
+ subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>ska), :inherited=>true)
45
112
  subclass.instance_eval do
46
113
  @sti_key = sk
114
+ @sti_key_array = ska
47
115
  @sti_dataset = sd
116
+ @sti_key_map = skm
117
+ @sti_model_map = smm
48
118
  @simple_table = nil
49
119
  end
50
120
  end
@@ -52,21 +122,43 @@ module Sequel
52
122
  # Return an instance of the class specified by sti_key,
53
123
  # used by the row_proc.
54
124
  def sti_load(r)
55
- v = r[sti_key]
56
- model = if (v && v != '')
125
+ sti_class(sti_model_map[r[sti_key]]).load(r)
126
+ end
127
+
128
+ # Make sure that all subclasses of the parent class correctly include
129
+ # keys for all of their descendant classes.
130
+ def sti_subclass_added(key)
131
+ if sti_key_array
132
+ sti_key_array << key
133
+ superclass.sti_subclass_added(key)
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ # Return a class object. If a class is given, return it directly.
140
+ # Treat strings and symbols as class names. If nil is given or
141
+ # an invalid class name string or symbol is used, return self.
142
+ # Raise an error for other types.
143
+ def sti_class(v)
144
+ case v
145
+ when String, Symbol
57
146
  constantize(v) rescue self
58
- else
147
+ when nil
59
148
  self
149
+ when Class
150
+ v
151
+ else
152
+ raise(Error, "Invalid class type used: #{v.inspect}")
60
153
  end
61
- model.load(r)
62
154
  end
63
155
  end
64
156
 
65
157
  module InstanceMethods
66
- # Set the sti_key column to the name of the model.
158
+ # Set the sti_key column based on the sti_key_map.
67
159
  def before_create
68
- return false if super == false
69
- send("#{model.sti_key}=", model.name.to_s) unless send(model.sti_key)
160
+ send("#{model.sti_key}=", model.sti_key_map[model]) unless send(model.sti_key)
161
+ super
70
162
  end
71
163
  end
72
164
  end
@@ -0,0 +1,35 @@
1
+ module Sequel
2
+ module Plugins
3
+ # SkipCreateRefresh is a simple plugin that make Sequel not
4
+ # refresh after saving a new model object. Sequel does the
5
+ # refresh to make sure all columns are populated, which is
6
+ # necessary so that database defaults work correctly.
7
+ #
8
+ # This plugin is mostly for performance reasons where you
9
+ # want to save the cost of select statement after the insert,
10
+ # but it could also help cases where records are not
11
+ # immediately available for selection after insertion.
12
+ #
13
+ # Note that Sequel does not attempt to refresh records when
14
+ # updating existing model objects, only when inserting new
15
+ # model objects.
16
+ #
17
+ # Usage:
18
+ #
19
+ # # Make all model subclass instances skip refreshes when saving
20
+ # # (called before loading subclasses)
21
+ # Sequel::Model.plugin :skip_create_refresh
22
+ #
23
+ # # Make the Album class skip refreshes when saving
24
+ # Album.plugin :skip_create_refresh
25
+ module SkipCreateRefresh
26
+ module InstanceMethods
27
+ private
28
+ # Do nothing instead of refreshing the record inside of save.
29
+ def _save_refresh
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end