sequel 3.21.0 → 3.32.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 +539 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/advanced_associations.rdoc +17 -0
- data/doc/association_basics.rdoc +293 -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/release_notes/3.32.0.txt +202 -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 +52 -0
- data/lib/sequel/adapters/jdbc/derby.rb +260 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +66 -30
- data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -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 +88 -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 +339 -0
- data/lib/sequel/adapters/mysql.rb +85 -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 +355 -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 +328 -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 +278 -107
- data/lib/sequel/adapters/shared/mysql.rb +203 -65
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +237 -46
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +200 -50
- 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 +68 -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 +163 -59
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +240 -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 +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +59 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +676 -267
- 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/query.rb +1 -1
- 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 +1081 -344
- data/lib/sequel/model/base.rb +420 -133
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +20 -10
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +43 -10
- 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 +68 -7
- data/lib/sequel/plugins/nested_attributes.rb +65 -10
- 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 +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -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 +135 -30
- data/spec/adapters/mysql_spec.rb +244 -212
- data/spec/adapters/oracle_spec.rb +67 -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 +789 -512
- data/spec/core/dataset_spec.rb +1176 -1061
- 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/active_model_spec.rb +13 -0
- 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/query_spec.rb +6 -0
- 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 +72 -0
- data/spec/extensions/serialization_spec.rb +81 -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 +661 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +500 -67
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +367 -119
- data/spec/integration/prepared_statement_test.rb +222 -125
- data/spec/integration/schema_test.rb +217 -39
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +182 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +955 -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 +349 -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 +117 -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
|
|
@@ -86,6 +86,11 @@ module Sequel
|
|
|
86
86
|
# database to typecast the value correctly.
|
|
87
87
|
attr_accessor :typecast_on_assignment
|
|
88
88
|
|
|
89
|
+
# Whether to enable the after_commit and after_rollback hooks when saving/destroying
|
|
90
|
+
# instances. On by default, can be turned off for performance reasons or when using
|
|
91
|
+
# prepared transactions (which aren't compatible with after commit/rollback).
|
|
92
|
+
attr_accessor :use_after_commit_rollback
|
|
93
|
+
|
|
89
94
|
# Whether to use a transaction by default when saving/deleting records (default: true).
|
|
90
95
|
# If you are sending database queries in before_* or after_* hooks, you shouldn't change
|
|
91
96
|
# the default setting without a good reason.
|
|
@@ -105,6 +110,17 @@ module Sequel
|
|
|
105
110
|
args = args.first if (args.size == 1)
|
|
106
111
|
args.is_a?(Hash) ? dataset[args] : primary_key_lookup(args)
|
|
107
112
|
end
|
|
113
|
+
|
|
114
|
+
# Initializes a model instance as an existing record. This constructor is
|
|
115
|
+
# used by Sequel to initialize model instances when fetching records.
|
|
116
|
+
# Requires that values be a hash where all keys are symbols. It
|
|
117
|
+
# probably should not be used by external code.
|
|
118
|
+
def call(values)
|
|
119
|
+
o = allocate
|
|
120
|
+
o.set_values(values)
|
|
121
|
+
o.after_initialize
|
|
122
|
+
o
|
|
123
|
+
end
|
|
108
124
|
|
|
109
125
|
# Clear the setter_methods cache
|
|
110
126
|
def clear_setter_methods_cache
|
|
@@ -148,6 +164,36 @@ module Sequel
|
|
|
148
164
|
def dataset=(ds)
|
|
149
165
|
set_dataset(ds)
|
|
150
166
|
end
|
|
167
|
+
|
|
168
|
+
# Extend the dataset with a module, similar to adding
|
|
169
|
+
# a plugin with the methods defined in DatasetMethods. If a block
|
|
170
|
+
# is given, an anonymous module is created and the module_evaled, otherwise
|
|
171
|
+
# the argument should be a module. Returns the module given or the anonymous
|
|
172
|
+
# module created.
|
|
173
|
+
#
|
|
174
|
+
# Artist.dataset_module Sequel::ColumnsIntrospection
|
|
175
|
+
#
|
|
176
|
+
# Artist.dataset_module do
|
|
177
|
+
# def foo
|
|
178
|
+
# :bar
|
|
179
|
+
# end
|
|
180
|
+
# end
|
|
181
|
+
# Artist.dataset.foo
|
|
182
|
+
# # => :bar
|
|
183
|
+
# Artist.foo
|
|
184
|
+
# # => :bar
|
|
185
|
+
def dataset_module(mod = nil)
|
|
186
|
+
if mod
|
|
187
|
+
raise Error, "can't provide both argument and block to Model.dataset_module" if block_given?
|
|
188
|
+
dataset_extend(mod)
|
|
189
|
+
mod
|
|
190
|
+
else
|
|
191
|
+
@dataset_module ||= Module.new
|
|
192
|
+
@dataset_module.module_eval(&Proc.new) if block_given?
|
|
193
|
+
dataset_extend(@dataset_module)
|
|
194
|
+
@dataset_module
|
|
195
|
+
end
|
|
196
|
+
end
|
|
151
197
|
|
|
152
198
|
# Returns the database associated with the Model class.
|
|
153
199
|
# If this model doesn't have a database associated with it,
|
|
@@ -162,7 +208,7 @@ module Sequel
|
|
|
162
208
|
def db
|
|
163
209
|
return @db if @db
|
|
164
210
|
@db = self == Model ? DATABASES.first : superclass.db
|
|
165
|
-
raise(Error, "No database associated with #{self}") unless @db
|
|
211
|
+
raise(Error, "No database associated with #{self}: have you called Sequel.connect or #{self}.db= ?") unless @db
|
|
166
212
|
@db
|
|
167
213
|
end
|
|
168
214
|
|
|
@@ -193,6 +239,16 @@ module Sequel
|
|
|
193
239
|
@db_schema ||= get_db_schema
|
|
194
240
|
end
|
|
195
241
|
|
|
242
|
+
# Create a column alias, where the column methods have one name, but the underlying storage uses a
|
|
243
|
+
# different name.
|
|
244
|
+
def def_column_alias(meth, column)
|
|
245
|
+
clear_setter_methods_cache
|
|
246
|
+
overridable_methods_module.module_eval do
|
|
247
|
+
define_method(meth){self[column]}
|
|
248
|
+
define_method("#{meth}="){|v| self[column] = v}
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
196
252
|
# 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
253
|
# the given block. Also define a class method on the model that calls the
|
|
198
254
|
# dataset method. Stores the method name and block so that it can be reapplied if the model's
|
|
@@ -217,9 +273,30 @@ module Sequel
|
|
|
217
273
|
@dataset_methods[meth] = block
|
|
218
274
|
dataset.meta_def(meth, &block) if @dataset
|
|
219
275
|
end
|
|
220
|
-
args.each
|
|
276
|
+
args.each do |arg|
|
|
277
|
+
if arg.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
|
278
|
+
instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg, true)
|
|
279
|
+
else
|
|
280
|
+
def_model_dataset_method_block(arg)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
221
283
|
end
|
|
222
|
-
|
|
284
|
+
|
|
285
|
+
module_eval(if RUBY_VERSION < '1.8.7'
|
|
286
|
+
<<-END
|
|
287
|
+
def def_model_dataset_method_block(arg)
|
|
288
|
+
meta_def(arg){|*args| dataset.send(arg, *args)}
|
|
289
|
+
end
|
|
290
|
+
END
|
|
291
|
+
else
|
|
292
|
+
<<-END
|
|
293
|
+
def def_model_dataset_method_block(arg)
|
|
294
|
+
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
295
|
+
end
|
|
296
|
+
END
|
|
297
|
+
end, __FILE__, __LINE__ - 4)
|
|
298
|
+
private :def_model_dataset_method_block
|
|
299
|
+
|
|
223
300
|
# Finds a single record according to the supplied filter.
|
|
224
301
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
|
225
302
|
#
|
|
@@ -248,6 +325,13 @@ module Sequel
|
|
|
248
325
|
find(cond) || create(cond, &block)
|
|
249
326
|
end
|
|
250
327
|
|
|
328
|
+
# Clear the setter_methods cache when a module is included, as it
|
|
329
|
+
# may contain setter methods.
|
|
330
|
+
def include(mod)
|
|
331
|
+
clear_setter_methods_cache
|
|
332
|
+
super
|
|
333
|
+
end
|
|
334
|
+
|
|
251
335
|
# If possible, set the dataset for the model subclass as soon as it
|
|
252
336
|
# is created. Also, make sure the inherited class instance variables
|
|
253
337
|
# are copied into the subclass.
|
|
@@ -271,7 +355,8 @@ module Sequel
|
|
|
271
355
|
db
|
|
272
356
|
begin
|
|
273
357
|
if self == Model || !@dataset
|
|
274
|
-
|
|
358
|
+
n = subclass.name
|
|
359
|
+
subclass.set_dataset(subclass.implicit_table_name) unless n.nil? || n.empty?
|
|
275
360
|
elsif @dataset
|
|
276
361
|
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
277
362
|
end
|
|
@@ -290,12 +375,9 @@ module Sequel
|
|
|
290
375
|
pluralize(underscore(demodulize(name))).to_sym
|
|
291
376
|
end
|
|
292
377
|
|
|
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.
|
|
378
|
+
# Calls #call with the values hash. Only for backwards compatibility.
|
|
297
379
|
def load(values)
|
|
298
|
-
|
|
380
|
+
call(values)
|
|
299
381
|
end
|
|
300
382
|
|
|
301
383
|
# Clear the setter_methods cache when a setter method is added
|
|
@@ -325,18 +407,13 @@ module Sequel
|
|
|
325
407
|
unless @plugins.include?(m)
|
|
326
408
|
@plugins << m
|
|
327
409
|
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
|
|
410
|
+
include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
|
|
411
|
+
extend(m::ClassMethods)if plugin_module_defined?(m, :ClassMethods)
|
|
412
|
+
dataset_extend(m::DatasetMethods) if plugin_module_defined?(m, :DatasetMethods)
|
|
336
413
|
end
|
|
337
414
|
m.configure(self, *args, &blk) if m.respond_to?(:configure)
|
|
338
415
|
end
|
|
339
|
-
|
|
416
|
+
|
|
340
417
|
# Returns primary key attribute hash. If using a composite primary key
|
|
341
418
|
# value such be an array with values for each primary key in the correct
|
|
342
419
|
# order. For a standard primary key, value should be an object with a
|
|
@@ -389,8 +466,8 @@ module Sequel
|
|
|
389
466
|
# setter methods (methods that end in =) that you want to be used during
|
|
390
467
|
# mass assignment, they need to be listed here as well (without the =).
|
|
391
468
|
#
|
|
392
|
-
# It may be better to use a method such as +set_only+
|
|
393
|
-
#
|
|
469
|
+
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
|
470
|
+
# the allowed fields per call.
|
|
394
471
|
#
|
|
395
472
|
# Artist.set_allowed_columns(:name, :hometown)
|
|
396
473
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -400,10 +477,12 @@ module Sequel
|
|
|
400
477
|
@allowed_columns = cols
|
|
401
478
|
end
|
|
402
479
|
|
|
403
|
-
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol
|
|
404
|
-
#
|
|
480
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
|
481
|
+
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
|
482
|
+
# <tt>SQL::AliasedExpression</tt>
|
|
483
|
+
# (all specifying a table name in the current database), or a +Dataset+.
|
|
405
484
|
# If a dataset is used, the model's database is changed to the database of the given
|
|
406
|
-
# dataset. If a
|
|
485
|
+
# dataset. If a dataset is not used, a dataset is created from the current
|
|
407
486
|
# database with the table name given. Other arguments raise an +Error+.
|
|
408
487
|
# Returns self.
|
|
409
488
|
#
|
|
@@ -418,17 +497,21 @@ module Sequel
|
|
|
418
497
|
def set_dataset(ds, opts={})
|
|
419
498
|
inherited = opts[:inherited]
|
|
420
499
|
@dataset = case ds
|
|
421
|
-
when Symbol
|
|
500
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
|
422
501
|
@simple_table = db.literal(ds)
|
|
423
|
-
db
|
|
502
|
+
db.from(ds)
|
|
424
503
|
when Dataset
|
|
425
|
-
@simple_table =
|
|
504
|
+
@simple_table = if ds.send(:simple_select_all?)
|
|
505
|
+
ds.literal(ds.first_source_table)
|
|
506
|
+
else
|
|
507
|
+
nil
|
|
508
|
+
end
|
|
426
509
|
@db = ds.db
|
|
427
510
|
ds
|
|
428
511
|
else
|
|
429
|
-
raise(Error, "Model.set_dataset takes
|
|
512
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
|
430
513
|
end
|
|
431
|
-
@dataset.row_proc =
|
|
514
|
+
@dataset.row_proc = self
|
|
432
515
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
|
433
516
|
if inherited
|
|
434
517
|
@simple_table = superclass.simple_table
|
|
@@ -441,10 +524,11 @@ module Sequel
|
|
|
441
524
|
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
|
442
525
|
self
|
|
443
526
|
end
|
|
444
|
-
|
|
527
|
+
|
|
445
528
|
# Sets the primary key for this model. You can use either a regular
|
|
446
529
|
# or a composite primary key. To not use a primary key, set to nil
|
|
447
|
-
# or use +no_primary_key+.
|
|
530
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
|
531
|
+
# determine the primary key to use, so this method is not needed often.
|
|
448
532
|
#
|
|
449
533
|
# class Person < Sequel::Model
|
|
450
534
|
# # regular key
|
|
@@ -458,7 +542,11 @@ module Sequel
|
|
|
458
542
|
def set_primary_key(*key)
|
|
459
543
|
clear_setter_methods_cache
|
|
460
544
|
key = key.flatten
|
|
461
|
-
@simple_pk = key.length == 1
|
|
545
|
+
@simple_pk = if key.length == 1
|
|
546
|
+
(@dataset || db).literal(key.first)
|
|
547
|
+
else
|
|
548
|
+
nil
|
|
549
|
+
end
|
|
462
550
|
@primary_key = (key.length == 1) ? key[0] : key
|
|
463
551
|
end
|
|
464
552
|
|
|
@@ -468,10 +556,9 @@ module Sequel
|
|
|
468
556
|
# If you have any virtual setter methods (methods that end in =) that you
|
|
469
557
|
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
|
470
558
|
#
|
|
471
|
-
# It
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# method uses, where everything is allowed other than what you restrict.
|
|
559
|
+
# It's generally a bad idea to rely on a blacklist approach for security. Using a whitelist
|
|
560
|
+
# approach such as set_allowed_columns or the instance level set_only or set_fields methods
|
|
561
|
+
# is usually a better choice. So use of this method is generally a bad idea.
|
|
475
562
|
#
|
|
476
563
|
# Artist.set_restricted_column(:records_sold)
|
|
477
564
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -511,6 +598,9 @@ module Sequel
|
|
|
511
598
|
# 7 days ago.
|
|
512
599
|
#
|
|
513
600
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
|
601
|
+
#
|
|
602
|
+
# This method creates dataset methods that do not accept arguments. To create
|
|
603
|
+
# dataset methods that accept arguments, you have to use def_dataset_method.
|
|
514
604
|
def subset(name, *args, &block)
|
|
515
605
|
def_dataset_method(name){filter(*args, &block)}
|
|
516
606
|
end
|
|
@@ -526,6 +616,7 @@ module Sequel
|
|
|
526
616
|
end
|
|
527
617
|
|
|
528
618
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
|
619
|
+
# Using this method can open up security issues, be very careful before using it.
|
|
529
620
|
#
|
|
530
621
|
# Artist.set(:id=>1) # Error
|
|
531
622
|
# Artist.unrestrict_primary_key
|
|
@@ -548,6 +639,16 @@ module Sequel
|
|
|
548
639
|
end
|
|
549
640
|
end
|
|
550
641
|
|
|
642
|
+
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
|
643
|
+
# module if the model has a dataset. Add dataset methods to the class for all
|
|
644
|
+
# public dataset methods.
|
|
645
|
+
def dataset_extend(mod)
|
|
646
|
+
dataset.extend(mod) if @dataset
|
|
647
|
+
dataset_method_modules << mod
|
|
648
|
+
meths = mod.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
|
|
649
|
+
def_dataset_method(*meths) unless meths.empty?
|
|
650
|
+
end
|
|
651
|
+
|
|
551
652
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
|
552
653
|
def def_bad_column_accessor(column)
|
|
553
654
|
overridable_methods_module.module_eval do
|
|
@@ -578,10 +679,9 @@ module Sequel
|
|
|
578
679
|
return nil unless @dataset
|
|
579
680
|
schema_hash = {}
|
|
580
681
|
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
682
|
get_columns = proc{check_non_connection_error{columns} || []}
|
|
584
|
-
|
|
683
|
+
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)}
|
|
684
|
+
if schema_array
|
|
585
685
|
schema_array.each{|k,v| schema_hash[k] = v}
|
|
586
686
|
if ds_opts.include?(:select)
|
|
587
687
|
# We don't remove the columns from the schema_hash,
|
|
@@ -589,6 +689,7 @@ module Sequel
|
|
|
589
689
|
# even if they are not selected.
|
|
590
690
|
cols = get_columns.call
|
|
591
691
|
cols.each{|c| schema_hash[c] ||= {}}
|
|
692
|
+
def_column_accessor(*schema_hash.keys)
|
|
592
693
|
else
|
|
593
694
|
# Dataset is for a single table with all columns,
|
|
594
695
|
# so set the columns based on the order they were
|
|
@@ -657,6 +758,15 @@ module Sequel
|
|
|
657
758
|
Sequel::Plugins.const_get(module_name)
|
|
658
759
|
end
|
|
659
760
|
|
|
761
|
+
# Check if the plugin module +plugin+ defines the constant named by +submod+.
|
|
762
|
+
def plugin_module_defined?(plugin, submod)
|
|
763
|
+
if RUBY_VERSION >= '1.9'
|
|
764
|
+
plugin.const_defined?(submod, false)
|
|
765
|
+
else
|
|
766
|
+
plugin.const_defined?(submod)
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
660
770
|
# Find the row in the dataset that matches the primary key. Uses
|
|
661
771
|
# a static SQL optimization if the table and primary key are simple.
|
|
662
772
|
def primary_key_lookup(pk)
|
|
@@ -686,18 +796,19 @@ module Sequel
|
|
|
686
796
|
|
|
687
797
|
# Sequel::Model instance methods that implement basic model functionality.
|
|
688
798
|
#
|
|
689
|
-
# * All of the methods in +HOOKS+ create instance methods that are called
|
|
799
|
+
# * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
|
|
690
800
|
# 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+.
|
|
801
|
+
# a model object, Sequel will call +around_destroy+, which will call +before_destroy+, do
|
|
802
|
+
# the destroy, and then call +after_destroy+.
|
|
693
803
|
# * The following instance_methods all call the class method of the same
|
|
694
|
-
# name: columns,
|
|
804
|
+
# name: columns, db, primary_key, db_schema.
|
|
695
805
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
|
696
|
-
# to set values for the attribute. It also creates
|
|
806
|
+
# to set values for the attribute. It also creates instance getters returning
|
|
697
807
|
# the value of the setting. If the value has not yet been set, it
|
|
698
808
|
# gets the default value from the class by calling the class method of the same name.
|
|
699
809
|
module InstanceMethods
|
|
700
810
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
|
811
|
+
AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
|
701
812
|
|
|
702
813
|
# Define instance method(s) that calls class method(s) of the
|
|
703
814
|
# same name, caching the result in an instance variable. Define
|
|
@@ -735,7 +846,7 @@ module Sequel
|
|
|
735
846
|
#
|
|
736
847
|
# Arguments:
|
|
737
848
|
# values :: should be a hash to pass to set.
|
|
738
|
-
# from_db ::
|
|
849
|
+
# from_db :: only for backwards compatibility, forget it exists.
|
|
739
850
|
#
|
|
740
851
|
# Artist.new(:name=>'Bob')
|
|
741
852
|
#
|
|
@@ -744,13 +855,12 @@ module Sequel
|
|
|
744
855
|
# end
|
|
745
856
|
def initialize(values = {}, from_db = false)
|
|
746
857
|
if from_db
|
|
747
|
-
@new = false
|
|
748
858
|
set_values(values)
|
|
749
859
|
else
|
|
750
860
|
@values = {}
|
|
751
861
|
@new = true
|
|
752
862
|
@modified = true
|
|
753
|
-
|
|
863
|
+
initialize_set(values)
|
|
754
864
|
changed_columns.clear
|
|
755
865
|
yield self if block_given?
|
|
756
866
|
end
|
|
@@ -766,7 +876,7 @@ module Sequel
|
|
|
766
876
|
|
|
767
877
|
# Sets the value for the given column. If typecasting is enabled for
|
|
768
878
|
# this object, typecast the value based on the column's type.
|
|
769
|
-
# If this
|
|
879
|
+
# If this is a new record or the typecasted value isn't the same
|
|
770
880
|
# as the current value for the column, mark the column as changed.
|
|
771
881
|
#
|
|
772
882
|
# a = Artist.new
|
|
@@ -874,14 +984,25 @@ module Sequel
|
|
|
874
984
|
|
|
875
985
|
# Returns true when current instance exists, false otherwise.
|
|
876
986
|
# Generally an object that isn't new will exist unless it has
|
|
877
|
-
# been deleted.
|
|
987
|
+
# been deleted. Uses a database query to check for existence,
|
|
988
|
+
# unless the model object is new, in which case this is always
|
|
989
|
+
# false.
|
|
878
990
|
#
|
|
879
991
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
880
992
|
# # => true
|
|
993
|
+
# Artist.new.exists?
|
|
994
|
+
# # => false
|
|
881
995
|
def exists?
|
|
882
|
-
!this.get(1).nil?
|
|
996
|
+
new? ? false : !this.get(1).nil?
|
|
883
997
|
end
|
|
884
998
|
|
|
999
|
+
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
1000
|
+
# module may contain setter methods.
|
|
1001
|
+
def extend(mod)
|
|
1002
|
+
@singleton_setter_added = true
|
|
1003
|
+
super
|
|
1004
|
+
end
|
|
1005
|
+
|
|
885
1006
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
|
886
1007
|
# the same class and values (if pk is nil).
|
|
887
1008
|
#
|
|
@@ -890,7 +1011,14 @@ module Sequel
|
|
|
890
1011
|
# Artist.new.hash == Artist.new.hash # true
|
|
891
1012
|
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
|
892
1013
|
def hash
|
|
893
|
-
|
|
1014
|
+
case primary_key
|
|
1015
|
+
when Array
|
|
1016
|
+
[model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1017
|
+
when Symbol
|
|
1018
|
+
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1019
|
+
else
|
|
1020
|
+
[model, @values.sort_by{|k,v| k.to_s}].hash
|
|
1021
|
+
end
|
|
894
1022
|
end
|
|
895
1023
|
|
|
896
1024
|
# Returns value for the :id attribute, even if the primary key is
|
|
@@ -957,7 +1085,7 @@ module Sequel
|
|
|
957
1085
|
# a = Artist[1]
|
|
958
1086
|
# a.modified? # => false
|
|
959
1087
|
# a.set(:name=>'Jim')
|
|
960
|
-
# a.modified # => true
|
|
1088
|
+
# a.modified? # => true
|
|
961
1089
|
def modified?
|
|
962
1090
|
@modified || !changed_columns.empty?
|
|
963
1091
|
end
|
|
@@ -967,11 +1095,11 @@ module Sequel
|
|
|
967
1095
|
# Artist.new.new? # => true
|
|
968
1096
|
# Artist[1].new? # => false
|
|
969
1097
|
def new?
|
|
970
|
-
@new
|
|
1098
|
+
defined?(@new) ? @new : (@new = false)
|
|
971
1099
|
end
|
|
972
1100
|
|
|
973
1101
|
# Returns the primary key value identifying the model instance.
|
|
974
|
-
# Raises an
|
|
1102
|
+
# Raises an +Error+ if this model does not have a primary key.
|
|
975
1103
|
# If the model has a composite primary key, returns an array of values.
|
|
976
1104
|
#
|
|
977
1105
|
# Artist[1].pk # => 1
|
|
@@ -1016,26 +1144,31 @@ module Sequel
|
|
|
1016
1144
|
#
|
|
1017
1145
|
# If +save+ fails and either raise_on_save_failure or the
|
|
1018
1146
|
# :raise_on_failure option is true, it raises ValidationFailed
|
|
1019
|
-
# or
|
|
1147
|
+
# or HookFailed. Otherwise it returns nil.
|
|
1020
1148
|
#
|
|
1021
1149
|
# If it succeeds, it returns self.
|
|
1022
1150
|
#
|
|
1023
1151
|
# You can provide an optional list of columns to update, in which
|
|
1024
|
-
# case it only updates those columns.
|
|
1152
|
+
# case it only updates those columns, or a options hash.
|
|
1025
1153
|
#
|
|
1026
1154
|
# Takes the following options:
|
|
1027
1155
|
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
1030
|
-
#
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1156
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1157
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1158
|
+
# +raise_on_save_failure+ setting
|
|
1159
|
+
# :server :: set the server/shard on the object before saving, and use that
|
|
1160
|
+
# server/shard in any transaction.
|
|
1161
|
+
# :transaction :: set to true or false to override the current
|
|
1162
|
+
# +use_transactions+ setting
|
|
1163
|
+
# :validate :: set to false to skip validation
|
|
1034
1164
|
def save(*columns)
|
|
1035
1165
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1036
|
-
if opts[:
|
|
1037
|
-
|
|
1038
|
-
|
|
1166
|
+
set_server(opts[:server]) if opts[:server]
|
|
1167
|
+
if opts[:validate] != false
|
|
1168
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1169
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
|
1170
|
+
return
|
|
1171
|
+
end
|
|
1039
1172
|
end
|
|
1040
1173
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
|
1041
1174
|
end
|
|
@@ -1075,7 +1208,8 @@ module Sequel
|
|
|
1075
1208
|
end
|
|
1076
1209
|
|
|
1077
1210
|
# Set all values using the entries in the hash, except for the keys
|
|
1078
|
-
# given in except.
|
|
1211
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
|
1212
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1079
1213
|
#
|
|
1080
1214
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
|
1081
1215
|
# artist.name # => 'Jim'
|
|
@@ -1086,10 +1220,10 @@ module Sequel
|
|
|
1086
1220
|
# For each of the fields in the given array +fields+, call the setter
|
|
1087
1221
|
# method with the value of that +hash+ entry for the field. Returns self.
|
|
1088
1222
|
#
|
|
1089
|
-
# artist.set_fields({:name=>'Jim'}, :name)
|
|
1223
|
+
# artist.set_fields({:name=>'Jim'}, [:name])
|
|
1090
1224
|
# artist.name # => 'Jim'
|
|
1091
1225
|
#
|
|
1092
|
-
# artist.set_fields({:hometown=>'LA'}, :name)
|
|
1226
|
+
# artist.set_fields({:hometown=>'LA'}, [:name])
|
|
1093
1227
|
# artist.name # => nil
|
|
1094
1228
|
# artist.hometown # => 'Sac'
|
|
1095
1229
|
def set_fields(hash, fields)
|
|
@@ -1098,16 +1232,31 @@ module Sequel
|
|
|
1098
1232
|
end
|
|
1099
1233
|
|
|
1100
1234
|
# Set the values using the entries in the hash, only if the key
|
|
1101
|
-
# is included in only.
|
|
1235
|
+
# is included in only. It may be a better idea to use +set_fields+
|
|
1236
|
+
# instead of this method.
|
|
1102
1237
|
#
|
|
1103
1238
|
# artist.set_only({:name=>'Jim'}, :name)
|
|
1104
1239
|
# artist.name # => 'Jim'
|
|
1105
1240
|
#
|
|
1106
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
|
1241
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
|
1107
1242
|
def set_only(hash, *only)
|
|
1108
1243
|
set_restricted(hash, only.flatten, false)
|
|
1109
1244
|
end
|
|
1110
1245
|
|
|
1246
|
+
# Set the shard that this object is tied to. Returns self.
|
|
1247
|
+
def set_server(s)
|
|
1248
|
+
@server = s
|
|
1249
|
+
@this.opts[:server] = s if @this
|
|
1250
|
+
self
|
|
1251
|
+
end
|
|
1252
|
+
|
|
1253
|
+
# Replace the current values with hash. Should definitely not be
|
|
1254
|
+
# used with untrusted input, and should probably not be called
|
|
1255
|
+
# directly by user code.
|
|
1256
|
+
def set_values(hash)
|
|
1257
|
+
@values = hash
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1111
1260
|
# Clear the setter_methods cache when a method is added
|
|
1112
1261
|
def singleton_method_added(meth)
|
|
1113
1262
|
@singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
|
|
@@ -1119,10 +1268,10 @@ module Sequel
|
|
|
1119
1268
|
# Artist[1].this
|
|
1120
1269
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1121
1270
|
def this
|
|
1122
|
-
@this ||= model.dataset.filter(pk_hash).limit(1).naked
|
|
1271
|
+
@this ||= use_server(model.dataset.filter(pk_hash).limit(1).naked)
|
|
1123
1272
|
end
|
|
1124
1273
|
|
|
1125
|
-
# Runs set with the passed hash and then runs save_changes.
|
|
1274
|
+
# Runs #set with the passed hash and then runs save_changes.
|
|
1126
1275
|
#
|
|
1127
1276
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1128
1277
|
def update(hash)
|
|
@@ -1130,7 +1279,7 @@ module Sequel
|
|
|
1130
1279
|
end
|
|
1131
1280
|
|
|
1132
1281
|
# Update all values using the entries in the hash, ignoring any setting of
|
|
1133
|
-
# allowed_columns or
|
|
1282
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
|
1134
1283
|
#
|
|
1135
1284
|
# Artist.set_restricted_columns(:name)
|
|
1136
1285
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1139,7 +1288,8 @@ module Sequel
|
|
|
1139
1288
|
end
|
|
1140
1289
|
|
|
1141
1290
|
# Update all values using the entries in the hash, except for the keys
|
|
1142
|
-
# given in except.
|
|
1291
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
|
1292
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1143
1293
|
#
|
|
1144
1294
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1145
1295
|
def update_except(hash, *except)
|
|
@@ -1149,10 +1299,10 @@ module Sequel
|
|
|
1149
1299
|
# Update the instances values by calling +set_fields+ with the +hash+
|
|
1150
1300
|
# and +fields+, then save any changes to the record. Returns self.
|
|
1151
1301
|
#
|
|
1152
|
-
# artist.update_fields({:name=>'Jim'}, :name)
|
|
1302
|
+
# artist.update_fields({:name=>'Jim'}, [:name])
|
|
1153
1303
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1154
1304
|
#
|
|
1155
|
-
# artist.update_fields({:hometown=>'LA'}, :name)
|
|
1305
|
+
# artist.update_fields({:hometown=>'LA'}, [:name])
|
|
1156
1306
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
|
1157
1307
|
def update_fields(hash, fields)
|
|
1158
1308
|
set_fields(hash, fields)
|
|
@@ -1160,7 +1310,8 @@ module Sequel
|
|
|
1160
1310
|
end
|
|
1161
1311
|
|
|
1162
1312
|
# Update the values using the entries in the hash, only if the key
|
|
1163
|
-
# is included in only.
|
|
1313
|
+
# is included in only. It may be a better idea to use +update_fields+
|
|
1314
|
+
# instead of this method.
|
|
1164
1315
|
#
|
|
1165
1316
|
# artist.update_only({:name=>'Jim'}, :name)
|
|
1166
1317
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1185,21 +1336,15 @@ module Sequel
|
|
|
1185
1336
|
# artist(:name=>'Invalid').valid? # => false
|
|
1186
1337
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
|
1187
1338
|
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?
|
|
1339
|
+
_valid?(false, opts)
|
|
1196
1340
|
end
|
|
1197
1341
|
|
|
1198
1342
|
private
|
|
1199
1343
|
|
|
1200
|
-
#
|
|
1344
|
+
# Do the deletion of the object's dataset, and check that the row
|
|
1345
|
+
# was actually deleted.
|
|
1201
1346
|
def _delete
|
|
1202
|
-
n =
|
|
1347
|
+
n = _delete_without_checking
|
|
1203
1348
|
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
1349
|
n
|
|
1205
1350
|
end
|
|
@@ -1210,12 +1355,27 @@ module Sequel
|
|
|
1210
1355
|
this
|
|
1211
1356
|
end
|
|
1212
1357
|
|
|
1358
|
+
# Actually do the deletion of the object's dataset. Return the
|
|
1359
|
+
# number of rows modified.
|
|
1360
|
+
def _delete_without_checking
|
|
1361
|
+
_delete_dataset.delete
|
|
1362
|
+
end
|
|
1363
|
+
|
|
1213
1364
|
# Internal destroy method, separted from destroy to
|
|
1214
1365
|
# allow running inside a transaction
|
|
1215
1366
|
def _destroy(opts)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1367
|
+
sh = {:server=>this_server}
|
|
1368
|
+
db.after_rollback(sh){after_destroy_rollback} if use_after_commit_rollback
|
|
1369
|
+
called = false
|
|
1370
|
+
around_destroy do
|
|
1371
|
+
called = true
|
|
1372
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
|
1373
|
+
_destroy_delete
|
|
1374
|
+
after_destroy
|
|
1375
|
+
true
|
|
1376
|
+
end
|
|
1377
|
+
raise_hook_failure(:destroy) unless called
|
|
1378
|
+
db.after_commit(sh){after_destroy_commit} if use_after_commit_rollback
|
|
1219
1379
|
self
|
|
1220
1380
|
end
|
|
1221
1381
|
|
|
@@ -1230,11 +1390,11 @@ module Sequel
|
|
|
1230
1390
|
# the record should be refreshed from the database.
|
|
1231
1391
|
def _insert
|
|
1232
1392
|
ds = _insert_dataset
|
|
1233
|
-
if ds.
|
|
1393
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
|
1234
1394
|
@values = h
|
|
1235
1395
|
nil
|
|
1236
1396
|
else
|
|
1237
|
-
iid = ds
|
|
1397
|
+
iid = _insert_raw(ds)
|
|
1238
1398
|
# if we have a regular primary key and it's not set in @values,
|
|
1239
1399
|
# we assume it's the last inserted id
|
|
1240
1400
|
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !@values[pk]
|
|
@@ -1243,55 +1403,95 @@ module Sequel
|
|
|
1243
1403
|
pk
|
|
1244
1404
|
end
|
|
1245
1405
|
end
|
|
1246
|
-
|
|
1406
|
+
|
|
1247
1407
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1248
1408
|
# dataset by default.
|
|
1249
1409
|
def _insert_dataset
|
|
1250
|
-
model.dataset
|
|
1410
|
+
use_server(model.dataset)
|
|
1251
1411
|
end
|
|
1252
1412
|
|
|
1413
|
+
# Insert into the given dataset and return the primary key created (if any).
|
|
1414
|
+
def _insert_raw(ds)
|
|
1415
|
+
ds.insert(@values)
|
|
1416
|
+
end
|
|
1417
|
+
|
|
1418
|
+
# Insert into the given dataset and return the hash of column values.
|
|
1419
|
+
def _insert_select_raw(ds)
|
|
1420
|
+
ds.insert_select(@values)
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1253
1423
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1254
1424
|
# is used for reading newly inserted values from the database
|
|
1255
1425
|
def _refresh(dataset)
|
|
1256
|
-
set_values(dataset
|
|
1426
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
|
1257
1427
|
changed_columns.clear
|
|
1258
1428
|
self
|
|
1259
1429
|
end
|
|
1430
|
+
|
|
1431
|
+
# Get the row of column data from the database.
|
|
1432
|
+
def _refresh_get(dataset)
|
|
1433
|
+
dataset.first
|
|
1434
|
+
end
|
|
1260
1435
|
|
|
1261
1436
|
# Internal version of save, split from save to allow running inside
|
|
1262
1437
|
# it's own transaction.
|
|
1263
1438
|
def _save(columns, opts)
|
|
1264
|
-
|
|
1265
|
-
if
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1439
|
+
sh = {:server=>this_server}
|
|
1440
|
+
db.after_rollback(sh){after_rollback} if use_after_commit_rollback
|
|
1441
|
+
was_new = false
|
|
1442
|
+
pk = nil
|
|
1443
|
+
called_save = false
|
|
1444
|
+
called_cu = false
|
|
1445
|
+
around_save do
|
|
1446
|
+
called_save = true
|
|
1447
|
+
raise_hook_failure(:save) if before_save == false
|
|
1448
|
+
if new?
|
|
1449
|
+
was_new = true
|
|
1450
|
+
around_create do
|
|
1451
|
+
called_cu = true
|
|
1452
|
+
raise_hook_failure(:create) if before_create == false
|
|
1453
|
+
pk = _insert
|
|
1454
|
+
@this = nil
|
|
1455
|
+
@new = false
|
|
1456
|
+
@was_new = true
|
|
1457
|
+
after_create
|
|
1458
|
+
true
|
|
1459
|
+
end
|
|
1460
|
+
raise_hook_failure(:create) unless called_cu
|
|
1461
|
+
else
|
|
1462
|
+
around_update do
|
|
1463
|
+
called_cu = true
|
|
1464
|
+
raise_hook_failure(:update) if before_update == false
|
|
1465
|
+
if columns.empty?
|
|
1466
|
+
@columns_updated = if opts[:changed]
|
|
1467
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1468
|
+
else
|
|
1469
|
+
_save_update_all_columns_hash
|
|
1470
|
+
end
|
|
1471
|
+
changed_columns.clear
|
|
1472
|
+
else # update only the specified columns
|
|
1473
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1474
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
|
1475
|
+
end
|
|
1476
|
+
_update_columns(@columns_updated)
|
|
1477
|
+
@this = nil
|
|
1478
|
+
after_update
|
|
1479
|
+
true
|
|
1480
|
+
end
|
|
1481
|
+
raise_hook_failure(:update) unless called_cu
|
|
1482
|
+
end
|
|
1272
1483
|
after_save
|
|
1484
|
+
true
|
|
1485
|
+
end
|
|
1486
|
+
raise_hook_failure(:save) unless called_save
|
|
1487
|
+
if was_new
|
|
1273
1488
|
@was_new = nil
|
|
1274
1489
|
pk ? _save_refresh : changed_columns.clear
|
|
1275
1490
|
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
1491
|
@columns_updated = nil
|
|
1293
1492
|
end
|
|
1294
1493
|
@modified = false
|
|
1494
|
+
db.after_commit(sh){after_commit} if use_after_commit_rollback
|
|
1295
1495
|
self
|
|
1296
1496
|
end
|
|
1297
1497
|
|
|
@@ -1321,9 +1521,10 @@ module Sequel
|
|
|
1321
1521
|
_update(columns) unless columns.empty?
|
|
1322
1522
|
end
|
|
1323
1523
|
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
|
1524
|
+
# Update this instance's dataset with the supplied column hash,
|
|
1525
|
+
# checking that only a single row was modified.
|
|
1325
1526
|
def _update(columns)
|
|
1326
|
-
n =
|
|
1527
|
+
n = _update_without_checking(columns)
|
|
1327
1528
|
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
1529
|
n
|
|
1329
1530
|
end
|
|
@@ -1334,7 +1535,46 @@ module Sequel
|
|
|
1334
1535
|
this
|
|
1335
1536
|
end
|
|
1336
1537
|
|
|
1337
|
-
#
|
|
1538
|
+
# Update this instances dataset with the supplied column hash.
|
|
1539
|
+
def _update_without_checking(columns)
|
|
1540
|
+
_update_dataset.update(columns)
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1543
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
|
1544
|
+
# failures will be raised as HookFailure exceptions. If it is
|
|
1545
|
+
# +false+, +false+ will be returned instead.
|
|
1546
|
+
def _valid?(raise_errors, opts)
|
|
1547
|
+
errors.clear
|
|
1548
|
+
called = false
|
|
1549
|
+
error = false
|
|
1550
|
+
around_validation do
|
|
1551
|
+
called = true
|
|
1552
|
+
if before_validation == false
|
|
1553
|
+
if raise_errors
|
|
1554
|
+
raise_hook_failure(:validation)
|
|
1555
|
+
else
|
|
1556
|
+
error = true
|
|
1557
|
+
end
|
|
1558
|
+
false
|
|
1559
|
+
else
|
|
1560
|
+
validate
|
|
1561
|
+
after_validation
|
|
1562
|
+
errors.empty?
|
|
1563
|
+
end
|
|
1564
|
+
end
|
|
1565
|
+
error = true unless called
|
|
1566
|
+
if error
|
|
1567
|
+
if raise_errors
|
|
1568
|
+
raise_hook_failure(:validation)
|
|
1569
|
+
else
|
|
1570
|
+
false
|
|
1571
|
+
end
|
|
1572
|
+
else
|
|
1573
|
+
errors.empty?
|
|
1574
|
+
end
|
|
1575
|
+
end
|
|
1576
|
+
|
|
1577
|
+
# If not raising on failure, check for HookFailed
|
|
1338
1578
|
# being raised by yielding and swallow it.
|
|
1339
1579
|
def checked_save_failure(opts)
|
|
1340
1580
|
if raise_on_failure?(opts)
|
|
@@ -1342,7 +1582,7 @@ module Sequel
|
|
|
1342
1582
|
else
|
|
1343
1583
|
begin
|
|
1344
1584
|
yield
|
|
1345
|
-
rescue
|
|
1585
|
+
rescue HookFailed
|
|
1346
1586
|
nil
|
|
1347
1587
|
end
|
|
1348
1588
|
end
|
|
@@ -1350,7 +1590,14 @@ module Sequel
|
|
|
1350
1590
|
|
|
1351
1591
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1352
1592
|
def checked_transaction(opts={})
|
|
1353
|
-
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1593
|
+
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1594
|
+
end
|
|
1595
|
+
|
|
1596
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1597
|
+
# exists so it can be overridden. This is called only for new records, before
|
|
1598
|
+
# changed_columns is cleared.
|
|
1599
|
+
def initialize_set(h)
|
|
1600
|
+
set(h)
|
|
1354
1601
|
end
|
|
1355
1602
|
|
|
1356
1603
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
|
@@ -1369,7 +1616,7 @@ module Sequel
|
|
|
1369
1616
|
# Raise an error appropriate to the hook type. May be swallowed by
|
|
1370
1617
|
# checked_save_failure depending on the raise_on_failure? setting.
|
|
1371
1618
|
def raise_hook_failure(type)
|
|
1372
|
-
raise
|
|
1619
|
+
raise HookFailed, "one of the before_#{type} hooks returned false"
|
|
1373
1620
|
end
|
|
1374
1621
|
|
|
1375
1622
|
# Set the columns, filtered by the only and except arrays.
|
|
@@ -1385,17 +1632,21 @@ module Sequel
|
|
|
1385
1632
|
if meths.include?(m)
|
|
1386
1633
|
send(m, v)
|
|
1387
1634
|
elsif strict
|
|
1388
|
-
|
|
1635
|
+
# Avoid using respond_to? or creating symbols from user input
|
|
1636
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
|
1637
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
|
1638
|
+
raise Error, "#{k} is a restricted primary key"
|
|
1639
|
+
else
|
|
1640
|
+
raise Error, "#{k} is a restricted column"
|
|
1641
|
+
end
|
|
1642
|
+
else
|
|
1643
|
+
raise Error, "method #{m} doesn't exist"
|
|
1644
|
+
end
|
|
1389
1645
|
end
|
|
1390
1646
|
end
|
|
1391
1647
|
self
|
|
1392
1648
|
end
|
|
1393
1649
|
|
|
1394
|
-
# Replace the current values with hash.
|
|
1395
|
-
def set_values(hash)
|
|
1396
|
-
@values = hash
|
|
1397
|
-
end
|
|
1398
|
-
|
|
1399
1650
|
# Returns all methods that can be used for attribute
|
|
1400
1651
|
# assignment (those that end with =), modified by the only
|
|
1401
1652
|
# and except arguments:
|
|
@@ -1424,6 +1675,12 @@ module Sequel
|
|
|
1424
1675
|
meths
|
|
1425
1676
|
end
|
|
1426
1677
|
end
|
|
1678
|
+
|
|
1679
|
+
# The server/shard that the model object's dataset uses, or :default if the
|
|
1680
|
+
# model object's dataset does not have an associated shard.
|
|
1681
|
+
def this_server
|
|
1682
|
+
primary_key ? (this.opts[:server] || :default) : (model.dataset.opts[:server] || :default)
|
|
1683
|
+
end
|
|
1427
1684
|
|
|
1428
1685
|
# Typecast the value to the column's type if typecasting. Calls the database's
|
|
1429
1686
|
# typecast_value method, so database adapters can override/augment the handling
|
|
@@ -1444,6 +1701,11 @@ module Sequel
|
|
|
1444
1701
|
set_restricted(hash, only, except)
|
|
1445
1702
|
save_changes
|
|
1446
1703
|
end
|
|
1704
|
+
|
|
1705
|
+
# Set the given dataset to use the current object's shard.
|
|
1706
|
+
def use_server(ds)
|
|
1707
|
+
@server ? ds.server(@server) : ds
|
|
1708
|
+
end
|
|
1447
1709
|
|
|
1448
1710
|
# Whether to use a transaction for this action. If the :transaction
|
|
1449
1711
|
# option is present in the hash, use that, otherwise, fallback to the
|
|
@@ -1461,6 +1723,18 @@ module Sequel
|
|
|
1461
1723
|
# Artist.dataset.model # => Artist
|
|
1462
1724
|
attr_accessor :model
|
|
1463
1725
|
|
|
1726
|
+
# Assume if a single integer is given that it is a lookup by primary
|
|
1727
|
+
# key, and call with_pk with the argument.
|
|
1728
|
+
#
|
|
1729
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1730
|
+
def [](*args)
|
|
1731
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
|
1732
|
+
with_pk(i)
|
|
1733
|
+
else
|
|
1734
|
+
super
|
|
1735
|
+
end
|
|
1736
|
+
end
|
|
1737
|
+
|
|
1464
1738
|
# Destroy each row in the dataset by instantiating it and then calling
|
|
1465
1739
|
# destroy on the resulting model object. This isn't as fast as deleting
|
|
1466
1740
|
# the dataset, which does a single SQL call, but this runs any destroy
|
|
@@ -1472,7 +1746,7 @@ module Sequel
|
|
|
1472
1746
|
# # ...
|
|
1473
1747
|
def destroy
|
|
1474
1748
|
pr = proc{all{|r| r.destroy}.length}
|
|
1475
|
-
model.use_transactions ? @db.transaction(&pr) : pr.call
|
|
1749
|
+
model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
|
|
1476
1750
|
end
|
|
1477
1751
|
|
|
1478
1752
|
# This allows you to call +to_hash+ without any arguments, which will
|
|
@@ -1491,6 +1765,19 @@ module Sequel
|
|
|
1491
1765
|
super(pk, value_column)
|
|
1492
1766
|
end
|
|
1493
1767
|
end
|
|
1768
|
+
|
|
1769
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
|
1770
|
+
# value.
|
|
1771
|
+
#
|
|
1772
|
+
# # Single primary key
|
|
1773
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1774
|
+
#
|
|
1775
|
+
# # Composite primary key
|
|
1776
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
|
1777
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
|
1778
|
+
def with_pk(pk)
|
|
1779
|
+
first(model.qualified_primary_key_hash(pk))
|
|
1780
|
+
end
|
|
1494
1781
|
end
|
|
1495
1782
|
|
|
1496
1783
|
extend ClassMethods
|