sequel 3.21.0 → 3.28.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 +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- 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 +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- 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/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -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 +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -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 +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- 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/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -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/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -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 +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
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
|
|
@@ -148,6 +148,26 @@ module Sequel
|
|
|
148
148
|
def dataset=(ds)
|
|
149
149
|
set_dataset(ds)
|
|
150
150
|
end
|
|
151
|
+
|
|
152
|
+
# Extend the dataset with an anonymous module, similar to adding
|
|
153
|
+
# a plugin with the methods defined in DatasetMethods. If a block
|
|
154
|
+
# is given, it is module_evaled.
|
|
155
|
+
#
|
|
156
|
+
# Artist.dataset_module do
|
|
157
|
+
# def foo
|
|
158
|
+
# :bar
|
|
159
|
+
# end
|
|
160
|
+
# end
|
|
161
|
+
# Artist.dataset.foo
|
|
162
|
+
# # => :bar
|
|
163
|
+
# Artist.foo
|
|
164
|
+
# # => :bar
|
|
165
|
+
def dataset_module
|
|
166
|
+
@dataset_module ||= Module.new
|
|
167
|
+
@dataset_module.module_eval(&Proc.new) if block_given?
|
|
168
|
+
dataset_extend(@dataset_module)
|
|
169
|
+
@dataset_module
|
|
170
|
+
end
|
|
151
171
|
|
|
152
172
|
# Returns the database associated with the Model class.
|
|
153
173
|
# If this model doesn't have a database associated with it,
|
|
@@ -162,7 +182,7 @@ module Sequel
|
|
|
162
182
|
def db
|
|
163
183
|
return @db if @db
|
|
164
184
|
@db = self == Model ? DATABASES.first : superclass.db
|
|
165
|
-
raise(Error, "No database associated with #{self}") unless @db
|
|
185
|
+
raise(Error, "No database associated with #{self}: have you called Sequel.connect or #{self}.db= ?") unless @db
|
|
166
186
|
@db
|
|
167
187
|
end
|
|
168
188
|
|
|
@@ -248,6 +268,13 @@ module Sequel
|
|
|
248
268
|
find(cond) || create(cond, &block)
|
|
249
269
|
end
|
|
250
270
|
|
|
271
|
+
# Clear the setter_methods cache when a module is included, as it
|
|
272
|
+
# may contain setter methods.
|
|
273
|
+
def include(mod)
|
|
274
|
+
clear_setter_methods_cache
|
|
275
|
+
super
|
|
276
|
+
end
|
|
277
|
+
|
|
251
278
|
# If possible, set the dataset for the model subclass as soon as it
|
|
252
279
|
# is created. Also, make sure the inherited class instance variables
|
|
253
280
|
# are copied into the subclass.
|
|
@@ -271,7 +298,8 @@ module Sequel
|
|
|
271
298
|
db
|
|
272
299
|
begin
|
|
273
300
|
if self == Model || !@dataset
|
|
274
|
-
|
|
301
|
+
n = subclass.name
|
|
302
|
+
subclass.set_dataset(subclass.implicit_table_name) unless n.nil? || n.empty?
|
|
275
303
|
elsif @dataset
|
|
276
304
|
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
277
305
|
end
|
|
@@ -325,18 +353,13 @@ module Sequel
|
|
|
325
353
|
unless @plugins.include?(m)
|
|
326
354
|
@plugins << m
|
|
327
355
|
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
|
|
356
|
+
include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
|
|
357
|
+
extend(m::ClassMethods)if plugin_module_defined?(m, :ClassMethods)
|
|
358
|
+
dataset_extend(m::DatasetMethods) if plugin_module_defined?(m, :DatasetMethods)
|
|
336
359
|
end
|
|
337
360
|
m.configure(self, *args, &blk) if m.respond_to?(:configure)
|
|
338
361
|
end
|
|
339
|
-
|
|
362
|
+
|
|
340
363
|
# Returns primary key attribute hash. If using a composite primary key
|
|
341
364
|
# value such be an array with values for each primary key in the correct
|
|
342
365
|
# order. For a standard primary key, value should be an object with a
|
|
@@ -389,8 +412,8 @@ module Sequel
|
|
|
389
412
|
# setter methods (methods that end in =) that you want to be used during
|
|
390
413
|
# mass assignment, they need to be listed here as well (without the =).
|
|
391
414
|
#
|
|
392
|
-
# It may be better to use a method such as +set_only+
|
|
393
|
-
#
|
|
415
|
+
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
|
416
|
+
# the allowed fields per call.
|
|
394
417
|
#
|
|
395
418
|
# Artist.set_allowed_columns(:name, :hometown)
|
|
396
419
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -400,10 +423,12 @@ module Sequel
|
|
|
400
423
|
@allowed_columns = cols
|
|
401
424
|
end
|
|
402
425
|
|
|
403
|
-
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol
|
|
404
|
-
#
|
|
426
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
|
427
|
+
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
|
428
|
+
# <tt>SQL::AliasedExpression</tt>
|
|
429
|
+
# (all specifying a table name in the current database), or a +Dataset+.
|
|
405
430
|
# If a dataset is used, the model's database is changed to the database of the given
|
|
406
|
-
# dataset. If a
|
|
431
|
+
# dataset. If a dataset is not used, a dataset is created from the current
|
|
407
432
|
# database with the table name given. Other arguments raise an +Error+.
|
|
408
433
|
# Returns self.
|
|
409
434
|
#
|
|
@@ -418,15 +443,15 @@ module Sequel
|
|
|
418
443
|
def set_dataset(ds, opts={})
|
|
419
444
|
inherited = opts[:inherited]
|
|
420
445
|
@dataset = case ds
|
|
421
|
-
when Symbol
|
|
446
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
|
422
447
|
@simple_table = db.literal(ds)
|
|
423
|
-
db
|
|
448
|
+
db.from(ds)
|
|
424
449
|
when Dataset
|
|
425
450
|
@simple_table = nil
|
|
426
451
|
@db = ds.db
|
|
427
452
|
ds
|
|
428
453
|
else
|
|
429
|
-
raise(Error, "Model.set_dataset takes
|
|
454
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
|
430
455
|
end
|
|
431
456
|
@dataset.row_proc = Proc.new{|r| load(r)}
|
|
432
457
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
|
@@ -444,7 +469,8 @@ module Sequel
|
|
|
444
469
|
|
|
445
470
|
# Sets the primary key for this model. You can use either a regular
|
|
446
471
|
# or a composite primary key. To not use a primary key, set to nil
|
|
447
|
-
# or use +no_primary_key+.
|
|
472
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
|
473
|
+
# determine the primary key to use, so this method is not needed often.
|
|
448
474
|
#
|
|
449
475
|
# class Person < Sequel::Model
|
|
450
476
|
# # regular key
|
|
@@ -468,10 +494,9 @@ module Sequel
|
|
|
468
494
|
# If you have any virtual setter methods (methods that end in =) that you
|
|
469
495
|
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
|
470
496
|
#
|
|
471
|
-
# It
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# method uses, where everything is allowed other than what you restrict.
|
|
497
|
+
# It's generally a bad idea to rely on a blacklist approach for security. Using a whitelist
|
|
498
|
+
# approach such as set_allowed_columns or the instance level set_only or set_fields methods
|
|
499
|
+
# is usually a better choice. So use of this method is generally a bad idea.
|
|
475
500
|
#
|
|
476
501
|
# Artist.set_restricted_column(:records_sold)
|
|
477
502
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -511,6 +536,9 @@ module Sequel
|
|
|
511
536
|
# 7 days ago.
|
|
512
537
|
#
|
|
513
538
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
|
539
|
+
#
|
|
540
|
+
# This method creates dataset methods that do not accept arguments. To create
|
|
541
|
+
# dataset methods that accept arguments, you have to use def_dataset_method.
|
|
514
542
|
def subset(name, *args, &block)
|
|
515
543
|
def_dataset_method(name){filter(*args, &block)}
|
|
516
544
|
end
|
|
@@ -526,6 +554,7 @@ module Sequel
|
|
|
526
554
|
end
|
|
527
555
|
|
|
528
556
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
|
557
|
+
# Using this method can open up security issues, be very careful before using it.
|
|
529
558
|
#
|
|
530
559
|
# Artist.set(:id=>1) # Error
|
|
531
560
|
# Artist.unrestrict_primary_key
|
|
@@ -548,6 +577,16 @@ module Sequel
|
|
|
548
577
|
end
|
|
549
578
|
end
|
|
550
579
|
|
|
580
|
+
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
|
581
|
+
# module if the model has a dataset. Add dataset methods to the class for all
|
|
582
|
+
# public dataset methods.
|
|
583
|
+
def dataset_extend(mod)
|
|
584
|
+
dataset.extend(mod) if @dataset
|
|
585
|
+
dataset_method_modules << mod
|
|
586
|
+
meths = mod.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
|
|
587
|
+
def_dataset_method(*meths) unless meths.empty?
|
|
588
|
+
end
|
|
589
|
+
|
|
551
590
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
|
552
591
|
def def_bad_column_accessor(column)
|
|
553
592
|
overridable_methods_module.module_eval do
|
|
@@ -657,6 +696,15 @@ module Sequel
|
|
|
657
696
|
Sequel::Plugins.const_get(module_name)
|
|
658
697
|
end
|
|
659
698
|
|
|
699
|
+
# Check if the plugin module +plugin+ defines the constant named by +submod+.
|
|
700
|
+
def plugin_module_defined?(plugin, submod)
|
|
701
|
+
if RUBY_VERSION >= '1.9'
|
|
702
|
+
plugin.const_defined?(submod, false)
|
|
703
|
+
else
|
|
704
|
+
plugin.const_defined?(submod)
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
660
708
|
# Find the row in the dataset that matches the primary key. Uses
|
|
661
709
|
# a static SQL optimization if the table and primary key are simple.
|
|
662
710
|
def primary_key_lookup(pk)
|
|
@@ -686,18 +734,19 @@ module Sequel
|
|
|
686
734
|
|
|
687
735
|
# Sequel::Model instance methods that implement basic model functionality.
|
|
688
736
|
#
|
|
689
|
-
# * All of the methods in +HOOKS+ create instance methods that are called
|
|
737
|
+
# * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
|
|
690
738
|
# 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+.
|
|
739
|
+
# a model object, Sequel will call +around_destory+, which will call +before_destroy+, do
|
|
740
|
+
# the destroy, and then call +after_destroy+.
|
|
693
741
|
# * The following instance_methods all call the class method of the same
|
|
694
|
-
# name: columns,
|
|
742
|
+
# name: columns, db, primary_key, db_schema.
|
|
695
743
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
|
696
744
|
# to set values for the attribute. It also creates instnace getters returning
|
|
697
745
|
# the value of the setting. If the value has not yet been set, it
|
|
698
746
|
# gets the default value from the class by calling the class method of the same name.
|
|
699
747
|
module InstanceMethods
|
|
700
748
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
|
749
|
+
AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
|
701
750
|
|
|
702
751
|
# Define instance method(s) that calls class method(s) of the
|
|
703
752
|
# same name, caching the result in an instance variable. Define
|
|
@@ -750,7 +799,7 @@ module Sequel
|
|
|
750
799
|
@values = {}
|
|
751
800
|
@new = true
|
|
752
801
|
@modified = true
|
|
753
|
-
|
|
802
|
+
initialize_set(values)
|
|
754
803
|
changed_columns.clear
|
|
755
804
|
yield self if block_given?
|
|
756
805
|
end
|
|
@@ -766,7 +815,7 @@ module Sequel
|
|
|
766
815
|
|
|
767
816
|
# Sets the value for the given column. If typecasting is enabled for
|
|
768
817
|
# this object, typecast the value based on the column's type.
|
|
769
|
-
# If this
|
|
818
|
+
# If this is a new record or the typecasted value isn't the same
|
|
770
819
|
# as the current value for the column, mark the column as changed.
|
|
771
820
|
#
|
|
772
821
|
# a = Artist.new
|
|
@@ -874,14 +923,25 @@ module Sequel
|
|
|
874
923
|
|
|
875
924
|
# Returns true when current instance exists, false otherwise.
|
|
876
925
|
# Generally an object that isn't new will exist unless it has
|
|
877
|
-
# been deleted.
|
|
926
|
+
# been deleted. Uses a database query to check for existence,
|
|
927
|
+
# unless the model object is new, in which case this is always
|
|
928
|
+
# false.
|
|
878
929
|
#
|
|
879
930
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
880
931
|
# # => true
|
|
932
|
+
# Artist.new.exists?
|
|
933
|
+
# # => false
|
|
881
934
|
def exists?
|
|
882
|
-
!this.get(1).nil?
|
|
935
|
+
new? ? false : !this.get(1).nil?
|
|
883
936
|
end
|
|
884
937
|
|
|
938
|
+
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
939
|
+
# module may contain setter methods.
|
|
940
|
+
def extend(mod)
|
|
941
|
+
@singleton_setter_added = true
|
|
942
|
+
super
|
|
943
|
+
end
|
|
944
|
+
|
|
885
945
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
|
886
946
|
# the same class and values (if pk is nil).
|
|
887
947
|
#
|
|
@@ -890,7 +950,14 @@ module Sequel
|
|
|
890
950
|
# Artist.new.hash == Artist.new.hash # true
|
|
891
951
|
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
|
892
952
|
def hash
|
|
893
|
-
|
|
953
|
+
case primary_key
|
|
954
|
+
when Array
|
|
955
|
+
[model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
956
|
+
when Symbol
|
|
957
|
+
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
958
|
+
else
|
|
959
|
+
[model, @values.sort_by{|k,v| k.to_s}].hash
|
|
960
|
+
end
|
|
894
961
|
end
|
|
895
962
|
|
|
896
963
|
# Returns value for the :id attribute, even if the primary key is
|
|
@@ -971,7 +1038,7 @@ module Sequel
|
|
|
971
1038
|
end
|
|
972
1039
|
|
|
973
1040
|
# Returns the primary key value identifying the model instance.
|
|
974
|
-
# Raises an
|
|
1041
|
+
# Raises an +Error+ if this model does not have a primary key.
|
|
975
1042
|
# If the model has a composite primary key, returns an array of values.
|
|
976
1043
|
#
|
|
977
1044
|
# Artist[1].pk # => 1
|
|
@@ -1016,26 +1083,28 @@ module Sequel
|
|
|
1016
1083
|
#
|
|
1017
1084
|
# If +save+ fails and either raise_on_save_failure or the
|
|
1018
1085
|
# :raise_on_failure option is true, it raises ValidationFailed
|
|
1019
|
-
# or
|
|
1086
|
+
# or HookFailed. Otherwise it returns nil.
|
|
1020
1087
|
#
|
|
1021
1088
|
# If it succeeds, it returns self.
|
|
1022
1089
|
#
|
|
1023
1090
|
# You can provide an optional list of columns to update, in which
|
|
1024
|
-
# case it only updates those columns.
|
|
1091
|
+
# case it only updates those columns, or a options hash.
|
|
1025
1092
|
#
|
|
1026
1093
|
# Takes the following options:
|
|
1027
1094
|
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
1030
|
-
#
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1095
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1096
|
+
# :transaction :: set to true or false to override the current
|
|
1097
|
+
# +use_transactions+ setting
|
|
1098
|
+
# :validate :: set to false to skip validation
|
|
1099
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1100
|
+
# +raise_on_save_failure+ setting
|
|
1034
1101
|
def save(*columns)
|
|
1035
1102
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1036
|
-
if opts[:validate] != false
|
|
1037
|
-
|
|
1038
|
-
|
|
1103
|
+
if opts[:validate] != false
|
|
1104
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1105
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
|
1106
|
+
return
|
|
1107
|
+
end
|
|
1039
1108
|
end
|
|
1040
1109
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
|
1041
1110
|
end
|
|
@@ -1075,7 +1144,8 @@ module Sequel
|
|
|
1075
1144
|
end
|
|
1076
1145
|
|
|
1077
1146
|
# Set all values using the entries in the hash, except for the keys
|
|
1078
|
-
# given in except.
|
|
1147
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
|
1148
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1079
1149
|
#
|
|
1080
1150
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
|
1081
1151
|
# artist.name # => 'Jim'
|
|
@@ -1086,10 +1156,10 @@ module Sequel
|
|
|
1086
1156
|
# For each of the fields in the given array +fields+, call the setter
|
|
1087
1157
|
# method with the value of that +hash+ entry for the field. Returns self.
|
|
1088
1158
|
#
|
|
1089
|
-
# artist.set_fields({:name=>'Jim'}, :name)
|
|
1159
|
+
# artist.set_fields({:name=>'Jim'}, [:name])
|
|
1090
1160
|
# artist.name # => 'Jim'
|
|
1091
1161
|
#
|
|
1092
|
-
# artist.set_fields({:hometown=>'LA'}, :name)
|
|
1162
|
+
# artist.set_fields({:hometown=>'LA'}, [:name])
|
|
1093
1163
|
# artist.name # => nil
|
|
1094
1164
|
# artist.hometown # => 'Sac'
|
|
1095
1165
|
def set_fields(hash, fields)
|
|
@@ -1098,12 +1168,13 @@ module Sequel
|
|
|
1098
1168
|
end
|
|
1099
1169
|
|
|
1100
1170
|
# Set the values using the entries in the hash, only if the key
|
|
1101
|
-
# is included in only.
|
|
1171
|
+
# is included in only. It may be a better idea to use +set_fields+
|
|
1172
|
+
# instead of this method.
|
|
1102
1173
|
#
|
|
1103
1174
|
# artist.set_only({:name=>'Jim'}, :name)
|
|
1104
1175
|
# artist.name # => 'Jim'
|
|
1105
1176
|
#
|
|
1106
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
|
1177
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
|
1107
1178
|
def set_only(hash, *only)
|
|
1108
1179
|
set_restricted(hash, only.flatten, false)
|
|
1109
1180
|
end
|
|
@@ -1122,7 +1193,7 @@ module Sequel
|
|
|
1122
1193
|
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
|
1123
1194
|
end
|
|
1124
1195
|
|
|
1125
|
-
# Runs set with the passed hash and then runs save_changes.
|
|
1196
|
+
# Runs #set with the passed hash and then runs save_changes.
|
|
1126
1197
|
#
|
|
1127
1198
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1128
1199
|
def update(hash)
|
|
@@ -1130,7 +1201,7 @@ module Sequel
|
|
|
1130
1201
|
end
|
|
1131
1202
|
|
|
1132
1203
|
# Update all values using the entries in the hash, ignoring any setting of
|
|
1133
|
-
# allowed_columns or
|
|
1204
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
|
1134
1205
|
#
|
|
1135
1206
|
# Artist.set_restricted_columns(:name)
|
|
1136
1207
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1139,7 +1210,8 @@ module Sequel
|
|
|
1139
1210
|
end
|
|
1140
1211
|
|
|
1141
1212
|
# Update all values using the entries in the hash, except for the keys
|
|
1142
|
-
# given in except.
|
|
1213
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
|
1214
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1143
1215
|
#
|
|
1144
1216
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1145
1217
|
def update_except(hash, *except)
|
|
@@ -1149,10 +1221,10 @@ module Sequel
|
|
|
1149
1221
|
# Update the instances values by calling +set_fields+ with the +hash+
|
|
1150
1222
|
# and +fields+, then save any changes to the record. Returns self.
|
|
1151
1223
|
#
|
|
1152
|
-
# artist.update_fields({:name=>'Jim'}, :name)
|
|
1224
|
+
# artist.update_fields({:name=>'Jim'}, [:name])
|
|
1153
1225
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1154
1226
|
#
|
|
1155
|
-
# artist.update_fields({:hometown=>'LA'}, :name)
|
|
1227
|
+
# artist.update_fields({:hometown=>'LA'}, [:name])
|
|
1156
1228
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
|
1157
1229
|
def update_fields(hash, fields)
|
|
1158
1230
|
set_fields(hash, fields)
|
|
@@ -1160,7 +1232,8 @@ module Sequel
|
|
|
1160
1232
|
end
|
|
1161
1233
|
|
|
1162
1234
|
# Update the values using the entries in the hash, only if the key
|
|
1163
|
-
# is included in only.
|
|
1235
|
+
# is included in only. It may be a better idea to use +update_fields+
|
|
1236
|
+
# instead of this method.
|
|
1164
1237
|
#
|
|
1165
1238
|
# artist.update_only({:name=>'Jim'}, :name)
|
|
1166
1239
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1185,21 +1258,15 @@ module Sequel
|
|
|
1185
1258
|
# artist(:name=>'Invalid').valid? # => false
|
|
1186
1259
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
|
1187
1260
|
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?
|
|
1261
|
+
_valid?(false, opts)
|
|
1196
1262
|
end
|
|
1197
1263
|
|
|
1198
1264
|
private
|
|
1199
1265
|
|
|
1200
|
-
#
|
|
1266
|
+
# Do the deletion of the object's dataset, and check that the row
|
|
1267
|
+
# was actually deleted.
|
|
1201
1268
|
def _delete
|
|
1202
|
-
n =
|
|
1269
|
+
n = _delete_without_checking
|
|
1203
1270
|
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
1271
|
n
|
|
1205
1272
|
end
|
|
@@ -1210,12 +1277,24 @@ module Sequel
|
|
|
1210
1277
|
this
|
|
1211
1278
|
end
|
|
1212
1279
|
|
|
1280
|
+
# Actually do the deletion of the object's dataset. Return the
|
|
1281
|
+
# number of rows modified.
|
|
1282
|
+
def _delete_without_checking
|
|
1283
|
+
_delete_dataset.delete
|
|
1284
|
+
end
|
|
1285
|
+
|
|
1213
1286
|
# Internal destroy method, separted from destroy to
|
|
1214
1287
|
# allow running inside a transaction
|
|
1215
1288
|
def _destroy(opts)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1289
|
+
called = false
|
|
1290
|
+
around_destroy do
|
|
1291
|
+
called = true
|
|
1292
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
|
1293
|
+
_destroy_delete
|
|
1294
|
+
after_destroy
|
|
1295
|
+
true
|
|
1296
|
+
end
|
|
1297
|
+
raise_hook_failure(:destroy) unless called
|
|
1219
1298
|
self
|
|
1220
1299
|
end
|
|
1221
1300
|
|
|
@@ -1230,11 +1309,11 @@ module Sequel
|
|
|
1230
1309
|
# the record should be refreshed from the database.
|
|
1231
1310
|
def _insert
|
|
1232
1311
|
ds = _insert_dataset
|
|
1233
|
-
if ds.
|
|
1312
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
|
1234
1313
|
@values = h
|
|
1235
1314
|
nil
|
|
1236
1315
|
else
|
|
1237
|
-
iid = ds
|
|
1316
|
+
iid = _insert_raw(ds)
|
|
1238
1317
|
# if we have a regular primary key and it's not set in @values,
|
|
1239
1318
|
# we assume it's the last inserted id
|
|
1240
1319
|
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !@values[pk]
|
|
@@ -1243,52 +1322,89 @@ module Sequel
|
|
|
1243
1322
|
pk
|
|
1244
1323
|
end
|
|
1245
1324
|
end
|
|
1246
|
-
|
|
1325
|
+
|
|
1247
1326
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1248
1327
|
# dataset by default.
|
|
1249
1328
|
def _insert_dataset
|
|
1250
1329
|
model.dataset
|
|
1251
1330
|
end
|
|
1252
1331
|
|
|
1332
|
+
# Insert into the given dataset and return the primary key created (if any).
|
|
1333
|
+
def _insert_raw(ds)
|
|
1334
|
+
ds.insert(@values)
|
|
1335
|
+
end
|
|
1336
|
+
|
|
1337
|
+
# Insert into the given dataset and return the hash of column values.
|
|
1338
|
+
def _insert_select_raw(ds)
|
|
1339
|
+
ds.insert_select(@values)
|
|
1340
|
+
end
|
|
1341
|
+
|
|
1253
1342
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1254
1343
|
# is used for reading newly inserted values from the database
|
|
1255
1344
|
def _refresh(dataset)
|
|
1256
|
-
set_values(dataset
|
|
1345
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
|
1257
1346
|
changed_columns.clear
|
|
1258
1347
|
self
|
|
1259
1348
|
end
|
|
1349
|
+
|
|
1350
|
+
# Get the row of column data from the database.
|
|
1351
|
+
def _refresh_get(dataset)
|
|
1352
|
+
dataset.first
|
|
1353
|
+
end
|
|
1260
1354
|
|
|
1261
1355
|
# Internal version of save, split from save to allow running inside
|
|
1262
1356
|
# it's own transaction.
|
|
1263
1357
|
def _save(columns, opts)
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1358
|
+
was_new = false
|
|
1359
|
+
pk = nil
|
|
1360
|
+
called_save = false
|
|
1361
|
+
called_cu = false
|
|
1362
|
+
around_save do
|
|
1363
|
+
called_save = true
|
|
1364
|
+
raise_hook_failure(:save) if before_save == false
|
|
1365
|
+
if new?
|
|
1366
|
+
was_new = true
|
|
1367
|
+
around_create do
|
|
1368
|
+
called_cu = true
|
|
1369
|
+
raise_hook_failure(:create) if before_create == false
|
|
1370
|
+
pk = _insert
|
|
1371
|
+
@this = nil
|
|
1372
|
+
@new = false
|
|
1373
|
+
@was_new = true
|
|
1374
|
+
after_create
|
|
1375
|
+
true
|
|
1376
|
+
end
|
|
1377
|
+
raise_hook_failure(:create) unless called_cu
|
|
1378
|
+
else
|
|
1379
|
+
around_update do
|
|
1380
|
+
called_cu = true
|
|
1381
|
+
raise_hook_failure(:update) if before_update == false
|
|
1382
|
+
if columns.empty?
|
|
1383
|
+
@columns_updated = if opts[:changed]
|
|
1384
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1385
|
+
else
|
|
1386
|
+
_save_update_all_columns_hash
|
|
1387
|
+
end
|
|
1388
|
+
changed_columns.clear
|
|
1389
|
+
else # update only the specified columns
|
|
1390
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1391
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
|
1392
|
+
end
|
|
1393
|
+
_update_columns(@columns_updated)
|
|
1394
|
+
@this = nil
|
|
1395
|
+
after_update
|
|
1396
|
+
true
|
|
1397
|
+
end
|
|
1398
|
+
raise_hook_failure(:update) unless called_cu
|
|
1399
|
+
end
|
|
1272
1400
|
after_save
|
|
1401
|
+
true
|
|
1402
|
+
end
|
|
1403
|
+
raise_hook_failure(:save) unless called_save
|
|
1404
|
+
if was_new
|
|
1273
1405
|
@was_new = nil
|
|
1274
1406
|
pk ? _save_refresh : changed_columns.clear
|
|
1275
1407
|
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
1408
|
@columns_updated = nil
|
|
1293
1409
|
end
|
|
1294
1410
|
@modified = false
|
|
@@ -1321,9 +1437,10 @@ module Sequel
|
|
|
1321
1437
|
_update(columns) unless columns.empty?
|
|
1322
1438
|
end
|
|
1323
1439
|
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
|
1440
|
+
# Update this instance's dataset with the supplied column hash,
|
|
1441
|
+
# checking that only a single row was modified.
|
|
1325
1442
|
def _update(columns)
|
|
1326
|
-
n =
|
|
1443
|
+
n = _update_without_checking(columns)
|
|
1327
1444
|
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
1445
|
n
|
|
1329
1446
|
end
|
|
@@ -1334,7 +1451,46 @@ module Sequel
|
|
|
1334
1451
|
this
|
|
1335
1452
|
end
|
|
1336
1453
|
|
|
1337
|
-
#
|
|
1454
|
+
# Update this instances dataset with the supplied column hash.
|
|
1455
|
+
def _update_without_checking(columns)
|
|
1456
|
+
_update_dataset.update(columns)
|
|
1457
|
+
end
|
|
1458
|
+
|
|
1459
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
|
1460
|
+
# failures will be raised as HookFailure exceptions. If it is
|
|
1461
|
+
# +false+, +false+ will be returned instead.
|
|
1462
|
+
def _valid?(raise_errors, opts)
|
|
1463
|
+
errors.clear
|
|
1464
|
+
called = false
|
|
1465
|
+
error = false
|
|
1466
|
+
around_validation do
|
|
1467
|
+
called = true
|
|
1468
|
+
if before_validation == false
|
|
1469
|
+
if raise_errors
|
|
1470
|
+
raise_hook_failure(:validation)
|
|
1471
|
+
else
|
|
1472
|
+
error = true
|
|
1473
|
+
end
|
|
1474
|
+
false
|
|
1475
|
+
else
|
|
1476
|
+
validate
|
|
1477
|
+
after_validation
|
|
1478
|
+
errors.empty?
|
|
1479
|
+
end
|
|
1480
|
+
end
|
|
1481
|
+
error = true unless called
|
|
1482
|
+
if error
|
|
1483
|
+
if raise_errors
|
|
1484
|
+
raise_hook_failure(:validation)
|
|
1485
|
+
else
|
|
1486
|
+
false
|
|
1487
|
+
end
|
|
1488
|
+
else
|
|
1489
|
+
errors.empty?
|
|
1490
|
+
end
|
|
1491
|
+
end
|
|
1492
|
+
|
|
1493
|
+
# If not raising on failure, check for HookFailed
|
|
1338
1494
|
# being raised by yielding and swallow it.
|
|
1339
1495
|
def checked_save_failure(opts)
|
|
1340
1496
|
if raise_on_failure?(opts)
|
|
@@ -1342,7 +1498,7 @@ module Sequel
|
|
|
1342
1498
|
else
|
|
1343
1499
|
begin
|
|
1344
1500
|
yield
|
|
1345
|
-
rescue
|
|
1501
|
+
rescue HookFailed
|
|
1346
1502
|
nil
|
|
1347
1503
|
end
|
|
1348
1504
|
end
|
|
@@ -1353,6 +1509,13 @@ module Sequel
|
|
|
1353
1509
|
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1354
1510
|
end
|
|
1355
1511
|
|
|
1512
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1513
|
+
# exists so it can be overridden. This is called only for new records, before
|
|
1514
|
+
# changed_columns is cleared.
|
|
1515
|
+
def initialize_set(h)
|
|
1516
|
+
set(h)
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1356
1519
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
|
1357
1520
|
def inspect_values
|
|
1358
1521
|
@values.inspect
|
|
@@ -1369,7 +1532,7 @@ module Sequel
|
|
|
1369
1532
|
# Raise an error appropriate to the hook type. May be swallowed by
|
|
1370
1533
|
# checked_save_failure depending on the raise_on_failure? setting.
|
|
1371
1534
|
def raise_hook_failure(type)
|
|
1372
|
-
raise
|
|
1535
|
+
raise HookFailed, "one of the before_#{type} hooks returned false"
|
|
1373
1536
|
end
|
|
1374
1537
|
|
|
1375
1538
|
# Set the columns, filtered by the only and except arrays.
|
|
@@ -1385,7 +1548,16 @@ module Sequel
|
|
|
1385
1548
|
if meths.include?(m)
|
|
1386
1549
|
send(m, v)
|
|
1387
1550
|
elsif strict
|
|
1388
|
-
|
|
1551
|
+
# Avoid using respond_to? or creating symbols from user input
|
|
1552
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
|
1553
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
|
1554
|
+
raise Error, "#{k} is a restricted primary key"
|
|
1555
|
+
else
|
|
1556
|
+
raise Error, "#{k} is a restricted column"
|
|
1557
|
+
end
|
|
1558
|
+
else
|
|
1559
|
+
raise Error, "method #{m} doesn't exist"
|
|
1560
|
+
end
|
|
1389
1561
|
end
|
|
1390
1562
|
end
|
|
1391
1563
|
self
|
|
@@ -1461,6 +1633,18 @@ module Sequel
|
|
|
1461
1633
|
# Artist.dataset.model # => Artist
|
|
1462
1634
|
attr_accessor :model
|
|
1463
1635
|
|
|
1636
|
+
# Assume if a single integer is given that it is a lookup by primary
|
|
1637
|
+
# key, and call with_pk with the argument.
|
|
1638
|
+
#
|
|
1639
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1640
|
+
def [](*args)
|
|
1641
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
|
1642
|
+
with_pk(i)
|
|
1643
|
+
else
|
|
1644
|
+
super
|
|
1645
|
+
end
|
|
1646
|
+
end
|
|
1647
|
+
|
|
1464
1648
|
# Destroy each row in the dataset by instantiating it and then calling
|
|
1465
1649
|
# destroy on the resulting model object. This isn't as fast as deleting
|
|
1466
1650
|
# the dataset, which does a single SQL call, but this runs any destroy
|
|
@@ -1491,6 +1675,19 @@ module Sequel
|
|
|
1491
1675
|
super(pk, value_column)
|
|
1492
1676
|
end
|
|
1493
1677
|
end
|
|
1678
|
+
|
|
1679
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
|
1680
|
+
# value.
|
|
1681
|
+
#
|
|
1682
|
+
# # Single primary key
|
|
1683
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1684
|
+
#
|
|
1685
|
+
# # Composite primary key
|
|
1686
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
|
1687
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
|
1688
|
+
def with_pk(pk)
|
|
1689
|
+
first(model.qualified_primary_key_hash(pk))
|
|
1690
|
+
end
|
|
1494
1691
|
end
|
|
1495
1692
|
|
|
1496
1693
|
extend ClassMethods
|