sequel 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
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