sequel 3.21.0 → 3.31.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 +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- 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 +89 -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/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -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 +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- 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 +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- 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 +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- 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 +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- 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 +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- 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 +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- 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 +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -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_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- 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 +212 -122
- 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 +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
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
|
|
|
@@ -193,6 +234,16 @@ module Sequel
|
|
|
193
234
|
@db_schema ||= get_db_schema
|
|
194
235
|
end
|
|
195
236
|
|
|
237
|
+
# Create a column alias, where the column methods have one name, but the underlying storage uses a
|
|
238
|
+
# different name.
|
|
239
|
+
def def_column_alias(meth, column)
|
|
240
|
+
clear_setter_methods_cache
|
|
241
|
+
overridable_methods_module.module_eval do
|
|
242
|
+
define_method(meth){self[column]}
|
|
243
|
+
define_method("#{meth}="){|v| self[column] = v}
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
196
247
|
# If a block is given, define a method on the dataset (if the model currently has an dataset) with the given argument name using
|
|
197
248
|
# the given block. Also define a class method on the model that calls the
|
|
198
249
|
# dataset method. Stores the method name and block so that it can be reapplied if the model's
|
|
@@ -217,9 +268,30 @@ module Sequel
|
|
|
217
268
|
@dataset_methods[meth] = block
|
|
218
269
|
dataset.meta_def(meth, &block) if @dataset
|
|
219
270
|
end
|
|
220
|
-
args.each
|
|
271
|
+
args.each do |arg|
|
|
272
|
+
if arg.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
|
273
|
+
instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg, true)
|
|
274
|
+
else
|
|
275
|
+
def_model_dataset_method_block(arg)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
221
278
|
end
|
|
222
|
-
|
|
279
|
+
|
|
280
|
+
module_eval(if RUBY_VERSION < '1.8.7'
|
|
281
|
+
<<-END
|
|
282
|
+
def def_model_dataset_method_block(arg)
|
|
283
|
+
meta_def(arg){|*args| dataset.send(arg, *args)}
|
|
284
|
+
end
|
|
285
|
+
END
|
|
286
|
+
else
|
|
287
|
+
<<-END
|
|
288
|
+
def def_model_dataset_method_block(arg)
|
|
289
|
+
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
290
|
+
end
|
|
291
|
+
END
|
|
292
|
+
end, __FILE__, __LINE__ - 4)
|
|
293
|
+
private :def_model_dataset_method_block
|
|
294
|
+
|
|
223
295
|
# Finds a single record according to the supplied filter.
|
|
224
296
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
|
225
297
|
#
|
|
@@ -248,6 +320,13 @@ module Sequel
|
|
|
248
320
|
find(cond) || create(cond, &block)
|
|
249
321
|
end
|
|
250
322
|
|
|
323
|
+
# Clear the setter_methods cache when a module is included, as it
|
|
324
|
+
# may contain setter methods.
|
|
325
|
+
def include(mod)
|
|
326
|
+
clear_setter_methods_cache
|
|
327
|
+
super
|
|
328
|
+
end
|
|
329
|
+
|
|
251
330
|
# If possible, set the dataset for the model subclass as soon as it
|
|
252
331
|
# is created. Also, make sure the inherited class instance variables
|
|
253
332
|
# are copied into the subclass.
|
|
@@ -271,7 +350,8 @@ module Sequel
|
|
|
271
350
|
db
|
|
272
351
|
begin
|
|
273
352
|
if self == Model || !@dataset
|
|
274
|
-
|
|
353
|
+
n = subclass.name
|
|
354
|
+
subclass.set_dataset(subclass.implicit_table_name) unless n.nil? || n.empty?
|
|
275
355
|
elsif @dataset
|
|
276
356
|
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
277
357
|
end
|
|
@@ -290,12 +370,9 @@ module Sequel
|
|
|
290
370
|
pluralize(underscore(demodulize(name))).to_sym
|
|
291
371
|
end
|
|
292
372
|
|
|
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.
|
|
373
|
+
# Calls #call with the values hash. Only for backwards compatibility.
|
|
297
374
|
def load(values)
|
|
298
|
-
|
|
375
|
+
call(values)
|
|
299
376
|
end
|
|
300
377
|
|
|
301
378
|
# Clear the setter_methods cache when a setter method is added
|
|
@@ -325,18 +402,13 @@ module Sequel
|
|
|
325
402
|
unless @plugins.include?(m)
|
|
326
403
|
@plugins << m
|
|
327
404
|
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
|
|
405
|
+
include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
|
|
406
|
+
extend(m::ClassMethods)if plugin_module_defined?(m, :ClassMethods)
|
|
407
|
+
dataset_extend(m::DatasetMethods) if plugin_module_defined?(m, :DatasetMethods)
|
|
336
408
|
end
|
|
337
409
|
m.configure(self, *args, &blk) if m.respond_to?(:configure)
|
|
338
410
|
end
|
|
339
|
-
|
|
411
|
+
|
|
340
412
|
# Returns primary key attribute hash. If using a composite primary key
|
|
341
413
|
# value such be an array with values for each primary key in the correct
|
|
342
414
|
# order. For a standard primary key, value should be an object with a
|
|
@@ -389,8 +461,8 @@ module Sequel
|
|
|
389
461
|
# setter methods (methods that end in =) that you want to be used during
|
|
390
462
|
# mass assignment, they need to be listed here as well (without the =).
|
|
391
463
|
#
|
|
392
|
-
# It may be better to use a method such as +set_only+
|
|
393
|
-
#
|
|
464
|
+
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
|
465
|
+
# the allowed fields per call.
|
|
394
466
|
#
|
|
395
467
|
# Artist.set_allowed_columns(:name, :hometown)
|
|
396
468
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -400,10 +472,12 @@ module Sequel
|
|
|
400
472
|
@allowed_columns = cols
|
|
401
473
|
end
|
|
402
474
|
|
|
403
|
-
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol
|
|
404
|
-
#
|
|
475
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
|
476
|
+
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
|
477
|
+
# <tt>SQL::AliasedExpression</tt>
|
|
478
|
+
# (all specifying a table name in the current database), or a +Dataset+.
|
|
405
479
|
# If a dataset is used, the model's database is changed to the database of the given
|
|
406
|
-
# dataset. If a
|
|
480
|
+
# dataset. If a dataset is not used, a dataset is created from the current
|
|
407
481
|
# database with the table name given. Other arguments raise an +Error+.
|
|
408
482
|
# Returns self.
|
|
409
483
|
#
|
|
@@ -418,17 +492,21 @@ module Sequel
|
|
|
418
492
|
def set_dataset(ds, opts={})
|
|
419
493
|
inherited = opts[:inherited]
|
|
420
494
|
@dataset = case ds
|
|
421
|
-
when Symbol
|
|
495
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
|
422
496
|
@simple_table = db.literal(ds)
|
|
423
|
-
db
|
|
497
|
+
db.from(ds)
|
|
424
498
|
when Dataset
|
|
425
|
-
@simple_table =
|
|
499
|
+
@simple_table = if ds.send(:simple_select_all?)
|
|
500
|
+
ds.literal(ds.first_source_table)
|
|
501
|
+
else
|
|
502
|
+
nil
|
|
503
|
+
end
|
|
426
504
|
@db = ds.db
|
|
427
505
|
ds
|
|
428
506
|
else
|
|
429
|
-
raise(Error, "Model.set_dataset takes
|
|
507
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
|
430
508
|
end
|
|
431
|
-
@dataset.row_proc =
|
|
509
|
+
@dataset.row_proc = self
|
|
432
510
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
|
433
511
|
if inherited
|
|
434
512
|
@simple_table = superclass.simple_table
|
|
@@ -441,10 +519,11 @@ module Sequel
|
|
|
441
519
|
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
|
442
520
|
self
|
|
443
521
|
end
|
|
444
|
-
|
|
522
|
+
|
|
445
523
|
# Sets the primary key for this model. You can use either a regular
|
|
446
524
|
# or a composite primary key. To not use a primary key, set to nil
|
|
447
|
-
# or use +no_primary_key+.
|
|
525
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
|
526
|
+
# determine the primary key to use, so this method is not needed often.
|
|
448
527
|
#
|
|
449
528
|
# class Person < Sequel::Model
|
|
450
529
|
# # regular key
|
|
@@ -458,7 +537,11 @@ module Sequel
|
|
|
458
537
|
def set_primary_key(*key)
|
|
459
538
|
clear_setter_methods_cache
|
|
460
539
|
key = key.flatten
|
|
461
|
-
@simple_pk = key.length == 1
|
|
540
|
+
@simple_pk = if key.length == 1
|
|
541
|
+
(@dataset || db).literal(key.first)
|
|
542
|
+
else
|
|
543
|
+
nil
|
|
544
|
+
end
|
|
462
545
|
@primary_key = (key.length == 1) ? key[0] : key
|
|
463
546
|
end
|
|
464
547
|
|
|
@@ -468,10 +551,9 @@ module Sequel
|
|
|
468
551
|
# If you have any virtual setter methods (methods that end in =) that you
|
|
469
552
|
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
|
470
553
|
#
|
|
471
|
-
# It
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# method uses, where everything is allowed other than what you restrict.
|
|
554
|
+
# It's generally a bad idea to rely on a blacklist approach for security. Using a whitelist
|
|
555
|
+
# approach such as set_allowed_columns or the instance level set_only or set_fields methods
|
|
556
|
+
# is usually a better choice. So use of this method is generally a bad idea.
|
|
475
557
|
#
|
|
476
558
|
# Artist.set_restricted_column(:records_sold)
|
|
477
559
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -511,6 +593,9 @@ module Sequel
|
|
|
511
593
|
# 7 days ago.
|
|
512
594
|
#
|
|
513
595
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
|
596
|
+
#
|
|
597
|
+
# This method creates dataset methods that do not accept arguments. To create
|
|
598
|
+
# dataset methods that accept arguments, you have to use def_dataset_method.
|
|
514
599
|
def subset(name, *args, &block)
|
|
515
600
|
def_dataset_method(name){filter(*args, &block)}
|
|
516
601
|
end
|
|
@@ -526,6 +611,7 @@ module Sequel
|
|
|
526
611
|
end
|
|
527
612
|
|
|
528
613
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
|
614
|
+
# Using this method can open up security issues, be very careful before using it.
|
|
529
615
|
#
|
|
530
616
|
# Artist.set(:id=>1) # Error
|
|
531
617
|
# Artist.unrestrict_primary_key
|
|
@@ -548,6 +634,16 @@ module Sequel
|
|
|
548
634
|
end
|
|
549
635
|
end
|
|
550
636
|
|
|
637
|
+
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
|
638
|
+
# module if the model has a dataset. Add dataset methods to the class for all
|
|
639
|
+
# public dataset methods.
|
|
640
|
+
def dataset_extend(mod)
|
|
641
|
+
dataset.extend(mod) if @dataset
|
|
642
|
+
dataset_method_modules << mod
|
|
643
|
+
meths = mod.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
|
|
644
|
+
def_dataset_method(*meths) unless meths.empty?
|
|
645
|
+
end
|
|
646
|
+
|
|
551
647
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
|
552
648
|
def def_bad_column_accessor(column)
|
|
553
649
|
overridable_methods_module.module_eval do
|
|
@@ -578,10 +674,9 @@ module Sequel
|
|
|
578
674
|
return nil unless @dataset
|
|
579
675
|
schema_hash = {}
|
|
580
676
|
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
677
|
get_columns = proc{check_non_connection_error{columns} || []}
|
|
584
|
-
|
|
678
|
+
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)}
|
|
679
|
+
if schema_array
|
|
585
680
|
schema_array.each{|k,v| schema_hash[k] = v}
|
|
586
681
|
if ds_opts.include?(:select)
|
|
587
682
|
# We don't remove the columns from the schema_hash,
|
|
@@ -589,6 +684,7 @@ module Sequel
|
|
|
589
684
|
# even if they are not selected.
|
|
590
685
|
cols = get_columns.call
|
|
591
686
|
cols.each{|c| schema_hash[c] ||= {}}
|
|
687
|
+
def_column_accessor(*schema_hash.keys)
|
|
592
688
|
else
|
|
593
689
|
# Dataset is for a single table with all columns,
|
|
594
690
|
# so set the columns based on the order they were
|
|
@@ -657,6 +753,15 @@ module Sequel
|
|
|
657
753
|
Sequel::Plugins.const_get(module_name)
|
|
658
754
|
end
|
|
659
755
|
|
|
756
|
+
# Check if the plugin module +plugin+ defines the constant named by +submod+.
|
|
757
|
+
def plugin_module_defined?(plugin, submod)
|
|
758
|
+
if RUBY_VERSION >= '1.9'
|
|
759
|
+
plugin.const_defined?(submod, false)
|
|
760
|
+
else
|
|
761
|
+
plugin.const_defined?(submod)
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
|
|
660
765
|
# Find the row in the dataset that matches the primary key. Uses
|
|
661
766
|
# a static SQL optimization if the table and primary key are simple.
|
|
662
767
|
def primary_key_lookup(pk)
|
|
@@ -686,18 +791,19 @@ module Sequel
|
|
|
686
791
|
|
|
687
792
|
# Sequel::Model instance methods that implement basic model functionality.
|
|
688
793
|
#
|
|
689
|
-
# * All of the methods in +HOOKS+ create instance methods that are called
|
|
794
|
+
# * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
|
|
690
795
|
# 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+.
|
|
796
|
+
# a model object, Sequel will call +around_destory+, which will call +before_destroy+, do
|
|
797
|
+
# the destroy, and then call +after_destroy+.
|
|
693
798
|
# * The following instance_methods all call the class method of the same
|
|
694
|
-
# name: columns,
|
|
799
|
+
# name: columns, db, primary_key, db_schema.
|
|
695
800
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
|
696
|
-
# to set values for the attribute. It also creates
|
|
801
|
+
# to set values for the attribute. It also creates instance getters returning
|
|
697
802
|
# the value of the setting. If the value has not yet been set, it
|
|
698
803
|
# gets the default value from the class by calling the class method of the same name.
|
|
699
804
|
module InstanceMethods
|
|
700
805
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
|
806
|
+
AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
|
701
807
|
|
|
702
808
|
# Define instance method(s) that calls class method(s) of the
|
|
703
809
|
# same name, caching the result in an instance variable. Define
|
|
@@ -735,7 +841,7 @@ module Sequel
|
|
|
735
841
|
#
|
|
736
842
|
# Arguments:
|
|
737
843
|
# values :: should be a hash to pass to set.
|
|
738
|
-
# from_db ::
|
|
844
|
+
# from_db :: only for backwards compatibility, forget it exists.
|
|
739
845
|
#
|
|
740
846
|
# Artist.new(:name=>'Bob')
|
|
741
847
|
#
|
|
@@ -744,13 +850,12 @@ module Sequel
|
|
|
744
850
|
# end
|
|
745
851
|
def initialize(values = {}, from_db = false)
|
|
746
852
|
if from_db
|
|
747
|
-
@new = false
|
|
748
853
|
set_values(values)
|
|
749
854
|
else
|
|
750
855
|
@values = {}
|
|
751
856
|
@new = true
|
|
752
857
|
@modified = true
|
|
753
|
-
|
|
858
|
+
initialize_set(values)
|
|
754
859
|
changed_columns.clear
|
|
755
860
|
yield self if block_given?
|
|
756
861
|
end
|
|
@@ -766,7 +871,7 @@ module Sequel
|
|
|
766
871
|
|
|
767
872
|
# Sets the value for the given column. If typecasting is enabled for
|
|
768
873
|
# this object, typecast the value based on the column's type.
|
|
769
|
-
# If this
|
|
874
|
+
# If this is a new record or the typecasted value isn't the same
|
|
770
875
|
# as the current value for the column, mark the column as changed.
|
|
771
876
|
#
|
|
772
877
|
# a = Artist.new
|
|
@@ -874,14 +979,25 @@ module Sequel
|
|
|
874
979
|
|
|
875
980
|
# Returns true when current instance exists, false otherwise.
|
|
876
981
|
# Generally an object that isn't new will exist unless it has
|
|
877
|
-
# been deleted.
|
|
982
|
+
# been deleted. Uses a database query to check for existence,
|
|
983
|
+
# unless the model object is new, in which case this is always
|
|
984
|
+
# false.
|
|
878
985
|
#
|
|
879
986
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
880
987
|
# # => true
|
|
988
|
+
# Artist.new.exists?
|
|
989
|
+
# # => false
|
|
881
990
|
def exists?
|
|
882
|
-
!this.get(1).nil?
|
|
991
|
+
new? ? false : !this.get(1).nil?
|
|
883
992
|
end
|
|
884
993
|
|
|
994
|
+
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
995
|
+
# module may contain setter methods.
|
|
996
|
+
def extend(mod)
|
|
997
|
+
@singleton_setter_added = true
|
|
998
|
+
super
|
|
999
|
+
end
|
|
1000
|
+
|
|
885
1001
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
|
886
1002
|
# the same class and values (if pk is nil).
|
|
887
1003
|
#
|
|
@@ -890,7 +1006,14 @@ module Sequel
|
|
|
890
1006
|
# Artist.new.hash == Artist.new.hash # true
|
|
891
1007
|
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
|
892
1008
|
def hash
|
|
893
|
-
|
|
1009
|
+
case primary_key
|
|
1010
|
+
when Array
|
|
1011
|
+
[model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1012
|
+
when Symbol
|
|
1013
|
+
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1014
|
+
else
|
|
1015
|
+
[model, @values.sort_by{|k,v| k.to_s}].hash
|
|
1016
|
+
end
|
|
894
1017
|
end
|
|
895
1018
|
|
|
896
1019
|
# Returns value for the :id attribute, even if the primary key is
|
|
@@ -967,11 +1090,11 @@ module Sequel
|
|
|
967
1090
|
# Artist.new.new? # => true
|
|
968
1091
|
# Artist[1].new? # => false
|
|
969
1092
|
def new?
|
|
970
|
-
@new
|
|
1093
|
+
defined?(@new) ? @new : (@new = false)
|
|
971
1094
|
end
|
|
972
1095
|
|
|
973
1096
|
# Returns the primary key value identifying the model instance.
|
|
974
|
-
# Raises an
|
|
1097
|
+
# Raises an +Error+ if this model does not have a primary key.
|
|
975
1098
|
# If the model has a composite primary key, returns an array of values.
|
|
976
1099
|
#
|
|
977
1100
|
# Artist[1].pk # => 1
|
|
@@ -1016,26 +1139,31 @@ module Sequel
|
|
|
1016
1139
|
#
|
|
1017
1140
|
# If +save+ fails and either raise_on_save_failure or the
|
|
1018
1141
|
# :raise_on_failure option is true, it raises ValidationFailed
|
|
1019
|
-
# or
|
|
1142
|
+
# or HookFailed. Otherwise it returns nil.
|
|
1020
1143
|
#
|
|
1021
1144
|
# If it succeeds, it returns self.
|
|
1022
1145
|
#
|
|
1023
1146
|
# You can provide an optional list of columns to update, in which
|
|
1024
|
-
# case it only updates those columns.
|
|
1147
|
+
# case it only updates those columns, or a options hash.
|
|
1025
1148
|
#
|
|
1026
1149
|
# Takes the following options:
|
|
1027
1150
|
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
1030
|
-
#
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1151
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1152
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1153
|
+
# +raise_on_save_failure+ setting
|
|
1154
|
+
# :server :: set the server/shard on the object before saving, and use that
|
|
1155
|
+
# server/shard in any transaction.
|
|
1156
|
+
# :transaction :: set to true or false to override the current
|
|
1157
|
+
# +use_transactions+ setting
|
|
1158
|
+
# :validate :: set to false to skip validation
|
|
1034
1159
|
def save(*columns)
|
|
1035
1160
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1036
|
-
if opts[:
|
|
1037
|
-
|
|
1038
|
-
|
|
1161
|
+
set_server(opts[:server]) if opts[:server]
|
|
1162
|
+
if opts[:validate] != false
|
|
1163
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1164
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
|
1165
|
+
return
|
|
1166
|
+
end
|
|
1039
1167
|
end
|
|
1040
1168
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
|
1041
1169
|
end
|
|
@@ -1075,7 +1203,8 @@ module Sequel
|
|
|
1075
1203
|
end
|
|
1076
1204
|
|
|
1077
1205
|
# Set all values using the entries in the hash, except for the keys
|
|
1078
|
-
# given in except.
|
|
1206
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
|
1207
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1079
1208
|
#
|
|
1080
1209
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
|
1081
1210
|
# artist.name # => 'Jim'
|
|
@@ -1086,10 +1215,10 @@ module Sequel
|
|
|
1086
1215
|
# For each of the fields in the given array +fields+, call the setter
|
|
1087
1216
|
# method with the value of that +hash+ entry for the field. Returns self.
|
|
1088
1217
|
#
|
|
1089
|
-
# artist.set_fields({:name=>'Jim'}, :name)
|
|
1218
|
+
# artist.set_fields({:name=>'Jim'}, [:name])
|
|
1090
1219
|
# artist.name # => 'Jim'
|
|
1091
1220
|
#
|
|
1092
|
-
# artist.set_fields({:hometown=>'LA'}, :name)
|
|
1221
|
+
# artist.set_fields({:hometown=>'LA'}, [:name])
|
|
1093
1222
|
# artist.name # => nil
|
|
1094
1223
|
# artist.hometown # => 'Sac'
|
|
1095
1224
|
def set_fields(hash, fields)
|
|
@@ -1098,16 +1227,31 @@ module Sequel
|
|
|
1098
1227
|
end
|
|
1099
1228
|
|
|
1100
1229
|
# Set the values using the entries in the hash, only if the key
|
|
1101
|
-
# is included in only.
|
|
1230
|
+
# is included in only. It may be a better idea to use +set_fields+
|
|
1231
|
+
# instead of this method.
|
|
1102
1232
|
#
|
|
1103
1233
|
# artist.set_only({:name=>'Jim'}, :name)
|
|
1104
1234
|
# artist.name # => 'Jim'
|
|
1105
1235
|
#
|
|
1106
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
|
1236
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
|
1107
1237
|
def set_only(hash, *only)
|
|
1108
1238
|
set_restricted(hash, only.flatten, false)
|
|
1109
1239
|
end
|
|
1110
1240
|
|
|
1241
|
+
# Set the shard that this object is tied to. Returns self.
|
|
1242
|
+
def set_server(s)
|
|
1243
|
+
@server = s
|
|
1244
|
+
@this.opts[:server] = s if @this
|
|
1245
|
+
self
|
|
1246
|
+
end
|
|
1247
|
+
|
|
1248
|
+
# Replace the current values with hash. Should definitely not be
|
|
1249
|
+
# used with untrusted input, and should probably not be called
|
|
1250
|
+
# directly by user code.
|
|
1251
|
+
def set_values(hash)
|
|
1252
|
+
@values = hash
|
|
1253
|
+
end
|
|
1254
|
+
|
|
1111
1255
|
# Clear the setter_methods cache when a method is added
|
|
1112
1256
|
def singleton_method_added(meth)
|
|
1113
1257
|
@singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
|
|
@@ -1119,10 +1263,10 @@ module Sequel
|
|
|
1119
1263
|
# Artist[1].this
|
|
1120
1264
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1121
1265
|
def this
|
|
1122
|
-
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
|
1266
|
+
@this ||= use_server(model.dataset.filter(pk_hash).limit(1).naked)
|
|
1123
1267
|
end
|
|
1124
1268
|
|
|
1125
|
-
# Runs set with the passed hash and then runs save_changes.
|
|
1269
|
+
# Runs #set with the passed hash and then runs save_changes.
|
|
1126
1270
|
#
|
|
1127
1271
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1128
1272
|
def update(hash)
|
|
@@ -1130,7 +1274,7 @@ module Sequel
|
|
|
1130
1274
|
end
|
|
1131
1275
|
|
|
1132
1276
|
# Update all values using the entries in the hash, ignoring any setting of
|
|
1133
|
-
# allowed_columns or
|
|
1277
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
|
1134
1278
|
#
|
|
1135
1279
|
# Artist.set_restricted_columns(:name)
|
|
1136
1280
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1139,7 +1283,8 @@ module Sequel
|
|
|
1139
1283
|
end
|
|
1140
1284
|
|
|
1141
1285
|
# Update all values using the entries in the hash, except for the keys
|
|
1142
|
-
# given in except.
|
|
1286
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
|
1287
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1143
1288
|
#
|
|
1144
1289
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1145
1290
|
def update_except(hash, *except)
|
|
@@ -1149,10 +1294,10 @@ module Sequel
|
|
|
1149
1294
|
# Update the instances values by calling +set_fields+ with the +hash+
|
|
1150
1295
|
# and +fields+, then save any changes to the record. Returns self.
|
|
1151
1296
|
#
|
|
1152
|
-
# artist.update_fields({:name=>'Jim'}, :name)
|
|
1297
|
+
# artist.update_fields({:name=>'Jim'}, [:name])
|
|
1153
1298
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1154
1299
|
#
|
|
1155
|
-
# artist.update_fields({:hometown=>'LA'}, :name)
|
|
1300
|
+
# artist.update_fields({:hometown=>'LA'}, [:name])
|
|
1156
1301
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
|
1157
1302
|
def update_fields(hash, fields)
|
|
1158
1303
|
set_fields(hash, fields)
|
|
@@ -1160,7 +1305,8 @@ module Sequel
|
|
|
1160
1305
|
end
|
|
1161
1306
|
|
|
1162
1307
|
# Update the values using the entries in the hash, only if the key
|
|
1163
|
-
# is included in only.
|
|
1308
|
+
# is included in only. It may be a better idea to use +update_fields+
|
|
1309
|
+
# instead of this method.
|
|
1164
1310
|
#
|
|
1165
1311
|
# artist.update_only({:name=>'Jim'}, :name)
|
|
1166
1312
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1185,21 +1331,15 @@ module Sequel
|
|
|
1185
1331
|
# artist(:name=>'Invalid').valid? # => false
|
|
1186
1332
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
|
1187
1333
|
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?
|
|
1334
|
+
_valid?(false, opts)
|
|
1196
1335
|
end
|
|
1197
1336
|
|
|
1198
1337
|
private
|
|
1199
1338
|
|
|
1200
|
-
#
|
|
1339
|
+
# Do the deletion of the object's dataset, and check that the row
|
|
1340
|
+
# was actually deleted.
|
|
1201
1341
|
def _delete
|
|
1202
|
-
n =
|
|
1342
|
+
n = _delete_without_checking
|
|
1203
1343
|
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
1344
|
n
|
|
1205
1345
|
end
|
|
@@ -1210,12 +1350,27 @@ module Sequel
|
|
|
1210
1350
|
this
|
|
1211
1351
|
end
|
|
1212
1352
|
|
|
1353
|
+
# Actually do the deletion of the object's dataset. Return the
|
|
1354
|
+
# number of rows modified.
|
|
1355
|
+
def _delete_without_checking
|
|
1356
|
+
_delete_dataset.delete
|
|
1357
|
+
end
|
|
1358
|
+
|
|
1213
1359
|
# Internal destroy method, separted from destroy to
|
|
1214
1360
|
# allow running inside a transaction
|
|
1215
1361
|
def _destroy(opts)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1362
|
+
sh = {:server=>this_server}
|
|
1363
|
+
db.after_rollback(sh){after_destroy_rollback}
|
|
1364
|
+
called = false
|
|
1365
|
+
around_destroy do
|
|
1366
|
+
called = true
|
|
1367
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
|
1368
|
+
_destroy_delete
|
|
1369
|
+
after_destroy
|
|
1370
|
+
true
|
|
1371
|
+
end
|
|
1372
|
+
raise_hook_failure(:destroy) unless called
|
|
1373
|
+
db.after_commit(sh){after_destroy_commit}
|
|
1219
1374
|
self
|
|
1220
1375
|
end
|
|
1221
1376
|
|
|
@@ -1230,11 +1385,11 @@ module Sequel
|
|
|
1230
1385
|
# the record should be refreshed from the database.
|
|
1231
1386
|
def _insert
|
|
1232
1387
|
ds = _insert_dataset
|
|
1233
|
-
if ds.
|
|
1388
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
|
1234
1389
|
@values = h
|
|
1235
1390
|
nil
|
|
1236
1391
|
else
|
|
1237
|
-
iid = ds
|
|
1392
|
+
iid = _insert_raw(ds)
|
|
1238
1393
|
# if we have a regular primary key and it's not set in @values,
|
|
1239
1394
|
# we assume it's the last inserted id
|
|
1240
1395
|
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !@values[pk]
|
|
@@ -1243,55 +1398,95 @@ module Sequel
|
|
|
1243
1398
|
pk
|
|
1244
1399
|
end
|
|
1245
1400
|
end
|
|
1246
|
-
|
|
1401
|
+
|
|
1247
1402
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1248
1403
|
# dataset by default.
|
|
1249
1404
|
def _insert_dataset
|
|
1250
|
-
model.dataset
|
|
1405
|
+
use_server(model.dataset)
|
|
1251
1406
|
end
|
|
1252
1407
|
|
|
1408
|
+
# Insert into the given dataset and return the primary key created (if any).
|
|
1409
|
+
def _insert_raw(ds)
|
|
1410
|
+
ds.insert(@values)
|
|
1411
|
+
end
|
|
1412
|
+
|
|
1413
|
+
# Insert into the given dataset and return the hash of column values.
|
|
1414
|
+
def _insert_select_raw(ds)
|
|
1415
|
+
ds.insert_select(@values)
|
|
1416
|
+
end
|
|
1417
|
+
|
|
1253
1418
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1254
1419
|
# is used for reading newly inserted values from the database
|
|
1255
1420
|
def _refresh(dataset)
|
|
1256
|
-
set_values(dataset
|
|
1421
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
|
1257
1422
|
changed_columns.clear
|
|
1258
1423
|
self
|
|
1259
1424
|
end
|
|
1425
|
+
|
|
1426
|
+
# Get the row of column data from the database.
|
|
1427
|
+
def _refresh_get(dataset)
|
|
1428
|
+
dataset.first
|
|
1429
|
+
end
|
|
1260
1430
|
|
|
1261
1431
|
# Internal version of save, split from save to allow running inside
|
|
1262
1432
|
# it's own transaction.
|
|
1263
1433
|
def _save(columns, opts)
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1434
|
+
sh = {:server=>this_server}
|
|
1435
|
+
db.after_rollback(sh){after_rollback}
|
|
1436
|
+
was_new = false
|
|
1437
|
+
pk = nil
|
|
1438
|
+
called_save = false
|
|
1439
|
+
called_cu = false
|
|
1440
|
+
around_save do
|
|
1441
|
+
called_save = true
|
|
1442
|
+
raise_hook_failure(:save) if before_save == false
|
|
1443
|
+
if new?
|
|
1444
|
+
was_new = true
|
|
1445
|
+
around_create do
|
|
1446
|
+
called_cu = true
|
|
1447
|
+
raise_hook_failure(:create) if before_create == false
|
|
1448
|
+
pk = _insert
|
|
1449
|
+
@this = nil
|
|
1450
|
+
@new = false
|
|
1451
|
+
@was_new = true
|
|
1452
|
+
after_create
|
|
1453
|
+
true
|
|
1454
|
+
end
|
|
1455
|
+
raise_hook_failure(:create) unless called_cu
|
|
1456
|
+
else
|
|
1457
|
+
around_update do
|
|
1458
|
+
called_cu = true
|
|
1459
|
+
raise_hook_failure(:update) if before_update == false
|
|
1460
|
+
if columns.empty?
|
|
1461
|
+
@columns_updated = if opts[:changed]
|
|
1462
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1463
|
+
else
|
|
1464
|
+
_save_update_all_columns_hash
|
|
1465
|
+
end
|
|
1466
|
+
changed_columns.clear
|
|
1467
|
+
else # update only the specified columns
|
|
1468
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1469
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
|
1470
|
+
end
|
|
1471
|
+
_update_columns(@columns_updated)
|
|
1472
|
+
@this = nil
|
|
1473
|
+
after_update
|
|
1474
|
+
true
|
|
1475
|
+
end
|
|
1476
|
+
raise_hook_failure(:update) unless called_cu
|
|
1477
|
+
end
|
|
1272
1478
|
after_save
|
|
1479
|
+
true
|
|
1480
|
+
end
|
|
1481
|
+
raise_hook_failure(:save) unless called_save
|
|
1482
|
+
if was_new
|
|
1273
1483
|
@was_new = nil
|
|
1274
1484
|
pk ? _save_refresh : changed_columns.clear
|
|
1275
1485
|
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
1486
|
@columns_updated = nil
|
|
1293
1487
|
end
|
|
1294
1488
|
@modified = false
|
|
1489
|
+
db.after_commit(sh){after_commit}
|
|
1295
1490
|
self
|
|
1296
1491
|
end
|
|
1297
1492
|
|
|
@@ -1321,9 +1516,10 @@ module Sequel
|
|
|
1321
1516
|
_update(columns) unless columns.empty?
|
|
1322
1517
|
end
|
|
1323
1518
|
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
|
1519
|
+
# Update this instance's dataset with the supplied column hash,
|
|
1520
|
+
# checking that only a single row was modified.
|
|
1325
1521
|
def _update(columns)
|
|
1326
|
-
n =
|
|
1522
|
+
n = _update_without_checking(columns)
|
|
1327
1523
|
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
1524
|
n
|
|
1329
1525
|
end
|
|
@@ -1334,7 +1530,46 @@ module Sequel
|
|
|
1334
1530
|
this
|
|
1335
1531
|
end
|
|
1336
1532
|
|
|
1337
|
-
#
|
|
1533
|
+
# Update this instances dataset with the supplied column hash.
|
|
1534
|
+
def _update_without_checking(columns)
|
|
1535
|
+
_update_dataset.update(columns)
|
|
1536
|
+
end
|
|
1537
|
+
|
|
1538
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
|
1539
|
+
# failures will be raised as HookFailure exceptions. If it is
|
|
1540
|
+
# +false+, +false+ will be returned instead.
|
|
1541
|
+
def _valid?(raise_errors, opts)
|
|
1542
|
+
errors.clear
|
|
1543
|
+
called = false
|
|
1544
|
+
error = false
|
|
1545
|
+
around_validation do
|
|
1546
|
+
called = true
|
|
1547
|
+
if before_validation == false
|
|
1548
|
+
if raise_errors
|
|
1549
|
+
raise_hook_failure(:validation)
|
|
1550
|
+
else
|
|
1551
|
+
error = true
|
|
1552
|
+
end
|
|
1553
|
+
false
|
|
1554
|
+
else
|
|
1555
|
+
validate
|
|
1556
|
+
after_validation
|
|
1557
|
+
errors.empty?
|
|
1558
|
+
end
|
|
1559
|
+
end
|
|
1560
|
+
error = true unless called
|
|
1561
|
+
if error
|
|
1562
|
+
if raise_errors
|
|
1563
|
+
raise_hook_failure(:validation)
|
|
1564
|
+
else
|
|
1565
|
+
false
|
|
1566
|
+
end
|
|
1567
|
+
else
|
|
1568
|
+
errors.empty?
|
|
1569
|
+
end
|
|
1570
|
+
end
|
|
1571
|
+
|
|
1572
|
+
# If not raising on failure, check for HookFailed
|
|
1338
1573
|
# being raised by yielding and swallow it.
|
|
1339
1574
|
def checked_save_failure(opts)
|
|
1340
1575
|
if raise_on_failure?(opts)
|
|
@@ -1342,7 +1577,7 @@ module Sequel
|
|
|
1342
1577
|
else
|
|
1343
1578
|
begin
|
|
1344
1579
|
yield
|
|
1345
|
-
rescue
|
|
1580
|
+
rescue HookFailed
|
|
1346
1581
|
nil
|
|
1347
1582
|
end
|
|
1348
1583
|
end
|
|
@@ -1350,7 +1585,14 @@ module Sequel
|
|
|
1350
1585
|
|
|
1351
1586
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1352
1587
|
def checked_transaction(opts={})
|
|
1353
|
-
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1588
|
+
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1589
|
+
end
|
|
1590
|
+
|
|
1591
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1592
|
+
# exists so it can be overridden. This is called only for new records, before
|
|
1593
|
+
# changed_columns is cleared.
|
|
1594
|
+
def initialize_set(h)
|
|
1595
|
+
set(h)
|
|
1354
1596
|
end
|
|
1355
1597
|
|
|
1356
1598
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
|
@@ -1369,7 +1611,7 @@ module Sequel
|
|
|
1369
1611
|
# Raise an error appropriate to the hook type. May be swallowed by
|
|
1370
1612
|
# checked_save_failure depending on the raise_on_failure? setting.
|
|
1371
1613
|
def raise_hook_failure(type)
|
|
1372
|
-
raise
|
|
1614
|
+
raise HookFailed, "one of the before_#{type} hooks returned false"
|
|
1373
1615
|
end
|
|
1374
1616
|
|
|
1375
1617
|
# Set the columns, filtered by the only and except arrays.
|
|
@@ -1385,17 +1627,21 @@ module Sequel
|
|
|
1385
1627
|
if meths.include?(m)
|
|
1386
1628
|
send(m, v)
|
|
1387
1629
|
elsif strict
|
|
1388
|
-
|
|
1630
|
+
# Avoid using respond_to? or creating symbols from user input
|
|
1631
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
|
1632
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
|
1633
|
+
raise Error, "#{k} is a restricted primary key"
|
|
1634
|
+
else
|
|
1635
|
+
raise Error, "#{k} is a restricted column"
|
|
1636
|
+
end
|
|
1637
|
+
else
|
|
1638
|
+
raise Error, "method #{m} doesn't exist"
|
|
1639
|
+
end
|
|
1389
1640
|
end
|
|
1390
1641
|
end
|
|
1391
1642
|
self
|
|
1392
1643
|
end
|
|
1393
1644
|
|
|
1394
|
-
# Replace the current values with hash.
|
|
1395
|
-
def set_values(hash)
|
|
1396
|
-
@values = hash
|
|
1397
|
-
end
|
|
1398
|
-
|
|
1399
1645
|
# Returns all methods that can be used for attribute
|
|
1400
1646
|
# assignment (those that end with =), modified by the only
|
|
1401
1647
|
# and except arguments:
|
|
@@ -1424,6 +1670,12 @@ module Sequel
|
|
|
1424
1670
|
meths
|
|
1425
1671
|
end
|
|
1426
1672
|
end
|
|
1673
|
+
|
|
1674
|
+
# The server/shard that the model object's dataset uses, or :default if the
|
|
1675
|
+
# model object's dataset does not have an associated shard.
|
|
1676
|
+
def this_server
|
|
1677
|
+
primary_key ? (this.opts[:server] || :default) : (model.dataset.opts[:server] || :default)
|
|
1678
|
+
end
|
|
1427
1679
|
|
|
1428
1680
|
# Typecast the value to the column's type if typecasting. Calls the database's
|
|
1429
1681
|
# typecast_value method, so database adapters can override/augment the handling
|
|
@@ -1444,6 +1696,11 @@ module Sequel
|
|
|
1444
1696
|
set_restricted(hash, only, except)
|
|
1445
1697
|
save_changes
|
|
1446
1698
|
end
|
|
1699
|
+
|
|
1700
|
+
# Set the given dataset to use the current object's shard.
|
|
1701
|
+
def use_server(ds)
|
|
1702
|
+
@server ? ds.server(@server) : ds
|
|
1703
|
+
end
|
|
1447
1704
|
|
|
1448
1705
|
# Whether to use a transaction for this action. If the :transaction
|
|
1449
1706
|
# option is present in the hash, use that, otherwise, fallback to the
|
|
@@ -1461,6 +1718,18 @@ module Sequel
|
|
|
1461
1718
|
# Artist.dataset.model # => Artist
|
|
1462
1719
|
attr_accessor :model
|
|
1463
1720
|
|
|
1721
|
+
# Assume if a single integer is given that it is a lookup by primary
|
|
1722
|
+
# key, and call with_pk with the argument.
|
|
1723
|
+
#
|
|
1724
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1725
|
+
def [](*args)
|
|
1726
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
|
1727
|
+
with_pk(i)
|
|
1728
|
+
else
|
|
1729
|
+
super
|
|
1730
|
+
end
|
|
1731
|
+
end
|
|
1732
|
+
|
|
1464
1733
|
# Destroy each row in the dataset by instantiating it and then calling
|
|
1465
1734
|
# destroy on the resulting model object. This isn't as fast as deleting
|
|
1466
1735
|
# the dataset, which does a single SQL call, but this runs any destroy
|
|
@@ -1472,7 +1741,7 @@ module Sequel
|
|
|
1472
1741
|
# # ...
|
|
1473
1742
|
def destroy
|
|
1474
1743
|
pr = proc{all{|r| r.destroy}.length}
|
|
1475
|
-
model.use_transactions ? @db.transaction(&pr) : pr.call
|
|
1744
|
+
model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
|
|
1476
1745
|
end
|
|
1477
1746
|
|
|
1478
1747
|
# This allows you to call +to_hash+ without any arguments, which will
|
|
@@ -1491,6 +1760,19 @@ module Sequel
|
|
|
1491
1760
|
super(pk, value_column)
|
|
1492
1761
|
end
|
|
1493
1762
|
end
|
|
1763
|
+
|
|
1764
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
|
1765
|
+
# value.
|
|
1766
|
+
#
|
|
1767
|
+
# # Single primary key
|
|
1768
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1769
|
+
#
|
|
1770
|
+
# # Composite primary key
|
|
1771
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
|
1772
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
|
1773
|
+
def with_pk(pk)
|
|
1774
|
+
first(model.qualified_primary_key_hash(pk))
|
|
1775
|
+
end
|
|
1494
1776
|
end
|
|
1495
1777
|
|
|
1496
1778
|
extend ClassMethods
|