sequel 2.11.0 → 2.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 (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -0,0 +1,937 @@
1
+ module Sequel
2
+ class Model
3
+ extend Enumerable
4
+ extend Inflections
5
+ extend Metaprogramming
6
+ include Metaprogramming
7
+
8
+ # Class methods for Sequel::Model that implement basic model functionality.
9
+ #
10
+ # * All of the method names in Model::DATASET_METHODS have class methods created that call
11
+ # the Model's dataset with the method of the same name with the given arguments.
12
+ module ClassMethods
13
+ # Which columns should be the only columns allowed in a call to set
14
+ # (default: not set, so all columns not otherwise restricted).
15
+ attr_reader :allowed_columns
16
+
17
+ # Array of modules that extend this model's dataset. Stored
18
+ # so that if the model's dataset is changed, it will be extended
19
+ # with all of these modules.
20
+ attr_reader :dataset_method_modules
21
+
22
+ # Hash of dataset methods with method name keys and proc values that are
23
+ # stored so when the dataset changes, methods defined with def_dataset_method
24
+ # will be applied to the new dataset.
25
+ attr_reader :dataset_methods
26
+
27
+ # The primary key for the class. Sequel can determine this automatically for
28
+ # many databases, but not all, so you may need to set it manually. If not
29
+ # determined automatically, the default is :id.
30
+ attr_reader :primary_key
31
+
32
+ # Whether to raise an error instead of returning nil on a failure
33
+ # to save/create/save_changes/etc due to a validation failure or
34
+ # a before_* hook returning false.
35
+ attr_accessor :raise_on_save_failure
36
+
37
+ # Whether to raise an error when unable to typecast data for a column
38
+ # (default: true). This should be set to false if you want to use
39
+ # validations to display nice error messages to the user (e.g. most
40
+ # web applications). You can use the validates_not_string validations
41
+ # (from either the validation_helpers or validation_class_methods standard
42
+ # plugins) in connection with option to check for typecast failures for
43
+ # columns that aren't blobs or strings.
44
+ attr_accessor :raise_on_typecast_failure
45
+
46
+ # Which columns are specifically restricted in a call to set/update/new/etc.
47
+ # (default: not set). Some columns are restricted regardless of
48
+ # this setting, such as the primary key column and columns in Model::RESTRICTED_SETTER_METHODS.
49
+ attr_reader :restricted_columns
50
+
51
+ # Should be the literal primary key column name if this Model's table has a simple primary key, or
52
+ # nil if the model has a compound primary key or no primary key.
53
+ attr_reader :simple_pk
54
+
55
+ # Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
56
+ # or nil otherwise. This and simple_pk are used for an optimization in Model.[].
57
+ attr_reader :simple_table
58
+
59
+ # Whether new/set/update and their variants should raise an error
60
+ # if an invalid key is used. A key is invalid if no setter method exists
61
+ # for that key or the access to the setter method is restricted (e.g. due to it
62
+ # being a primary key field). If set to false, silently skip
63
+ # any key where the setter method doesn't exist or access to it is restricted.
64
+ attr_accessor :strict_param_setting
65
+
66
+ # Whether to typecast the empty string ('') to nil for columns that
67
+ # are not string or blob. In most cases the empty string would be the
68
+ # way to specify a NULL SQL value in string form (nil.to_s == ''),
69
+ # and an empty string would not usually be typecast correctly for other
70
+ # types, so the default is true.
71
+ attr_accessor :typecast_empty_string_to_nil
72
+
73
+ # Whether to typecast attribute values on assignment (default: true).
74
+ # If set to false, no typecasting is done, so it will be left up to the
75
+ # database to typecast the value correctly.
76
+ attr_accessor :typecast_on_assignment
77
+
78
+ # Whether to use a transaction by default when saving/deleting records (default: true).
79
+ # If you are sending database queries in before_* or after_* hooks, you shouldn't change
80
+ # the default setting without a good reason.
81
+ attr_accessor :use_transactions
82
+
83
+ # Returns the first record from the database matching the conditions.
84
+ # If a hash is given, it is used as the conditions. If another
85
+ # object is given, it finds the first record whose primary key(s) match
86
+ # the given argument(s).
87
+ def [](*args)
88
+ args = args.first if (args.size == 1)
89
+ return dataset[args] if args.is_a?(Hash)
90
+ if t = simple_table and p = simple_pk
91
+ with_sql("SELECT * FROM #{t} WHERE #{p} = #{dataset.literal(args)}").first
92
+ else
93
+ dataset[primary_key_hash(args)]
94
+ end
95
+ end
96
+
97
+ # Returns the columns in the result set in their original order.
98
+ # Generally, this will use the columns determined via the database
99
+ # schema, but in certain cases (e.g. models that are based on a joined
100
+ # dataset) it will use Dataset#columns to find the columns, which
101
+ # may be empty if the Dataset has no records.
102
+ def columns
103
+ @columns || set_columns(dataset.naked.columns)
104
+ end
105
+
106
+ # Creates instance using new with the given values and block, and saves it.
107
+ def create(values = {}, &block)
108
+ new(values, &block).save
109
+ end
110
+
111
+ # Returns the dataset associated with the Model class. Raises
112
+ # an error if there is no associated dataset for this class.
113
+ def dataset
114
+ @dataset || raise(Error, "No dataset associated with #{self}")
115
+ end
116
+
117
+ # Returns the database associated with the Model class.
118
+ # If this model doesn't have a database associated with it,
119
+ # assumes the superclass's database, or the first object in
120
+ # Sequel::DATABASES. If no Sequel::Database object has
121
+ # been created, raises an error.
122
+ def db
123
+ return @db if @db
124
+ @db = self == Model ? DATABASES.first : superclass.db
125
+ raise(Error, "No database associated with #{self}") unless @db
126
+ @db
127
+ end
128
+
129
+ # Sets the database associated with the Model class. If the
130
+ # model has an associated dataset, sets the model's dataset
131
+ # to a dataset on the new database with the same options
132
+ # used by the current dataset.
133
+ def db=(db)
134
+ @db = db
135
+ set_dataset(db.dataset(@dataset.opts)) if @dataset
136
+ end
137
+
138
+ # Returns the cached schema information if available or gets it
139
+ # from the database.
140
+ def db_schema
141
+ @db_schema ||= get_db_schema
142
+ end
143
+
144
+ # If a block is given, define a method on the dataset (if the model has an associated dataset) with the given argument name using
145
+ # the given block as well as a method on the model that calls the
146
+ # dataset method. Stores the method name and block so that it can be reapplied if the model's
147
+ # dataset changes.
148
+ #
149
+ # If a block is not given, define a method on the model for each argument
150
+ # that calls the dataset method of the same argument name.
151
+ def def_dataset_method(*args, &block)
152
+ raise(Error, "No arguments given") if args.empty?
153
+ if block_given?
154
+ raise(Error, "Defining a dataset method using a block requires only one argument") if args.length > 1
155
+ meth = args.first
156
+ @dataset_methods[meth] = block
157
+ dataset.meta_def(meth, &block) if @dataset
158
+ end
159
+ args.each{|arg| instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg)}
160
+ end
161
+
162
+ # Finds a single record according to the supplied filter, e.g.:
163
+ #
164
+ # Ticket.find :author => 'Sharon' # => record
165
+ #
166
+ # You are encouraged to use Model.[] or Model.first instead of this method.
167
+ def find(*args, &block)
168
+ filter(*args, &block).first
169
+ end
170
+
171
+ # Like find but invokes create with given conditions when record does not
172
+ # exist.
173
+ def find_or_create(cond)
174
+ find(cond) || create(cond)
175
+ end
176
+
177
+ # If possible, set the dataset for the model subclass as soon as it
178
+ # is created. Also, make sure the inherited class instance variables
179
+ # are copied into the subclass.
180
+ def inherited(subclass)
181
+ ivs = subclass.instance_variables.collect{|x| x.to_s}
182
+ EMPTY_INSTANCE_VARIABLES.each{|iv| subclass.instance_variable_set(iv, nil) unless ivs.include?(iv.to_s)}
183
+ INHERITED_INSTANCE_VARIABLES.each do |iv, dup|
184
+ next if ivs.include?(iv.to_s)
185
+ sup_class_value = instance_variable_get(iv)
186
+ sup_class_value = sup_class_value.dup if dup == :dup && sup_class_value
187
+ subclass.instance_variable_set(iv, sup_class_value)
188
+ end
189
+ unless ivs.include?("@dataset")
190
+ db
191
+ begin
192
+ if self == Model
193
+ subclass.set_dataset(subclass.implicit_table_name) unless subclass.name.empty?
194
+ elsif ds = instance_variable_get(:@dataset)
195
+ subclass.set_dataset(ds.clone, :inherited=>true)
196
+ end
197
+ rescue
198
+ nil
199
+ end
200
+ end
201
+ end
202
+
203
+ # Returns the implicit table name for the model class.
204
+ def implicit_table_name
205
+ pluralize(underscore(demodulize(name))).to_sym
206
+ end
207
+
208
+ # Initializes a model instance as an existing record. This constructor is
209
+ # used by Sequel to initialize model instances when fetching records.
210
+ # load requires that values be a hash where all keys are symbols. It
211
+ # probably should not be used by external code.
212
+ def load(values)
213
+ new(values, true)
214
+ end
215
+
216
+ # Mark the model as not having a primary key. Not having a primary key
217
+ # can cause issues, among which is that you won't be able to update records.
218
+ def no_primary_key
219
+ @simple_pk = @primary_key = nil
220
+ end
221
+
222
+ # Returns primary key attribute hash. If using a composite primary key
223
+ # value such be an array with values for each primary key in the correct
224
+ # order. For a standard primary key, value should be an object with a
225
+ # compatible type for the key. If the model does not have a primary key,
226
+ # raises an Error.
227
+ def primary_key_hash(value)
228
+ raise(Error, "#{self} does not have a primary key") unless key = @primary_key
229
+ case key
230
+ when Array
231
+ hash = {}
232
+ key.each_with_index{|k,i| hash[k] = value[i]}
233
+ hash
234
+ else
235
+ {key => value}
236
+ end
237
+ end
238
+
239
+ # Restrict the setting of the primary key(s) inside new/set/update. Because
240
+ # this is the default, this only make sense to use in a subclass where the
241
+ # parent class has used unrestrict_primary_key.
242
+ def restrict_primary_key
243
+ @restrict_primary_key = true
244
+ end
245
+
246
+ # Whether or not setting the primary key inside new/set/update is
247
+ # restricted, true by default.
248
+ def restrict_primary_key?
249
+ @restrict_primary_key
250
+ end
251
+
252
+ # Set the columns to allow in new/set/update. Using this means that
253
+ # any columns not listed here will not be modified. If you have any virtual
254
+ # setter methods (methods that end in =) that you want to be used in
255
+ # new/set/update, they need to be listed here as well (without the =).
256
+ #
257
+ # It may be better to use (set|update)_only instead of this in places where
258
+ # only certain columns may be allowed.
259
+ def set_allowed_columns(*cols)
260
+ @allowed_columns = cols
261
+ end
262
+
263
+ # Sets the dataset associated with the Model class. ds can be a Symbol
264
+ # (specifying a table name in the current database), or a Dataset.
265
+ # If a dataset is used, the model's database is changed to the given
266
+ # dataset. If a symbol is used, a dataset is created from the current
267
+ # database with the table name given. Other arguments raise an Error.
268
+ #
269
+ # This changes the row_proc of the given dataset to return
270
+ # model objects, extends the dataset with the dataset_method_modules,
271
+ # and defines methods on the dataset using the dataset_methods.
272
+ # It also attempts to determine the database schema for the model,
273
+ # based on the given dataset.
274
+ def set_dataset(ds, opts={})
275
+ inherited = opts[:inherited]
276
+ @dataset = case ds
277
+ when Symbol
278
+ @simple_table = db.literal(ds)
279
+ db[ds]
280
+ when Dataset
281
+ @simple_table = nil
282
+ @db = ds.db
283
+ ds
284
+ else
285
+ raise(Error, "Model.set_dataset takes a Symbol or a Sequel::Dataset")
286
+ end
287
+ @dataset.row_proc = Proc.new{|r| load(r)}
288
+ @dataset.transform(@transform) if @transform
289
+ if inherited
290
+ @simple_table = superclass.simple_table
291
+ @columns = @dataset.columns rescue nil
292
+ else
293
+ @dataset_method_modules.each{|m| @dataset.extend(m)} if @dataset_method_modules
294
+ @dataset_methods.each{|meth, block| @dataset.meta_def(meth, &block)} if @dataset_methods
295
+ end
296
+ @dataset.model = self if @dataset.respond_to?(:model=)
297
+ @db_schema = (inherited ? superclass.db_schema : get_db_schema) rescue nil
298
+ self
299
+ end
300
+ alias dataset= set_dataset
301
+
302
+ # Sets the primary key for this model. You can use either a regular
303
+ # or a composite primary key.
304
+ #
305
+ # Example:
306
+ # class Tagging < Sequel::Model
307
+ # # composite key
308
+ # set_primary_key :taggable_id, :tag_id
309
+ # end
310
+ #
311
+ # class Person < Sequel::Model
312
+ # # regular key
313
+ # set_primary_key :person_id
314
+ # end
315
+ #
316
+ # You can set it to nil to not have a primary key, but that
317
+ # cause certain things not to work, see no_primary_key.
318
+ def set_primary_key(*key)
319
+ @simple_pk = key.length == 1 ? db.literal(key.first) : nil
320
+ @primary_key = (key.length == 1) ? key[0] : key.flatten
321
+ end
322
+
323
+ # Set the columns to restrict in new/set/update. Using this means that
324
+ # attempts to call setter methods for the columns listed here will cause an
325
+ # exception or be silently skipped (based on the strict_param_setting setting.
326
+ # If you have any virtual # setter methods (methods that end in =) that you
327
+ # want not to be used in new/set/update, they need to be listed here as well (without the =).
328
+ #
329
+ # It may be better to use (set|update)_except instead of this in places where
330
+ # only certain columns may be allowed.
331
+ def set_restricted_columns(*cols)
332
+ @restricted_columns = cols
333
+ end
334
+
335
+ # Defines a method that returns a filtered dataset. Subsets
336
+ # create dataset methods, so they can be chained for scoping.
337
+ # For example:
338
+ #
339
+ # Topic.subset(:joes, :username.like('%joe%'))
340
+ # Topic.subset(:popular){|o| o.num_posts > 100}
341
+ # Topic.subset(:recent){|o| o.created_on > Date.today - 7}
342
+ #
343
+ # Allows you to do:
344
+ #
345
+ # Topic.joes.recent.popular
346
+ #
347
+ # to get topics with a username that includes joe that
348
+ # have more than 100 posts and were created less than
349
+ # 7 days ago.
350
+ #
351
+ # Both the args given and the block are passed to Dataset#filter.
352
+ def subset(name, *args, &block)
353
+ def_dataset_method(name){filter(*args, &block)}
354
+ end
355
+
356
+ # Returns name of primary table for the dataset.
357
+ def table_name
358
+ dataset.opts[:from].first
359
+ end
360
+
361
+ # Allow the setting of the primary key(s) inside new/set/update.
362
+ def unrestrict_primary_key
363
+ @restrict_primary_key = false
364
+ end
365
+
366
+ private
367
+
368
+ # Create a column accessor for a column with a method name that is hard to use in ruby code.
369
+ def def_bad_column_accessor(column)
370
+ overridable_methods_module.module_eval do
371
+ define_method(column){self[column]}
372
+ define_method("#{column}="){|v| self[column] = v}
373
+ end
374
+ end
375
+
376
+ # Create the column accessors. For columns that can be used as method names directly in ruby code,
377
+ # use a string to define the method for speed. For other columns names, use a block.
378
+ def def_column_accessor(*columns)
379
+ columns, bad_columns = columns.partition{|x| NORMAL_METHOD_NAME_REGEXP.match(x.to_s)}
380
+ bad_columns.each{|x| def_bad_column_accessor(x)}
381
+ im = instance_methods.collect{|x| x.to_s}
382
+ columns.each do |column|
383
+ meth = "#{column}="
384
+ overridable_methods_module.module_eval("def #{column}; self[:#{column}] end") unless im.include?(column.to_s)
385
+ overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end") unless im.include?(meth)
386
+ end
387
+ end
388
+
389
+ # Get the schema from the database, fall back on checking the columns
390
+ # via the database if that will return inaccurate results or if
391
+ # it raises an error.
392
+ def get_db_schema(reload = false)
393
+ set_columns(nil)
394
+ return nil unless @dataset
395
+ schema_hash = {}
396
+ ds_opts = dataset.opts
397
+ single_table = ds_opts[:from] && (ds_opts[:from].length == 1) \
398
+ && !ds_opts.include?(:join) && !ds_opts.include?(:sql)
399
+ get_columns = proc{columns rescue []}
400
+ if single_table && (schema_array = (db.schema(table_name, :reload=>reload) rescue nil))
401
+ schema_array.each{|k,v| schema_hash[k] = v}
402
+ if ds_opts.include?(:select)
403
+ # Dataset only selects certain columns, delete the other
404
+ # columns from the schema
405
+ cols = get_columns.call
406
+ schema_hash.delete_if{|k,v| !cols.include?(k)}
407
+ cols.each{|c| schema_hash[c] ||= {}}
408
+ else
409
+ # Dataset is for a single table with all columns,
410
+ # so set the columns based on the order they were
411
+ # returned by the schema.
412
+ cols = schema_array.collect{|k,v| k}
413
+ set_columns(cols)
414
+ # Set the primary key(s) based on the schema information
415
+ pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
416
+ pks.length > 0 ? set_primary_key(*pks) : no_primary_key
417
+ # Also set the columns for the dataset, so the dataset
418
+ # doesn't have to do a query to get them.
419
+ dataset.instance_variable_set(:@columns, cols)
420
+ end
421
+ else
422
+ # If the dataset uses multiple tables or custom sql or getting
423
+ # the schema raised an error, just get the columns and
424
+ # create an empty schema hash for it.
425
+ get_columns.call.each{|c| schema_hash[c] = {}}
426
+ end
427
+ schema_hash
428
+ end
429
+
430
+ # Module that the class includes that holds methods the class adds for column accessors and
431
+ # associations so that the methods can be overridden with super
432
+ def overridable_methods_module
433
+ include(@overridable_methods_module = Module.new) unless @overridable_methods_module
434
+ @overridable_methods_module
435
+ end
436
+
437
+ # Set the columns for this model and create accessor methods for each column.
438
+ def set_columns(new_columns)
439
+ @columns = new_columns
440
+ def_column_accessor(*new_columns) if new_columns
441
+ # Deprecation.deprecated
442
+ @str_columns = nil
443
+ @columns
444
+ end
445
+
446
+ # Add model methods that call dataset methods
447
+ DATASET_METHODS.each{|arg| class_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__)}
448
+
449
+ # Returns a copy of the model's dataset with custom SQL
450
+ alias fetch with_sql
451
+ end
452
+
453
+ # Sequel::Model instance methods that implement basic model functionality.
454
+ #
455
+ # * All of the methods in HOOKS create instance methods that are called
456
+ # by Sequel when the appropriate action occurs. For example, when destroying
457
+ # a model object, Sequel will call before_destroy, do the destroy,
458
+ # and then call after_destroy.
459
+ # * The following instance_methods all call the class method of the same
460
+ # name: columns, dataset, db, primary_key, db_schema.
461
+ # * The following instance methods allow boolean flags to be set on a per-object
462
+ # basis: raise_on_save_failure, raise_on_typecast_failure, strict_param_setting,
463
+ # typecast_empty_string_to_nil, typecast_on_assignment, use_transactions.
464
+ # If they are not used, the object will default to whatever the model setting is.
465
+ module InstanceMethods
466
+ HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
467
+
468
+ # Define instance method(s) that calls class method(s) of the
469
+ # same name, caching the result in an instance variable. Define
470
+ # standard attr_writer method for modifying that instance variable
471
+ def self.class_attr_overridable(*meths) # :nodoc:
472
+ meths.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (@#{meth} = self.class.#{meth}) : @#{meth} end")}
473
+ attr_writer(*meths)
474
+ end
475
+
476
+ # Define instance method(s) that calls class method(s) of the
477
+ # same name. Replaces the construct:
478
+ #
479
+ # define_method(meth){self.class.send(meth)}
480
+ def self.class_attr_reader(*meths) # :nodoc:
481
+ meths.each{|meth| class_eval("def #{meth}; model.#{meth} end")}
482
+ end
483
+
484
+ private_class_method :class_attr_overridable, :class_attr_reader
485
+
486
+ class_attr_reader :columns, :db, :primary_key, :db_schema
487
+ class_attr_overridable :raise_on_save_failure, :raise_on_typecast_failure, :strict_param_setting, :typecast_empty_string_to_nil, :typecast_on_assignment, :use_transactions
488
+
489
+ # The hash of attribute values. Keys are symbols with the names of the
490
+ # underlying database columns.
491
+ attr_reader :values
492
+
493
+ # Creates new instance and passes the given values to set.
494
+ # If a block is given, yield the instance to the block unless
495
+ # from_db is true.
496
+ # This method runs the after_initialize hook after
497
+ # it has optionally yielded itself to the block.
498
+ #
499
+ # Arguments:
500
+ # * values - should be a hash to pass to set.
501
+ # * from_db - should only be set by Model.load, forget it
502
+ # exists.
503
+ def initialize(values = {}, from_db = false)
504
+ if from_db
505
+ @new = false
506
+ @values = values
507
+ else
508
+ @values = {}
509
+ @new = true
510
+ set(values)
511
+ changed_columns.clear
512
+ yield self if block_given?
513
+ end
514
+ after_initialize
515
+ end
516
+
517
+ # Returns value of the column's attribute.
518
+ def [](column)
519
+ @values[column]
520
+ end
521
+
522
+ # Sets value of the column's attribute and marks the column as changed.
523
+ # If the column already has the same value, this is a no-op. Note that
524
+ # changing a columns value and then changing it back will cause the
525
+ # column to appear in changed_columns. Similarly, providing a
526
+ # value that is different from the column's current value but is the
527
+ # same after typecasting will also cause changed_columns to include the
528
+ # column.
529
+ def []=(column, value)
530
+ # If it is new, it doesn't have a value yet, so we should
531
+ # definitely set the new value.
532
+ # If the column isn't in @values, we can't assume it is
533
+ # NULL in the database, so assume it has changed.
534
+ if new? || !@values.include?(column) || value != @values[column]
535
+ changed_columns << column unless changed_columns.include?(column)
536
+ @values[column] = typecast_value(column, value)
537
+ end
538
+ end
539
+
540
+ # Compares model instances by values.
541
+ def ==(obj)
542
+ (obj.class == model) && (obj.values == @values)
543
+ end
544
+ alias eql? ==
545
+
546
+ # If pk is not nil, true only if the objects have the same class and pk.
547
+ # If pk is nil, false.
548
+ def ===(obj)
549
+ pk.nil? ? false : (obj.class == model) && (obj.pk == pk)
550
+ end
551
+
552
+ # class is defined in Object, but it is also a keyword,
553
+ # and since a lot of instance methods call class methods,
554
+ # this alias makes it so you can use model instead of
555
+ # self.class.
556
+ alias_method :model, :class
557
+
558
+ # The current cached associations. A hash with the keys being the
559
+ # association name symbols and the values being the associated object
560
+ # or nil (many_to_one), or the array of associated objects (*_to_many).
561
+ def associations
562
+ @associations ||= {}
563
+ end
564
+
565
+ # The columns that have been updated. This isn't completely accurate,
566
+ # see Model#[]=.
567
+ def changed_columns
568
+ @changed_columns ||= []
569
+ end
570
+
571
+ # Deletes and returns self. Does not run destroy hooks.
572
+ # Look into using destroy instead.
573
+ def delete
574
+ this.delete
575
+ self
576
+ end
577
+
578
+ # Like delete but runs hooks before and after delete.
579
+ # If before_destroy returns false, returns false without
580
+ # deleting the object the the database. Otherwise, deletes
581
+ # the item from the database and returns self. Uses a transaction
582
+ # if use_transactions is true.
583
+ def destroy
584
+ use_transactions ? db.transaction{_destroy} : _destroy
585
+ end
586
+
587
+ # Iterates through all of the current values using each.
588
+ #
589
+ # Example:
590
+ # Ticket.find(7).each { |k, v| puts "#{k} => #{v}" }
591
+ def each(&block)
592
+ @values.each(&block)
593
+ end
594
+
595
+ # Returns the validation errors associated with this object.
596
+ def errors
597
+ @errors ||= Errors.new
598
+ end
599
+
600
+ # Returns true when current instance exists, false otherwise.
601
+ # Generally an object that isn't new will exist unless it has
602
+ # been deleted.
603
+ def exists?
604
+ this.count > 0
605
+ end
606
+
607
+ # Value that should be unique for objects with the same class and pk (if pk is not nil), or
608
+ # the same class and values (if pk is nil).
609
+ def hash
610
+ [model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
611
+ end
612
+
613
+ # Returns value for the :id attribute, even if the primary key is
614
+ # not id. To get the primary key value, use #pk.
615
+ def id
616
+ @values[:id]
617
+ end
618
+
619
+ # Returns a string representation of the model instance including
620
+ # the class name and values.
621
+ def inspect
622
+ "#<#{model.name} @values=#{inspect_values}>"
623
+ end
624
+
625
+ # Returns the keys in values. May not include all column names.
626
+ def keys
627
+ @values.keys
628
+ end
629
+
630
+ # Returns true if the current instance represents a new record.
631
+ def new?
632
+ @new
633
+ end
634
+
635
+ # Returns the primary key value identifying the model instance.
636
+ # Raises an error if this model does not have a primary key.
637
+ # If the model has a composite primary key, returns an array of values.
638
+ def pk
639
+ raise(Error, "No primary key is associated with this model") unless key = primary_key
640
+ case key
641
+ when Array
642
+ key.collect{|k| @values[k]}
643
+ else
644
+ @values[key]
645
+ end
646
+ end
647
+
648
+ # Returns a hash identifying the model instance. It should be true that:
649
+ #
650
+ # Model[model_instance.pk_hash] === model_instance
651
+ def pk_hash
652
+ model.primary_key_hash(pk)
653
+ end
654
+
655
+ # Reloads attributes from database and returns self. Also clears all
656
+ # cached association and changed_columns information. Raises an Error if the record no longer
657
+ # exists in the database.
658
+ def refresh
659
+ @values = this.first || raise(Error, "Record not found")
660
+ changed_columns.clear
661
+ associations.clear
662
+ self
663
+ end
664
+
665
+ # Alias of refresh, but not aliased directly to make overriding in a plugin easier.
666
+ def reload
667
+ refresh
668
+ end
669
+
670
+ # Creates or updates the record, after making sure the record
671
+ # is valid. If the record is not valid, or before_save,
672
+ # before_create (if new?), or before_update (if !new?) return
673
+ # false, returns nil unless raise_on_save_failure is true (if it
674
+ # is true, it raises an error).
675
+ # Otherwise, returns self. You can provide an optional list of
676
+ # columns to update, in which case it only updates those columns.
677
+ #
678
+ # Takes the following options:
679
+ #
680
+ # * :changed - save all changed columns, instead of all columns or the columns
681
+ # * :transaction - set to false not to use a transaction
682
+ # * :validate - set to false not to validate the model before saving
683
+ def save(*columns)
684
+ opts = columns.last.is_a?(Hash) ? columns.pop : {}
685
+ return save_failure(:invalid) if opts[:validate] != false and !valid?
686
+ use_transaction = opts.include?(:transaction) ? opts[:transaction] : use_transactions
687
+ use_transaction ? db.transaction(opts){_save(columns, opts)} : _save(columns, opts)
688
+ end
689
+
690
+ # Saves only changed columns or does nothing if no columns are marked as
691
+ # chanaged. If no columns have been changed, returns nil. If unable to
692
+ # save, returns false unless raise_on_save_failure is true.
693
+ def save_changes
694
+ save(:changed=>true) || false unless changed_columns.empty?
695
+ end
696
+
697
+ # Updates the instance with the supplied values with support for virtual
698
+ # attributes, raising an exception if a value is used that doesn't have
699
+ # a setter method (or ignoring it if strict_param_setting = false).
700
+ # Does not save the record.
701
+ def set(hash)
702
+ set_restricted(hash, nil, nil)
703
+ end
704
+
705
+ # Set all values using the entries in the hash, ignoring any setting of
706
+ # allowed_columns or restricted columns in the model.
707
+ def set_all(hash)
708
+ set_restricted(hash, false, false)
709
+ end
710
+
711
+ # Set all values using the entries in the hash, except for the keys
712
+ # given in except.
713
+ def set_except(hash, *except)
714
+ set_restricted(hash, false, except.flatten)
715
+ end
716
+
717
+ # Set the values using the entries in the hash, only if the key
718
+ # is included in only.
719
+ def set_only(hash, *only)
720
+ set_restricted(hash, only.flatten, false)
721
+ end
722
+
723
+ # Returns (naked) dataset that should return only this instance.
724
+ def this
725
+ @this ||= model.dataset.filter(pk_hash).limit(1).naked
726
+ end
727
+
728
+ # Runs set with the passed hash and runs save_changes (which runs any callback methods).
729
+ def update(hash)
730
+ update_restricted(hash, nil, nil)
731
+ end
732
+
733
+ # Update all values using the entries in the hash, ignoring any setting of
734
+ # allowed_columns or restricted columns in the model.
735
+ def update_all(hash)
736
+ update_restricted(hash, false, false)
737
+ end
738
+
739
+ # Update all values using the entries in the hash, except for the keys
740
+ # given in except.
741
+ def update_except(hash, *except)
742
+ update_restricted(hash, false, except.flatten)
743
+ end
744
+
745
+ # Update the values using the entries in the hash, only if the key
746
+ # is included in only.
747
+ def update_only(hash, *only)
748
+ update_restricted(hash, only.flatten, false)
749
+ end
750
+
751
+ # Validates the object. If the object is invalid, errors should be added
752
+ # to the errors attribute. By default, does nothing, as all models
753
+ # are valid by default.
754
+ def validate
755
+ end
756
+
757
+ # Validates the object and returns true if no errors are reported.
758
+ def valid?
759
+ errors.clear
760
+ if before_validation == false
761
+ save_failure(:validation)
762
+ return false
763
+ end
764
+ validate
765
+ after_validation
766
+ errors.empty?
767
+ end
768
+
769
+ private
770
+
771
+ # Internal destroy method, separted from destroy to
772
+ # allow running inside a transaction
773
+ def _destroy
774
+ return save_failure(:destroy) if before_destroy == false
775
+ delete
776
+ after_destroy
777
+ self
778
+ end
779
+
780
+ # Internal version of save, split from save to allow running inside
781
+ # it's own transaction.
782
+ def _save(columns, opts)
783
+ return save_failure(:save) if before_save == false
784
+ if new?
785
+ return save_failure(:create) if before_create == false
786
+ ds = model.dataset
787
+ if ds.respond_to?(:insert_select) and h = ds.insert_select(@values)
788
+ @values = h
789
+ @this = nil
790
+ else
791
+ iid = ds.insert(@values)
792
+ # if we have a regular primary key and it's not set in @values,
793
+ # we assume it's the last inserted id
794
+ if (pk = primary_key) && !(Array === pk) && !@values[pk]
795
+ @values[pk] = iid
796
+ end
797
+ @this = nil if pk
798
+ end
799
+ @new = false
800
+ @was_new = true
801
+ after_create
802
+ after_save
803
+ @was_new = nil
804
+ refresh if pk
805
+ else
806
+ return save_failure(:update) if before_update == false
807
+ if columns.empty?
808
+ @columns_updated = opts[:changed] ? @values.reject{|k,v| !changed_columns.include?(k)} : @values
809
+ changed_columns.clear
810
+ else # update only the specified columns
811
+ @columns_updated = @values.reject{|k, v| !columns.include?(k)}
812
+ changed_columns.reject!{|c| columns.include?(c)}
813
+ end
814
+ this.update(@columns_updated)
815
+ after_update
816
+ after_save
817
+ @columns_updated = nil
818
+ end
819
+ self
820
+ end
821
+
822
+ # Default inspection output for the values hash, overwrite to change what #inspect displays.
823
+ def inspect_values
824
+ @values.inspect
825
+ end
826
+
827
+ # Raise an error if raise_on_save_failure is true, return nil otherwise.
828
+ def save_failure(type)
829
+ if raise_on_save_failure
830
+ if type == :invalid
831
+ raise ValidationFailed, errors.full_messages.join(', ')
832
+ else
833
+ raise BeforeHookFailed, "one of the before_#{type} hooks returned false"
834
+ end
835
+ end
836
+ end
837
+
838
+ # Set the columns, filtered by the only and except arrays.
839
+ def set_restricted(hash, only, except)
840
+ columns_not_set = [nil, false, "", [], {}].include?(model.instance_variable_get(:@columns))
841
+ meths = setter_methods(only, except)
842
+ strict = strict_param_setting
843
+ hash.each do |k,v|
844
+ m = "#{k}="
845
+ if meths.include?(m)
846
+ send(m, v)
847
+ elsif columns_not_set && (Symbol === k)
848
+ Deprecation.deprecate('Calling Model#set_restricted for a column without a setter method when the model class does not have any columns', 'Use Model#[] for these columns')
849
+ self[k] = v
850
+ elsif strict
851
+ raise Error, "method #{m} doesn't exist or access is restricted to it"
852
+ end
853
+ end
854
+ self
855
+ end
856
+
857
+ # Returns all methods that can be used for attribute
858
+ # assignment (those that end with =), modified by the only
859
+ # and except arguments:
860
+ #
861
+ # * only
862
+ # * false - Don't modify the results
863
+ # * nil - if the model has allowed_columns, use only these, otherwise, don't modify
864
+ # * Array - allow only the given methods to be used
865
+ # * except
866
+ # * false - Don't modify the results
867
+ # * nil - if the model has restricted_columns, remove these, otherwise, don't modify
868
+ # * Array - remove the given methods
869
+ #
870
+ # only takes precedence over except, and if only is not used, certain methods are always
871
+ # restricted (RESTRICTED_SETTER_METHODS). The primary key is restricted by default as
872
+ # well, see Model.unrestrict_primary_key to change this.
873
+ def setter_methods(only, except)
874
+ only = only.nil? ? model.allowed_columns : only
875
+ except = except.nil? ? model.restricted_columns : except
876
+ if only
877
+ only.map{|x| "#{x}="}
878
+ else
879
+ meths = methods.collect{|x| x.to_s}.grep(SETTER_METHOD_REGEXP) - RESTRICTED_SETTER_METHODS
880
+ meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && model.restrict_primary_key?
881
+ meths -= except.map{|x| "#{x}="} if except
882
+ meths
883
+ end
884
+ end
885
+
886
+ # Typecast the value to the column's type if typecasting. Calls the database's
887
+ # typecast_value method, so database adapters can override/augment the handling
888
+ # for database specific column types.
889
+ def typecast_value(column, value)
890
+ # Deprecation.deprecate : Remove model.serialized call
891
+ return value unless typecast_on_assignment && db_schema && (col_schema = db_schema[column]) && !model.serialized?(column)
892
+ value = nil if value == '' and typecast_empty_string_to_nil and col_schema[:type] and ![:string, :blob].include?(col_schema[:type])
893
+ raise(InvalidValue, "nil/NULL is not allowed for the #{column} column") if raise_on_typecast_failure && value.nil? && (col_schema[:allow_null] == false)
894
+ begin
895
+ model.db.typecast_value(col_schema[:type], value)
896
+ rescue InvalidValue
897
+ raise_on_typecast_failure ? raise : value
898
+ end
899
+ end
900
+
901
+ # Set the columns, filtered by the only and except arrays.
902
+ def update_restricted(hash, only, except)
903
+ set_restricted(hash, only, except)
904
+ save_changes
905
+ end
906
+ end
907
+
908
+ # Dataset methods are methods that the model class extends its dataset with in
909
+ # the call to set_dataset.
910
+ module DatasetMethods
911
+ # The model class associated with this dataset
912
+ attr_accessor :model
913
+
914
+ # Destroy each row in the dataset by instantiating it and then calling
915
+ # destroy on the resulting model object. This isn't as fast as deleting
916
+ # the dataset, which does a single SQL call, but this runs any destroy
917
+ # hooks on each object in the dataset.
918
+ def destroy
919
+ @db.transaction{all{|r| r.destroy}.length}
920
+ end
921
+
922
+ # This allows you to call to_hash without any arguments, which will
923
+ # result in a hash with the primary key value being the key and the
924
+ # model object being the value.
925
+ def to_hash(key_column=nil, value_column=nil)
926
+ if key_column
927
+ super
928
+ else
929
+ raise(Sequel::Error, "No primary key for model") unless model and pk = model.primary_key
930
+ super(pk, value_column)
931
+ end
932
+ end
933
+ end
934
+
935
+ plugin self
936
+ end
937
+ end