sequel 3.13.0 → 3.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +36 -0
- data/doc/release_notes/3.14.0.txt +118 -0
- data/lib/sequel/adapters/oracle.rb +7 -2
- data/lib/sequel/adapters/shared/mssql.rb +9 -3
- data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +3 -3
- data/lib/sequel/database/connecting.rb +47 -11
- data/lib/sequel/database/dataset.rb +17 -6
- data/lib/sequel/database/dataset_defaults.rb +15 -3
- data/lib/sequel/database/logging.rb +4 -3
- data/lib/sequel/database/misc.rb +33 -21
- data/lib/sequel/database/query.rb +61 -22
- data/lib/sequel/database/schema_generator.rb +108 -45
- data/lib/sequel/database/schema_methods.rb +8 -5
- data/lib/sequel/dataset/actions.rb +194 -45
- data/lib/sequel/dataset/features.rb +1 -1
- data/lib/sequel/dataset/graph.rb +51 -43
- data/lib/sequel/dataset/misc.rb +29 -5
- data/lib/sequel/dataset/mutation.rb +0 -1
- data/lib/sequel/dataset/prepared_statements.rb +14 -2
- data/lib/sequel/dataset/query.rb +268 -125
- data/lib/sequel/dataset/sql.rb +33 -44
- data/lib/sequel/extensions/migration.rb +3 -2
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/model/associations.rb +89 -87
- data/lib/sequel/model/base.rb +386 -109
- data/lib/sequel/model/errors.rb +15 -1
- data/lib/sequel/model/exceptions.rb +3 -3
- data/lib/sequel/model/inflections.rb +2 -2
- data/lib/sequel/model/plugins.rb +9 -5
- data/lib/sequel/plugins/rcte_tree.rb +43 -15
- data/lib/sequel/plugins/schema.rb +6 -5
- data/lib/sequel/plugins/serialization.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tree.rb +33 -1
- data/lib/sequel/timezones.rb +16 -10
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +36 -2
- data/spec/adapters/mysql_spec.rb +4 -4
- data/spec/adapters/postgres_spec.rb +1 -1
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/core/database_spec.rb +8 -1
- data/spec/core/dataset_spec.rb +36 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +40 -8
- data/spec/extensions/schema_spec.rb +5 -0
- data/spec/extensions/serialization_spec.rb +4 -4
- data/spec/extensions/single_table_inheritance_spec.rb +7 -0
- data/spec/extensions/tree_spec.rb +36 -0
- data/spec/integration/dataset_test.rb +19 -0
- data/spec/integration/prepared_statement_test.rb +2 -2
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/spec_helper.rb +4 -4
- data/spec/integration/timezone_test.rb +27 -21
- data/spec/model/associations_spec.rb +5 -5
- data/spec/model/dataset_methods_spec.rb +13 -0
- data/spec/model/hooks_spec.rb +31 -0
- data/spec/model/record_spec.rb +24 -7
- data/spec/model/validations_spec.rb +9 -4
- metadata +6 -4
data/lib/sequel/model/base.rb
CHANGED
@@ -10,7 +10,7 @@ module Sequel
|
|
10
10
|
# * All of the method names in Model::DATASET_METHODS have class methods created that call
|
11
11
|
# the Model's dataset with the method of the same name with the given arguments.
|
12
12
|
module ClassMethods
|
13
|
-
# Which columns should be the only columns allowed in a call to set
|
13
|
+
# Which columns should be the only columns allowed in a call to a mass assignment method (e.g. set)
|
14
14
|
# (default: not set, so all columns not otherwise restricted).
|
15
15
|
attr_reader :allowed_columns
|
16
16
|
|
@@ -88,7 +88,13 @@ module Sequel
|
|
88
88
|
# Returns the first record from the database matching the conditions.
|
89
89
|
# If a hash is given, it is used as the conditions. If another
|
90
90
|
# object is given, it finds the first record whose primary key(s) match
|
91
|
-
# the given argument(s).
|
91
|
+
# the given argument(s). If no object is returned by the dataset, returns nil.
|
92
|
+
#
|
93
|
+
# Artist[1] # SELECT * FROM artists WHERE id = 1
|
94
|
+
# # => #<Artist {:id=>1, ...}>
|
95
|
+
#
|
96
|
+
# Artist[:name=>'Bob'] # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
97
|
+
# # => #<Artist {:name=>'Bob', ...}>
|
92
98
|
def [](*args)
|
93
99
|
args = args.first if (args.size == 1)
|
94
100
|
args.is_a?(Hash) ? dataset[args] : primary_key_lookup(args)
|
@@ -97,19 +103,32 @@ module Sequel
|
|
97
103
|
# Returns the columns in the result set in their original order.
|
98
104
|
# Generally, this will use the columns determined via the database
|
99
105
|
# 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
|
101
|
-
#
|
106
|
+
# dataset) it will use <tt>Dataset#columns</tt> to find the columns.
|
107
|
+
#
|
108
|
+
# Artist.columns
|
109
|
+
# # => [:id, :name]
|
102
110
|
def columns
|
103
111
|
@columns || set_columns(dataset.naked.columns)
|
104
112
|
end
|
105
113
|
|
106
114
|
# Creates instance using new with the given values and block, and saves it.
|
115
|
+
#
|
116
|
+
# Artist.create(:name=>'Bob')
|
117
|
+
# # INSERT INTO artists (name) VALUES ('Bob')
|
118
|
+
#
|
119
|
+
# Artist.create do |a|
|
120
|
+
# a.name = 'Jim'
|
121
|
+
# end # INSERT INTO artists (name) VALUES ('Jim')
|
107
122
|
def create(values = {}, &block)
|
108
123
|
new(values, &block).save
|
109
124
|
end
|
110
125
|
|
111
126
|
# Returns the dataset associated with the Model class. Raises
|
112
|
-
# an
|
127
|
+
# an +Error+ if there is no associated dataset for this class.
|
128
|
+
# In most cases, you don't need to call this directly, as Model
|
129
|
+
# proxies many dataset methods to the underlying dataset.
|
130
|
+
#
|
131
|
+
# Artist.dataset.all # SELECT * FROM artists
|
113
132
|
def dataset
|
114
133
|
@dataset || raise(Error, "No dataset associated with #{self}")
|
115
134
|
end
|
@@ -124,6 +143,11 @@ module Sequel
|
|
124
143
|
# assumes the superclass's database, or the first object in
|
125
144
|
# Sequel::DATABASES. If no Sequel::Database object has
|
126
145
|
# been created, raises an error.
|
146
|
+
#
|
147
|
+
# Artist.db.transaction do # BEGIN
|
148
|
+
# Artist.create(:name=>'Bob')
|
149
|
+
# # INSERT INTO artists (name) VALUES ('Bob')
|
150
|
+
# end # COMMIT
|
127
151
|
def db
|
128
152
|
return @db if @db
|
129
153
|
@db = self == Model ? DATABASES.first : superclass.db
|
@@ -134,25 +158,46 @@ module Sequel
|
|
134
158
|
# Sets the database associated with the Model class. If the
|
135
159
|
# model has an associated dataset, sets the model's dataset
|
136
160
|
# to a dataset on the new database with the same options
|
137
|
-
# used by the current dataset.
|
161
|
+
# used by the current dataset. This can be used directly on
|
162
|
+
# Sequel::Model to set the default database to be used
|
163
|
+
# by subclasses, or to override the database used for specific
|
164
|
+
# models:
|
165
|
+
#
|
166
|
+
# Sequel::Model.db = DB1
|
167
|
+
# Artist.db = DB2
|
138
168
|
def db=(db)
|
139
169
|
@db = db
|
140
170
|
set_dataset(db.dataset(@dataset.opts)) if @dataset
|
141
171
|
end
|
142
172
|
|
143
173
|
# Returns the cached schema information if available or gets it
|
144
|
-
# from the database.
|
174
|
+
# from the database. This is a hash where keys are column symbols
|
175
|
+
# and values are hashes of information related to the column. See
|
176
|
+
# <tt>Database#schema</tt>.
|
177
|
+
#
|
178
|
+
# Artist.db_schema
|
179
|
+
# # {:id=>{:type=>:integer, :primary_key=>true, ...},
|
180
|
+
# # :name=>{:type=>:string, :primary_key=>false, ...}}
|
145
181
|
def db_schema
|
146
182
|
@db_schema ||= get_db_schema
|
147
183
|
end
|
148
184
|
|
149
|
-
# If a block is given, define a method on the dataset (if the model has an
|
150
|
-
# the given block
|
185
|
+
# If a block is given, define a method on the dataset (if the model currently has an dataset) with the given argument name using
|
186
|
+
# the given block. Also define a class method on the model that calls the
|
151
187
|
# dataset method. Stores the method name and block so that it can be reapplied if the model's
|
152
188
|
# dataset changes.
|
153
189
|
#
|
154
|
-
# If a block is not given, define a method on the model for each argument
|
190
|
+
# If a block is not given, just define a class method on the model for each argument
|
155
191
|
# that calls the dataset method of the same argument name.
|
192
|
+
#
|
193
|
+
# # Add new dataset method and class method that calls it
|
194
|
+
# Artist.def_dataset_method(:by_name){order(:name)}
|
195
|
+
# Artist.filter(:name.like('A%')).by_name
|
196
|
+
# Artist.by_name.filter(:name.like('A%'))
|
197
|
+
#
|
198
|
+
# # Just add a class method that calls an existing dataset method
|
199
|
+
# Artist.def_dataset_method(:server!)
|
200
|
+
# Artist.server!(:server1)
|
156
201
|
def def_dataset_method(*args, &block)
|
157
202
|
raise(Error, "No arguments given") if args.empty?
|
158
203
|
if block_given?
|
@@ -164,17 +209,30 @@ module Sequel
|
|
164
209
|
args.each{|arg| instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg)}
|
165
210
|
end
|
166
211
|
|
167
|
-
# Finds a single record according to the supplied filter
|
212
|
+
# Finds a single record according to the supplied filter.
|
213
|
+
# You are encouraged to use Model.[] or Model.first instead of this method.
|
168
214
|
#
|
169
|
-
#
|
215
|
+
# Artist.find(:name=>'Bob')
|
216
|
+
# # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
170
217
|
#
|
171
|
-
#
|
218
|
+
# Artist.find{name > 'M'}
|
219
|
+
# # SELECT * FROM artists WHERE (name > 'M') LIMIT 1
|
172
220
|
def find(*args, &block)
|
173
221
|
filter(*args, &block).first
|
174
222
|
end
|
175
223
|
|
176
|
-
# Like find but invokes create with given conditions when record does not
|
177
|
-
# exist.
|
224
|
+
# Like +find+ but invokes create with given conditions when record does not
|
225
|
+
# exist. Unlike +find+ in that the block used in this method is not passed
|
226
|
+
# to +find+, but instead is passed to +create+ only if +find+ does not
|
227
|
+
# return an object.
|
228
|
+
#
|
229
|
+
# Artist.find_or_create(:name=>'Bob')
|
230
|
+
# # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
231
|
+
# # INSERT INTO artists (name) VALUES ('Bob')
|
232
|
+
#
|
233
|
+
# Artist.find_or_create(:name=>'Jim'){|a| a.hometown = 'Sactown'}
|
234
|
+
# # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
235
|
+
# # INSERT INTO artists (name, hometown) VALUES ('Bob', 'Sactown')
|
178
236
|
def find_or_create(cond, &block)
|
179
237
|
find(cond) || create(cond, &block)
|
180
238
|
end
|
@@ -182,6 +240,12 @@ module Sequel
|
|
182
240
|
# If possible, set the dataset for the model subclass as soon as it
|
183
241
|
# is created. Also, make sure the inherited class instance variables
|
184
242
|
# are copied into the subclass.
|
243
|
+
#
|
244
|
+
# Sequel queries the database to get schema information as soon as
|
245
|
+
# a model class is created:
|
246
|
+
#
|
247
|
+
# class Artist < Sequel::Model # Causes schema query
|
248
|
+
# end
|
185
249
|
def inherited(subclass)
|
186
250
|
super
|
187
251
|
ivs = subclass.instance_variables.collect{|x| x.to_s}
|
@@ -206,14 +270,18 @@ module Sequel
|
|
206
270
|
end
|
207
271
|
end
|
208
272
|
|
209
|
-
# Returns the implicit table name for the model class
|
273
|
+
# Returns the implicit table name for the model class, which is the demodulized,
|
274
|
+
# underscored, pluralized name of the class.
|
275
|
+
#
|
276
|
+
# Artist.implicit_table_name # => :artists
|
277
|
+
# Foo::ArtistAlias.implicit_table_name # => :artist_aliases
|
210
278
|
def implicit_table_name
|
211
279
|
pluralize(underscore(demodulize(name))).to_sym
|
212
280
|
end
|
213
281
|
|
214
282
|
# Initializes a model instance as an existing record. This constructor is
|
215
283
|
# used by Sequel to initialize model instances when fetching records.
|
216
|
-
# load requires that values be a hash where all keys are symbols. It
|
284
|
+
# +load+ requires that values be a hash where all keys are symbols. It
|
217
285
|
# probably should not be used by external code.
|
218
286
|
def load(values)
|
219
287
|
new(values, true)
|
@@ -221,6 +289,10 @@ module Sequel
|
|
221
289
|
|
222
290
|
# Mark the model as not having a primary key. Not having a primary key
|
223
291
|
# can cause issues, among which is that you won't be able to update records.
|
292
|
+
#
|
293
|
+
# Artist.primary_key # => :id
|
294
|
+
# Artist.no_primary_key
|
295
|
+
# Artist.primary_key # => nil
|
224
296
|
def no_primary_key
|
225
297
|
@simple_pk = @primary_key = nil
|
226
298
|
end
|
@@ -229,7 +301,10 @@ module Sequel
|
|
229
301
|
# value such be an array with values for each primary key in the correct
|
230
302
|
# order. For a standard primary key, value should be an object with a
|
231
303
|
# compatible type for the key. If the model does not have a primary key,
|
232
|
-
# raises an Error
|
304
|
+
# raises an +Error+.
|
305
|
+
#
|
306
|
+
# Artist.primary_key_hash(1) # => {:id=>1}
|
307
|
+
# Artist.primary_key_hash([1, 2]) # => {:id1=>1, :id2=>2}
|
233
308
|
def primary_key_hash(value)
|
234
309
|
raise(Error, "#{self} does not have a primary key") unless key = @primary_key
|
235
310
|
case key
|
@@ -243,49 +318,61 @@ module Sequel
|
|
243
318
|
end
|
244
319
|
|
245
320
|
# Return a hash where the keys are qualified column references. Uses the given
|
246
|
-
# qualifier if provided, or the table_name otherwise.
|
321
|
+
# qualifier if provided, or the table_name otherwise. This is useful if you
|
322
|
+
# plan to join other tables to this table and you want the column references
|
323
|
+
# to be qualified.
|
324
|
+
#
|
325
|
+
# Artist.filter(Artist.qualified_primary_key_hash(1))
|
326
|
+
# # SELECT * FROM artists WHERE (artists.id = 1)
|
247
327
|
def qualified_primary_key_hash(value, qualifier=table_name)
|
248
328
|
h = primary_key_hash(value)
|
249
329
|
h.to_a.each{|k,v| h[SQL::QualifiedIdentifier.new(qualifier, k)] = h.delete(k)}
|
250
330
|
h
|
251
331
|
end
|
252
332
|
|
253
|
-
# Restrict the setting of the primary key(s)
|
333
|
+
# Restrict the setting of the primary key(s) when using mass assignment (e.g. +set+). Because
|
254
334
|
# this is the default, this only make sense to use in a subclass where the
|
255
|
-
# parent class has used unrestrict_primary_key
|
335
|
+
# parent class has used +unrestrict_primary_key+.
|
256
336
|
def restrict_primary_key
|
257
337
|
@restrict_primary_key = true
|
258
338
|
end
|
259
339
|
|
260
|
-
# Whether or not setting the primary key
|
340
|
+
# Whether or not setting the primary key(s) when using mass assignment (e.g. +set+) is
|
261
341
|
# restricted, true by default.
|
262
342
|
def restrict_primary_key?
|
263
343
|
@restrict_primary_key
|
264
344
|
end
|
265
345
|
|
266
|
-
# Set the columns to allow
|
346
|
+
# Set the columns to allow when using mass assignment (e.g. +set+). Using this means that
|
267
347
|
# any columns not listed here will not be modified. If you have any virtual
|
268
|
-
# setter methods (methods that end in =) that you want to be used
|
269
|
-
#
|
348
|
+
# setter methods (methods that end in =) that you want to be used during
|
349
|
+
# mass assignment, they need to be listed here as well (without the =).
|
270
350
|
#
|
271
|
-
# It may be better to use
|
351
|
+
# It may be better to use a method such as +set_only+ instead of this in places where
|
272
352
|
# only certain columns may be allowed.
|
353
|
+
#
|
354
|
+
# Artist.set_allowed_columns(:name, :hometown)
|
355
|
+
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
356
|
+
# Artist.set(:name=>'Bob', :records_sold=>30000) # Error
|
273
357
|
def set_allowed_columns(*cols)
|
274
358
|
@allowed_columns = cols
|
275
359
|
end
|
276
360
|
|
277
|
-
# Sets the dataset associated with the Model class. ds can be a Symbol
|
278
|
-
# (specifying a table name in the current database), or a Dataset
|
279
|
-
# If a dataset is used, the model's database is changed to the given
|
361
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+
|
362
|
+
# (specifying a table name in the current database), or a +Dataset+.
|
363
|
+
# If a dataset is used, the model's database is changed to the database of the given
|
280
364
|
# dataset. If a symbol is used, a dataset is created from the current
|
281
|
-
# database with the table name given. Other arguments raise an Error
|
365
|
+
# database with the table name given. Other arguments raise an +Error+.
|
282
366
|
# Returns self.
|
283
367
|
#
|
284
|
-
# This changes the row_proc of the
|
368
|
+
# This changes the row_proc of the dataset to return
|
285
369
|
# model objects, extends the dataset with the dataset_method_modules,
|
286
370
|
# and defines methods on the dataset using the dataset_methods.
|
287
371
|
# It also attempts to determine the database schema for the model,
|
288
372
|
# based on the given dataset.
|
373
|
+
#
|
374
|
+
# Artist.set_dataset(:tbl_artists)
|
375
|
+
# Artist.set_dataset(DB[:artists])
|
289
376
|
def set_dataset(ds, opts={})
|
290
377
|
inherited = opts[:inherited]
|
291
378
|
@dataset = case ds
|
@@ -314,46 +401,50 @@ module Sequel
|
|
314
401
|
end
|
315
402
|
|
316
403
|
# Sets the primary key for this model. You can use either a regular
|
317
|
-
# or a composite primary key.
|
318
|
-
#
|
319
|
-
# Example:
|
320
|
-
# class Tagging < Sequel::Model
|
321
|
-
# # composite key
|
322
|
-
# set_primary_key [:taggable_id, :tag_id]
|
323
|
-
# end
|
404
|
+
# or a composite primary key. To not use a primary key, set to nil
|
405
|
+
# or use +no_primary_key+.
|
324
406
|
#
|
325
407
|
# class Person < Sequel::Model
|
326
408
|
# # regular key
|
327
409
|
# set_primary_key :person_id
|
328
410
|
# end
|
329
411
|
#
|
330
|
-
#
|
331
|
-
#
|
412
|
+
# class Tagging < Sequel::Model
|
413
|
+
# # composite key
|
414
|
+
# set_primary_key [:taggable_id, :tag_id]
|
415
|
+
# end
|
332
416
|
def set_primary_key(*key)
|
333
417
|
key = key.flatten
|
334
418
|
@simple_pk = key.length == 1 ? db.literal(key.first) : nil
|
335
419
|
@primary_key = (key.length == 1) ? key[0] : key
|
336
420
|
end
|
337
421
|
|
338
|
-
# Set the columns to restrict
|
422
|
+
# Set the columns to restrict when using mass assignment (e.g. +set+). Using this means that
|
339
423
|
# attempts to call setter methods for the columns listed here will cause an
|
340
|
-
# exception or be silently skipped (based on the strict_param_setting setting.
|
341
|
-
# If you have any virtual
|
342
|
-
# want not to be used
|
424
|
+
# exception or be silently skipped (based on the +strict_param_setting+ setting.
|
425
|
+
# If you have any virtual setter methods (methods that end in =) that you
|
426
|
+
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
343
427
|
#
|
344
|
-
# It may be better to use
|
345
|
-
#
|
428
|
+
# It may be better to use a method such as +set_except+ instead of this in places where
|
429
|
+
# certain columns are restricted. In general, it's better to have a whitelist approach
|
430
|
+
# where you specify only what is allowed, as opposed to a blacklist approach that this
|
431
|
+
# method uses, where everything is allowed other than what you restrict.
|
432
|
+
#
|
433
|
+
# Artist.set_restricted_column(:records_sold)
|
434
|
+
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
435
|
+
# Artist.set(:name=>'Bob', :records_sold=>30000) # Error
|
346
436
|
def set_restricted_columns(*cols)
|
347
437
|
@restricted_columns = cols
|
348
438
|
end
|
349
439
|
|
350
|
-
#
|
351
|
-
#
|
440
|
+
# Shortcut for +def_dataset_method+ that is restricted to modifying the
|
441
|
+
# dataset's filter. Sometimes thought of as a scope, and like most dataset methods,
|
442
|
+
# they can be chained.
|
352
443
|
# For example:
|
353
444
|
#
|
354
445
|
# Topic.subset(:joes, :username.like('%joe%'))
|
355
|
-
# Topic.subset(:popular){
|
356
|
-
# Topic.subset(:recent){
|
446
|
+
# Topic.subset(:popular){num_posts > 100}
|
447
|
+
# Topic.subset(:recent){created_on > Date.today - 7}
|
357
448
|
#
|
358
449
|
# Allows you to do:
|
359
450
|
#
|
@@ -363,17 +454,26 @@ module Sequel
|
|
363
454
|
# have more than 100 posts and were created less than
|
364
455
|
# 7 days ago.
|
365
456
|
#
|
366
|
-
# Both the args given and the block are passed to Dataset#filter
|
457
|
+
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
367
458
|
def subset(name, *args, &block)
|
368
459
|
def_dataset_method(name){filter(*args, &block)}
|
369
460
|
end
|
370
461
|
|
371
|
-
# Returns name of primary table for the dataset.
|
462
|
+
# Returns name of primary table for the dataset. If the table for the dataset
|
463
|
+
# is aliased, returns the aliased name.
|
464
|
+
#
|
465
|
+
# Artist.table_name # => :artists
|
466
|
+
# Sequel::Model(:foo).table_name # => :foo
|
467
|
+
# Sequel::Model(:foo___bar).table_name # => :bar
|
372
468
|
def table_name
|
373
469
|
dataset.first_source_alias
|
374
470
|
end
|
375
471
|
|
376
|
-
# Allow the setting of the primary key(s)
|
472
|
+
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
473
|
+
#
|
474
|
+
# Artist.set(:id=>1) # Error
|
475
|
+
# Artist.unrestrict_primary_key
|
476
|
+
# Artist.set(:id=>1) # No Error
|
377
477
|
def unrestrict_primary_key
|
378
478
|
@restrict_primary_key = false
|
379
479
|
end
|
@@ -472,14 +572,14 @@ module Sequel
|
|
472
572
|
end
|
473
573
|
|
474
574
|
# Module that the class includes that holds methods the class adds for column accessors and
|
475
|
-
# associations so that the methods can be overridden with super
|
575
|
+
# associations so that the methods can be overridden with +super+.
|
476
576
|
def overridable_methods_module
|
477
577
|
include(@overridable_methods_module = Module.new) unless @overridable_methods_module
|
478
578
|
@overridable_methods_module
|
479
579
|
end
|
480
580
|
|
481
581
|
# Find the row in the dataset that matches the primary key. Uses
|
482
|
-
#
|
582
|
+
# a static SQL optimization if the table and primary key are simple.
|
483
583
|
def primary_key_lookup(pk)
|
484
584
|
if t = simple_table and p = simple_pk
|
485
585
|
with_sql("SELECT * FROM #{t} WHERE #{p} = #{dataset.literal(pk)}").first
|
@@ -499,27 +599,30 @@ module Sequel
|
|
499
599
|
DATASET_METHODS.each{|arg| class_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__)}
|
500
600
|
|
501
601
|
# Returns a copy of the model's dataset with custom SQL
|
602
|
+
#
|
603
|
+
# Artist.fetch("SELECT * FROM artists WHERE name LIKE 'A%'")
|
604
|
+
# Artist.fetch("SELECT * FROM artists WHERE id = ?", 1)
|
502
605
|
alias fetch with_sql
|
503
606
|
end
|
504
607
|
|
505
608
|
# Sequel::Model instance methods that implement basic model functionality.
|
506
609
|
#
|
507
|
-
# * All of the methods in HOOKS create instance methods that are called
|
610
|
+
# * All of the methods in +HOOKS+ create instance methods that are called
|
508
611
|
# by Sequel when the appropriate action occurs. For example, when destroying
|
509
|
-
# a model object, Sequel will call before_destroy
|
510
|
-
# and then call after_destroy
|
612
|
+
# a model object, Sequel will call +before_destroy+, do the destroy,
|
613
|
+
# and then call +after_destroy+.
|
511
614
|
# * The following instance_methods all call the class method of the same
|
512
615
|
# name: columns, dataset, db, primary_key, db_schema.
|
513
|
-
# *
|
514
|
-
#
|
515
|
-
#
|
516
|
-
#
|
616
|
+
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
617
|
+
# to set values for the attribute. It also creates instnace getters returning
|
618
|
+
# the value of the setting. If the value has not yet been set, it
|
619
|
+
# gets the default value from the class by calling the class method of the same name.
|
517
620
|
module InstanceMethods
|
518
621
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
519
622
|
|
520
623
|
# Define instance method(s) that calls class method(s) of the
|
521
624
|
# same name, caching the result in an instance variable. Define
|
522
|
-
# standard attr_writer method for modifying that instance variable
|
625
|
+
# standard attr_writer method for modifying that instance variable.
|
523
626
|
def self.class_attr_overridable(*meths) # :nodoc:
|
524
627
|
meths.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (@#{meth} = self.class.#{meth}) : @#{meth} end", __FILE__, __LINE__)}
|
525
628
|
attr_writer(*meths)
|
@@ -540,6 +643,9 @@ module Sequel
|
|
540
643
|
|
541
644
|
# The hash of attribute values. Keys are symbols with the names of the
|
542
645
|
# underlying database columns.
|
646
|
+
#
|
647
|
+
# Artist.new(:name=>'Bob').values # => {:name=>'Bob'}
|
648
|
+
# Artist[1].values # => {:id=>1, :name=>'Jim', ...}
|
543
649
|
attr_reader :values
|
544
650
|
|
545
651
|
# Creates new instance and passes the given values to set.
|
@@ -549,9 +655,14 @@ module Sequel
|
|
549
655
|
# it has optionally yielded itself to the block.
|
550
656
|
#
|
551
657
|
# Arguments:
|
552
|
-
#
|
553
|
-
#
|
554
|
-
#
|
658
|
+
# values :: should be a hash to pass to set.
|
659
|
+
# from_db :: should only be set by <tt>Model.load</tt>, forget it exists.
|
660
|
+
#
|
661
|
+
# Artist.new(:name=>'Bob')
|
662
|
+
#
|
663
|
+
# Artist.new do |a|
|
664
|
+
# a.name = 'Bob'
|
665
|
+
# end
|
555
666
|
def initialize(values = {}, from_db = false)
|
556
667
|
if from_db
|
557
668
|
@new = false
|
@@ -568,6 +679,8 @@ module Sequel
|
|
568
679
|
end
|
569
680
|
|
570
681
|
# Returns value of the column's attribute.
|
682
|
+
#
|
683
|
+
# Artist[1][:id] #=> 1
|
571
684
|
def [](column)
|
572
685
|
@values[column]
|
573
686
|
end
|
@@ -576,6 +689,10 @@ module Sequel
|
|
576
689
|
# this object, typecast the value based on the column's type.
|
577
690
|
# If this a a new record or the typecasted value isn't the same
|
578
691
|
# as the current value for the column, mark the column as changed.
|
692
|
+
#
|
693
|
+
# a = Artist.new
|
694
|
+
# a[:name] = 'Bob'
|
695
|
+
# a.values #=> {:name=>'Bob'}
|
579
696
|
def []=(column, value)
|
580
697
|
# If it is new, it doesn't have a value yet, so we should
|
581
698
|
# definitely set the new value.
|
@@ -595,6 +712,10 @@ module Sequel
|
|
595
712
|
|
596
713
|
# If pk is not nil, true only if the objects have the same class and pk.
|
597
714
|
# If pk is nil, false.
|
715
|
+
#
|
716
|
+
# Artist[1] === Artist[1] # true
|
717
|
+
# Artist.new === Artist.new # false
|
718
|
+
# Artist[1].set(:name=>'Bob') == Artist[1] # => true
|
598
719
|
def ===(obj)
|
599
720
|
pk.nil? ? false : (obj.class == model) && (obj.pk == pk)
|
600
721
|
end
|
@@ -603,6 +724,8 @@ module Sequel
|
|
603
724
|
# and since a lot of instance methods call class methods,
|
604
725
|
# this alias makes it so you can use model instead of
|
605
726
|
# self.class.
|
727
|
+
#
|
728
|
+
# Artist.new.model # => Artist
|
606
729
|
alias_method :model, :class
|
607
730
|
|
608
731
|
# The autoincrementing primary key for this model object. Should be
|
@@ -613,13 +736,21 @@ module Sequel
|
|
613
736
|
end
|
614
737
|
|
615
738
|
# The columns that have been updated. This isn't completely accurate,
|
616
|
-
#
|
739
|
+
# as it could contain columns whose values have not changed.
|
740
|
+
#
|
741
|
+
# a = Artist[1]
|
742
|
+
# a.changed_columns # => []
|
743
|
+
# a.name = 'Bob'
|
744
|
+
# a.changed_columns # => [:name]
|
617
745
|
def changed_columns
|
618
746
|
@changed_columns ||= []
|
619
747
|
end
|
620
748
|
|
621
|
-
# Deletes and returns self
|
622
|
-
# Look into using destroy instead.
|
749
|
+
# Deletes and returns +self+. Does not run destroy hooks.
|
750
|
+
# Look into using +destroy+ instead.
|
751
|
+
#
|
752
|
+
# Artist[1].delete # DELETE FROM artists WHERE (id = 1)
|
753
|
+
# # => #<Artist {:id=>1, ...}>
|
623
754
|
def delete
|
624
755
|
_delete
|
625
756
|
self
|
@@ -631,24 +762,33 @@ module Sequel
|
|
631
762
|
# the item from the database and returns self. Uses a transaction
|
632
763
|
# if use_transactions is true or if the :transaction option is given and
|
633
764
|
# true.
|
765
|
+
#
|
766
|
+
# Artist[1].destroy # BEGIN; DELETE FROM artists WHERE (id = 1); COMMIT;
|
767
|
+
# # => #<Artist {:id=>1, ...}>
|
634
768
|
def destroy(opts = {})
|
635
|
-
checked_save_failure{checked_transaction(opts){_destroy(opts)}}
|
769
|
+
checked_save_failure(opts){checked_transaction(opts){_destroy(opts)}}
|
636
770
|
end
|
637
771
|
|
638
772
|
# Iterates through all of the current values using each.
|
639
773
|
#
|
640
|
-
#
|
641
|
-
#
|
774
|
+
# Album[1].each{|k, v| puts "#{k} => #{v}"}
|
775
|
+
# # id => 1
|
776
|
+
# # name => 'Bob'
|
642
777
|
def each(&block)
|
643
778
|
@values.each(&block)
|
644
779
|
end
|
645
780
|
|
646
781
|
# Compares model instances by values.
|
782
|
+
#
|
783
|
+
# Artist[1] == Artist[1] # => true
|
784
|
+
# Artist.new == Artist.new # => true
|
785
|
+
# Artist[1].set(:name=>'Bob') == Artist[1] # => false
|
647
786
|
def eql?(obj)
|
648
787
|
(obj.class == model) && (obj.values == @values)
|
649
788
|
end
|
650
789
|
|
651
790
|
# Returns the validation errors associated with this object.
|
791
|
+
# See +Errors+.
|
652
792
|
def errors
|
653
793
|
@errors ||= Errors.new
|
654
794
|
end
|
@@ -656,18 +796,28 @@ module Sequel
|
|
656
796
|
# Returns true when current instance exists, false otherwise.
|
657
797
|
# Generally an object that isn't new will exist unless it has
|
658
798
|
# been deleted.
|
799
|
+
#
|
800
|
+
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
801
|
+
# # => true
|
659
802
|
def exists?
|
660
|
-
this.
|
803
|
+
!this.get(1).nil?
|
661
804
|
end
|
662
805
|
|
663
806
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
664
807
|
# the same class and values (if pk is nil).
|
808
|
+
#
|
809
|
+
# Artist[1].hash == Artist[1].hash # true
|
810
|
+
# Artist[1].set(:name=>'Bob').hash == Artist[1].hash # true
|
811
|
+
# Artist.new.hash == Artist.new.hash # true
|
812
|
+
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
665
813
|
def hash
|
666
814
|
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
667
815
|
end
|
668
816
|
|
669
817
|
# Returns value for the :id attribute, even if the primary key is
|
670
|
-
# not id. To get the primary key value, use
|
818
|
+
# not id. To get the primary key value, use +pk+.
|
819
|
+
#
|
820
|
+
# Artist[1].id # => 1
|
671
821
|
def id
|
672
822
|
@values[:id]
|
673
823
|
end
|
@@ -678,24 +828,45 @@ module Sequel
|
|
678
828
|
"#<#{model.name} @values=#{inspect_values}>"
|
679
829
|
end
|
680
830
|
|
681
|
-
# Returns the keys in values
|
831
|
+
# Returns the keys in +values+. May not include all column names.
|
832
|
+
#
|
833
|
+
# Artist.new.keys # => []
|
834
|
+
# Artist.new(:name=>'Bob').keys # => [:name]
|
835
|
+
# Artist[1].keys # => [:id, :name]
|
682
836
|
def keys
|
683
837
|
@values.keys
|
684
838
|
end
|
685
839
|
|
686
|
-
# Refresh this record using for_update unless this is a new record. Returns self.
|
840
|
+
# Refresh this record using +for_update+ unless this is a new record. Returns self.
|
841
|
+
# This can be used to make sure no other process is updating the record at the
|
842
|
+
# same time.
|
843
|
+
#
|
844
|
+
# a = Artist[1]
|
845
|
+
# Artist.db.transaction do
|
846
|
+
# a.lock!
|
847
|
+
# a.update(...)
|
848
|
+
# end
|
687
849
|
def lock!
|
688
850
|
new? ? self : _refresh(this.for_update)
|
689
851
|
end
|
690
852
|
|
691
853
|
# Remove elements of the model object that make marshalling fail. Returns self.
|
854
|
+
#
|
855
|
+
# a = Artist[1]
|
856
|
+
# a.marshallable!
|
857
|
+
# Marshal.dump(a)
|
692
858
|
def marshallable!
|
693
859
|
@this = nil
|
694
860
|
self
|
695
861
|
end
|
696
862
|
|
697
|
-
# Explicitly mark the object as modified, so save_changes
|
863
|
+
# Explicitly mark the object as modified, so +save_changes+/+update+ will
|
698
864
|
# run callbacks even if no columns have changed.
|
865
|
+
#
|
866
|
+
# a = Artist[1]
|
867
|
+
# a.save_changes # No callbacks run, as no changes
|
868
|
+
# a.modified!
|
869
|
+
# a.save_changes # Callbacks run, even though no changes made
|
699
870
|
def modified!
|
700
871
|
@modified = true
|
701
872
|
end
|
@@ -703,11 +874,19 @@ module Sequel
|
|
703
874
|
# Whether this object has been modified since last saved, used by
|
704
875
|
# save_changes to determine whether changes should be saved. New
|
705
876
|
# values are always considered modified.
|
877
|
+
#
|
878
|
+
# a = Artist[1]
|
879
|
+
# a.modified? # => false
|
880
|
+
# a.set(:name=>'Jim')
|
881
|
+
# a.modified # => true
|
706
882
|
def modified?
|
707
883
|
@modified || !changed_columns.empty?
|
708
884
|
end
|
709
885
|
|
710
886
|
# Returns true if the current instance represents a new record.
|
887
|
+
#
|
888
|
+
# Artist.new.new? # => true
|
889
|
+
# Artist[1].new? # => false
|
711
890
|
def new?
|
712
891
|
@new
|
713
892
|
end
|
@@ -715,21 +894,30 @@ module Sequel
|
|
715
894
|
# Returns the primary key value identifying the model instance.
|
716
895
|
# Raises an error if this model does not have a primary key.
|
717
896
|
# If the model has a composite primary key, returns an array of values.
|
897
|
+
#
|
898
|
+
# Artist[1].pk # => 1
|
899
|
+
# Artist[[1, 2]].pk # => [1, 2]
|
718
900
|
def pk
|
719
901
|
raise(Error, "No primary key is associated with this model") unless key = primary_key
|
720
902
|
key.is_a?(Array) ? key.map{|k| @values[k]} : @values[key]
|
721
903
|
end
|
722
904
|
|
723
|
-
# Returns a hash identifying the
|
905
|
+
# Returns a hash identifying mapping the receivers primary key column(s) to their values.
|
724
906
|
#
|
725
|
-
#
|
907
|
+
# Artist[1].pk_hash # => {:id=>1}
|
908
|
+
# Artist[[1, 2]].pk_hash # => {:id1=>1, :id2=>2}
|
726
909
|
def pk_hash
|
727
910
|
model.primary_key_hash(pk)
|
728
911
|
end
|
729
912
|
|
730
913
|
# Reloads attributes from database and returns self. Also clears all
|
731
|
-
#
|
914
|
+
# changed_columns information. Raises an +Error+ if the record no longer
|
732
915
|
# exists in the database.
|
916
|
+
#
|
917
|
+
# a = Artist[1]
|
918
|
+
# a.name = 'Jim'
|
919
|
+
# a.refresh
|
920
|
+
# a.name # => 'Bob'
|
733
921
|
def refresh
|
734
922
|
_refresh(this)
|
735
923
|
end
|
@@ -740,56 +928,91 @@ module Sequel
|
|
740
928
|
end
|
741
929
|
|
742
930
|
# Creates or updates the record, after making sure the record
|
743
|
-
# is valid
|
744
|
-
#
|
745
|
-
#
|
746
|
-
#
|
747
|
-
#
|
748
|
-
#
|
931
|
+
# is valid and before hooks execute successfully. Fails if:
|
932
|
+
#
|
933
|
+
# * the record is not valid, or
|
934
|
+
# * before_save returns false, or
|
935
|
+
# * the record is new and before_create returns false, or
|
936
|
+
# * the record is not new and before_update returns false.
|
937
|
+
#
|
938
|
+
# If +save+ fails and either raise_on_save_failure or the
|
939
|
+
# :raise_on_failure option is true, it raises ValidationFailed
|
940
|
+
# or BeforeHookFailed. Otherwise it returns nil.
|
941
|
+
#
|
942
|
+
# If it succeeds, it returns self.
|
943
|
+
#
|
944
|
+
# You can provide an optional list of columns to update, in which
|
945
|
+
# case it only updates those columns.
|
749
946
|
#
|
750
947
|
# Takes the following options:
|
751
948
|
#
|
752
949
|
# * :changed - save all changed columns, instead of all columns or the columns given
|
753
|
-
# * :transaction - set to false
|
754
|
-
#
|
950
|
+
# * :transaction - set to true or false to override the current
|
951
|
+
# use_transactions setting
|
952
|
+
# * :validate - set to false to skip validation
|
953
|
+
# * :raise_on_failure - set to true or false to override the current
|
954
|
+
# raise_on_save_failure setting
|
755
955
|
def save(*columns)
|
756
956
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
757
|
-
if opts[:validate] != false and !valid?
|
758
|
-
raise(ValidationFailed.new(errors)) if
|
957
|
+
if opts[:validate] != false and !valid?(opts)
|
958
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
759
959
|
return
|
760
960
|
end
|
761
|
-
checked_save_failure{checked_transaction(opts){_save(columns, opts)}}
|
961
|
+
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
762
962
|
end
|
763
963
|
|
764
964
|
# Saves only changed columns if the object has been modified.
|
765
965
|
# If the object has not been modified, returns nil. If unable to
|
766
|
-
# save, returns false unless raise_on_save_failure is true.
|
966
|
+
# save, returns false unless +raise_on_save_failure+ is true.
|
967
|
+
#
|
968
|
+
# a = Artist[1]
|
969
|
+
# a.save_changes # => nil
|
970
|
+
# a.name = 'Jim'
|
971
|
+
# a.save_changes # UPDATE artists SET name = 'Bob' WHERE (id = 1)
|
972
|
+
# # => #<Artist {:id=>1, :name=>'Jim', ...}
|
767
973
|
def save_changes(opts={})
|
768
974
|
save(opts.merge(:changed=>true)) || false if modified?
|
769
975
|
end
|
770
976
|
|
771
977
|
# Updates the instance with the supplied values with support for virtual
|
772
978
|
# attributes, raising an exception if a value is used that doesn't have
|
773
|
-
# a setter method (or ignoring it if strict_param_setting = false).
|
979
|
+
# a setter method (or ignoring it if <tt>strict_param_setting = false</tt>).
|
774
980
|
# Does not save the record.
|
981
|
+
#
|
982
|
+
# artist.set(:name=>'Jim')
|
983
|
+
# artist.name # => 'Jim'
|
775
984
|
def set(hash)
|
776
985
|
set_restricted(hash, nil, nil)
|
777
986
|
end
|
778
987
|
|
779
988
|
# Set all values using the entries in the hash, ignoring any setting of
|
780
989
|
# allowed_columns or restricted columns in the model.
|
990
|
+
#
|
991
|
+
# Artist.set_restricted_columns(:name)
|
992
|
+
# artist.set_all(:name=>'Jim')
|
993
|
+
# artist.name # => 'Jim'
|
781
994
|
def set_all(hash)
|
782
995
|
set_restricted(hash, false, false)
|
783
996
|
end
|
784
997
|
|
785
998
|
# Set all values using the entries in the hash, except for the keys
|
786
999
|
# given in except.
|
1000
|
+
#
|
1001
|
+
# artist.set_except({:name=>'Jim'}, :hometown)
|
1002
|
+
# artist.name # => 'Jim'
|
787
1003
|
def set_except(hash, *except)
|
788
1004
|
set_restricted(hash, false, except.flatten)
|
789
1005
|
end
|
790
1006
|
|
791
1007
|
# For each of the fields in the given array +fields+, call the setter
|
792
1008
|
# method with the value of that +hash+ entry for the field. Returns self.
|
1009
|
+
#
|
1010
|
+
# artist.set_fields({:name=>'Jim'}, :name)
|
1011
|
+
# artist.name # => 'Jim'
|
1012
|
+
#
|
1013
|
+
# artist.set_fields({:hometown=>'LA'}, :name)
|
1014
|
+
# artist.name # => nil
|
1015
|
+
# artist.hometown # => 'Sac'
|
793
1016
|
def set_fields(hash, fields)
|
794
1017
|
fields.each{|f| send("#{f}=", hash[f])}
|
795
1018
|
self
|
@@ -797,34 +1020,55 @@ module Sequel
|
|
797
1020
|
|
798
1021
|
# Set the values using the entries in the hash, only if the key
|
799
1022
|
# is included in only.
|
1023
|
+
#
|
1024
|
+
# artist.set_only({:name=>'Jim'}, :name)
|
1025
|
+
# artist.name # => 'Jim'
|
1026
|
+
#
|
1027
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise error
|
800
1028
|
def set_only(hash, *only)
|
801
1029
|
set_restricted(hash, only.flatten, false)
|
802
1030
|
end
|
803
1031
|
|
804
1032
|
# Returns (naked) dataset that should return only this instance.
|
1033
|
+
#
|
1034
|
+
# Artist[1].this
|
1035
|
+
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
805
1036
|
def this
|
806
1037
|
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
807
1038
|
end
|
808
1039
|
|
809
1040
|
# Runs set with the passed hash and then runs save_changes.
|
1041
|
+
#
|
1042
|
+
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
810
1043
|
def update(hash)
|
811
1044
|
update_restricted(hash, nil, nil)
|
812
1045
|
end
|
813
1046
|
|
814
1047
|
# Update all values using the entries in the hash, ignoring any setting of
|
815
1048
|
# allowed_columns or restricted columns in the model.
|
1049
|
+
#
|
1050
|
+
# Artist.set_restricted_columns(:name)
|
1051
|
+
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
816
1052
|
def update_all(hash)
|
817
1053
|
update_restricted(hash, false, false)
|
818
1054
|
end
|
819
1055
|
|
820
1056
|
# Update all values using the entries in the hash, except for the keys
|
821
1057
|
# given in except.
|
1058
|
+
#
|
1059
|
+
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
822
1060
|
def update_except(hash, *except)
|
823
1061
|
update_restricted(hash, false, except.flatten)
|
824
1062
|
end
|
825
1063
|
|
826
1064
|
# Update the instances values by calling +set_fields+ with the +hash+
|
827
1065
|
# and +fields+, then save any changes to the record. Returns self.
|
1066
|
+
#
|
1067
|
+
# artist.update_fields({:name=>'Jim'}, :name)
|
1068
|
+
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1069
|
+
#
|
1070
|
+
# artist.update_fields({:hometown=>'LA'}, :name)
|
1071
|
+
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
828
1072
|
def update_fields(hash, fields)
|
829
1073
|
set_fields(hash, fields)
|
830
1074
|
save_changes
|
@@ -832,6 +1076,11 @@ module Sequel
|
|
832
1076
|
|
833
1077
|
# Update the values using the entries in the hash, only if the key
|
834
1078
|
# is included in only.
|
1079
|
+
#
|
1080
|
+
# artist.update_only({:name=>'Jim'}, :name)
|
1081
|
+
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1082
|
+
#
|
1083
|
+
# artist.update_only({:hometown=>'LA'}, :name) # Raise Error
|
835
1084
|
def update_only(hash, *only)
|
836
1085
|
update_restricted(hash, only.flatten, false)
|
837
1086
|
end
|
@@ -839,15 +1088,21 @@ module Sequel
|
|
839
1088
|
# Validates the object. If the object is invalid, errors should be added
|
840
1089
|
# to the errors attribute. By default, does nothing, as all models
|
841
1090
|
# are valid by default. See the {"Model Validations" guide}[link:files/doc/validations_rdoc.html].
|
842
|
-
# for details about validation.
|
1091
|
+
# for details about validation. Should not be called directly by
|
1092
|
+
# user code, call <tt>valid?</tt> instead to check if an object
|
1093
|
+
# is valid.
|
843
1094
|
def validate
|
844
1095
|
end
|
845
1096
|
|
846
1097
|
# Validates the object and returns true if no errors are reported.
|
847
|
-
|
1098
|
+
#
|
1099
|
+
# artist(:name=>'Valid').valid? # => true
|
1100
|
+
# artist(:name=>'Invalid').valid? # => false
|
1101
|
+
# artist.errors.full_messages # => ['name cannot be Invalid']
|
1102
|
+
def valid?(opts = {})
|
848
1103
|
errors.clear
|
849
1104
|
if before_validation == false
|
850
|
-
|
1105
|
+
raise_hook_failure(:validation) if raise_on_failure?(opts)
|
851
1106
|
return false
|
852
1107
|
end
|
853
1108
|
validate
|
@@ -873,7 +1128,7 @@ module Sequel
|
|
873
1128
|
# Internal destroy method, separted from destroy to
|
874
1129
|
# allow running inside a transaction
|
875
1130
|
def _destroy(opts)
|
876
|
-
|
1131
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
877
1132
|
_destroy_delete
|
878
1133
|
after_destroy
|
879
1134
|
self
|
@@ -921,9 +1176,9 @@ module Sequel
|
|
921
1176
|
# Internal version of save, split from save to allow running inside
|
922
1177
|
# it's own transaction.
|
923
1178
|
def _save(columns, opts)
|
924
|
-
|
1179
|
+
raise_hook_failure(:save) if before_save == false
|
925
1180
|
if new?
|
926
|
-
|
1181
|
+
raise_hook_failure(:create) if before_create == false
|
927
1182
|
pk = _insert
|
928
1183
|
@this = nil
|
929
1184
|
@new = false
|
@@ -933,7 +1188,7 @@ module Sequel
|
|
933
1188
|
@was_new = nil
|
934
1189
|
pk ? _save_refresh : changed_columns.clear
|
935
1190
|
else
|
936
|
-
|
1191
|
+
raise_hook_failure(:update) if before_update == false
|
937
1192
|
if columns.empty?
|
938
1193
|
@columns_updated = if opts[:changed]
|
939
1194
|
@values.reject{|k,v| !changed_columns.include?(k)}
|
@@ -987,10 +1242,10 @@ module Sequel
|
|
987
1242
|
this
|
988
1243
|
end
|
989
1244
|
|
990
|
-
# If
|
991
|
-
#
|
992
|
-
def checked_save_failure
|
993
|
-
if
|
1245
|
+
# If not raising on failure, check for BeforeHookFailed
|
1246
|
+
# being raised by yielding and swallow it.
|
1247
|
+
def checked_save_failure(opts)
|
1248
|
+
if raise_on_failure?(opts)
|
994
1249
|
yield
|
995
1250
|
else
|
996
1251
|
begin
|
@@ -1010,9 +1265,18 @@ module Sequel
|
|
1010
1265
|
def inspect_values
|
1011
1266
|
@values.inspect
|
1012
1267
|
end
|
1013
|
-
|
1014
|
-
#
|
1015
|
-
|
1268
|
+
|
1269
|
+
# Whether to raise or return false if this action fails. If the
|
1270
|
+
# :raise_on_failure option is present in the hash, use that, otherwise,
|
1271
|
+
# fallback to the object's raise_on_save_failure (if set), or
|
1272
|
+
# class's default (if not).
|
1273
|
+
def raise_on_failure?(opts)
|
1274
|
+
opts.fetch(:raise_on_failure, raise_on_save_failure)
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
# Raise an error appropriate to the hook type. May be swallowed by
|
1278
|
+
# checked_save_failure depending on the raise_on_failure? setting.
|
1279
|
+
def raise_hook_failure(type)
|
1016
1280
|
raise BeforeHookFailed, "one of the before_#{type} hooks returned false"
|
1017
1281
|
end
|
1018
1282
|
|
@@ -1097,19 +1361,32 @@ module Sequel
|
|
1097
1361
|
# the call to set_dataset.
|
1098
1362
|
module DatasetMethods
|
1099
1363
|
# The model class associated with this dataset
|
1364
|
+
#
|
1365
|
+
# Artist.dataset.model # => Artist
|
1100
1366
|
attr_accessor :model
|
1101
1367
|
|
1102
1368
|
# Destroy each row in the dataset by instantiating it and then calling
|
1103
1369
|
# destroy on the resulting model object. This isn't as fast as deleting
|
1104
1370
|
# the dataset, which does a single SQL call, but this runs any destroy
|
1105
1371
|
# hooks on each object in the dataset.
|
1372
|
+
#
|
1373
|
+
# Artist.dataset.destroy
|
1374
|
+
# # DELETE FROM artists WHERE (id = 1)
|
1375
|
+
# # DELETE FROM artists WHERE (id = 2)
|
1376
|
+
# # ...
|
1106
1377
|
def destroy
|
1107
|
-
|
1378
|
+
pr = proc{all{|r| r.destroy}.length}
|
1379
|
+
model.use_transactions ? @db.transaction(&pr) : pr.call
|
1108
1380
|
end
|
1109
1381
|
|
1110
|
-
# This allows you to call to_hash without any arguments, which will
|
1382
|
+
# This allows you to call +to_hash+ without any arguments, which will
|
1111
1383
|
# result in a hash with the primary key value being the key and the
|
1112
1384
|
# model object being the value.
|
1385
|
+
#
|
1386
|
+
# Artist.dataset.to_hash # SELECT * FROM artists
|
1387
|
+
# # => {1=>#<Artist {:id=>1, ...}>,
|
1388
|
+
# # 2=>#<Artist {:id=>2, ...}>,
|
1389
|
+
# # ...}
|
1113
1390
|
def to_hash(key_column=nil, value_column=nil)
|
1114
1391
|
if key_column
|
1115
1392
|
super
|