sequel 3.21.0 → 3.29.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 +413 -3
- data/README.rdoc +20 -6
- data/Rakefile +23 -16
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +86 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +115 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +15 -7
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +5 -31
- data/lib/sequel/adapters/jdbc/db2.rb +44 -0
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +26 -17
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +107 -38
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +78 -182
- data/lib/sequel/adapters/mysql2.rb +24 -23
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +349 -51
- data/lib/sequel/adapters/postgres.rb +169 -21
- data/lib/sequel/adapters/shared/access.rb +21 -6
- data/lib/sequel/adapters/shared/db2.rb +288 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +108 -65
- data/lib/sequel/adapters/shared/mysql.rb +56 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -34
- data/lib/sequel/adapters/shared/postgres.rb +107 -58
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +168 -41
- data/lib/sequel/adapters/sqlite.rb +27 -18
- data/lib/sequel/adapters/swift/mysql.rb +9 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +152 -17
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +100 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +194 -57
- data/lib/sequel/dataset/features.rb +81 -8
- data/lib/sequel/dataset/graph.rb +8 -7
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +32 -15
- data/lib/sequel/dataset/query.rb +223 -42
- data/lib/sequel/dataset/sql.rb +58 -61
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +997 -331
- data/lib/sequel/model/base.rb +386 -129
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -25
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +44 -35
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +65 -29
- data/spec/adapters/mysql_spec.rb +122 -123
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +213 -61
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +729 -508
- data/spec/core/dataset_spec.rb +980 -1044
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +4 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +7 -1
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +522 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +375 -62
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +200 -120
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +174 -7
- data/spec/model/associations_spec.rb +849 -661
- data/spec/model/base_spec.rb +255 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +640 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +207 -167
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +321 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +95 -38
data/lib/sequel/model/base.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Sequel
|
|
|
11
11
|
# the Model's dataset with the method of the same name with the given arguments.
|
|
12
12
|
module ClassMethods
|
|
13
13
|
# Which columns should be the only columns allowed in a call to a mass assignment method (e.g. set)
|
|
14
|
-
# (default: not set, so all columns not otherwise restricted).
|
|
14
|
+
# (default: not set, so all columns not otherwise restricted are allowed).
|
|
15
15
|
attr_reader :allowed_columns
|
|
16
16
|
|
|
17
17
|
# Array of modules that extend this model's dataset. Stored
|
|
@@ -23,7 +23,7 @@ module Sequel
|
|
|
23
23
|
# stored so when the dataset changes, methods defined with def_dataset_method
|
|
24
24
|
# will be applied to the new dataset.
|
|
25
25
|
attr_reader :dataset_methods
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
# Array of plugin modules loaded by this class
|
|
28
28
|
#
|
|
29
29
|
# Sequel::Model.plugins
|
|
@@ -105,6 +105,17 @@ module Sequel
|
|
|
105
105
|
args = args.first if (args.size == 1)
|
|
106
106
|
args.is_a?(Hash) ? dataset[args] : primary_key_lookup(args)
|
|
107
107
|
end
|
|
108
|
+
|
|
109
|
+
# Initializes a model instance as an existing record. This constructor is
|
|
110
|
+
# used by Sequel to initialize model instances when fetching records.
|
|
111
|
+
# Requires that values be a hash where all keys are symbols. It
|
|
112
|
+
# probably should not be used by external code.
|
|
113
|
+
def call(values)
|
|
114
|
+
o = allocate
|
|
115
|
+
o.set_values(values)
|
|
116
|
+
o.after_initialize
|
|
117
|
+
o
|
|
118
|
+
end
|
|
108
119
|
|
|
109
120
|
# Clear the setter_methods cache
|
|
110
121
|
def clear_setter_methods_cache
|
|
@@ -148,6 +159,36 @@ module Sequel
|
|
|
148
159
|
def dataset=(ds)
|
|
149
160
|
set_dataset(ds)
|
|
150
161
|
end
|
|
162
|
+
|
|
163
|
+
# Extend the dataset with a module, similar to adding
|
|
164
|
+
# a plugin with the methods defined in DatasetMethods. If a block
|
|
165
|
+
# is given, an anonymous module is created and the module_evaled, otherwise
|
|
166
|
+
# the argument should be a module. Returns the module given or the anonymous
|
|
167
|
+
# module created.
|
|
168
|
+
#
|
|
169
|
+
# Artist.dataset_module Sequel::ColumnsIntrospection
|
|
170
|
+
#
|
|
171
|
+
# Artist.dataset_module do
|
|
172
|
+
# def foo
|
|
173
|
+
# :bar
|
|
174
|
+
# end
|
|
175
|
+
# end
|
|
176
|
+
# Artist.dataset.foo
|
|
177
|
+
# # => :bar
|
|
178
|
+
# Artist.foo
|
|
179
|
+
# # => :bar
|
|
180
|
+
def dataset_module(mod = nil)
|
|
181
|
+
if mod
|
|
182
|
+
raise Error, "can't provide both argument and block to Model.dataset_module" if block_given?
|
|
183
|
+
dataset_extend(mod)
|
|
184
|
+
mod
|
|
185
|
+
else
|
|
186
|
+
@dataset_module ||= Module.new
|
|
187
|
+
@dataset_module.module_eval(&Proc.new) if block_given?
|
|
188
|
+
dataset_extend(@dataset_module)
|
|
189
|
+
@dataset_module
|
|
190
|
+
end
|
|
191
|
+
end
|
|
151
192
|
|
|
152
193
|
# Returns the database associated with the Model class.
|
|
153
194
|
# If this model doesn't have a database associated with it,
|
|
@@ -162,7 +203,7 @@ module Sequel
|
|
|
162
203
|
def db
|
|
163
204
|
return @db if @db
|
|
164
205
|
@db = self == Model ? DATABASES.first : superclass.db
|
|
165
|
-
raise(Error, "No database associated with #{self}") unless @db
|
|
206
|
+
raise(Error, "No database associated with #{self}: have you called Sequel.connect or #{self}.db= ?") unless @db
|
|
166
207
|
@db
|
|
167
208
|
end
|
|
168
209
|
|
|
@@ -217,9 +258,30 @@ module Sequel
|
|
|
217
258
|
@dataset_methods[meth] = block
|
|
218
259
|
dataset.meta_def(meth, &block) if @dataset
|
|
219
260
|
end
|
|
220
|
-
args.each
|
|
261
|
+
args.each do |arg|
|
|
262
|
+
if arg.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
|
263
|
+
instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg, true)
|
|
264
|
+
else
|
|
265
|
+
def_model_dataset_method_block(arg)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
221
268
|
end
|
|
222
|
-
|
|
269
|
+
|
|
270
|
+
module_eval(if RUBY_VERSION < '1.8.7'
|
|
271
|
+
<<-END
|
|
272
|
+
def def_model_dataset_method_block(arg)
|
|
273
|
+
meta_def(arg){|*args| dataset.send(arg, *args)}
|
|
274
|
+
end
|
|
275
|
+
END
|
|
276
|
+
else
|
|
277
|
+
<<-END
|
|
278
|
+
def def_model_dataset_method_block(arg)
|
|
279
|
+
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
280
|
+
end
|
|
281
|
+
END
|
|
282
|
+
end, __FILE__, __LINE__ - 4)
|
|
283
|
+
private :def_model_dataset_method_block
|
|
284
|
+
|
|
223
285
|
# Finds a single record according to the supplied filter.
|
|
224
286
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
|
225
287
|
#
|
|
@@ -248,6 +310,13 @@ module Sequel
|
|
|
248
310
|
find(cond) || create(cond, &block)
|
|
249
311
|
end
|
|
250
312
|
|
|
313
|
+
# Clear the setter_methods cache when a module is included, as it
|
|
314
|
+
# may contain setter methods.
|
|
315
|
+
def include(mod)
|
|
316
|
+
clear_setter_methods_cache
|
|
317
|
+
super
|
|
318
|
+
end
|
|
319
|
+
|
|
251
320
|
# If possible, set the dataset for the model subclass as soon as it
|
|
252
321
|
# is created. Also, make sure the inherited class instance variables
|
|
253
322
|
# are copied into the subclass.
|
|
@@ -271,7 +340,8 @@ module Sequel
|
|
|
271
340
|
db
|
|
272
341
|
begin
|
|
273
342
|
if self == Model || !@dataset
|
|
274
|
-
|
|
343
|
+
n = subclass.name
|
|
344
|
+
subclass.set_dataset(subclass.implicit_table_name) unless n.nil? || n.empty?
|
|
275
345
|
elsif @dataset
|
|
276
346
|
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
277
347
|
end
|
|
@@ -290,12 +360,9 @@ module Sequel
|
|
|
290
360
|
pluralize(underscore(demodulize(name))).to_sym
|
|
291
361
|
end
|
|
292
362
|
|
|
293
|
-
#
|
|
294
|
-
# used by Sequel to initialize model instances when fetching records.
|
|
295
|
-
# +load+ requires that values be a hash where all keys are symbols. It
|
|
296
|
-
# probably should not be used by external code.
|
|
363
|
+
# Calls #call with the values hash. Only for backwards compatibility.
|
|
297
364
|
def load(values)
|
|
298
|
-
|
|
365
|
+
call(values)
|
|
299
366
|
end
|
|
300
367
|
|
|
301
368
|
# Clear the setter_methods cache when a setter method is added
|
|
@@ -325,18 +392,13 @@ module Sequel
|
|
|
325
392
|
unless @plugins.include?(m)
|
|
326
393
|
@plugins << m
|
|
327
394
|
m.apply(self, *args, &blk) if m.respond_to?(:apply)
|
|
328
|
-
include(m::InstanceMethods) if
|
|
329
|
-
extend(m::ClassMethods)if
|
|
330
|
-
if
|
|
331
|
-
dataset.extend(m::DatasetMethods) if @dataset
|
|
332
|
-
dataset_method_modules << m::DatasetMethods
|
|
333
|
-
meths = m::DatasetMethods.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
|
|
334
|
-
def_dataset_method(*meths) unless meths.empty?
|
|
335
|
-
end
|
|
395
|
+
include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
|
|
396
|
+
extend(m::ClassMethods)if plugin_module_defined?(m, :ClassMethods)
|
|
397
|
+
dataset_extend(m::DatasetMethods) if plugin_module_defined?(m, :DatasetMethods)
|
|
336
398
|
end
|
|
337
399
|
m.configure(self, *args, &blk) if m.respond_to?(:configure)
|
|
338
400
|
end
|
|
339
|
-
|
|
401
|
+
|
|
340
402
|
# Returns primary key attribute hash. If using a composite primary key
|
|
341
403
|
# value such be an array with values for each primary key in the correct
|
|
342
404
|
# order. For a standard primary key, value should be an object with a
|
|
@@ -389,8 +451,8 @@ module Sequel
|
|
|
389
451
|
# setter methods (methods that end in =) that you want to be used during
|
|
390
452
|
# mass assignment, they need to be listed here as well (without the =).
|
|
391
453
|
#
|
|
392
|
-
# It may be better to use a method such as +set_only+
|
|
393
|
-
#
|
|
454
|
+
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
|
455
|
+
# the allowed fields per call.
|
|
394
456
|
#
|
|
395
457
|
# Artist.set_allowed_columns(:name, :hometown)
|
|
396
458
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -400,10 +462,12 @@ module Sequel
|
|
|
400
462
|
@allowed_columns = cols
|
|
401
463
|
end
|
|
402
464
|
|
|
403
|
-
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol
|
|
404
|
-
#
|
|
465
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
|
466
|
+
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
|
467
|
+
# <tt>SQL::AliasedExpression</tt>
|
|
468
|
+
# (all specifying a table name in the current database), or a +Dataset+.
|
|
405
469
|
# If a dataset is used, the model's database is changed to the database of the given
|
|
406
|
-
# dataset. If a
|
|
470
|
+
# dataset. If a dataset is not used, a dataset is created from the current
|
|
407
471
|
# database with the table name given. Other arguments raise an +Error+.
|
|
408
472
|
# Returns self.
|
|
409
473
|
#
|
|
@@ -418,17 +482,21 @@ module Sequel
|
|
|
418
482
|
def set_dataset(ds, opts={})
|
|
419
483
|
inherited = opts[:inherited]
|
|
420
484
|
@dataset = case ds
|
|
421
|
-
when Symbol
|
|
485
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
|
422
486
|
@simple_table = db.literal(ds)
|
|
423
|
-
db
|
|
487
|
+
db.from(ds)
|
|
424
488
|
when Dataset
|
|
425
|
-
@simple_table =
|
|
489
|
+
@simple_table = if ds.send(:simple_select_all?)
|
|
490
|
+
ds.literal(ds.first_source_table)
|
|
491
|
+
else
|
|
492
|
+
nil
|
|
493
|
+
end
|
|
426
494
|
@db = ds.db
|
|
427
495
|
ds
|
|
428
496
|
else
|
|
429
|
-
raise(Error, "Model.set_dataset takes
|
|
497
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
|
430
498
|
end
|
|
431
|
-
@dataset.row_proc =
|
|
499
|
+
@dataset.row_proc = self
|
|
432
500
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
|
433
501
|
if inherited
|
|
434
502
|
@simple_table = superclass.simple_table
|
|
@@ -441,10 +509,11 @@ module Sequel
|
|
|
441
509
|
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
|
442
510
|
self
|
|
443
511
|
end
|
|
444
|
-
|
|
512
|
+
|
|
445
513
|
# Sets the primary key for this model. You can use either a regular
|
|
446
514
|
# or a composite primary key. To not use a primary key, set to nil
|
|
447
|
-
# or use +no_primary_key+.
|
|
515
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
|
516
|
+
# determine the primary key to use, so this method is not needed often.
|
|
448
517
|
#
|
|
449
518
|
# class Person < Sequel::Model
|
|
450
519
|
# # regular key
|
|
@@ -458,7 +527,11 @@ module Sequel
|
|
|
458
527
|
def set_primary_key(*key)
|
|
459
528
|
clear_setter_methods_cache
|
|
460
529
|
key = key.flatten
|
|
461
|
-
@simple_pk = key.length == 1
|
|
530
|
+
@simple_pk = if key.length == 1
|
|
531
|
+
(@dataset || db).literal(key.first)
|
|
532
|
+
else
|
|
533
|
+
nil
|
|
534
|
+
end
|
|
462
535
|
@primary_key = (key.length == 1) ? key[0] : key
|
|
463
536
|
end
|
|
464
537
|
|
|
@@ -468,10 +541,9 @@ module Sequel
|
|
|
468
541
|
# If you have any virtual setter methods (methods that end in =) that you
|
|
469
542
|
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
|
470
543
|
#
|
|
471
|
-
# It
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# method uses, where everything is allowed other than what you restrict.
|
|
544
|
+
# It's generally a bad idea to rely on a blacklist approach for security. Using a whitelist
|
|
545
|
+
# approach such as set_allowed_columns or the instance level set_only or set_fields methods
|
|
546
|
+
# is usually a better choice. So use of this method is generally a bad idea.
|
|
475
547
|
#
|
|
476
548
|
# Artist.set_restricted_column(:records_sold)
|
|
477
549
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -511,6 +583,9 @@ module Sequel
|
|
|
511
583
|
# 7 days ago.
|
|
512
584
|
#
|
|
513
585
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
|
586
|
+
#
|
|
587
|
+
# This method creates dataset methods that do not accept arguments. To create
|
|
588
|
+
# dataset methods that accept arguments, you have to use def_dataset_method.
|
|
514
589
|
def subset(name, *args, &block)
|
|
515
590
|
def_dataset_method(name){filter(*args, &block)}
|
|
516
591
|
end
|
|
@@ -526,6 +601,7 @@ module Sequel
|
|
|
526
601
|
end
|
|
527
602
|
|
|
528
603
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
|
604
|
+
# Using this method can open up security issues, be very careful before using it.
|
|
529
605
|
#
|
|
530
606
|
# Artist.set(:id=>1) # Error
|
|
531
607
|
# Artist.unrestrict_primary_key
|
|
@@ -548,6 +624,16 @@ module Sequel
|
|
|
548
624
|
end
|
|
549
625
|
end
|
|
550
626
|
|
|
627
|
+
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
|
628
|
+
# module if the model has a dataset. Add dataset methods to the class for all
|
|
629
|
+
# public dataset methods.
|
|
630
|
+
def dataset_extend(mod)
|
|
631
|
+
dataset.extend(mod) if @dataset
|
|
632
|
+
dataset_method_modules << mod
|
|
633
|
+
meths = mod.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
|
|
634
|
+
def_dataset_method(*meths) unless meths.empty?
|
|
635
|
+
end
|
|
636
|
+
|
|
551
637
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
|
552
638
|
def def_bad_column_accessor(column)
|
|
553
639
|
overridable_methods_module.module_eval do
|
|
@@ -578,10 +664,9 @@ module Sequel
|
|
|
578
664
|
return nil unless @dataset
|
|
579
665
|
schema_hash = {}
|
|
580
666
|
ds_opts = dataset.opts
|
|
581
|
-
single_table = ds_opts[:from] && (ds_opts[:from].length == 1) \
|
|
582
|
-
&& !ds_opts.include?(:join) && !ds_opts.include?(:sql)
|
|
583
667
|
get_columns = proc{check_non_connection_error{columns} || []}
|
|
584
|
-
|
|
668
|
+
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)}
|
|
669
|
+
if schema_array
|
|
585
670
|
schema_array.each{|k,v| schema_hash[k] = v}
|
|
586
671
|
if ds_opts.include?(:select)
|
|
587
672
|
# We don't remove the columns from the schema_hash,
|
|
@@ -589,6 +674,7 @@ module Sequel
|
|
|
589
674
|
# even if they are not selected.
|
|
590
675
|
cols = get_columns.call
|
|
591
676
|
cols.each{|c| schema_hash[c] ||= {}}
|
|
677
|
+
def_column_accessor(*schema_hash.keys)
|
|
592
678
|
else
|
|
593
679
|
# Dataset is for a single table with all columns,
|
|
594
680
|
# so set the columns based on the order they were
|
|
@@ -657,6 +743,15 @@ module Sequel
|
|
|
657
743
|
Sequel::Plugins.const_get(module_name)
|
|
658
744
|
end
|
|
659
745
|
|
|
746
|
+
# Check if the plugin module +plugin+ defines the constant named by +submod+.
|
|
747
|
+
def plugin_module_defined?(plugin, submod)
|
|
748
|
+
if RUBY_VERSION >= '1.9'
|
|
749
|
+
plugin.const_defined?(submod, false)
|
|
750
|
+
else
|
|
751
|
+
plugin.const_defined?(submod)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
660
755
|
# Find the row in the dataset that matches the primary key. Uses
|
|
661
756
|
# a static SQL optimization if the table and primary key are simple.
|
|
662
757
|
def primary_key_lookup(pk)
|
|
@@ -686,18 +781,19 @@ module Sequel
|
|
|
686
781
|
|
|
687
782
|
# Sequel::Model instance methods that implement basic model functionality.
|
|
688
783
|
#
|
|
689
|
-
# * All of the methods in +HOOKS+ create instance methods that are called
|
|
784
|
+
# * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
|
|
690
785
|
# by Sequel when the appropriate action occurs. For example, when destroying
|
|
691
|
-
# a model object, Sequel will call +before_destroy+, do
|
|
692
|
-
# and then call +after_destroy+.
|
|
786
|
+
# a model object, Sequel will call +around_destory+, which will call +before_destroy+, do
|
|
787
|
+
# the destroy, and then call +after_destroy+.
|
|
693
788
|
# * The following instance_methods all call the class method of the same
|
|
694
|
-
# name: columns,
|
|
789
|
+
# name: columns, db, primary_key, db_schema.
|
|
695
790
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
|
696
|
-
# to set values for the attribute. It also creates
|
|
791
|
+
# to set values for the attribute. It also creates instance getters returning
|
|
697
792
|
# the value of the setting. If the value has not yet been set, it
|
|
698
793
|
# gets the default value from the class by calling the class method of the same name.
|
|
699
794
|
module InstanceMethods
|
|
700
795
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
|
796
|
+
AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
|
701
797
|
|
|
702
798
|
# Define instance method(s) that calls class method(s) of the
|
|
703
799
|
# same name, caching the result in an instance variable. Define
|
|
@@ -735,7 +831,7 @@ module Sequel
|
|
|
735
831
|
#
|
|
736
832
|
# Arguments:
|
|
737
833
|
# values :: should be a hash to pass to set.
|
|
738
|
-
# from_db ::
|
|
834
|
+
# from_db :: only for backwards compatibility, forget it exists.
|
|
739
835
|
#
|
|
740
836
|
# Artist.new(:name=>'Bob')
|
|
741
837
|
#
|
|
@@ -744,13 +840,12 @@ module Sequel
|
|
|
744
840
|
# end
|
|
745
841
|
def initialize(values = {}, from_db = false)
|
|
746
842
|
if from_db
|
|
747
|
-
@new = false
|
|
748
843
|
set_values(values)
|
|
749
844
|
else
|
|
750
845
|
@values = {}
|
|
751
846
|
@new = true
|
|
752
847
|
@modified = true
|
|
753
|
-
|
|
848
|
+
initialize_set(values)
|
|
754
849
|
changed_columns.clear
|
|
755
850
|
yield self if block_given?
|
|
756
851
|
end
|
|
@@ -766,7 +861,7 @@ module Sequel
|
|
|
766
861
|
|
|
767
862
|
# Sets the value for the given column. If typecasting is enabled for
|
|
768
863
|
# this object, typecast the value based on the column's type.
|
|
769
|
-
# If this
|
|
864
|
+
# If this is a new record or the typecasted value isn't the same
|
|
770
865
|
# as the current value for the column, mark the column as changed.
|
|
771
866
|
#
|
|
772
867
|
# a = Artist.new
|
|
@@ -874,14 +969,25 @@ module Sequel
|
|
|
874
969
|
|
|
875
970
|
# Returns true when current instance exists, false otherwise.
|
|
876
971
|
# Generally an object that isn't new will exist unless it has
|
|
877
|
-
# been deleted.
|
|
972
|
+
# been deleted. Uses a database query to check for existence,
|
|
973
|
+
# unless the model object is new, in which case this is always
|
|
974
|
+
# false.
|
|
878
975
|
#
|
|
879
976
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
880
977
|
# # => true
|
|
978
|
+
# Artist.new.exists?
|
|
979
|
+
# # => false
|
|
881
980
|
def exists?
|
|
882
|
-
!this.get(1).nil?
|
|
981
|
+
new? ? false : !this.get(1).nil?
|
|
883
982
|
end
|
|
884
983
|
|
|
984
|
+
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
985
|
+
# module may contain setter methods.
|
|
986
|
+
def extend(mod)
|
|
987
|
+
@singleton_setter_added = true
|
|
988
|
+
super
|
|
989
|
+
end
|
|
990
|
+
|
|
885
991
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
|
886
992
|
# the same class and values (if pk is nil).
|
|
887
993
|
#
|
|
@@ -890,7 +996,14 @@ module Sequel
|
|
|
890
996
|
# Artist.new.hash == Artist.new.hash # true
|
|
891
997
|
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
|
892
998
|
def hash
|
|
893
|
-
|
|
999
|
+
case primary_key
|
|
1000
|
+
when Array
|
|
1001
|
+
[model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1002
|
+
when Symbol
|
|
1003
|
+
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1004
|
+
else
|
|
1005
|
+
[model, @values.sort_by{|k,v| k.to_s}].hash
|
|
1006
|
+
end
|
|
894
1007
|
end
|
|
895
1008
|
|
|
896
1009
|
# Returns value for the :id attribute, even if the primary key is
|
|
@@ -967,11 +1080,11 @@ module Sequel
|
|
|
967
1080
|
# Artist.new.new? # => true
|
|
968
1081
|
# Artist[1].new? # => false
|
|
969
1082
|
def new?
|
|
970
|
-
@new
|
|
1083
|
+
defined?(@new) ? @new : (@new = false)
|
|
971
1084
|
end
|
|
972
1085
|
|
|
973
1086
|
# Returns the primary key value identifying the model instance.
|
|
974
|
-
# Raises an
|
|
1087
|
+
# Raises an +Error+ if this model does not have a primary key.
|
|
975
1088
|
# If the model has a composite primary key, returns an array of values.
|
|
976
1089
|
#
|
|
977
1090
|
# Artist[1].pk # => 1
|
|
@@ -1016,26 +1129,28 @@ module Sequel
|
|
|
1016
1129
|
#
|
|
1017
1130
|
# If +save+ fails and either raise_on_save_failure or the
|
|
1018
1131
|
# :raise_on_failure option is true, it raises ValidationFailed
|
|
1019
|
-
# or
|
|
1132
|
+
# or HookFailed. Otherwise it returns nil.
|
|
1020
1133
|
#
|
|
1021
1134
|
# If it succeeds, it returns self.
|
|
1022
1135
|
#
|
|
1023
1136
|
# You can provide an optional list of columns to update, in which
|
|
1024
|
-
# case it only updates those columns.
|
|
1137
|
+
# case it only updates those columns, or a options hash.
|
|
1025
1138
|
#
|
|
1026
1139
|
# Takes the following options:
|
|
1027
1140
|
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
1030
|
-
#
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1141
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1142
|
+
# :transaction :: set to true or false to override the current
|
|
1143
|
+
# +use_transactions+ setting
|
|
1144
|
+
# :validate :: set to false to skip validation
|
|
1145
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1146
|
+
# +raise_on_save_failure+ setting
|
|
1034
1147
|
def save(*columns)
|
|
1035
1148
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1036
|
-
if opts[:validate] != false
|
|
1037
|
-
|
|
1038
|
-
|
|
1149
|
+
if opts[:validate] != false
|
|
1150
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1151
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
|
1152
|
+
return
|
|
1153
|
+
end
|
|
1039
1154
|
end
|
|
1040
1155
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
|
1041
1156
|
end
|
|
@@ -1075,7 +1190,8 @@ module Sequel
|
|
|
1075
1190
|
end
|
|
1076
1191
|
|
|
1077
1192
|
# Set all values using the entries in the hash, except for the keys
|
|
1078
|
-
# given in except.
|
|
1193
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
|
1194
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1079
1195
|
#
|
|
1080
1196
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
|
1081
1197
|
# artist.name # => 'Jim'
|
|
@@ -1086,10 +1202,10 @@ module Sequel
|
|
|
1086
1202
|
# For each of the fields in the given array +fields+, call the setter
|
|
1087
1203
|
# method with the value of that +hash+ entry for the field. Returns self.
|
|
1088
1204
|
#
|
|
1089
|
-
# artist.set_fields({:name=>'Jim'}, :name)
|
|
1205
|
+
# artist.set_fields({:name=>'Jim'}, [:name])
|
|
1090
1206
|
# artist.name # => 'Jim'
|
|
1091
1207
|
#
|
|
1092
|
-
# artist.set_fields({:hometown=>'LA'}, :name)
|
|
1208
|
+
# artist.set_fields({:hometown=>'LA'}, [:name])
|
|
1093
1209
|
# artist.name # => nil
|
|
1094
1210
|
# artist.hometown # => 'Sac'
|
|
1095
1211
|
def set_fields(hash, fields)
|
|
@@ -1098,16 +1214,24 @@ module Sequel
|
|
|
1098
1214
|
end
|
|
1099
1215
|
|
|
1100
1216
|
# Set the values using the entries in the hash, only if the key
|
|
1101
|
-
# is included in only.
|
|
1217
|
+
# is included in only. It may be a better idea to use +set_fields+
|
|
1218
|
+
# instead of this method.
|
|
1102
1219
|
#
|
|
1103
1220
|
# artist.set_only({:name=>'Jim'}, :name)
|
|
1104
1221
|
# artist.name # => 'Jim'
|
|
1105
1222
|
#
|
|
1106
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
|
1223
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
|
1107
1224
|
def set_only(hash, *only)
|
|
1108
1225
|
set_restricted(hash, only.flatten, false)
|
|
1109
1226
|
end
|
|
1110
1227
|
|
|
1228
|
+
# Replace the current values with hash. Should definitely not be
|
|
1229
|
+
# used with untrusted input, and should probably not be called
|
|
1230
|
+
# directly by user code.
|
|
1231
|
+
def set_values(hash)
|
|
1232
|
+
@values = hash
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1111
1235
|
# Clear the setter_methods cache when a method is added
|
|
1112
1236
|
def singleton_method_added(meth)
|
|
1113
1237
|
@singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
|
|
@@ -1122,7 +1246,7 @@ module Sequel
|
|
|
1122
1246
|
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
|
1123
1247
|
end
|
|
1124
1248
|
|
|
1125
|
-
# Runs set with the passed hash and then runs save_changes.
|
|
1249
|
+
# Runs #set with the passed hash and then runs save_changes.
|
|
1126
1250
|
#
|
|
1127
1251
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1128
1252
|
def update(hash)
|
|
@@ -1130,7 +1254,7 @@ module Sequel
|
|
|
1130
1254
|
end
|
|
1131
1255
|
|
|
1132
1256
|
# Update all values using the entries in the hash, ignoring any setting of
|
|
1133
|
-
# allowed_columns or
|
|
1257
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
|
1134
1258
|
#
|
|
1135
1259
|
# Artist.set_restricted_columns(:name)
|
|
1136
1260
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1139,7 +1263,8 @@ module Sequel
|
|
|
1139
1263
|
end
|
|
1140
1264
|
|
|
1141
1265
|
# Update all values using the entries in the hash, except for the keys
|
|
1142
|
-
# given in except.
|
|
1266
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
|
1267
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1143
1268
|
#
|
|
1144
1269
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1145
1270
|
def update_except(hash, *except)
|
|
@@ -1149,10 +1274,10 @@ module Sequel
|
|
|
1149
1274
|
# Update the instances values by calling +set_fields+ with the +hash+
|
|
1150
1275
|
# and +fields+, then save any changes to the record. Returns self.
|
|
1151
1276
|
#
|
|
1152
|
-
# artist.update_fields({:name=>'Jim'}, :name)
|
|
1277
|
+
# artist.update_fields({:name=>'Jim'}, [:name])
|
|
1153
1278
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1154
1279
|
#
|
|
1155
|
-
# artist.update_fields({:hometown=>'LA'}, :name)
|
|
1280
|
+
# artist.update_fields({:hometown=>'LA'}, [:name])
|
|
1156
1281
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
|
1157
1282
|
def update_fields(hash, fields)
|
|
1158
1283
|
set_fields(hash, fields)
|
|
@@ -1160,7 +1285,8 @@ module Sequel
|
|
|
1160
1285
|
end
|
|
1161
1286
|
|
|
1162
1287
|
# Update the values using the entries in the hash, only if the key
|
|
1163
|
-
# is included in only.
|
|
1288
|
+
# is included in only. It may be a better idea to use +update_fields+
|
|
1289
|
+
# instead of this method.
|
|
1164
1290
|
#
|
|
1165
1291
|
# artist.update_only({:name=>'Jim'}, :name)
|
|
1166
1292
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1185,21 +1311,15 @@ module Sequel
|
|
|
1185
1311
|
# artist(:name=>'Invalid').valid? # => false
|
|
1186
1312
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
|
1187
1313
|
def valid?(opts = {})
|
|
1188
|
-
|
|
1189
|
-
if before_validation == false
|
|
1190
|
-
raise_hook_failure(:validation) if raise_on_failure?(opts)
|
|
1191
|
-
return false
|
|
1192
|
-
end
|
|
1193
|
-
validate
|
|
1194
|
-
after_validation
|
|
1195
|
-
errors.empty?
|
|
1314
|
+
_valid?(false, opts)
|
|
1196
1315
|
end
|
|
1197
1316
|
|
|
1198
1317
|
private
|
|
1199
1318
|
|
|
1200
|
-
#
|
|
1319
|
+
# Do the deletion of the object's dataset, and check that the row
|
|
1320
|
+
# was actually deleted.
|
|
1201
1321
|
def _delete
|
|
1202
|
-
n =
|
|
1322
|
+
n = _delete_without_checking
|
|
1203
1323
|
raise(NoExistingObject, "Attempt to delete object did not result in a single row modification (Rows Deleted: #{n}, SQL: #{_delete_dataset.delete_sql})") if require_modification && n != 1
|
|
1204
1324
|
n
|
|
1205
1325
|
end
|
|
@@ -1210,12 +1330,27 @@ module Sequel
|
|
|
1210
1330
|
this
|
|
1211
1331
|
end
|
|
1212
1332
|
|
|
1333
|
+
# Actually do the deletion of the object's dataset. Return the
|
|
1334
|
+
# number of rows modified.
|
|
1335
|
+
def _delete_without_checking
|
|
1336
|
+
_delete_dataset.delete
|
|
1337
|
+
end
|
|
1338
|
+
|
|
1213
1339
|
# Internal destroy method, separted from destroy to
|
|
1214
1340
|
# allow running inside a transaction
|
|
1215
1341
|
def _destroy(opts)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1342
|
+
sh = {:server=>this_server}
|
|
1343
|
+
db.after_rollback(sh){after_destroy_rollback}
|
|
1344
|
+
called = false
|
|
1345
|
+
around_destroy do
|
|
1346
|
+
called = true
|
|
1347
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
|
1348
|
+
_destroy_delete
|
|
1349
|
+
after_destroy
|
|
1350
|
+
true
|
|
1351
|
+
end
|
|
1352
|
+
raise_hook_failure(:destroy) unless called
|
|
1353
|
+
db.after_commit(sh){after_destroy_commit}
|
|
1219
1354
|
self
|
|
1220
1355
|
end
|
|
1221
1356
|
|
|
@@ -1230,11 +1365,11 @@ module Sequel
|
|
|
1230
1365
|
# the record should be refreshed from the database.
|
|
1231
1366
|
def _insert
|
|
1232
1367
|
ds = _insert_dataset
|
|
1233
|
-
if ds.
|
|
1368
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
|
1234
1369
|
@values = h
|
|
1235
1370
|
nil
|
|
1236
1371
|
else
|
|
1237
|
-
iid = ds
|
|
1372
|
+
iid = _insert_raw(ds)
|
|
1238
1373
|
# if we have a regular primary key and it's not set in @values,
|
|
1239
1374
|
# we assume it's the last inserted id
|
|
1240
1375
|
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !@values[pk]
|
|
@@ -1243,55 +1378,95 @@ module Sequel
|
|
|
1243
1378
|
pk
|
|
1244
1379
|
end
|
|
1245
1380
|
end
|
|
1246
|
-
|
|
1381
|
+
|
|
1247
1382
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1248
1383
|
# dataset by default.
|
|
1249
1384
|
def _insert_dataset
|
|
1250
1385
|
model.dataset
|
|
1251
1386
|
end
|
|
1252
1387
|
|
|
1388
|
+
# Insert into the given dataset and return the primary key created (if any).
|
|
1389
|
+
def _insert_raw(ds)
|
|
1390
|
+
ds.insert(@values)
|
|
1391
|
+
end
|
|
1392
|
+
|
|
1393
|
+
# Insert into the given dataset and return the hash of column values.
|
|
1394
|
+
def _insert_select_raw(ds)
|
|
1395
|
+
ds.insert_select(@values)
|
|
1396
|
+
end
|
|
1397
|
+
|
|
1253
1398
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1254
1399
|
# is used for reading newly inserted values from the database
|
|
1255
1400
|
def _refresh(dataset)
|
|
1256
|
-
set_values(dataset
|
|
1401
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
|
1257
1402
|
changed_columns.clear
|
|
1258
1403
|
self
|
|
1259
1404
|
end
|
|
1405
|
+
|
|
1406
|
+
# Get the row of column data from the database.
|
|
1407
|
+
def _refresh_get(dataset)
|
|
1408
|
+
dataset.first
|
|
1409
|
+
end
|
|
1260
1410
|
|
|
1261
1411
|
# Internal version of save, split from save to allow running inside
|
|
1262
1412
|
# it's own transaction.
|
|
1263
1413
|
def _save(columns, opts)
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1414
|
+
sh = {:server=>this_server}
|
|
1415
|
+
db.after_rollback(sh){after_rollback}
|
|
1416
|
+
was_new = false
|
|
1417
|
+
pk = nil
|
|
1418
|
+
called_save = false
|
|
1419
|
+
called_cu = false
|
|
1420
|
+
around_save do
|
|
1421
|
+
called_save = true
|
|
1422
|
+
raise_hook_failure(:save) if before_save == false
|
|
1423
|
+
if new?
|
|
1424
|
+
was_new = true
|
|
1425
|
+
around_create do
|
|
1426
|
+
called_cu = true
|
|
1427
|
+
raise_hook_failure(:create) if before_create == false
|
|
1428
|
+
pk = _insert
|
|
1429
|
+
@this = nil
|
|
1430
|
+
@new = false
|
|
1431
|
+
@was_new = true
|
|
1432
|
+
after_create
|
|
1433
|
+
true
|
|
1434
|
+
end
|
|
1435
|
+
raise_hook_failure(:create) unless called_cu
|
|
1436
|
+
else
|
|
1437
|
+
around_update do
|
|
1438
|
+
called_cu = true
|
|
1439
|
+
raise_hook_failure(:update) if before_update == false
|
|
1440
|
+
if columns.empty?
|
|
1441
|
+
@columns_updated = if opts[:changed]
|
|
1442
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1443
|
+
else
|
|
1444
|
+
_save_update_all_columns_hash
|
|
1445
|
+
end
|
|
1446
|
+
changed_columns.clear
|
|
1447
|
+
else # update only the specified columns
|
|
1448
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1449
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
|
1450
|
+
end
|
|
1451
|
+
_update_columns(@columns_updated)
|
|
1452
|
+
@this = nil
|
|
1453
|
+
after_update
|
|
1454
|
+
true
|
|
1455
|
+
end
|
|
1456
|
+
raise_hook_failure(:update) unless called_cu
|
|
1457
|
+
end
|
|
1272
1458
|
after_save
|
|
1459
|
+
true
|
|
1460
|
+
end
|
|
1461
|
+
raise_hook_failure(:save) unless called_save
|
|
1462
|
+
if was_new
|
|
1273
1463
|
@was_new = nil
|
|
1274
1464
|
pk ? _save_refresh : changed_columns.clear
|
|
1275
1465
|
else
|
|
1276
|
-
raise_hook_failure(:update) if before_update == false
|
|
1277
|
-
if columns.empty?
|
|
1278
|
-
@columns_updated = if opts[:changed]
|
|
1279
|
-
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1280
|
-
else
|
|
1281
|
-
_save_update_all_columns_hash
|
|
1282
|
-
end
|
|
1283
|
-
changed_columns.clear
|
|
1284
|
-
else # update only the specified columns
|
|
1285
|
-
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1286
|
-
changed_columns.reject!{|c| columns.include?(c)}
|
|
1287
|
-
end
|
|
1288
|
-
_update_columns(@columns_updated)
|
|
1289
|
-
@this = nil
|
|
1290
|
-
after_update
|
|
1291
|
-
after_save
|
|
1292
1466
|
@columns_updated = nil
|
|
1293
1467
|
end
|
|
1294
1468
|
@modified = false
|
|
1469
|
+
db.after_commit(sh){after_commit}
|
|
1295
1470
|
self
|
|
1296
1471
|
end
|
|
1297
1472
|
|
|
@@ -1321,9 +1496,10 @@ module Sequel
|
|
|
1321
1496
|
_update(columns) unless columns.empty?
|
|
1322
1497
|
end
|
|
1323
1498
|
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
|
1499
|
+
# Update this instance's dataset with the supplied column hash,
|
|
1500
|
+
# checking that only a single row was modified.
|
|
1325
1501
|
def _update(columns)
|
|
1326
|
-
n =
|
|
1502
|
+
n = _update_without_checking(columns)
|
|
1327
1503
|
raise(NoExistingObject, "Attempt to update object did not result in a single row modification (SQL: #{_update_dataset.update_sql(columns)})") if require_modification && n != 1
|
|
1328
1504
|
n
|
|
1329
1505
|
end
|
|
@@ -1334,7 +1510,46 @@ module Sequel
|
|
|
1334
1510
|
this
|
|
1335
1511
|
end
|
|
1336
1512
|
|
|
1337
|
-
#
|
|
1513
|
+
# Update this instances dataset with the supplied column hash.
|
|
1514
|
+
def _update_without_checking(columns)
|
|
1515
|
+
_update_dataset.update(columns)
|
|
1516
|
+
end
|
|
1517
|
+
|
|
1518
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
|
1519
|
+
# failures will be raised as HookFailure exceptions. If it is
|
|
1520
|
+
# +false+, +false+ will be returned instead.
|
|
1521
|
+
def _valid?(raise_errors, opts)
|
|
1522
|
+
errors.clear
|
|
1523
|
+
called = false
|
|
1524
|
+
error = false
|
|
1525
|
+
around_validation do
|
|
1526
|
+
called = true
|
|
1527
|
+
if before_validation == false
|
|
1528
|
+
if raise_errors
|
|
1529
|
+
raise_hook_failure(:validation)
|
|
1530
|
+
else
|
|
1531
|
+
error = true
|
|
1532
|
+
end
|
|
1533
|
+
false
|
|
1534
|
+
else
|
|
1535
|
+
validate
|
|
1536
|
+
after_validation
|
|
1537
|
+
errors.empty?
|
|
1538
|
+
end
|
|
1539
|
+
end
|
|
1540
|
+
error = true unless called
|
|
1541
|
+
if error
|
|
1542
|
+
if raise_errors
|
|
1543
|
+
raise_hook_failure(:validation)
|
|
1544
|
+
else
|
|
1545
|
+
false
|
|
1546
|
+
end
|
|
1547
|
+
else
|
|
1548
|
+
errors.empty?
|
|
1549
|
+
end
|
|
1550
|
+
end
|
|
1551
|
+
|
|
1552
|
+
# If not raising on failure, check for HookFailed
|
|
1338
1553
|
# being raised by yielding and swallow it.
|
|
1339
1554
|
def checked_save_failure(opts)
|
|
1340
1555
|
if raise_on_failure?(opts)
|
|
@@ -1342,7 +1557,7 @@ module Sequel
|
|
|
1342
1557
|
else
|
|
1343
1558
|
begin
|
|
1344
1559
|
yield
|
|
1345
|
-
rescue
|
|
1560
|
+
rescue HookFailed
|
|
1346
1561
|
nil
|
|
1347
1562
|
end
|
|
1348
1563
|
end
|
|
@@ -1350,7 +1565,14 @@ module Sequel
|
|
|
1350
1565
|
|
|
1351
1566
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1352
1567
|
def checked_transaction(opts={})
|
|
1353
|
-
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1568
|
+
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1569
|
+
end
|
|
1570
|
+
|
|
1571
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1572
|
+
# exists so it can be overridden. This is called only for new records, before
|
|
1573
|
+
# changed_columns is cleared.
|
|
1574
|
+
def initialize_set(h)
|
|
1575
|
+
set(h)
|
|
1354
1576
|
end
|
|
1355
1577
|
|
|
1356
1578
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
|
@@ -1369,7 +1591,7 @@ module Sequel
|
|
|
1369
1591
|
# Raise an error appropriate to the hook type. May be swallowed by
|
|
1370
1592
|
# checked_save_failure depending on the raise_on_failure? setting.
|
|
1371
1593
|
def raise_hook_failure(type)
|
|
1372
|
-
raise
|
|
1594
|
+
raise HookFailed, "one of the before_#{type} hooks returned false"
|
|
1373
1595
|
end
|
|
1374
1596
|
|
|
1375
1597
|
# Set the columns, filtered by the only and except arrays.
|
|
@@ -1385,17 +1607,21 @@ module Sequel
|
|
|
1385
1607
|
if meths.include?(m)
|
|
1386
1608
|
send(m, v)
|
|
1387
1609
|
elsif strict
|
|
1388
|
-
|
|
1610
|
+
# Avoid using respond_to? or creating symbols from user input
|
|
1611
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
|
1612
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
|
1613
|
+
raise Error, "#{k} is a restricted primary key"
|
|
1614
|
+
else
|
|
1615
|
+
raise Error, "#{k} is a restricted column"
|
|
1616
|
+
end
|
|
1617
|
+
else
|
|
1618
|
+
raise Error, "method #{m} doesn't exist"
|
|
1619
|
+
end
|
|
1389
1620
|
end
|
|
1390
1621
|
end
|
|
1391
1622
|
self
|
|
1392
1623
|
end
|
|
1393
1624
|
|
|
1394
|
-
# Replace the current values with hash.
|
|
1395
|
-
def set_values(hash)
|
|
1396
|
-
@values = hash
|
|
1397
|
-
end
|
|
1398
|
-
|
|
1399
1625
|
# Returns all methods that can be used for attribute
|
|
1400
1626
|
# assignment (those that end with =), modified by the only
|
|
1401
1627
|
# and except arguments:
|
|
@@ -1424,6 +1650,12 @@ module Sequel
|
|
|
1424
1650
|
meths
|
|
1425
1651
|
end
|
|
1426
1652
|
end
|
|
1653
|
+
|
|
1654
|
+
# The server/shard that the model object's dataset uses, or :default if the
|
|
1655
|
+
# model object's dataset does not have an associated shard.
|
|
1656
|
+
def this_server
|
|
1657
|
+
primary_key ? (this.opts[:server] || :default) : (model.dataset.opts[:server] || :default)
|
|
1658
|
+
end
|
|
1427
1659
|
|
|
1428
1660
|
# Typecast the value to the column's type if typecasting. Calls the database's
|
|
1429
1661
|
# typecast_value method, so database adapters can override/augment the handling
|
|
@@ -1461,6 +1693,18 @@ module Sequel
|
|
|
1461
1693
|
# Artist.dataset.model # => Artist
|
|
1462
1694
|
attr_accessor :model
|
|
1463
1695
|
|
|
1696
|
+
# Assume if a single integer is given that it is a lookup by primary
|
|
1697
|
+
# key, and call with_pk with the argument.
|
|
1698
|
+
#
|
|
1699
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1700
|
+
def [](*args)
|
|
1701
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
|
1702
|
+
with_pk(i)
|
|
1703
|
+
else
|
|
1704
|
+
super
|
|
1705
|
+
end
|
|
1706
|
+
end
|
|
1707
|
+
|
|
1464
1708
|
# Destroy each row in the dataset by instantiating it and then calling
|
|
1465
1709
|
# destroy on the resulting model object. This isn't as fast as deleting
|
|
1466
1710
|
# the dataset, which does a single SQL call, but this runs any destroy
|
|
@@ -1491,6 +1735,19 @@ module Sequel
|
|
|
1491
1735
|
super(pk, value_column)
|
|
1492
1736
|
end
|
|
1493
1737
|
end
|
|
1738
|
+
|
|
1739
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
|
1740
|
+
# value.
|
|
1741
|
+
#
|
|
1742
|
+
# # Single primary key
|
|
1743
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1744
|
+
#
|
|
1745
|
+
# # Composite primary key
|
|
1746
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
|
1747
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
|
1748
|
+
def with_pk(pk)
|
|
1749
|
+
first(model.qualified_primary_key_hash(pk))
|
|
1750
|
+
end
|
|
1494
1751
|
end
|
|
1495
1752
|
|
|
1496
1753
|
extend ClassMethods
|