sequel 3.21.0 → 3.36.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 +855 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -7
- data/Rakefile +42 -24
- data/bin/sequel +26 -8
- data/doc/active_record.rdoc +1 -0
- data/doc/advanced_associations.rdoc +171 -11
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +85 -552
- data/doc/model_hooks.rdoc +78 -27
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +97 -50
- data/doc/prepared_statements.rdoc +40 -14
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +30 -4
- 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/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +572 -0
- data/doc/sharding.rdoc +95 -3
- data/doc/testing.rdoc +129 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +51 -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 +17 -11
- data/lib/sequel/adapters/do/postgres.rb +1 -57
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -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 +47 -0
- data/lib/sequel/adapters/jdbc/derby.rb +287 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +265 -94
- data/lib/sequel/adapters/mock.rb +361 -0
- data/lib/sequel/adapters/mysql.rb +118 -222
- data/lib/sequel/adapters/mysql2.rb +58 -57
- data/lib/sequel/adapters/odbc/mssql.rb +15 -7
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +356 -107
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +342 -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 +335 -118
- data/lib/sequel/adapters/shared/mysql.rb +340 -76
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +243 -45
- data/lib/sequel/adapters/shared/postgres.rb +524 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +265 -89
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +9 -12
- data/lib/sequel/adapters/swift/postgres.rb +9 -74
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +8 -7
- data/lib/sequel/adapters/tinytds.rb +161 -21
- 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 +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +38 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +118 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +150 -46
- data/lib/sequel/database/query.rb +215 -65
- data/lib/sequel/database/schema_generator.rb +64 -20
- data/lib/sequel/database/schema_methods.rb +212 -54
- data/lib/sequel/dataset/actions.rb +322 -85
- data/lib/sequel/dataset/features.rb +96 -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 +68 -30
- data/lib/sequel/dataset/query.rb +237 -44
- data/lib/sequel/dataset/sql.rb +698 -278
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
- data/lib/sequel/extensions/migration.rb +97 -11
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +251 -37
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- 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 +1187 -393
- data/lib/sequel/model/base.rb +578 -170
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +123 -8
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/plugins/many_through_many.rb +79 -28
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +139 -63
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -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/schema.rb +7 -2
- 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 +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -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/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +526 -59
- 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/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +142 -40
- data/spec/adapters/mysql_spec.rb +380 -234
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +1050 -212
- data/spec/adapters/spec_helper.rb +8 -5
- data/spec/adapters/sqlite_spec.rb +139 -27
- data/spec/core/connection_pool_spec.rb +196 -93
- data/spec/core/database_spec.rb +879 -510
- data/spec/core/dataset_spec.rb +1415 -1086
- data/spec/core/expression_filters_spec.rb +554 -109
- data/spec/core/mock_adapter_spec.rb +449 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +482 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/caching_spec.rb +47 -51
- 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/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +69 -92
- data/spec/extensions/identity_map_spec.rb +131 -22
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -6
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +48 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +251 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- 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 +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +429 -22
- data/spec/extensions/schema_spec.rb +25 -21
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +85 -43
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +22 -61
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -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 +9 -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 +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +9 -3
- data/spec/integration/dataset_test.rb +605 -91
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +454 -143
- data/spec/integration/prepared_statement_test.rb +263 -128
- data/spec/integration/schema_test.rb +309 -43
- data/spec/integration/spec_helper.rb +40 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +18 -4
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +1067 -661
- data/spec/model/base_spec.rb +329 -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 +240 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +490 -232
- data/spec/model/spec_helper.rb +14 -71
- data/spec/model/validations_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- metadata +178 -80
data/lib/sequel/model/base.rb
CHANGED
|
@@ -11,8 +11,13 @@ 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
|
+
|
|
17
|
+
# Whether to cache the anonymous models created by Sequel::Model(). This is
|
|
18
|
+
# required for reloading them correctly (avoiding the superclass mismatch). True
|
|
19
|
+
# by default for backwards compatibility.
|
|
20
|
+
attr_accessor :cache_anonymous_models
|
|
16
21
|
|
|
17
22
|
# Array of modules that extend this model's dataset. Stored
|
|
18
23
|
# so that if the model's dataset is changed, it will be extended
|
|
@@ -23,7 +28,15 @@ module Sequel
|
|
|
23
28
|
# stored so when the dataset changes, methods defined with def_dataset_method
|
|
24
29
|
# will be applied to the new dataset.
|
|
25
30
|
attr_reader :dataset_methods
|
|
26
|
-
|
|
31
|
+
|
|
32
|
+
# SQL string fragment used for faster DELETE statement creation when deleting/destroying
|
|
33
|
+
# model instances, or nil if the optimization should not be used. For internal use only.
|
|
34
|
+
attr_reader :fast_instance_delete_sql
|
|
35
|
+
|
|
36
|
+
# The dataset that instance datasets (#this) are based on. Generally a naked version of
|
|
37
|
+
# the model's dataset limited to one row. For internal use only.
|
|
38
|
+
attr_reader :instance_dataset
|
|
39
|
+
|
|
27
40
|
# Array of plugin modules loaded by this class
|
|
28
41
|
#
|
|
29
42
|
# Sequel::Model.plugins
|
|
@@ -86,6 +99,11 @@ module Sequel
|
|
|
86
99
|
# database to typecast the value correctly.
|
|
87
100
|
attr_accessor :typecast_on_assignment
|
|
88
101
|
|
|
102
|
+
# Whether to enable the after_commit and after_rollback hooks when saving/destroying
|
|
103
|
+
# instances. On by default, can be turned off for performance reasons or when using
|
|
104
|
+
# prepared transactions (which aren't compatible with after commit/rollback).
|
|
105
|
+
attr_accessor :use_after_commit_rollback
|
|
106
|
+
|
|
89
107
|
# Whether to use a transaction by default when saving/deleting records (default: true).
|
|
90
108
|
# If you are sending database queries in before_* or after_* hooks, you shouldn't change
|
|
91
109
|
# the default setting without a good reason.
|
|
@@ -102,8 +120,19 @@ module Sequel
|
|
|
102
120
|
# Artist[:name=>'Bob'] # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
|
103
121
|
# # => #<Artist {:name=>'Bob', ...}>
|
|
104
122
|
def [](*args)
|
|
105
|
-
args = args.first if
|
|
106
|
-
args.is_a?(Hash) ? dataset[args] : primary_key_lookup(args)
|
|
123
|
+
args = args.first if args.size <= 1
|
|
124
|
+
args.is_a?(Hash) ? dataset[args] : (primary_key_lookup(args) unless args.nil?)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Initializes a model instance as an existing record. This constructor is
|
|
128
|
+
# used by Sequel to initialize model instances when fetching records.
|
|
129
|
+
# Requires that values be a hash where all keys are symbols. It
|
|
130
|
+
# probably should not be used by external code.
|
|
131
|
+
def call(values)
|
|
132
|
+
o = allocate
|
|
133
|
+
o.set_values(values)
|
|
134
|
+
o.after_initialize
|
|
135
|
+
o
|
|
107
136
|
end
|
|
108
137
|
|
|
109
138
|
# Clear the setter_methods cache
|
|
@@ -148,6 +177,36 @@ module Sequel
|
|
|
148
177
|
def dataset=(ds)
|
|
149
178
|
set_dataset(ds)
|
|
150
179
|
end
|
|
180
|
+
|
|
181
|
+
# Extend the dataset with a module, similar to adding
|
|
182
|
+
# a plugin with the methods defined in DatasetMethods. If a block
|
|
183
|
+
# is given, an anonymous module is created and the module_evaled, otherwise
|
|
184
|
+
# the argument should be a module. Returns the module given or the anonymous
|
|
185
|
+
# module created.
|
|
186
|
+
#
|
|
187
|
+
# Artist.dataset_module Sequel::ColumnsIntrospection
|
|
188
|
+
#
|
|
189
|
+
# Artist.dataset_module do
|
|
190
|
+
# def foo
|
|
191
|
+
# :bar
|
|
192
|
+
# end
|
|
193
|
+
# end
|
|
194
|
+
# Artist.dataset.foo
|
|
195
|
+
# # => :bar
|
|
196
|
+
# Artist.foo
|
|
197
|
+
# # => :bar
|
|
198
|
+
def dataset_module(mod = nil)
|
|
199
|
+
if mod
|
|
200
|
+
raise Error, "can't provide both argument and block to Model.dataset_module" if block_given?
|
|
201
|
+
dataset_extend(mod)
|
|
202
|
+
mod
|
|
203
|
+
else
|
|
204
|
+
@dataset_module ||= Module.new
|
|
205
|
+
@dataset_module.module_eval(&Proc.new) if block_given?
|
|
206
|
+
dataset_extend(@dataset_module)
|
|
207
|
+
@dataset_module
|
|
208
|
+
end
|
|
209
|
+
end
|
|
151
210
|
|
|
152
211
|
# Returns the database associated with the Model class.
|
|
153
212
|
# If this model doesn't have a database associated with it,
|
|
@@ -161,8 +220,8 @@ module Sequel
|
|
|
161
220
|
# end # COMMIT
|
|
162
221
|
def db
|
|
163
222
|
return @db if @db
|
|
164
|
-
@db = self == Model ? DATABASES.first : superclass.db
|
|
165
|
-
raise(Error, "No database associated with #{self}") unless @db
|
|
223
|
+
@db = self == Model ? Sequel.synchronize{DATABASES.first} : superclass.db
|
|
224
|
+
raise(Error, "No database associated with #{self}: have you called Sequel.connect or #{self}.db= ?") unless @db
|
|
166
225
|
@db
|
|
167
226
|
end
|
|
168
227
|
|
|
@@ -193,6 +252,16 @@ module Sequel
|
|
|
193
252
|
@db_schema ||= get_db_schema
|
|
194
253
|
end
|
|
195
254
|
|
|
255
|
+
# Create a column alias, where the column methods have one name, but the underlying storage uses a
|
|
256
|
+
# different name.
|
|
257
|
+
def def_column_alias(meth, column)
|
|
258
|
+
clear_setter_methods_cache
|
|
259
|
+
overridable_methods_module.module_eval do
|
|
260
|
+
define_method(meth){self[column]}
|
|
261
|
+
define_method("#{meth}="){|v| self[column] = v}
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
196
265
|
# 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
266
|
# the given block. Also define a class method on the model that calls the
|
|
198
267
|
# dataset method. Stores the method name and block so that it can be reapplied if the model's
|
|
@@ -217,9 +286,15 @@ module Sequel
|
|
|
217
286
|
@dataset_methods[meth] = block
|
|
218
287
|
dataset.meta_def(meth, &block) if @dataset
|
|
219
288
|
end
|
|
220
|
-
args.each
|
|
289
|
+
args.each do |arg|
|
|
290
|
+
if arg.to_s =~ NORMAL_METHOD_NAME_REGEXP
|
|
291
|
+
instance_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__) unless respond_to?(arg, true)
|
|
292
|
+
else
|
|
293
|
+
def_model_dataset_method_block(arg)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
221
296
|
end
|
|
222
|
-
|
|
297
|
+
|
|
223
298
|
# Finds a single record according to the supplied filter.
|
|
224
299
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
|
225
300
|
#
|
|
@@ -248,6 +323,13 @@ module Sequel
|
|
|
248
323
|
find(cond) || create(cond, &block)
|
|
249
324
|
end
|
|
250
325
|
|
|
326
|
+
# Clear the setter_methods cache when a module is included, as it
|
|
327
|
+
# may contain setter methods.
|
|
328
|
+
def include(mod)
|
|
329
|
+
clear_setter_methods_cache
|
|
330
|
+
super
|
|
331
|
+
end
|
|
332
|
+
|
|
251
333
|
# If possible, set the dataset for the model subclass as soon as it
|
|
252
334
|
# is created. Also, make sure the inherited class instance variables
|
|
253
335
|
# are copied into the subclass.
|
|
@@ -268,15 +350,14 @@ module Sequel
|
|
|
268
350
|
subclass.instance_variable_set(iv, sup_class_value)
|
|
269
351
|
end
|
|
270
352
|
unless ivs.include?("@dataset")
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
|
353
|
+
if self == Model || !@dataset
|
|
354
|
+
n = subclass.name
|
|
355
|
+
unless n.nil? || n.empty?
|
|
356
|
+
db
|
|
357
|
+
subclass.set_dataset(subclass.implicit_table_name) rescue nil
|
|
277
358
|
end
|
|
278
|
-
|
|
279
|
-
nil
|
|
359
|
+
elsif @dataset
|
|
360
|
+
subclass.set_dataset(@dataset.clone, :inherited=>true) rescue nil
|
|
280
361
|
end
|
|
281
362
|
end
|
|
282
363
|
end
|
|
@@ -290,12 +371,9 @@ module Sequel
|
|
|
290
371
|
pluralize(underscore(demodulize(name))).to_sym
|
|
291
372
|
end
|
|
292
373
|
|
|
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.
|
|
374
|
+
# Calls #call with the values hash. Only for backwards compatibility.
|
|
297
375
|
def load(values)
|
|
298
|
-
|
|
376
|
+
call(values)
|
|
299
377
|
end
|
|
300
378
|
|
|
301
379
|
# Clear the setter_methods cache when a setter method is added
|
|
@@ -312,7 +390,7 @@ module Sequel
|
|
|
312
390
|
# Artist.primary_key # => nil
|
|
313
391
|
def no_primary_key
|
|
314
392
|
clear_setter_methods_cache
|
|
315
|
-
|
|
393
|
+
self.simple_pk = @primary_key = nil
|
|
316
394
|
end
|
|
317
395
|
|
|
318
396
|
# Loads a plugin for use with the model class, passing optional arguments
|
|
@@ -320,23 +398,18 @@ module Sequel
|
|
|
320
398
|
# require the plugin from either sequel/plugins/#{plugin} or
|
|
321
399
|
# sequel_#{plugin}, and then attempt to load the module using a
|
|
322
400
|
# the camelized plugin name under Sequel::Plugins.
|
|
323
|
-
def plugin(plugin, *args, &
|
|
401
|
+
def plugin(plugin, *args, &block)
|
|
324
402
|
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
|
325
403
|
unless @plugins.include?(m)
|
|
326
404
|
@plugins << m
|
|
327
|
-
m.apply(self, *args, &
|
|
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
|
+
m.apply(self, *args, &block) if m.respond_to?(:apply)
|
|
406
|
+
include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
|
|
407
|
+
extend(m::ClassMethods)if plugin_module_defined?(m, :ClassMethods)
|
|
408
|
+
dataset_extend(m::DatasetMethods) if plugin_module_defined?(m, :DatasetMethods)
|
|
336
409
|
end
|
|
337
|
-
m.configure(self, *args, &
|
|
410
|
+
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
|
338
411
|
end
|
|
339
|
-
|
|
412
|
+
|
|
340
413
|
# Returns primary key attribute hash. If using a composite primary key
|
|
341
414
|
# value such be an array with values for each primary key in the correct
|
|
342
415
|
# order. For a standard primary key, value should be an object with a
|
|
@@ -389,8 +462,8 @@ module Sequel
|
|
|
389
462
|
# setter methods (methods that end in =) that you want to be used during
|
|
390
463
|
# mass assignment, they need to be listed here as well (without the =).
|
|
391
464
|
#
|
|
392
|
-
# It may be better to use a method such as +set_only+
|
|
393
|
-
#
|
|
465
|
+
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
|
466
|
+
# the allowed fields per call.
|
|
394
467
|
#
|
|
395
468
|
# Artist.set_allowed_columns(:name, :hometown)
|
|
396
469
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -400,10 +473,12 @@ module Sequel
|
|
|
400
473
|
@allowed_columns = cols
|
|
401
474
|
end
|
|
402
475
|
|
|
403
|
-
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol
|
|
404
|
-
#
|
|
476
|
+
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
|
477
|
+
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
|
478
|
+
# <tt>SQL::AliasedExpression</tt>
|
|
479
|
+
# (all specifying a table name in the current database), or a +Dataset+.
|
|
405
480
|
# If a dataset is used, the model's database is changed to the database of the given
|
|
406
|
-
# dataset. If a
|
|
481
|
+
# dataset. If a dataset is not used, a dataset is created from the current
|
|
407
482
|
# database with the table name given. Other arguments raise an +Error+.
|
|
408
483
|
# Returns self.
|
|
409
484
|
#
|
|
@@ -418,20 +493,24 @@ module Sequel
|
|
|
418
493
|
def set_dataset(ds, opts={})
|
|
419
494
|
inherited = opts[:inherited]
|
|
420
495
|
@dataset = case ds
|
|
421
|
-
when Symbol
|
|
422
|
-
|
|
423
|
-
db
|
|
496
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
|
497
|
+
self.simple_table = db.literal(ds)
|
|
498
|
+
db.from(ds)
|
|
424
499
|
when Dataset
|
|
425
|
-
|
|
500
|
+
self.simple_table = if ds.send(:simple_select_all?)
|
|
501
|
+
ds.literal(ds.first_source_table)
|
|
502
|
+
else
|
|
503
|
+
nil
|
|
504
|
+
end
|
|
426
505
|
@db = ds.db
|
|
427
506
|
ds
|
|
428
507
|
else
|
|
429
|
-
raise(Error, "Model.set_dataset takes
|
|
508
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
|
430
509
|
end
|
|
431
|
-
@dataset.row_proc =
|
|
510
|
+
@dataset.row_proc = self
|
|
432
511
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
|
433
512
|
if inherited
|
|
434
|
-
|
|
513
|
+
self.simple_table = superclass.simple_table
|
|
435
514
|
@columns = @dataset.columns rescue nil
|
|
436
515
|
else
|
|
437
516
|
@dataset_method_modules.each{|m| @dataset.extend(m)} if @dataset_method_modules
|
|
@@ -439,12 +518,14 @@ module Sequel
|
|
|
439
518
|
end
|
|
440
519
|
@dataset.model = self if @dataset.respond_to?(:model=)
|
|
441
520
|
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
|
521
|
+
@instance_dataset = @dataset.limit(1).naked
|
|
442
522
|
self
|
|
443
523
|
end
|
|
444
|
-
|
|
524
|
+
|
|
445
525
|
# Sets the primary key for this model. You can use either a regular
|
|
446
526
|
# or a composite primary key. To not use a primary key, set to nil
|
|
447
|
-
# or use +no_primary_key+.
|
|
527
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
|
528
|
+
# determine the primary key to use, so this method is not needed often.
|
|
448
529
|
#
|
|
449
530
|
# class Person < Sequel::Model
|
|
450
531
|
# # regular key
|
|
@@ -458,20 +539,23 @@ module Sequel
|
|
|
458
539
|
def set_primary_key(*key)
|
|
459
540
|
clear_setter_methods_cache
|
|
460
541
|
key = key.flatten
|
|
461
|
-
|
|
542
|
+
self.simple_pk = if key.length == 1
|
|
543
|
+
(@dataset || db).literal(key.first)
|
|
544
|
+
else
|
|
545
|
+
nil
|
|
546
|
+
end
|
|
462
547
|
@primary_key = (key.length == 1) ? key[0] : key
|
|
463
548
|
end
|
|
464
549
|
|
|
465
550
|
# Set the columns to restrict when using mass assignment (e.g. +set+). Using this means that
|
|
466
551
|
# attempts to call setter methods for the columns listed here will cause an
|
|
467
|
-
# exception or be silently skipped (based on the +strict_param_setting+ setting.
|
|
552
|
+
# exception or be silently skipped (based on the +strict_param_setting+ setting).
|
|
468
553
|
# If you have any virtual setter methods (methods that end in =) that you
|
|
469
554
|
# want not to be used during mass assignment, they need to be listed here as well (without the =).
|
|
470
555
|
#
|
|
471
|
-
# It
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# method uses, where everything is allowed other than what you restrict.
|
|
556
|
+
# It's generally a bad idea to rely on a blacklist approach for security. Using a whitelist
|
|
557
|
+
# approach such as set_allowed_columns or the instance level set_only or set_fields methods
|
|
558
|
+
# is usually a better choice. So use of this method is generally a bad idea.
|
|
475
559
|
#
|
|
476
560
|
# Artist.set_restricted_column(:records_sold)
|
|
477
561
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
@@ -511,6 +595,9 @@ module Sequel
|
|
|
511
595
|
# 7 days ago.
|
|
512
596
|
#
|
|
513
597
|
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
|
598
|
+
#
|
|
599
|
+
# This method creates dataset methods that do not accept arguments. To create
|
|
600
|
+
# dataset methods that accept arguments, you have to use def_dataset_method.
|
|
514
601
|
def subset(name, *args, &block)
|
|
515
602
|
def_dataset_method(name){filter(*args, &block)}
|
|
516
603
|
end
|
|
@@ -526,6 +613,7 @@ module Sequel
|
|
|
526
613
|
end
|
|
527
614
|
|
|
528
615
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
|
616
|
+
# Using this method can open up security issues, be very careful before using it.
|
|
529
617
|
#
|
|
530
618
|
# Artist.set(:id=>1) # Error
|
|
531
619
|
# Artist.unrestrict_primary_key
|
|
@@ -548,6 +636,16 @@ module Sequel
|
|
|
548
636
|
end
|
|
549
637
|
end
|
|
550
638
|
|
|
639
|
+
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
|
640
|
+
# module if the model has a dataset. Add dataset methods to the class for all
|
|
641
|
+
# public dataset methods.
|
|
642
|
+
def dataset_extend(mod)
|
|
643
|
+
dataset.extend(mod) if @dataset
|
|
644
|
+
dataset_method_modules << mod
|
|
645
|
+
meths = mod.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
|
|
646
|
+
def_dataset_method(*meths) unless meths.empty?
|
|
647
|
+
end
|
|
648
|
+
|
|
551
649
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
|
552
650
|
def def_bad_column_accessor(column)
|
|
553
651
|
overridable_methods_module.module_eval do
|
|
@@ -570,6 +668,13 @@ module Sequel
|
|
|
570
668
|
end
|
|
571
669
|
end
|
|
572
670
|
|
|
671
|
+
# Define a model method that calls the dataset method with the same name,
|
|
672
|
+
# only used for methods with names that can't be presented directly in
|
|
673
|
+
# ruby code.
|
|
674
|
+
def def_model_dataset_method_block(arg)
|
|
675
|
+
meta_def(arg){|*args, &block| dataset.send(arg, *args, &block)}
|
|
676
|
+
end
|
|
677
|
+
|
|
573
678
|
# Get the schema from the database, fall back on checking the columns
|
|
574
679
|
# via the database if that will return inaccurate results or if
|
|
575
680
|
# it raises an error.
|
|
@@ -578,10 +683,9 @@ module Sequel
|
|
|
578
683
|
return nil unless @dataset
|
|
579
684
|
schema_hash = {}
|
|
580
685
|
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
686
|
get_columns = proc{check_non_connection_error{columns} || []}
|
|
584
|
-
|
|
687
|
+
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)}
|
|
688
|
+
if schema_array
|
|
585
689
|
schema_array.each{|k,v| schema_hash[k] = v}
|
|
586
690
|
if ds_opts.include?(:select)
|
|
587
691
|
# We don't remove the columns from the schema_hash,
|
|
@@ -589,6 +693,7 @@ module Sequel
|
|
|
589
693
|
# even if they are not selected.
|
|
590
694
|
cols = get_columns.call
|
|
591
695
|
cols.each{|c| schema_hash[c] ||= {}}
|
|
696
|
+
def_column_accessor(*schema_hash.keys)
|
|
592
697
|
else
|
|
593
698
|
# Dataset is for a single table with all columns,
|
|
594
699
|
# so set the columns based on the order they were
|
|
@@ -657,15 +762,43 @@ module Sequel
|
|
|
657
762
|
Sequel::Plugins.const_get(module_name)
|
|
658
763
|
end
|
|
659
764
|
|
|
765
|
+
# Check if the plugin module +plugin+ defines the constant named by +submod+.
|
|
766
|
+
def plugin_module_defined?(plugin, submod)
|
|
767
|
+
if RUBY_VERSION >= '1.9'
|
|
768
|
+
plugin.const_defined?(submod, false)
|
|
769
|
+
else
|
|
770
|
+
plugin.const_defined?(submod)
|
|
771
|
+
end
|
|
772
|
+
end
|
|
773
|
+
|
|
660
774
|
# Find the row in the dataset that matches the primary key. Uses
|
|
661
775
|
# a static SQL optimization if the table and primary key are simple.
|
|
776
|
+
#
|
|
777
|
+
# This method should not be called with a nil primary key, in case
|
|
778
|
+
# it is overridden by plugins which assume that the passed argument
|
|
779
|
+
# is valid.
|
|
662
780
|
def primary_key_lookup(pk)
|
|
663
|
-
if
|
|
664
|
-
|
|
781
|
+
if sql = @fast_pk_lookup_sql
|
|
782
|
+
sql = sql.dup
|
|
783
|
+
ds = dataset
|
|
784
|
+
ds.literal_append(sql, pk)
|
|
785
|
+
ds.fetch_rows(sql){|r| return ds.row_proc.call(r)}
|
|
786
|
+
nil
|
|
665
787
|
else
|
|
666
788
|
dataset[primary_key_hash(pk)]
|
|
667
789
|
end
|
|
668
790
|
end
|
|
791
|
+
|
|
792
|
+
# Reset the cached fast primary lookup SQL if a simple table and primary key
|
|
793
|
+
# are used, or set it to nil if not used.
|
|
794
|
+
def reset_fast_pk_lookup_sql
|
|
795
|
+
@fast_pk_lookup_sql = if @simple_table && @simple_pk
|
|
796
|
+
"SELECT * FROM #@simple_table WHERE #@simple_pk = ".freeze
|
|
797
|
+
end
|
|
798
|
+
@fast_instance_delete_sql = if @simple_table && @simple_pk
|
|
799
|
+
"DELETE FROM #@simple_table WHERE #@simple_pk = ".freeze
|
|
800
|
+
end
|
|
801
|
+
end
|
|
669
802
|
|
|
670
803
|
# Set the columns for this model and create accessor methods for each column.
|
|
671
804
|
def set_columns(new_columns)
|
|
@@ -674,6 +807,18 @@ module Sequel
|
|
|
674
807
|
@columns
|
|
675
808
|
end
|
|
676
809
|
|
|
810
|
+
# Reset the fast primary key lookup SQL when the simple_pk value changes.
|
|
811
|
+
def simple_pk=(pk)
|
|
812
|
+
@simple_pk = pk
|
|
813
|
+
reset_fast_pk_lookup_sql
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
# Reset the fast primary key lookup SQL when the simple_table value changes.
|
|
817
|
+
def simple_table=(t)
|
|
818
|
+
@simple_table = t
|
|
819
|
+
reset_fast_pk_lookup_sql
|
|
820
|
+
end
|
|
821
|
+
|
|
677
822
|
# Add model methods that call dataset methods
|
|
678
823
|
DATASET_METHODS.each{|arg| class_eval("def #{arg}(*args, &block); dataset.#{arg}(*args, &block) end", __FILE__, __LINE__)}
|
|
679
824
|
|
|
@@ -686,24 +831,25 @@ module Sequel
|
|
|
686
831
|
|
|
687
832
|
# Sequel::Model instance methods that implement basic model functionality.
|
|
688
833
|
#
|
|
689
|
-
# * All of the methods in +HOOKS+ create instance methods that are called
|
|
834
|
+
# * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
|
|
690
835
|
# 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+.
|
|
836
|
+
# a model object, Sequel will call +around_destroy+, which will call +before_destroy+, do
|
|
837
|
+
# the destroy, and then call +after_destroy+.
|
|
693
838
|
# * The following instance_methods all call the class method of the same
|
|
694
|
-
# name: columns,
|
|
839
|
+
# name: columns, db, primary_key, db_schema.
|
|
695
840
|
# * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
|
|
696
|
-
# to set values for the attribute. It also creates
|
|
841
|
+
# to set values for the attribute. It also creates instance getters returning
|
|
697
842
|
# the value of the setting. If the value has not yet been set, it
|
|
698
843
|
# gets the default value from the class by calling the class method of the same name.
|
|
699
844
|
module InstanceMethods
|
|
700
845
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
|
846
|
+
AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
|
701
847
|
|
|
702
848
|
# Define instance method(s) that calls class method(s) of the
|
|
703
849
|
# same name, caching the result in an instance variable. Define
|
|
704
850
|
# standard attr_writer method for modifying that instance variable.
|
|
705
851
|
def self.class_attr_overridable(*meths) # :nodoc:
|
|
706
|
-
meths.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (@#{meth} = self.class.#{meth}) : @#{meth} end", __FILE__, __LINE__)}
|
|
852
|
+
meths.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (frozen? ? self.class.#{meth} : (@#{meth} = self.class.#{meth})) : @#{meth} end", __FILE__, __LINE__)}
|
|
707
853
|
attr_writer(*meths)
|
|
708
854
|
end
|
|
709
855
|
|
|
@@ -712,7 +858,7 @@ module Sequel
|
|
|
712
858
|
#
|
|
713
859
|
# define_method(meth){self.class.send(meth)}
|
|
714
860
|
def self.class_attr_reader(*meths) # :nodoc:
|
|
715
|
-
meths.each{|meth| class_eval("def #{meth};
|
|
861
|
+
meths.each{|meth| class_eval("def #{meth}; self.class.#{meth} end", __FILE__, __LINE__)}
|
|
716
862
|
end
|
|
717
863
|
|
|
718
864
|
private_class_method :class_attr_overridable, :class_attr_reader
|
|
@@ -735,7 +881,7 @@ module Sequel
|
|
|
735
881
|
#
|
|
736
882
|
# Arguments:
|
|
737
883
|
# values :: should be a hash to pass to set.
|
|
738
|
-
# from_db ::
|
|
884
|
+
# from_db :: only for backwards compatibility, forget it exists.
|
|
739
885
|
#
|
|
740
886
|
# Artist.new(:name=>'Bob')
|
|
741
887
|
#
|
|
@@ -744,13 +890,12 @@ module Sequel
|
|
|
744
890
|
# end
|
|
745
891
|
def initialize(values = {}, from_db = false)
|
|
746
892
|
if from_db
|
|
747
|
-
@new = false
|
|
748
893
|
set_values(values)
|
|
749
894
|
else
|
|
750
895
|
@values = {}
|
|
751
896
|
@new = true
|
|
752
897
|
@modified = true
|
|
753
|
-
|
|
898
|
+
initialize_set(values)
|
|
754
899
|
changed_columns.clear
|
|
755
900
|
yield self if block_given?
|
|
756
901
|
end
|
|
@@ -766,7 +911,7 @@ module Sequel
|
|
|
766
911
|
|
|
767
912
|
# Sets the value for the given column. If typecasting is enabled for
|
|
768
913
|
# this object, typecast the value based on the column's type.
|
|
769
|
-
# If this
|
|
914
|
+
# If this is a new record or the typecasted value isn't the same
|
|
770
915
|
# as the current value for the column, mark the column as changed.
|
|
771
916
|
#
|
|
772
917
|
# a = Artist.new
|
|
@@ -778,9 +923,9 @@ module Sequel
|
|
|
778
923
|
# If the column isn't in @values, we can't assume it is
|
|
779
924
|
# NULL in the database, so assume it has changed.
|
|
780
925
|
v = typecast_value(column, value)
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
926
|
+
vals = @values
|
|
927
|
+
if new? || !vals.include?(column) || v != (c = vals[column]) || v.class != c.class
|
|
928
|
+
change_column_value(column, v)
|
|
784
929
|
end
|
|
785
930
|
end
|
|
786
931
|
|
|
@@ -831,6 +976,7 @@ module Sequel
|
|
|
831
976
|
# Artist[1].delete # DELETE FROM artists WHERE (id = 1)
|
|
832
977
|
# # => #<Artist {:id=>1, ...}>
|
|
833
978
|
def delete
|
|
979
|
+
raise Sequel::Error, "can't delete frozen object" if frozen?
|
|
834
980
|
_delete
|
|
835
981
|
self
|
|
836
982
|
end
|
|
@@ -845,6 +991,7 @@ module Sequel
|
|
|
845
991
|
# Artist[1].destroy # BEGIN; DELETE FROM artists WHERE (id = 1); COMMIT;
|
|
846
992
|
# # => #<Artist {:id=>1, ...}>
|
|
847
993
|
def destroy(opts = {})
|
|
994
|
+
raise Sequel::Error, "can't destroy frozen object" if frozen?
|
|
848
995
|
checked_save_failure(opts){checked_transaction(opts){_destroy(opts)}}
|
|
849
996
|
end
|
|
850
997
|
|
|
@@ -874,14 +1021,38 @@ module Sequel
|
|
|
874
1021
|
|
|
875
1022
|
# Returns true when current instance exists, false otherwise.
|
|
876
1023
|
# Generally an object that isn't new will exist unless it has
|
|
877
|
-
# been deleted.
|
|
1024
|
+
# been deleted. Uses a database query to check for existence,
|
|
1025
|
+
# unless the model object is new, in which case this is always
|
|
1026
|
+
# false.
|
|
878
1027
|
#
|
|
879
1028
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
880
1029
|
# # => true
|
|
1030
|
+
# Artist.new.exists?
|
|
1031
|
+
# # => false
|
|
881
1032
|
def exists?
|
|
882
|
-
!this.get(1).nil?
|
|
1033
|
+
new? ? false : !this.get(1).nil?
|
|
883
1034
|
end
|
|
884
1035
|
|
|
1036
|
+
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
1037
|
+
# module may contain setter methods.
|
|
1038
|
+
def extend(mod)
|
|
1039
|
+
@singleton_setter_added = true
|
|
1040
|
+
super
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
# Freeze the object in such a way that it is still usable but not modifiable.
|
|
1044
|
+
# Once an object is frozen, you cannot modify it's values, changed_columns,
|
|
1045
|
+
# errors, or dataset.
|
|
1046
|
+
def freeze
|
|
1047
|
+
values.freeze
|
|
1048
|
+
changed_columns.freeze
|
|
1049
|
+
errors
|
|
1050
|
+
validate
|
|
1051
|
+
errors.freeze
|
|
1052
|
+
this.freeze unless new?
|
|
1053
|
+
super
|
|
1054
|
+
end
|
|
1055
|
+
|
|
885
1056
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
|
886
1057
|
# the same class and values (if pk is nil).
|
|
887
1058
|
#
|
|
@@ -890,7 +1061,14 @@ module Sequel
|
|
|
890
1061
|
# Artist.new.hash == Artist.new.hash # true
|
|
891
1062
|
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
|
892
1063
|
def hash
|
|
893
|
-
|
|
1064
|
+
case primary_key
|
|
1065
|
+
when Array
|
|
1066
|
+
[model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1067
|
+
when Symbol
|
|
1068
|
+
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1069
|
+
else
|
|
1070
|
+
[model, @values.sort_by{|k,v| k.to_s}].hash
|
|
1071
|
+
end
|
|
894
1072
|
end
|
|
895
1073
|
|
|
896
1074
|
# Returns value for the :id attribute, even if the primary key is
|
|
@@ -926,7 +1104,8 @@ module Sequel
|
|
|
926
1104
|
# a.update(...)
|
|
927
1105
|
# end
|
|
928
1106
|
def lock!
|
|
929
|
-
|
|
1107
|
+
_refresh(this.for_update) unless new?
|
|
1108
|
+
self
|
|
930
1109
|
end
|
|
931
1110
|
|
|
932
1111
|
# Remove elements of the model object that make marshalling fail. Returns self.
|
|
@@ -957,7 +1136,7 @@ module Sequel
|
|
|
957
1136
|
# a = Artist[1]
|
|
958
1137
|
# a.modified? # => false
|
|
959
1138
|
# a.set(:name=>'Jim')
|
|
960
|
-
# a.modified # => true
|
|
1139
|
+
# a.modified? # => true
|
|
961
1140
|
def modified?
|
|
962
1141
|
@modified || !changed_columns.empty?
|
|
963
1142
|
end
|
|
@@ -967,18 +1146,23 @@ module Sequel
|
|
|
967
1146
|
# Artist.new.new? # => true
|
|
968
1147
|
# Artist[1].new? # => false
|
|
969
1148
|
def new?
|
|
970
|
-
@new
|
|
1149
|
+
defined?(@new) ? @new : (@new = false)
|
|
971
1150
|
end
|
|
972
1151
|
|
|
973
1152
|
# Returns the primary key value identifying the model instance.
|
|
974
|
-
# Raises an
|
|
1153
|
+
# Raises an +Error+ if this model does not have a primary key.
|
|
975
1154
|
# If the model has a composite primary key, returns an array of values.
|
|
976
1155
|
#
|
|
977
1156
|
# Artist[1].pk # => 1
|
|
978
1157
|
# Artist[[1, 2]].pk # => [1, 2]
|
|
979
1158
|
def pk
|
|
980
1159
|
raise(Error, "No primary key is associated with this model") unless key = primary_key
|
|
981
|
-
key.is_a?(Array)
|
|
1160
|
+
if key.is_a?(Array)
|
|
1161
|
+
vals = @values
|
|
1162
|
+
key.map{|k| vals[k]}
|
|
1163
|
+
else
|
|
1164
|
+
@values[key]
|
|
1165
|
+
end
|
|
982
1166
|
end
|
|
983
1167
|
|
|
984
1168
|
# Returns a hash identifying mapping the receivers primary key column(s) to their values.
|
|
@@ -998,7 +1182,9 @@ module Sequel
|
|
|
998
1182
|
# a.refresh
|
|
999
1183
|
# a.name # => 'Bob'
|
|
1000
1184
|
def refresh
|
|
1185
|
+
raise Sequel::Error, "can't refresh frozen object" if frozen?
|
|
1001
1186
|
_refresh(this)
|
|
1187
|
+
self
|
|
1002
1188
|
end
|
|
1003
1189
|
|
|
1004
1190
|
# Alias of refresh, but not aliased directly to make overriding in a plugin easier.
|
|
@@ -1016,26 +1202,32 @@ module Sequel
|
|
|
1016
1202
|
#
|
|
1017
1203
|
# If +save+ fails and either raise_on_save_failure or the
|
|
1018
1204
|
# :raise_on_failure option is true, it raises ValidationFailed
|
|
1019
|
-
# or
|
|
1205
|
+
# or HookFailed. Otherwise it returns nil.
|
|
1020
1206
|
#
|
|
1021
1207
|
# If it succeeds, it returns self.
|
|
1022
1208
|
#
|
|
1023
1209
|
# You can provide an optional list of columns to update, in which
|
|
1024
|
-
# case it only updates those columns.
|
|
1210
|
+
# case it only updates those columns, or a options hash.
|
|
1025
1211
|
#
|
|
1026
1212
|
# Takes the following options:
|
|
1027
1213
|
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
1030
|
-
#
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1214
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1215
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1216
|
+
# +raise_on_save_failure+ setting
|
|
1217
|
+
# :server :: set the server/shard on the object before saving, and use that
|
|
1218
|
+
# server/shard in any transaction.
|
|
1219
|
+
# :transaction :: set to true or false to override the current
|
|
1220
|
+
# +use_transactions+ setting
|
|
1221
|
+
# :validate :: set to false to skip validation
|
|
1034
1222
|
def save(*columns)
|
|
1223
|
+
raise Sequel::Error, "can't save frozen object" if frozen?
|
|
1035
1224
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1036
|
-
if opts[:
|
|
1037
|
-
|
|
1038
|
-
|
|
1225
|
+
set_server(opts[:server]) if opts[:server]
|
|
1226
|
+
if opts[:validate] != false
|
|
1227
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1228
|
+
raise(ValidationFailed.new(errors)) if raise_on_failure?(opts)
|
|
1229
|
+
return
|
|
1230
|
+
end
|
|
1039
1231
|
end
|
|
1040
1232
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
|
1041
1233
|
end
|
|
@@ -1075,7 +1267,8 @@ module Sequel
|
|
|
1075
1267
|
end
|
|
1076
1268
|
|
|
1077
1269
|
# Set all values using the entries in the hash, except for the keys
|
|
1078
|
-
# given in except.
|
|
1270
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
|
1271
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1079
1272
|
#
|
|
1080
1273
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
|
1081
1274
|
# artist.name # => 'Jim'
|
|
@@ -1086,28 +1279,85 @@ module Sequel
|
|
|
1086
1279
|
# For each of the fields in the given array +fields+, call the setter
|
|
1087
1280
|
# method with the value of that +hash+ entry for the field. Returns self.
|
|
1088
1281
|
#
|
|
1089
|
-
#
|
|
1282
|
+
# You can provide an options hash, with the following options currently respected:
|
|
1283
|
+
# :missing :: Can be set to :skip to skip missing entries or :raise to raise an
|
|
1284
|
+
# Error for missing entries. The default behavior is not to check for
|
|
1285
|
+
# missing entries, in which case the default value is used. To be
|
|
1286
|
+
# friendly with most web frameworks, the missing check will also check
|
|
1287
|
+
# for the string version of the argument in the hash if given a symbol.
|
|
1288
|
+
#
|
|
1289
|
+
# Examples:
|
|
1290
|
+
#
|
|
1291
|
+
# artist.set_fields({:name=>'Jim'}, [:name])
|
|
1090
1292
|
# artist.name # => 'Jim'
|
|
1091
1293
|
#
|
|
1092
|
-
# artist.set_fields({:hometown=>'LA'}, :name)
|
|
1294
|
+
# artist.set_fields({:hometown=>'LA'}, [:name])
|
|
1093
1295
|
# artist.name # => nil
|
|
1094
1296
|
# artist.hometown # => 'Sac'
|
|
1095
|
-
|
|
1096
|
-
|
|
1297
|
+
#
|
|
1298
|
+
# artist.name # => 'Jim'
|
|
1299
|
+
# artist.set_fields({}, [:name], :missing=>:skip)
|
|
1300
|
+
# artist.name # => 'Jim'
|
|
1301
|
+
#
|
|
1302
|
+
# artist.name # => 'Jim'
|
|
1303
|
+
# artist.set_fields({}, [:name], :missing=>:raise)
|
|
1304
|
+
# # Sequel::Error raised
|
|
1305
|
+
def set_fields(hash, fields, opts=nil)
|
|
1306
|
+
if opts
|
|
1307
|
+
case opts[:missing]
|
|
1308
|
+
when :skip
|
|
1309
|
+
fields.each do |f|
|
|
1310
|
+
if hash.has_key?(f)
|
|
1311
|
+
send("#{f}=", hash[f])
|
|
1312
|
+
elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
|
|
1313
|
+
send("#{sf}=", hash[sf])
|
|
1314
|
+
end
|
|
1315
|
+
end
|
|
1316
|
+
when :raise
|
|
1317
|
+
fields.each do |f|
|
|
1318
|
+
if hash.has_key?(f)
|
|
1319
|
+
send("#{f}=", hash[f])
|
|
1320
|
+
elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
|
|
1321
|
+
send("#{sf}=", hash[sf])
|
|
1322
|
+
else
|
|
1323
|
+
raise(Sequel::Error, "missing field in hash: #{f.inspect} not in #{hash.inspect}")
|
|
1324
|
+
end
|
|
1325
|
+
end
|
|
1326
|
+
else
|
|
1327
|
+
fields.each{|f| send("#{f}=", hash[f])}
|
|
1328
|
+
end
|
|
1329
|
+
else
|
|
1330
|
+
fields.each{|f| send("#{f}=", hash[f])}
|
|
1331
|
+
end
|
|
1097
1332
|
self
|
|
1098
1333
|
end
|
|
1099
1334
|
|
|
1100
1335
|
# Set the values using the entries in the hash, only if the key
|
|
1101
|
-
# is included in only.
|
|
1336
|
+
# is included in only. It may be a better idea to use +set_fields+
|
|
1337
|
+
# instead of this method.
|
|
1102
1338
|
#
|
|
1103
1339
|
# artist.set_only({:name=>'Jim'}, :name)
|
|
1104
1340
|
# artist.name # => 'Jim'
|
|
1105
1341
|
#
|
|
1106
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
|
1342
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
|
1107
1343
|
def set_only(hash, *only)
|
|
1108
1344
|
set_restricted(hash, only.flatten, false)
|
|
1109
1345
|
end
|
|
1110
1346
|
|
|
1347
|
+
# Set the shard that this object is tied to. Returns self.
|
|
1348
|
+
def set_server(s)
|
|
1349
|
+
@server = s
|
|
1350
|
+
@this.opts[:server] = s if @this
|
|
1351
|
+
self
|
|
1352
|
+
end
|
|
1353
|
+
|
|
1354
|
+
# Replace the current values with hash. Should definitely not be
|
|
1355
|
+
# used with untrusted input, and should probably not be called
|
|
1356
|
+
# directly by user code.
|
|
1357
|
+
def set_values(hash)
|
|
1358
|
+
@values = hash
|
|
1359
|
+
end
|
|
1360
|
+
|
|
1111
1361
|
# Clear the setter_methods cache when a method is added
|
|
1112
1362
|
def singleton_method_added(meth)
|
|
1113
1363
|
@singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
|
|
@@ -1119,10 +1369,10 @@ module Sequel
|
|
|
1119
1369
|
# Artist[1].this
|
|
1120
1370
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1121
1371
|
def this
|
|
1122
|
-
@this ||= model.
|
|
1372
|
+
@this ||= use_server(model.instance_dataset.filter(pk_hash))
|
|
1123
1373
|
end
|
|
1124
1374
|
|
|
1125
|
-
# Runs set with the passed hash and then runs save_changes.
|
|
1375
|
+
# Runs #set with the passed hash and then runs save_changes.
|
|
1126
1376
|
#
|
|
1127
1377
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1128
1378
|
def update(hash)
|
|
@@ -1130,7 +1380,7 @@ module Sequel
|
|
|
1130
1380
|
end
|
|
1131
1381
|
|
|
1132
1382
|
# Update all values using the entries in the hash, ignoring any setting of
|
|
1133
|
-
# allowed_columns or
|
|
1383
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
|
1134
1384
|
#
|
|
1135
1385
|
# Artist.set_restricted_columns(:name)
|
|
1136
1386
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1139,28 +1389,30 @@ module Sequel
|
|
|
1139
1389
|
end
|
|
1140
1390
|
|
|
1141
1391
|
# Update all values using the entries in the hash, except for the keys
|
|
1142
|
-
# given in except.
|
|
1392
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
|
1393
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1143
1394
|
#
|
|
1144
1395
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1145
1396
|
def update_except(hash, *except)
|
|
1146
1397
|
update_restricted(hash, false, except.flatten)
|
|
1147
1398
|
end
|
|
1148
1399
|
|
|
1149
|
-
# Update the instances values by calling +set_fields+ with the
|
|
1150
|
-
#
|
|
1400
|
+
# Update the instances values by calling +set_fields+ with the arguments, then
|
|
1401
|
+
# saves any changes to the record. Returns self.
|
|
1151
1402
|
#
|
|
1152
|
-
# artist.update_fields({:name=>'Jim'}, :name)
|
|
1403
|
+
# artist.update_fields({:name=>'Jim'}, [:name])
|
|
1153
1404
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1154
1405
|
#
|
|
1155
|
-
# artist.update_fields({:hometown=>'LA'}, :name)
|
|
1406
|
+
# artist.update_fields({:hometown=>'LA'}, [:name])
|
|
1156
1407
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
|
1157
|
-
def update_fields(hash, fields)
|
|
1158
|
-
set_fields(hash, fields)
|
|
1408
|
+
def update_fields(hash, fields, opts=nil)
|
|
1409
|
+
set_fields(hash, fields, opts)
|
|
1159
1410
|
save_changes
|
|
1160
1411
|
end
|
|
1161
1412
|
|
|
1162
1413
|
# Update the values using the entries in the hash, only if the key
|
|
1163
|
-
# is included in only.
|
|
1414
|
+
# is included in only. It may be a better idea to use +update_fields+
|
|
1415
|
+
# instead of this method.
|
|
1164
1416
|
#
|
|
1165
1417
|
# artist.update_only({:name=>'Jim'}, :name)
|
|
1166
1418
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1185,21 +1437,15 @@ module Sequel
|
|
|
1185
1437
|
# artist(:name=>'Invalid').valid? # => false
|
|
1186
1438
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
|
1187
1439
|
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?
|
|
1440
|
+
_valid?(false, opts)
|
|
1196
1441
|
end
|
|
1197
1442
|
|
|
1198
1443
|
private
|
|
1199
1444
|
|
|
1200
|
-
#
|
|
1445
|
+
# Do the deletion of the object's dataset, and check that the row
|
|
1446
|
+
# was actually deleted.
|
|
1201
1447
|
def _delete
|
|
1202
|
-
n =
|
|
1448
|
+
n = _delete_without_checking
|
|
1203
1449
|
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
1450
|
n
|
|
1205
1451
|
end
|
|
@@ -1210,12 +1456,33 @@ module Sequel
|
|
|
1210
1456
|
this
|
|
1211
1457
|
end
|
|
1212
1458
|
|
|
1459
|
+
# Actually do the deletion of the object's dataset. Return the
|
|
1460
|
+
# number of rows modified.
|
|
1461
|
+
def _delete_without_checking
|
|
1462
|
+
if sql = (m = model).fast_instance_delete_sql
|
|
1463
|
+
sql = sql.dup
|
|
1464
|
+
(ds = m.dataset).literal_append(sql, pk)
|
|
1465
|
+
ds.with_sql_delete(sql)
|
|
1466
|
+
else
|
|
1467
|
+
_delete_dataset.delete
|
|
1468
|
+
end
|
|
1469
|
+
end
|
|
1470
|
+
|
|
1213
1471
|
# Internal destroy method, separted from destroy to
|
|
1214
1472
|
# allow running inside a transaction
|
|
1215
1473
|
def _destroy(opts)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1474
|
+
sh = {:server=>this_server}
|
|
1475
|
+
db.after_rollback(sh){after_destroy_rollback} if uacr = use_after_commit_rollback
|
|
1476
|
+
called = false
|
|
1477
|
+
around_destroy do
|
|
1478
|
+
called = true
|
|
1479
|
+
raise_hook_failure(:destroy) if before_destroy == false
|
|
1480
|
+
_destroy_delete
|
|
1481
|
+
after_destroy
|
|
1482
|
+
true
|
|
1483
|
+
end
|
|
1484
|
+
raise_hook_failure(:destroy) unless called
|
|
1485
|
+
db.after_commit(sh){after_destroy_commit} if uacr
|
|
1219
1486
|
self
|
|
1220
1487
|
end
|
|
1221
1488
|
|
|
@@ -1230,68 +1497,107 @@ module Sequel
|
|
|
1230
1497
|
# the record should be refreshed from the database.
|
|
1231
1498
|
def _insert
|
|
1232
1499
|
ds = _insert_dataset
|
|
1233
|
-
if ds.
|
|
1234
|
-
|
|
1500
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
|
1501
|
+
set_values(h)
|
|
1235
1502
|
nil
|
|
1236
1503
|
else
|
|
1237
|
-
iid = ds
|
|
1504
|
+
iid = _insert_raw(ds)
|
|
1238
1505
|
# if we have a regular primary key and it's not set in @values,
|
|
1239
1506
|
# we assume it's the last inserted id
|
|
1240
|
-
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) &&
|
|
1241
|
-
|
|
1507
|
+
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !(vals = @values)[pk]
|
|
1508
|
+
vals[pk] = iid
|
|
1242
1509
|
end
|
|
1243
1510
|
pk
|
|
1244
1511
|
end
|
|
1245
1512
|
end
|
|
1246
|
-
|
|
1513
|
+
|
|
1247
1514
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1248
1515
|
# dataset by default.
|
|
1249
1516
|
def _insert_dataset
|
|
1250
|
-
model.
|
|
1517
|
+
use_server(model.instance_dataset)
|
|
1251
1518
|
end
|
|
1252
1519
|
|
|
1520
|
+
# Insert into the given dataset and return the primary key created (if any).
|
|
1521
|
+
def _insert_raw(ds)
|
|
1522
|
+
ds.insert(@values)
|
|
1523
|
+
end
|
|
1524
|
+
|
|
1525
|
+
# Insert into the given dataset and return the hash of column values.
|
|
1526
|
+
def _insert_select_raw(ds)
|
|
1527
|
+
ds.insert_select(@values)
|
|
1528
|
+
end
|
|
1529
|
+
|
|
1253
1530
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1254
1531
|
# is used for reading newly inserted values from the database
|
|
1255
1532
|
def _refresh(dataset)
|
|
1256
|
-
set_values(dataset
|
|
1533
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
|
1257
1534
|
changed_columns.clear
|
|
1258
|
-
|
|
1535
|
+
end
|
|
1536
|
+
|
|
1537
|
+
# Get the row of column data from the database.
|
|
1538
|
+
def _refresh_get(dataset)
|
|
1539
|
+
dataset.first
|
|
1259
1540
|
end
|
|
1260
1541
|
|
|
1261
1542
|
# Internal version of save, split from save to allow running inside
|
|
1262
1543
|
# it's own transaction.
|
|
1263
1544
|
def _save(columns, opts)
|
|
1264
|
-
|
|
1265
|
-
if
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1545
|
+
sh = {:server=>this_server}
|
|
1546
|
+
db.after_rollback(sh){after_rollback} if uacr = use_after_commit_rollback
|
|
1547
|
+
was_new = false
|
|
1548
|
+
pk = nil
|
|
1549
|
+
called_save = false
|
|
1550
|
+
called_cu = false
|
|
1551
|
+
around_save do
|
|
1552
|
+
called_save = true
|
|
1553
|
+
raise_hook_failure(:save) if before_save == false
|
|
1554
|
+
if new?
|
|
1555
|
+
was_new = true
|
|
1556
|
+
around_create do
|
|
1557
|
+
called_cu = true
|
|
1558
|
+
raise_hook_failure(:create) if before_create == false
|
|
1559
|
+
pk = _insert
|
|
1560
|
+
@this = nil
|
|
1561
|
+
@new = false
|
|
1562
|
+
@was_new = true
|
|
1563
|
+
after_create
|
|
1564
|
+
true
|
|
1565
|
+
end
|
|
1566
|
+
raise_hook_failure(:create) unless called_cu
|
|
1567
|
+
else
|
|
1568
|
+
around_update do
|
|
1569
|
+
called_cu = true
|
|
1570
|
+
raise_hook_failure(:update) if before_update == false
|
|
1571
|
+
if columns.empty?
|
|
1572
|
+
@columns_updated = if opts[:changed]
|
|
1573
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1574
|
+
else
|
|
1575
|
+
_save_update_all_columns_hash
|
|
1576
|
+
end
|
|
1577
|
+
changed_columns.clear
|
|
1578
|
+
else # update only the specified columns
|
|
1579
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1580
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
|
1581
|
+
end
|
|
1582
|
+
_update_columns(@columns_updated)
|
|
1583
|
+
@this = nil
|
|
1584
|
+
after_update
|
|
1585
|
+
true
|
|
1586
|
+
end
|
|
1587
|
+
raise_hook_failure(:update) unless called_cu
|
|
1588
|
+
end
|
|
1272
1589
|
after_save
|
|
1590
|
+
true
|
|
1591
|
+
end
|
|
1592
|
+
raise_hook_failure(:save) unless called_save
|
|
1593
|
+
if was_new
|
|
1273
1594
|
@was_new = nil
|
|
1274
1595
|
pk ? _save_refresh : changed_columns.clear
|
|
1275
1596
|
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
1597
|
@columns_updated = nil
|
|
1293
1598
|
end
|
|
1294
1599
|
@modified = false
|
|
1600
|
+
db.after_commit(sh){after_commit} if uacr
|
|
1295
1601
|
self
|
|
1296
1602
|
end
|
|
1297
1603
|
|
|
@@ -1321,9 +1627,10 @@ module Sequel
|
|
|
1321
1627
|
_update(columns) unless columns.empty?
|
|
1322
1628
|
end
|
|
1323
1629
|
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
|
1630
|
+
# Update this instance's dataset with the supplied column hash,
|
|
1631
|
+
# checking that only a single row was modified.
|
|
1325
1632
|
def _update(columns)
|
|
1326
|
-
n =
|
|
1633
|
+
n = _update_without_checking(columns)
|
|
1327
1634
|
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
1635
|
n
|
|
1329
1636
|
end
|
|
@@ -1334,7 +1641,47 @@ module Sequel
|
|
|
1334
1641
|
this
|
|
1335
1642
|
end
|
|
1336
1643
|
|
|
1337
|
-
#
|
|
1644
|
+
# Update this instances dataset with the supplied column hash.
|
|
1645
|
+
def _update_without_checking(columns)
|
|
1646
|
+
_update_dataset.update(columns)
|
|
1647
|
+
end
|
|
1648
|
+
|
|
1649
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
|
1650
|
+
# failures will be raised as HookFailure exceptions. If it is
|
|
1651
|
+
# +false+, +false+ will be returned instead.
|
|
1652
|
+
def _valid?(raise_errors, opts)
|
|
1653
|
+
return errors.empty? if frozen?
|
|
1654
|
+
errors.clear
|
|
1655
|
+
called = false
|
|
1656
|
+
error = false
|
|
1657
|
+
around_validation do
|
|
1658
|
+
called = true
|
|
1659
|
+
if before_validation == false
|
|
1660
|
+
if raise_errors
|
|
1661
|
+
raise_hook_failure(:validation)
|
|
1662
|
+
else
|
|
1663
|
+
error = true
|
|
1664
|
+
end
|
|
1665
|
+
false
|
|
1666
|
+
else
|
|
1667
|
+
validate
|
|
1668
|
+
after_validation
|
|
1669
|
+
errors.empty?
|
|
1670
|
+
end
|
|
1671
|
+
end
|
|
1672
|
+
error = true unless called
|
|
1673
|
+
if error
|
|
1674
|
+
if raise_errors
|
|
1675
|
+
raise_hook_failure(:validation)
|
|
1676
|
+
else
|
|
1677
|
+
false
|
|
1678
|
+
end
|
|
1679
|
+
else
|
|
1680
|
+
errors.empty?
|
|
1681
|
+
end
|
|
1682
|
+
end
|
|
1683
|
+
|
|
1684
|
+
# If not raising on failure, check for HookFailed
|
|
1338
1685
|
# being raised by yielding and swallow it.
|
|
1339
1686
|
def checked_save_failure(opts)
|
|
1340
1687
|
if raise_on_failure?(opts)
|
|
@@ -1342,7 +1689,7 @@ module Sequel
|
|
|
1342
1689
|
else
|
|
1343
1690
|
begin
|
|
1344
1691
|
yield
|
|
1345
|
-
rescue
|
|
1692
|
+
rescue HookFailed
|
|
1346
1693
|
nil
|
|
1347
1694
|
end
|
|
1348
1695
|
end
|
|
@@ -1350,7 +1697,21 @@ module Sequel
|
|
|
1350
1697
|
|
|
1351
1698
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1352
1699
|
def checked_transaction(opts={})
|
|
1353
|
-
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1700
|
+
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1701
|
+
end
|
|
1702
|
+
|
|
1703
|
+
# Change the value of the column to given value, recording the change.
|
|
1704
|
+
def change_column_value(column, value)
|
|
1705
|
+
cc = changed_columns
|
|
1706
|
+
cc << column unless cc.include?(column)
|
|
1707
|
+
@values[column] = value
|
|
1708
|
+
end
|
|
1709
|
+
|
|
1710
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1711
|
+
# exists so it can be overridden. This is called only for new records, before
|
|
1712
|
+
# changed_columns is cleared.
|
|
1713
|
+
def initialize_set(h)
|
|
1714
|
+
set(h) unless h.empty?
|
|
1354
1715
|
end
|
|
1355
1716
|
|
|
1356
1717
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
|
@@ -1369,11 +1730,12 @@ module Sequel
|
|
|
1369
1730
|
# Raise an error appropriate to the hook type. May be swallowed by
|
|
1370
1731
|
# checked_save_failure depending on the raise_on_failure? setting.
|
|
1371
1732
|
def raise_hook_failure(type)
|
|
1372
|
-
raise
|
|
1733
|
+
raise HookFailed, "one of the before_#{type} hooks returned false"
|
|
1373
1734
|
end
|
|
1374
1735
|
|
|
1375
1736
|
# Set the columns, filtered by the only and except arrays.
|
|
1376
1737
|
def set_restricted(hash, only, except)
|
|
1738
|
+
return self if hash.empty?
|
|
1377
1739
|
meths = if only.nil? && except.nil? && !@singleton_setter_added
|
|
1378
1740
|
model.setter_methods
|
|
1379
1741
|
else
|
|
@@ -1385,17 +1747,21 @@ module Sequel
|
|
|
1385
1747
|
if meths.include?(m)
|
|
1386
1748
|
send(m, v)
|
|
1387
1749
|
elsif strict
|
|
1388
|
-
|
|
1750
|
+
# Avoid using respond_to? or creating symbols from user input
|
|
1751
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
|
1752
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
|
1753
|
+
raise Error, "#{k} is a restricted primary key"
|
|
1754
|
+
else
|
|
1755
|
+
raise Error, "#{k} is a restricted column"
|
|
1756
|
+
end
|
|
1757
|
+
else
|
|
1758
|
+
raise Error, "method #{m} doesn't exist"
|
|
1759
|
+
end
|
|
1389
1760
|
end
|
|
1390
1761
|
end
|
|
1391
1762
|
self
|
|
1392
1763
|
end
|
|
1393
1764
|
|
|
1394
|
-
# Replace the current values with hash.
|
|
1395
|
-
def set_values(hash)
|
|
1396
|
-
@values = hash
|
|
1397
|
-
end
|
|
1398
|
-
|
|
1399
1765
|
# Returns all methods that can be used for attribute
|
|
1400
1766
|
# assignment (those that end with =), modified by the only
|
|
1401
1767
|
# and except arguments:
|
|
@@ -1424,13 +1790,25 @@ module Sequel
|
|
|
1424
1790
|
meths
|
|
1425
1791
|
end
|
|
1426
1792
|
end
|
|
1793
|
+
|
|
1794
|
+
# The server/shard that the model object's dataset uses, or :default if the
|
|
1795
|
+
# model object's dataset does not have an associated shard.
|
|
1796
|
+
def this_server
|
|
1797
|
+
if (s = @server)
|
|
1798
|
+
s
|
|
1799
|
+
elsif (t = @this)
|
|
1800
|
+
t.opts[:server] || :default
|
|
1801
|
+
else
|
|
1802
|
+
model.dataset.opts[:server] || :default
|
|
1803
|
+
end
|
|
1804
|
+
end
|
|
1427
1805
|
|
|
1428
1806
|
# Typecast the value to the column's type if typecasting. Calls the database's
|
|
1429
1807
|
# typecast_value method, so database adapters can override/augment the handling
|
|
1430
1808
|
# for database specific column types.
|
|
1431
1809
|
def typecast_value(column, value)
|
|
1432
1810
|
return value unless typecast_on_assignment && db_schema && (col_schema = db_schema[column])
|
|
1433
|
-
value = nil if
|
|
1811
|
+
value = nil if '' == value and typecast_empty_string_to_nil and col_schema[:type] and ![:string, :blob].include?(col_schema[:type])
|
|
1434
1812
|
raise(InvalidValue, "nil/NULL is not allowed for the #{column} column") if raise_on_typecast_failure && value.nil? && (col_schema[:allow_null] == false)
|
|
1435
1813
|
begin
|
|
1436
1814
|
model.db.typecast_value(col_schema[:type], value)
|
|
@@ -1444,6 +1822,11 @@ module Sequel
|
|
|
1444
1822
|
set_restricted(hash, only, except)
|
|
1445
1823
|
save_changes
|
|
1446
1824
|
end
|
|
1825
|
+
|
|
1826
|
+
# Set the given dataset to use the current object's shard.
|
|
1827
|
+
def use_server(ds)
|
|
1828
|
+
@server ? ds.server(@server) : ds
|
|
1829
|
+
end
|
|
1447
1830
|
|
|
1448
1831
|
# Whether to use a transaction for this action. If the :transaction
|
|
1449
1832
|
# option is present in the hash, use that, otherwise, fallback to the
|
|
@@ -1461,6 +1844,18 @@ module Sequel
|
|
|
1461
1844
|
# Artist.dataset.model # => Artist
|
|
1462
1845
|
attr_accessor :model
|
|
1463
1846
|
|
|
1847
|
+
# Assume if a single integer is given that it is a lookup by primary
|
|
1848
|
+
# key, and call with_pk with the argument.
|
|
1849
|
+
#
|
|
1850
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1851
|
+
def [](*args)
|
|
1852
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
|
1853
|
+
with_pk(i)
|
|
1854
|
+
else
|
|
1855
|
+
super
|
|
1856
|
+
end
|
|
1857
|
+
end
|
|
1858
|
+
|
|
1464
1859
|
# Destroy each row in the dataset by instantiating it and then calling
|
|
1465
1860
|
# destroy on the resulting model object. This isn't as fast as deleting
|
|
1466
1861
|
# the dataset, which does a single SQL call, but this runs any destroy
|
|
@@ -1472,7 +1867,7 @@ module Sequel
|
|
|
1472
1867
|
# # ...
|
|
1473
1868
|
def destroy
|
|
1474
1869
|
pr = proc{all{|r| r.destroy}.length}
|
|
1475
|
-
model.use_transactions ? @db.transaction(&pr) : pr.call
|
|
1870
|
+
model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
|
|
1476
1871
|
end
|
|
1477
1872
|
|
|
1478
1873
|
# This allows you to call +to_hash+ without any arguments, which will
|
|
@@ -1491,6 +1886,19 @@ module Sequel
|
|
|
1491
1886
|
super(pk, value_column)
|
|
1492
1887
|
end
|
|
1493
1888
|
end
|
|
1889
|
+
|
|
1890
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
|
1891
|
+
# value.
|
|
1892
|
+
#
|
|
1893
|
+
# # Single primary key
|
|
1894
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1895
|
+
#
|
|
1896
|
+
# # Composite primary key
|
|
1897
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
|
1898
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
|
1899
|
+
def with_pk(pk)
|
|
1900
|
+
first(model.qualified_primary_key_hash(pk))
|
|
1901
|
+
end
|
|
1494
1902
|
end
|
|
1495
1903
|
|
|
1496
1904
|
extend ClassMethods
|