sequel 3.13.0 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|