sequel 3.21.0 → 3.41.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 +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +576 -0
- data/doc/opening_databases.rdoc +121 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- 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/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- data/lib/sequel/adapters/ado/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
- 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 +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -0
- data/lib/sequel/adapters/utils/split_alter_table.rb +36 -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 +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- 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 +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/connection_validator.rb +109 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +539 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +334 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- 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 +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- 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 +137 -16
- 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/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -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 +134 -15
- 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 +77 -6
- data/lib/sequel/plugins/list.rb +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/prepared_statements.rb +168 -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 +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- 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 +57 -12
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/touch.rb +18 -6
- data/lib/sequel/plugins/tree.rb +3 -3
- 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 +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- data/lib/sequel/timezones.rb +67 -40
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- 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 +227 -33
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- 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/extensions/connection_validator_spec.rb +118 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- 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 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- 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 +268 -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 +112 -0
- data/spec/extensions/pg_array_spec.rb +324 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +200 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +340 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -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 +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- 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 +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- 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 +86 -48
- 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 +65 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -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 +8 -11
- data/spec/extensions/touch_spec.rb +53 -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 +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -37
- 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 +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +223 -81
- data/spec/core/core_sql_spec.rb +0 -451
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,22 +539,25 @@ 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
|
-
# Artist.
|
|
560
|
+
# Artist.set_restricted_columns(:records_sold)
|
|
477
561
|
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
|
478
562
|
# Artist.set(:name=>'Bob', :records_sold=>30000) # Error
|
|
479
563
|
def set_restricted_columns(*cols)
|
|
@@ -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
|
|
@@ -726,6 +872,7 @@ module Sequel
|
|
|
726
872
|
# Artist.new(:name=>'Bob').values # => {:name=>'Bob'}
|
|
727
873
|
# Artist[1].values # => {:id=>1, :name=>'Jim', ...}
|
|
728
874
|
attr_reader :values
|
|
875
|
+
alias to_hash values
|
|
729
876
|
|
|
730
877
|
# Creates new instance and passes the given values to set.
|
|
731
878
|
# If a block is given, yield the instance to the block unless
|
|
@@ -735,7 +882,7 @@ module Sequel
|
|
|
735
882
|
#
|
|
736
883
|
# Arguments:
|
|
737
884
|
# values :: should be a hash to pass to set.
|
|
738
|
-
# from_db ::
|
|
885
|
+
# from_db :: only for backwards compatibility, forget it exists.
|
|
739
886
|
#
|
|
740
887
|
# Artist.new(:name=>'Bob')
|
|
741
888
|
#
|
|
@@ -744,13 +891,12 @@ module Sequel
|
|
|
744
891
|
# end
|
|
745
892
|
def initialize(values = {}, from_db = false)
|
|
746
893
|
if from_db
|
|
747
|
-
@new = false
|
|
748
894
|
set_values(values)
|
|
749
895
|
else
|
|
750
896
|
@values = {}
|
|
751
897
|
@new = true
|
|
752
898
|
@modified = true
|
|
753
|
-
|
|
899
|
+
initialize_set(values)
|
|
754
900
|
changed_columns.clear
|
|
755
901
|
yield self if block_given?
|
|
756
902
|
end
|
|
@@ -766,7 +912,7 @@ module Sequel
|
|
|
766
912
|
|
|
767
913
|
# Sets the value for the given column. If typecasting is enabled for
|
|
768
914
|
# this object, typecast the value based on the column's type.
|
|
769
|
-
# If this
|
|
915
|
+
# If this is a new record or the typecasted value isn't the same
|
|
770
916
|
# as the current value for the column, mark the column as changed.
|
|
771
917
|
#
|
|
772
918
|
# a = Artist.new
|
|
@@ -778,9 +924,9 @@ module Sequel
|
|
|
778
924
|
# If the column isn't in @values, we can't assume it is
|
|
779
925
|
# NULL in the database, so assume it has changed.
|
|
780
926
|
v = typecast_value(column, value)
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
927
|
+
vals = @values
|
|
928
|
+
if new? || !vals.include?(column) || v != (c = vals[column]) || v.class != c.class
|
|
929
|
+
change_column_value(column, v)
|
|
784
930
|
end
|
|
785
931
|
end
|
|
786
932
|
|
|
@@ -831,6 +977,7 @@ module Sequel
|
|
|
831
977
|
# Artist[1].delete # DELETE FROM artists WHERE (id = 1)
|
|
832
978
|
# # => #<Artist {:id=>1, ...}>
|
|
833
979
|
def delete
|
|
980
|
+
raise Sequel::Error, "can't delete frozen object" if frozen?
|
|
834
981
|
_delete
|
|
835
982
|
self
|
|
836
983
|
end
|
|
@@ -845,6 +992,7 @@ module Sequel
|
|
|
845
992
|
# Artist[1].destroy # BEGIN; DELETE FROM artists WHERE (id = 1); COMMIT;
|
|
846
993
|
# # => #<Artist {:id=>1, ...}>
|
|
847
994
|
def destroy(opts = {})
|
|
995
|
+
raise Sequel::Error, "can't destroy frozen object" if frozen?
|
|
848
996
|
checked_save_failure(opts){checked_transaction(opts){_destroy(opts)}}
|
|
849
997
|
end
|
|
850
998
|
|
|
@@ -874,14 +1022,38 @@ module Sequel
|
|
|
874
1022
|
|
|
875
1023
|
# Returns true when current instance exists, false otherwise.
|
|
876
1024
|
# Generally an object that isn't new will exist unless it has
|
|
877
|
-
# been deleted.
|
|
1025
|
+
# been deleted. Uses a database query to check for existence,
|
|
1026
|
+
# unless the model object is new, in which case this is always
|
|
1027
|
+
# false.
|
|
878
1028
|
#
|
|
879
1029
|
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
|
880
1030
|
# # => true
|
|
1031
|
+
# Artist.new.exists?
|
|
1032
|
+
# # => false
|
|
881
1033
|
def exists?
|
|
882
|
-
!this.get(1).nil?
|
|
1034
|
+
new? ? false : !this.get(1).nil?
|
|
883
1035
|
end
|
|
884
1036
|
|
|
1037
|
+
# Ignore the model's setter method cache when this instances extends a module, as the
|
|
1038
|
+
# module may contain setter methods.
|
|
1039
|
+
def extend(mod)
|
|
1040
|
+
@singleton_setter_added = true
|
|
1041
|
+
super
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
# Freeze the object in such a way that it is still usable but not modifiable.
|
|
1045
|
+
# Once an object is frozen, you cannot modify it's values, changed_columns,
|
|
1046
|
+
# errors, or dataset.
|
|
1047
|
+
def freeze
|
|
1048
|
+
values.freeze
|
|
1049
|
+
changed_columns.freeze
|
|
1050
|
+
errors
|
|
1051
|
+
validate
|
|
1052
|
+
errors.freeze
|
|
1053
|
+
this.freeze unless new?
|
|
1054
|
+
super
|
|
1055
|
+
end
|
|
1056
|
+
|
|
885
1057
|
# Value that should be unique for objects with the same class and pk (if pk is not nil), or
|
|
886
1058
|
# the same class and values (if pk is nil).
|
|
887
1059
|
#
|
|
@@ -890,7 +1062,14 @@ module Sequel
|
|
|
890
1062
|
# Artist.new.hash == Artist.new.hash # true
|
|
891
1063
|
# Artist.new(:name=>'Bob').hash == Artist.new.hash # false
|
|
892
1064
|
def hash
|
|
893
|
-
|
|
1065
|
+
case primary_key
|
|
1066
|
+
when Array
|
|
1067
|
+
[model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1068
|
+
when Symbol
|
|
1069
|
+
[model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
|
|
1070
|
+
else
|
|
1071
|
+
[model, @values.sort_by{|k,v| k.to_s}].hash
|
|
1072
|
+
end
|
|
894
1073
|
end
|
|
895
1074
|
|
|
896
1075
|
# Returns value for the :id attribute, even if the primary key is
|
|
@@ -926,7 +1105,8 @@ module Sequel
|
|
|
926
1105
|
# a.update(...)
|
|
927
1106
|
# end
|
|
928
1107
|
def lock!
|
|
929
|
-
|
|
1108
|
+
_refresh(this.for_update) unless new?
|
|
1109
|
+
self
|
|
930
1110
|
end
|
|
931
1111
|
|
|
932
1112
|
# Remove elements of the model object that make marshalling fail. Returns self.
|
|
@@ -957,7 +1137,7 @@ module Sequel
|
|
|
957
1137
|
# a = Artist[1]
|
|
958
1138
|
# a.modified? # => false
|
|
959
1139
|
# a.set(:name=>'Jim')
|
|
960
|
-
# a.modified # => true
|
|
1140
|
+
# a.modified? # => true
|
|
961
1141
|
def modified?
|
|
962
1142
|
@modified || !changed_columns.empty?
|
|
963
1143
|
end
|
|
@@ -967,18 +1147,23 @@ module Sequel
|
|
|
967
1147
|
# Artist.new.new? # => true
|
|
968
1148
|
# Artist[1].new? # => false
|
|
969
1149
|
def new?
|
|
970
|
-
@new
|
|
1150
|
+
defined?(@new) ? @new : (@new = false)
|
|
971
1151
|
end
|
|
972
1152
|
|
|
973
1153
|
# Returns the primary key value identifying the model instance.
|
|
974
|
-
# Raises an
|
|
1154
|
+
# Raises an +Error+ if this model does not have a primary key.
|
|
975
1155
|
# If the model has a composite primary key, returns an array of values.
|
|
976
1156
|
#
|
|
977
1157
|
# Artist[1].pk # => 1
|
|
978
1158
|
# Artist[[1, 2]].pk # => [1, 2]
|
|
979
1159
|
def pk
|
|
980
1160
|
raise(Error, "No primary key is associated with this model") unless key = primary_key
|
|
981
|
-
key.is_a?(Array)
|
|
1161
|
+
if key.is_a?(Array)
|
|
1162
|
+
vals = @values
|
|
1163
|
+
key.map{|k| vals[k]}
|
|
1164
|
+
else
|
|
1165
|
+
@values[key]
|
|
1166
|
+
end
|
|
982
1167
|
end
|
|
983
1168
|
|
|
984
1169
|
# Returns a hash identifying mapping the receivers primary key column(s) to their values.
|
|
@@ -998,7 +1183,9 @@ module Sequel
|
|
|
998
1183
|
# a.refresh
|
|
999
1184
|
# a.name # => 'Bob'
|
|
1000
1185
|
def refresh
|
|
1186
|
+
raise Sequel::Error, "can't refresh frozen object" if frozen?
|
|
1001
1187
|
_refresh(this)
|
|
1188
|
+
self
|
|
1002
1189
|
end
|
|
1003
1190
|
|
|
1004
1191
|
# Alias of refresh, but not aliased directly to make overriding in a plugin easier.
|
|
@@ -1016,26 +1203,32 @@ module Sequel
|
|
|
1016
1203
|
#
|
|
1017
1204
|
# If +save+ fails and either raise_on_save_failure or the
|
|
1018
1205
|
# :raise_on_failure option is true, it raises ValidationFailed
|
|
1019
|
-
# or
|
|
1206
|
+
# or HookFailed. Otherwise it returns nil.
|
|
1020
1207
|
#
|
|
1021
1208
|
# If it succeeds, it returns self.
|
|
1022
1209
|
#
|
|
1023
1210
|
# You can provide an optional list of columns to update, in which
|
|
1024
|
-
# case it only updates those columns.
|
|
1211
|
+
# case it only updates those columns, or a options hash.
|
|
1025
1212
|
#
|
|
1026
1213
|
# Takes the following options:
|
|
1027
1214
|
#
|
|
1028
|
-
#
|
|
1029
|
-
#
|
|
1030
|
-
#
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1215
|
+
# :changed :: save all changed columns, instead of all columns or the columns given
|
|
1216
|
+
# :raise_on_failure :: set to true or false to override the current
|
|
1217
|
+
# +raise_on_save_failure+ setting
|
|
1218
|
+
# :server :: set the server/shard on the object before saving, and use that
|
|
1219
|
+
# server/shard in any transaction.
|
|
1220
|
+
# :transaction :: set to true or false to override the current
|
|
1221
|
+
# +use_transactions+ setting
|
|
1222
|
+
# :validate :: set to false to skip validation
|
|
1034
1223
|
def save(*columns)
|
|
1224
|
+
raise Sequel::Error, "can't save frozen object" if frozen?
|
|
1035
1225
|
opts = columns.last.is_a?(Hash) ? columns.pop : {}
|
|
1036
|
-
if opts[:
|
|
1037
|
-
|
|
1038
|
-
|
|
1226
|
+
set_server(opts[:server]) if opts[:server]
|
|
1227
|
+
if opts[:validate] != false
|
|
1228
|
+
unless checked_save_failure(opts){_valid?(true, opts)}
|
|
1229
|
+
raise(ValidationFailed.new(self)) if raise_on_failure?(opts)
|
|
1230
|
+
return
|
|
1231
|
+
end
|
|
1039
1232
|
end
|
|
1040
1233
|
checked_save_failure(opts){checked_transaction(opts){_save(columns, opts)}}
|
|
1041
1234
|
end
|
|
@@ -1075,7 +1268,8 @@ module Sequel
|
|
|
1075
1268
|
end
|
|
1076
1269
|
|
|
1077
1270
|
# Set all values using the entries in the hash, except for the keys
|
|
1078
|
-
# given in except.
|
|
1271
|
+
# given in except. You should probably use +set_fields+ or +set_only+
|
|
1272
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1079
1273
|
#
|
|
1080
1274
|
# artist.set_except({:name=>'Jim'}, :hometown)
|
|
1081
1275
|
# artist.name # => 'Jim'
|
|
@@ -1086,28 +1280,85 @@ module Sequel
|
|
|
1086
1280
|
# For each of the fields in the given array +fields+, call the setter
|
|
1087
1281
|
# method with the value of that +hash+ entry for the field. Returns self.
|
|
1088
1282
|
#
|
|
1089
|
-
#
|
|
1283
|
+
# You can provide an options hash, with the following options currently respected:
|
|
1284
|
+
# :missing :: Can be set to :skip to skip missing entries or :raise to raise an
|
|
1285
|
+
# Error for missing entries. The default behavior is not to check for
|
|
1286
|
+
# missing entries, in which case the default value is used. To be
|
|
1287
|
+
# friendly with most web frameworks, the missing check will also check
|
|
1288
|
+
# for the string version of the argument in the hash if given a symbol.
|
|
1289
|
+
#
|
|
1290
|
+
# Examples:
|
|
1291
|
+
#
|
|
1292
|
+
# artist.set_fields({:name=>'Jim'}, [:name])
|
|
1090
1293
|
# artist.name # => 'Jim'
|
|
1091
1294
|
#
|
|
1092
|
-
# artist.set_fields({:hometown=>'LA'}, :name)
|
|
1295
|
+
# artist.set_fields({:hometown=>'LA'}, [:name])
|
|
1093
1296
|
# artist.name # => nil
|
|
1094
1297
|
# artist.hometown # => 'Sac'
|
|
1095
|
-
|
|
1096
|
-
|
|
1298
|
+
#
|
|
1299
|
+
# artist.name # => 'Jim'
|
|
1300
|
+
# artist.set_fields({}, [:name], :missing=>:skip)
|
|
1301
|
+
# artist.name # => 'Jim'
|
|
1302
|
+
#
|
|
1303
|
+
# artist.name # => 'Jim'
|
|
1304
|
+
# artist.set_fields({}, [:name], :missing=>:raise)
|
|
1305
|
+
# # Sequel::Error raised
|
|
1306
|
+
def set_fields(hash, fields, opts=nil)
|
|
1307
|
+
if opts
|
|
1308
|
+
case opts[:missing]
|
|
1309
|
+
when :skip
|
|
1310
|
+
fields.each do |f|
|
|
1311
|
+
if hash.has_key?(f)
|
|
1312
|
+
send("#{f}=", hash[f])
|
|
1313
|
+
elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
|
|
1314
|
+
send("#{sf}=", hash[sf])
|
|
1315
|
+
end
|
|
1316
|
+
end
|
|
1317
|
+
when :raise
|
|
1318
|
+
fields.each do |f|
|
|
1319
|
+
if hash.has_key?(f)
|
|
1320
|
+
send("#{f}=", hash[f])
|
|
1321
|
+
elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
|
|
1322
|
+
send("#{sf}=", hash[sf])
|
|
1323
|
+
else
|
|
1324
|
+
raise(Sequel::Error, "missing field in hash: #{f.inspect} not in #{hash.inspect}")
|
|
1325
|
+
end
|
|
1326
|
+
end
|
|
1327
|
+
else
|
|
1328
|
+
fields.each{|f| send("#{f}=", hash[f])}
|
|
1329
|
+
end
|
|
1330
|
+
else
|
|
1331
|
+
fields.each{|f| send("#{f}=", hash[f])}
|
|
1332
|
+
end
|
|
1097
1333
|
self
|
|
1098
1334
|
end
|
|
1099
1335
|
|
|
1100
1336
|
# Set the values using the entries in the hash, only if the key
|
|
1101
|
-
# is included in only.
|
|
1337
|
+
# is included in only. It may be a better idea to use +set_fields+
|
|
1338
|
+
# instead of this method.
|
|
1102
1339
|
#
|
|
1103
1340
|
# artist.set_only({:name=>'Jim'}, :name)
|
|
1104
1341
|
# artist.name # => 'Jim'
|
|
1105
1342
|
#
|
|
1106
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise
|
|
1343
|
+
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
|
1107
1344
|
def set_only(hash, *only)
|
|
1108
1345
|
set_restricted(hash, only.flatten, false)
|
|
1109
1346
|
end
|
|
1110
1347
|
|
|
1348
|
+
# Set the shard that this object is tied to. Returns self.
|
|
1349
|
+
def set_server(s)
|
|
1350
|
+
@server = s
|
|
1351
|
+
@this.opts[:server] = s if @this
|
|
1352
|
+
self
|
|
1353
|
+
end
|
|
1354
|
+
|
|
1355
|
+
# Replace the current values with hash. Should definitely not be
|
|
1356
|
+
# used with untrusted input, and should probably not be called
|
|
1357
|
+
# directly by user code.
|
|
1358
|
+
def set_values(hash)
|
|
1359
|
+
@values = hash
|
|
1360
|
+
end
|
|
1361
|
+
|
|
1111
1362
|
# Clear the setter_methods cache when a method is added
|
|
1112
1363
|
def singleton_method_added(meth)
|
|
1113
1364
|
@singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
|
|
@@ -1119,10 +1370,10 @@ module Sequel
|
|
|
1119
1370
|
# Artist[1].this
|
|
1120
1371
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1121
1372
|
def this
|
|
1122
|
-
@this ||= model.
|
|
1373
|
+
@this ||= use_server(model.instance_dataset.filter(pk_hash))
|
|
1123
1374
|
end
|
|
1124
1375
|
|
|
1125
|
-
# Runs set with the passed hash and then runs save_changes.
|
|
1376
|
+
# Runs #set with the passed hash and then runs save_changes.
|
|
1126
1377
|
#
|
|
1127
1378
|
# artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1128
1379
|
def update(hash)
|
|
@@ -1130,7 +1381,7 @@ module Sequel
|
|
|
1130
1381
|
end
|
|
1131
1382
|
|
|
1132
1383
|
# Update all values using the entries in the hash, ignoring any setting of
|
|
1133
|
-
# allowed_columns or
|
|
1384
|
+
# +allowed_columns+ or +restricted_columns+ in the model.
|
|
1134
1385
|
#
|
|
1135
1386
|
# Artist.set_restricted_columns(:name)
|
|
1136
1387
|
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1139,28 +1390,30 @@ module Sequel
|
|
|
1139
1390
|
end
|
|
1140
1391
|
|
|
1141
1392
|
# Update all values using the entries in the hash, except for the keys
|
|
1142
|
-
# given in except.
|
|
1393
|
+
# given in except. You should probably use +update_fields+ or +update_only+
|
|
1394
|
+
# instead of this method, as blacklist approaches to security are a bad idea.
|
|
1143
1395
|
#
|
|
1144
1396
|
# artist.update_except({:name=>'Jim'}, :hometown) # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1145
1397
|
def update_except(hash, *except)
|
|
1146
1398
|
update_restricted(hash, false, except.flatten)
|
|
1147
1399
|
end
|
|
1148
1400
|
|
|
1149
|
-
# Update the instances values by calling +set_fields+ with the
|
|
1150
|
-
#
|
|
1401
|
+
# Update the instances values by calling +set_fields+ with the arguments, then
|
|
1402
|
+
# saves any changes to the record. Returns self.
|
|
1151
1403
|
#
|
|
1152
|
-
# artist.update_fields({:name=>'Jim'}, :name)
|
|
1404
|
+
# artist.update_fields({:name=>'Jim'}, [:name])
|
|
1153
1405
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
1154
1406
|
#
|
|
1155
|
-
# artist.update_fields({:hometown=>'LA'}, :name)
|
|
1407
|
+
# artist.update_fields({:hometown=>'LA'}, [:name])
|
|
1156
1408
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
|
1157
|
-
def update_fields(hash, fields)
|
|
1158
|
-
set_fields(hash, fields)
|
|
1409
|
+
def update_fields(hash, fields, opts=nil)
|
|
1410
|
+
set_fields(hash, fields, opts)
|
|
1159
1411
|
save_changes
|
|
1160
1412
|
end
|
|
1161
1413
|
|
|
1162
1414
|
# Update the values using the entries in the hash, only if the key
|
|
1163
|
-
# is included in only.
|
|
1415
|
+
# is included in only. It may be a better idea to use +update_fields+
|
|
1416
|
+
# instead of this method.
|
|
1164
1417
|
#
|
|
1165
1418
|
# artist.update_only({:name=>'Jim'}, :name)
|
|
1166
1419
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
|
@@ -1185,21 +1438,15 @@ module Sequel
|
|
|
1185
1438
|
# artist(:name=>'Invalid').valid? # => false
|
|
1186
1439
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
|
1187
1440
|
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?
|
|
1441
|
+
_valid?(false, opts)
|
|
1196
1442
|
end
|
|
1197
1443
|
|
|
1198
1444
|
private
|
|
1199
1445
|
|
|
1200
|
-
#
|
|
1446
|
+
# Do the deletion of the object's dataset, and check that the row
|
|
1447
|
+
# was actually deleted.
|
|
1201
1448
|
def _delete
|
|
1202
|
-
n =
|
|
1449
|
+
n = _delete_without_checking
|
|
1203
1450
|
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
1451
|
n
|
|
1205
1452
|
end
|
|
@@ -1210,12 +1457,33 @@ module Sequel
|
|
|
1210
1457
|
this
|
|
1211
1458
|
end
|
|
1212
1459
|
|
|
1460
|
+
# Actually do the deletion of the object's dataset. Return the
|
|
1461
|
+
# number of rows modified.
|
|
1462
|
+
def _delete_without_checking
|
|
1463
|
+
if sql = (m = model).fast_instance_delete_sql
|
|
1464
|
+
sql = sql.dup
|
|
1465
|
+
(ds = m.dataset).literal_append(sql, pk)
|
|
1466
|
+
ds.with_sql_delete(sql)
|
|
1467
|
+
else
|
|
1468
|
+
_delete_dataset.delete
|
|
1469
|
+
end
|
|
1470
|
+
end
|
|
1471
|
+
|
|
1213
1472
|
# Internal destroy method, separted from destroy to
|
|
1214
1473
|
# allow running inside a transaction
|
|
1215
1474
|
def _destroy(opts)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1475
|
+
sh = {:server=>this_server}
|
|
1476
|
+
db.after_rollback(sh){after_destroy_rollback} if uacr = use_after_commit_rollback
|
|
1477
|
+
called = false
|
|
1478
|
+
around_destroy do
|
|
1479
|
+
called = true
|
|
1480
|
+
raise_hook_failure(:before_destroy) if before_destroy == false
|
|
1481
|
+
_destroy_delete
|
|
1482
|
+
after_destroy
|
|
1483
|
+
true
|
|
1484
|
+
end
|
|
1485
|
+
raise_hook_failure(:around_destroy) unless called
|
|
1486
|
+
db.after_commit(sh){after_destroy_commit} if uacr
|
|
1219
1487
|
self
|
|
1220
1488
|
end
|
|
1221
1489
|
|
|
@@ -1230,68 +1498,107 @@ module Sequel
|
|
|
1230
1498
|
# the record should be refreshed from the database.
|
|
1231
1499
|
def _insert
|
|
1232
1500
|
ds = _insert_dataset
|
|
1233
|
-
if ds.
|
|
1234
|
-
|
|
1501
|
+
if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
|
|
1502
|
+
set_values(h)
|
|
1235
1503
|
nil
|
|
1236
1504
|
else
|
|
1237
|
-
iid = ds
|
|
1505
|
+
iid = _insert_raw(ds)
|
|
1238
1506
|
# if we have a regular primary key and it's not set in @values,
|
|
1239
1507
|
# we assume it's the last inserted id
|
|
1240
|
-
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) &&
|
|
1241
|
-
|
|
1508
|
+
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !(vals = @values)[pk]
|
|
1509
|
+
vals[pk] = iid
|
|
1242
1510
|
end
|
|
1243
1511
|
pk
|
|
1244
1512
|
end
|
|
1245
1513
|
end
|
|
1246
|
-
|
|
1514
|
+
|
|
1247
1515
|
# The dataset to use when inserting a new object. The same as the model's
|
|
1248
1516
|
# dataset by default.
|
|
1249
1517
|
def _insert_dataset
|
|
1250
|
-
model.
|
|
1518
|
+
use_server(model.instance_dataset)
|
|
1251
1519
|
end
|
|
1252
1520
|
|
|
1521
|
+
# Insert into the given dataset and return the primary key created (if any).
|
|
1522
|
+
def _insert_raw(ds)
|
|
1523
|
+
ds.insert(@values)
|
|
1524
|
+
end
|
|
1525
|
+
|
|
1526
|
+
# Insert into the given dataset and return the hash of column values.
|
|
1527
|
+
def _insert_select_raw(ds)
|
|
1528
|
+
ds.insert_select(@values)
|
|
1529
|
+
end
|
|
1530
|
+
|
|
1253
1531
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
|
1254
1532
|
# is used for reading newly inserted values from the database
|
|
1255
1533
|
def _refresh(dataset)
|
|
1256
|
-
set_values(dataset
|
|
1534
|
+
set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
|
|
1257
1535
|
changed_columns.clear
|
|
1258
|
-
|
|
1536
|
+
end
|
|
1537
|
+
|
|
1538
|
+
# Get the row of column data from the database.
|
|
1539
|
+
def _refresh_get(dataset)
|
|
1540
|
+
dataset.first
|
|
1259
1541
|
end
|
|
1260
1542
|
|
|
1261
1543
|
# Internal version of save, split from save to allow running inside
|
|
1262
1544
|
# it's own transaction.
|
|
1263
1545
|
def _save(columns, opts)
|
|
1264
|
-
|
|
1265
|
-
if
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1546
|
+
sh = {:server=>this_server}
|
|
1547
|
+
db.after_rollback(sh){after_rollback} if uacr = use_after_commit_rollback
|
|
1548
|
+
was_new = false
|
|
1549
|
+
pk = nil
|
|
1550
|
+
called_save = false
|
|
1551
|
+
called_cu = false
|
|
1552
|
+
around_save do
|
|
1553
|
+
called_save = true
|
|
1554
|
+
raise_hook_failure(:before_save) if before_save == false
|
|
1555
|
+
if new?
|
|
1556
|
+
was_new = true
|
|
1557
|
+
around_create do
|
|
1558
|
+
called_cu = true
|
|
1559
|
+
raise_hook_failure(:before_create) if before_create == false
|
|
1560
|
+
pk = _insert
|
|
1561
|
+
@this = nil
|
|
1562
|
+
@new = false
|
|
1563
|
+
@was_new = true
|
|
1564
|
+
after_create
|
|
1565
|
+
true
|
|
1566
|
+
end
|
|
1567
|
+
raise_hook_failure(:around_create) unless called_cu
|
|
1568
|
+
else
|
|
1569
|
+
around_update do
|
|
1570
|
+
called_cu = true
|
|
1571
|
+
raise_hook_failure(:before_update) if before_update == false
|
|
1572
|
+
if columns.empty?
|
|
1573
|
+
@columns_updated = if opts[:changed]
|
|
1574
|
+
@values.reject{|k,v| !changed_columns.include?(k)}
|
|
1575
|
+
else
|
|
1576
|
+
_save_update_all_columns_hash
|
|
1577
|
+
end
|
|
1578
|
+
changed_columns.clear
|
|
1579
|
+
else # update only the specified columns
|
|
1580
|
+
@columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
|
1581
|
+
changed_columns.reject!{|c| columns.include?(c)}
|
|
1582
|
+
end
|
|
1583
|
+
_update_columns(@columns_updated)
|
|
1584
|
+
@this = nil
|
|
1585
|
+
after_update
|
|
1586
|
+
true
|
|
1587
|
+
end
|
|
1588
|
+
raise_hook_failure(:around_update) unless called_cu
|
|
1589
|
+
end
|
|
1272
1590
|
after_save
|
|
1591
|
+
true
|
|
1592
|
+
end
|
|
1593
|
+
raise_hook_failure(:around_save) unless called_save
|
|
1594
|
+
if was_new
|
|
1273
1595
|
@was_new = nil
|
|
1274
1596
|
pk ? _save_refresh : changed_columns.clear
|
|
1275
1597
|
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
1598
|
@columns_updated = nil
|
|
1293
1599
|
end
|
|
1294
1600
|
@modified = false
|
|
1601
|
+
db.after_commit(sh){after_commit} if uacr
|
|
1295
1602
|
self
|
|
1296
1603
|
end
|
|
1297
1604
|
|
|
@@ -1321,9 +1628,10 @@ module Sequel
|
|
|
1321
1628
|
_update(columns) unless columns.empty?
|
|
1322
1629
|
end
|
|
1323
1630
|
|
|
1324
|
-
# Update this instance's dataset with the supplied column hash
|
|
1631
|
+
# Update this instance's dataset with the supplied column hash,
|
|
1632
|
+
# checking that only a single row was modified.
|
|
1325
1633
|
def _update(columns)
|
|
1326
|
-
n =
|
|
1634
|
+
n = _update_without_checking(columns)
|
|
1327
1635
|
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
1636
|
n
|
|
1329
1637
|
end
|
|
@@ -1334,7 +1642,47 @@ module Sequel
|
|
|
1334
1642
|
this
|
|
1335
1643
|
end
|
|
1336
1644
|
|
|
1337
|
-
#
|
|
1645
|
+
# Update this instances dataset with the supplied column hash.
|
|
1646
|
+
def _update_without_checking(columns)
|
|
1647
|
+
_update_dataset.update(columns)
|
|
1648
|
+
end
|
|
1649
|
+
|
|
1650
|
+
# Internal validation method. If +raise_errors+ is +true+, hook
|
|
1651
|
+
# failures will be raised as HookFailure exceptions. If it is
|
|
1652
|
+
# +false+, +false+ will be returned instead.
|
|
1653
|
+
def _valid?(raise_errors, opts)
|
|
1654
|
+
return errors.empty? if frozen?
|
|
1655
|
+
errors.clear
|
|
1656
|
+
called = false
|
|
1657
|
+
error = false
|
|
1658
|
+
around_validation do
|
|
1659
|
+
called = true
|
|
1660
|
+
if before_validation == false
|
|
1661
|
+
if raise_errors
|
|
1662
|
+
raise_hook_failure(:before_validation)
|
|
1663
|
+
else
|
|
1664
|
+
error = true
|
|
1665
|
+
end
|
|
1666
|
+
false
|
|
1667
|
+
else
|
|
1668
|
+
validate
|
|
1669
|
+
after_validation
|
|
1670
|
+
errors.empty?
|
|
1671
|
+
end
|
|
1672
|
+
end
|
|
1673
|
+
error = true unless called
|
|
1674
|
+
if error
|
|
1675
|
+
if raise_errors
|
|
1676
|
+
raise_hook_failure(:around_validation)
|
|
1677
|
+
else
|
|
1678
|
+
false
|
|
1679
|
+
end
|
|
1680
|
+
else
|
|
1681
|
+
errors.empty?
|
|
1682
|
+
end
|
|
1683
|
+
end
|
|
1684
|
+
|
|
1685
|
+
# If not raising on failure, check for HookFailed
|
|
1338
1686
|
# being raised by yielding and swallow it.
|
|
1339
1687
|
def checked_save_failure(opts)
|
|
1340
1688
|
if raise_on_failure?(opts)
|
|
@@ -1342,7 +1690,7 @@ module Sequel
|
|
|
1342
1690
|
else
|
|
1343
1691
|
begin
|
|
1344
1692
|
yield
|
|
1345
|
-
rescue
|
|
1693
|
+
rescue HookFailed
|
|
1346
1694
|
nil
|
|
1347
1695
|
end
|
|
1348
1696
|
end
|
|
@@ -1350,7 +1698,21 @@ module Sequel
|
|
|
1350
1698
|
|
|
1351
1699
|
# If transactions should be used, wrap the yield in a transaction block.
|
|
1352
1700
|
def checked_transaction(opts={})
|
|
1353
|
-
use_transaction?(opts) ? db.transaction(opts){yield} : yield
|
|
1701
|
+
use_transaction?(opts) ? db.transaction({:server=>this_server}.merge(opts)){yield} : yield
|
|
1702
|
+
end
|
|
1703
|
+
|
|
1704
|
+
# Change the value of the column to given value, recording the change.
|
|
1705
|
+
def change_column_value(column, value)
|
|
1706
|
+
cc = changed_columns
|
|
1707
|
+
cc << column unless cc.include?(column)
|
|
1708
|
+
@values[column] = value
|
|
1709
|
+
end
|
|
1710
|
+
|
|
1711
|
+
# Set the columns with the given hash. By default, the same as +set+, but
|
|
1712
|
+
# exists so it can be overridden. This is called only for new records, before
|
|
1713
|
+
# changed_columns is cleared.
|
|
1714
|
+
def initialize_set(h)
|
|
1715
|
+
set(h) unless h.empty?
|
|
1354
1716
|
end
|
|
1355
1717
|
|
|
1356
1718
|
# Default inspection output for the values hash, overwrite to change what #inspect displays.
|
|
@@ -1369,11 +1731,12 @@ module Sequel
|
|
|
1369
1731
|
# Raise an error appropriate to the hook type. May be swallowed by
|
|
1370
1732
|
# checked_save_failure depending on the raise_on_failure? setting.
|
|
1371
1733
|
def raise_hook_failure(type)
|
|
1372
|
-
raise
|
|
1734
|
+
raise HookFailed.new("the #{type} hook failed", self)
|
|
1373
1735
|
end
|
|
1374
1736
|
|
|
1375
1737
|
# Set the columns, filtered by the only and except arrays.
|
|
1376
1738
|
def set_restricted(hash, only, except)
|
|
1739
|
+
return self if hash.empty?
|
|
1377
1740
|
meths = if only.nil? && except.nil? && !@singleton_setter_added
|
|
1378
1741
|
model.setter_methods
|
|
1379
1742
|
else
|
|
@@ -1385,17 +1748,21 @@ module Sequel
|
|
|
1385
1748
|
if meths.include?(m)
|
|
1386
1749
|
send(m, v)
|
|
1387
1750
|
elsif strict
|
|
1388
|
-
|
|
1751
|
+
# Avoid using respond_to? or creating symbols from user input
|
|
1752
|
+
if public_methods.map{|s| s.to_s}.include?(m)
|
|
1753
|
+
if Array(model.primary_key).map{|s| s.to_s}.member?(k.to_s) && model.restrict_primary_key?
|
|
1754
|
+
raise Error, "#{k} is a restricted primary key"
|
|
1755
|
+
else
|
|
1756
|
+
raise Error, "#{k} is a restricted column"
|
|
1757
|
+
end
|
|
1758
|
+
else
|
|
1759
|
+
raise Error, "method #{m} doesn't exist"
|
|
1760
|
+
end
|
|
1389
1761
|
end
|
|
1390
1762
|
end
|
|
1391
1763
|
self
|
|
1392
1764
|
end
|
|
1393
1765
|
|
|
1394
|
-
# Replace the current values with hash.
|
|
1395
|
-
def set_values(hash)
|
|
1396
|
-
@values = hash
|
|
1397
|
-
end
|
|
1398
|
-
|
|
1399
1766
|
# Returns all methods that can be used for attribute
|
|
1400
1767
|
# assignment (those that end with =), modified by the only
|
|
1401
1768
|
# and except arguments:
|
|
@@ -1424,13 +1791,25 @@ module Sequel
|
|
|
1424
1791
|
meths
|
|
1425
1792
|
end
|
|
1426
1793
|
end
|
|
1794
|
+
|
|
1795
|
+
# The server/shard that the model object's dataset uses, or :default if the
|
|
1796
|
+
# model object's dataset does not have an associated shard.
|
|
1797
|
+
def this_server
|
|
1798
|
+
if (s = @server)
|
|
1799
|
+
s
|
|
1800
|
+
elsif (t = @this)
|
|
1801
|
+
t.opts[:server] || :default
|
|
1802
|
+
else
|
|
1803
|
+
model.dataset.opts[:server] || :default
|
|
1804
|
+
end
|
|
1805
|
+
end
|
|
1427
1806
|
|
|
1428
1807
|
# Typecast the value to the column's type if typecasting. Calls the database's
|
|
1429
1808
|
# typecast_value method, so database adapters can override/augment the handling
|
|
1430
1809
|
# for database specific column types.
|
|
1431
1810
|
def typecast_value(column, value)
|
|
1432
1811
|
return value unless typecast_on_assignment && db_schema && (col_schema = db_schema[column])
|
|
1433
|
-
value = nil if
|
|
1812
|
+
value = nil if '' == value and typecast_empty_string_to_nil and col_schema[:type] and ![:string, :blob].include?(col_schema[:type])
|
|
1434
1813
|
raise(InvalidValue, "nil/NULL is not allowed for the #{column} column") if raise_on_typecast_failure && value.nil? && (col_schema[:allow_null] == false)
|
|
1435
1814
|
begin
|
|
1436
1815
|
model.db.typecast_value(col_schema[:type], value)
|
|
@@ -1444,6 +1823,11 @@ module Sequel
|
|
|
1444
1823
|
set_restricted(hash, only, except)
|
|
1445
1824
|
save_changes
|
|
1446
1825
|
end
|
|
1826
|
+
|
|
1827
|
+
# Set the given dataset to use the current object's shard.
|
|
1828
|
+
def use_server(ds)
|
|
1829
|
+
@server ? ds.server(@server) : ds
|
|
1830
|
+
end
|
|
1447
1831
|
|
|
1448
1832
|
# Whether to use a transaction for this action. If the :transaction
|
|
1449
1833
|
# option is present in the hash, use that, otherwise, fallback to the
|
|
@@ -1461,6 +1845,18 @@ module Sequel
|
|
|
1461
1845
|
# Artist.dataset.model # => Artist
|
|
1462
1846
|
attr_accessor :model
|
|
1463
1847
|
|
|
1848
|
+
# Assume if a single integer is given that it is a lookup by primary
|
|
1849
|
+
# key, and call with_pk with the argument.
|
|
1850
|
+
#
|
|
1851
|
+
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1852
|
+
def [](*args)
|
|
1853
|
+
if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
|
|
1854
|
+
with_pk(i)
|
|
1855
|
+
else
|
|
1856
|
+
super
|
|
1857
|
+
end
|
|
1858
|
+
end
|
|
1859
|
+
|
|
1464
1860
|
# Destroy each row in the dataset by instantiating it and then calling
|
|
1465
1861
|
# destroy on the resulting model object. This isn't as fast as deleting
|
|
1466
1862
|
# the dataset, which does a single SQL call, but this runs any destroy
|
|
@@ -1472,7 +1868,51 @@ module Sequel
|
|
|
1472
1868
|
# # ...
|
|
1473
1869
|
def destroy
|
|
1474
1870
|
pr = proc{all{|r| r.destroy}.length}
|
|
1475
|
-
model.use_transactions ? @db.transaction(&pr) : pr.call
|
|
1871
|
+
model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
# Allow Sequel::Model classes to be used as dataset arguments when graphing:
|
|
1875
|
+
#
|
|
1876
|
+
# Artist.graph(Album, :artist_id=>id)
|
|
1877
|
+
# # SELECT artists.id, artists.name, albums.id AS albums_id, albums.artist_id, albums.name AS albums_name
|
|
1878
|
+
# # FROM artists LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
|
1879
|
+
def graph(table, *args, &block)
|
|
1880
|
+
if table.is_a?(Class) && table < Sequel::Model
|
|
1881
|
+
super(table.dataset, *args, &block)
|
|
1882
|
+
else
|
|
1883
|
+
super
|
|
1884
|
+
end
|
|
1885
|
+
end
|
|
1886
|
+
|
|
1887
|
+
# Handle Sequel::Model instances when inserting, using the model instance's
|
|
1888
|
+
# values for the insert, unless the model instance can be used directly in
|
|
1889
|
+
# SQL.
|
|
1890
|
+
#
|
|
1891
|
+
# Album.insert(Album.load(:name=>'A'))
|
|
1892
|
+
# # INSERT INTO albums (name) VALUES ('A')
|
|
1893
|
+
def insert_sql(*values)
|
|
1894
|
+
if values.size == 1 && (v = values.at(0)).is_a?(Sequel::Model) && !v.respond_to?(:sql_literal_append)
|
|
1895
|
+
super(v.to_hash)
|
|
1896
|
+
else
|
|
1897
|
+
super
|
|
1898
|
+
end
|
|
1899
|
+
end
|
|
1900
|
+
|
|
1901
|
+
# Allow Sequel::Model classes to be used as table name arguments in dataset
|
|
1902
|
+
# join methods:
|
|
1903
|
+
#
|
|
1904
|
+
# Artist.join(Album, :artist_id=>id)
|
|
1905
|
+
# # SELECT * FROM artists INNER JOIN albums ON (albums.artist_id = artists.id)
|
|
1906
|
+
def join_table(type, table, *args, &block)
|
|
1907
|
+
if table.is_a?(Class) && table < Sequel::Model
|
|
1908
|
+
if table.dataset.simple_select_all?
|
|
1909
|
+
super(type, table.table_name, *args, &block)
|
|
1910
|
+
else
|
|
1911
|
+
super(type, table.dataset, *args, &block)
|
|
1912
|
+
end
|
|
1913
|
+
else
|
|
1914
|
+
super
|
|
1915
|
+
end
|
|
1476
1916
|
end
|
|
1477
1917
|
|
|
1478
1918
|
# This allows you to call +to_hash+ without any arguments, which will
|
|
@@ -1491,6 +1931,19 @@ module Sequel
|
|
|
1491
1931
|
super(pk, value_column)
|
|
1492
1932
|
end
|
|
1493
1933
|
end
|
|
1934
|
+
|
|
1935
|
+
# Given a primary key value, return the first record in the dataset with that primary key
|
|
1936
|
+
# value.
|
|
1937
|
+
#
|
|
1938
|
+
# # Single primary key
|
|
1939
|
+
# Artist.dataset.with_pk(1) # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
|
1940
|
+
#
|
|
1941
|
+
# # Composite primary key
|
|
1942
|
+
# Artist.dataset.with_pk([1, 2]) # SELECT * FROM artists
|
|
1943
|
+
# # WHERE ((id1 = 1) AND (id2 = 2)) LIMIT 1
|
|
1944
|
+
def with_pk(pk)
|
|
1945
|
+
first(model.qualified_primary_key_hash(pk))
|
|
1946
|
+
end
|
|
1494
1947
|
end
|
|
1495
1948
|
|
|
1496
1949
|
extend ClassMethods
|