sequel 4.26.0 → 5.37.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG +405 -5656
- data/MIT-LICENSE +1 -1
- data/README.rdoc +232 -157
- data/bin/sequel +32 -9
- data/doc/advanced_associations.rdoc +252 -188
- data/doc/association_basics.rdoc +231 -273
- data/doc/bin_sequel.rdoc +5 -3
- data/doc/cheat_sheet.rdoc +75 -48
- data/doc/code_order.rdoc +28 -10
- data/doc/core_extensions.rdoc +104 -63
- data/doc/dataset_basics.rdoc +12 -21
- data/doc/dataset_filtering.rdoc +99 -86
- data/doc/extensions.rdoc +3 -10
- data/doc/mass_assignment.rdoc +74 -31
- data/doc/migration.rdoc +72 -46
- data/doc/model_dataset_method_design.rdoc +129 -0
- data/doc/model_hooks.rdoc +15 -25
- data/doc/model_plugins.rdoc +12 -12
- data/doc/mssql_stored_procedures.rdoc +3 -3
- data/doc/object_model.rdoc +59 -69
- data/doc/opening_databases.rdoc +84 -94
- data/doc/postgresql.rdoc +268 -38
- data/doc/prepared_statements.rdoc +29 -24
- data/doc/querying.rdoc +184 -164
- data/doc/reflection.rdoc +5 -6
- data/doc/release_notes/5.0.0.txt +159 -0
- data/doc/release_notes/5.1.0.txt +31 -0
- data/doc/release_notes/5.10.0.txt +84 -0
- data/doc/release_notes/5.11.0.txt +83 -0
- data/doc/release_notes/5.12.0.txt +141 -0
- data/doc/release_notes/5.13.0.txt +27 -0
- data/doc/release_notes/5.14.0.txt +63 -0
- data/doc/release_notes/5.15.0.txt +39 -0
- data/doc/release_notes/5.16.0.txt +110 -0
- data/doc/release_notes/5.17.0.txt +31 -0
- data/doc/release_notes/5.18.0.txt +69 -0
- data/doc/release_notes/5.19.0.txt +28 -0
- data/doc/release_notes/5.2.0.txt +33 -0
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/release_notes/5.27.0.txt +21 -0
- data/doc/release_notes/5.28.0.txt +16 -0
- data/doc/release_notes/5.29.0.txt +22 -0
- data/doc/release_notes/5.3.0.txt +121 -0
- data/doc/release_notes/5.30.0.txt +20 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.4.0.txt +80 -0
- data/doc/release_notes/5.5.0.txt +61 -0
- data/doc/release_notes/5.6.0.txt +31 -0
- data/doc/release_notes/5.7.0.txt +108 -0
- data/doc/release_notes/5.8.0.txt +170 -0
- data/doc/release_notes/5.9.0.txt +99 -0
- data/doc/schema_modification.rdoc +102 -77
- data/doc/security.rdoc +160 -87
- data/doc/sharding.rdoc +74 -47
- data/doc/sql.rdoc +135 -122
- data/doc/testing.rdoc +34 -18
- data/doc/thread_safety.rdoc +2 -4
- data/doc/transactions.rdoc +101 -19
- data/doc/validations.rdoc +64 -51
- data/doc/virtual_rows.rdoc +90 -109
- data/lib/sequel.rb +3 -1
- data/lib/sequel/adapters/ado.rb +154 -22
- data/lib/sequel/adapters/ado/access.rb +21 -21
- data/lib/sequel/adapters/ado/mssql.rb +8 -15
- data/lib/sequel/adapters/amalgalite.rb +17 -25
- data/lib/sequel/adapters/ibmdb.rb +52 -58
- data/lib/sequel/adapters/jdbc.rb +149 -127
- data/lib/sequel/adapters/jdbc/db2.rb +32 -40
- data/lib/sequel/adapters/jdbc/derby.rb +56 -58
- data/lib/sequel/adapters/jdbc/h2.rb +40 -30
- data/lib/sequel/adapters/jdbc/hsqldb.rb +22 -33
- data/lib/sequel/adapters/jdbc/jtds.rb +4 -10
- data/lib/sequel/adapters/jdbc/mssql.rb +6 -12
- data/lib/sequel/adapters/jdbc/mysql.rb +17 -18
- data/lib/sequel/adapters/jdbc/oracle.rb +25 -19
- data/lib/sequel/adapters/jdbc/postgresql.rb +90 -69
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +14 -24
- data/lib/sequel/adapters/jdbc/sqlite.rb +50 -12
- data/lib/sequel/adapters/jdbc/sqlserver.rb +36 -9
- data/lib/sequel/adapters/jdbc/transactions.rb +25 -39
- data/lib/sequel/adapters/mock.rb +104 -113
- data/lib/sequel/adapters/mysql.rb +42 -61
- data/lib/sequel/adapters/mysql2.rb +126 -35
- data/lib/sequel/adapters/odbc.rb +21 -28
- data/lib/sequel/adapters/odbc/db2.rb +3 -1
- data/lib/sequel/adapters/odbc/mssql.rb +11 -15
- data/lib/sequel/adapters/odbc/oracle.rb +11 -0
- data/lib/sequel/adapters/oracle.rb +62 -68
- data/lib/sequel/adapters/postgres.rb +257 -311
- data/lib/sequel/adapters/postgresql.rb +3 -1
- data/lib/sequel/adapters/shared/access.rb +75 -79
- data/lib/sequel/adapters/shared/db2.rb +96 -74
- data/lib/sequel/adapters/shared/mssql.rb +258 -213
- data/lib/sequel/adapters/shared/mysql.rb +284 -216
- data/lib/sequel/adapters/shared/oracle.rb +175 -60
- data/lib/sequel/adapters/shared/postgres.rb +829 -383
- data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -127
- data/lib/sequel/adapters/shared/sqlite.rb +382 -159
- data/lib/sequel/adapters/sqlanywhere.rb +53 -38
- data/lib/sequel/adapters/sqlite.rb +111 -105
- data/lib/sequel/adapters/tinytds.rb +38 -46
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -9
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -5
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
- data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
- data/lib/sequel/adapters/utils/replace.rb +3 -4
- data/lib/sequel/adapters/utils/split_alter_table.rb +2 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +9 -22
- data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
- data/lib/sequel/ast_transformer.rb +13 -89
- data/lib/sequel/connection_pool.rb +54 -26
- data/lib/sequel/connection_pool/sharded_single.rb +19 -12
- data/lib/sequel/connection_pool/sharded_threaded.rb +160 -111
- data/lib/sequel/connection_pool/single.rb +21 -12
- data/lib/sequel/connection_pool/threaded.rb +137 -119
- data/lib/sequel/core.rb +352 -320
- data/lib/sequel/database.rb +19 -2
- data/lib/sequel/database/connecting.rb +70 -55
- data/lib/sequel/database/dataset.rb +15 -5
- data/lib/sequel/database/dataset_defaults.rb +20 -102
- data/lib/sequel/database/features.rb +20 -4
- data/lib/sequel/database/logging.rb +25 -7
- data/lib/sequel/database/misc.rb +132 -118
- data/lib/sequel/database/query.rb +51 -28
- data/lib/sequel/database/schema_generator.rb +188 -75
- data/lib/sequel/database/schema_methods.rb +161 -92
- data/lib/sequel/database/transactions.rb +260 -58
- data/lib/sequel/dataset.rb +28 -12
- data/lib/sequel/dataset/actions.rb +354 -170
- data/lib/sequel/dataset/dataset_module.rb +46 -0
- data/lib/sequel/dataset/features.rb +81 -34
- data/lib/sequel/dataset/graph.rb +82 -58
- data/lib/sequel/dataset/misc.rb +139 -47
- data/lib/sequel/dataset/placeholder_literalizer.rb +66 -26
- data/lib/sequel/dataset/prepared_statements.rb +188 -85
- data/lib/sequel/dataset/query.rb +428 -214
- data/lib/sequel/dataset/sql.rb +446 -339
- data/lib/sequel/deprecated.rb +14 -2
- data/lib/sequel/exceptions.rb +48 -16
- data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
- data/lib/sequel/extensions/_model_pg_row.rb +43 -0
- data/lib/sequel/extensions/_pretty_table.rb +10 -9
- data/lib/sequel/extensions/any_not_empty.rb +45 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +15 -11
- data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
- data/lib/sequel/extensions/blank.rb +2 -0
- data/lib/sequel/extensions/caller_logging.rb +79 -0
- data/lib/sequel/extensions/columns_introspection.rb +9 -4
- data/lib/sequel/extensions/connection_expiration.rb +99 -0
- data/lib/sequel/extensions/connection_validator.rb +26 -13
- data/lib/sequel/extensions/constant_sql_override.rb +65 -0
- data/lib/sequel/extensions/constraint_validations.rb +93 -38
- data/lib/sequel/extensions/core_extensions.rb +45 -53
- data/lib/sequel/extensions/core_refinements.rb +44 -46
- data/lib/sequel/extensions/current_datetime_timestamp.rb +5 -4
- data/lib/sequel/extensions/dataset_source_alias.rb +4 -0
- data/lib/sequel/extensions/date_arithmetic.rb +42 -16
- data/lib/sequel/extensions/datetime_parse_to_time.rb +37 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +94 -0
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -3
- data/lib/sequel/extensions/error_sql.rb +7 -3
- data/lib/sequel/extensions/escaped_like.rb +100 -0
- data/lib/sequel/extensions/eval_inspect.rb +14 -15
- data/lib/sequel/extensions/exclude_or_null.rb +68 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/freeze_datasets.rb +3 -0
- data/lib/sequel/extensions/from_block.rb +2 -31
- data/lib/sequel/extensions/graph_each.rb +19 -6
- data/lib/sequel/extensions/identifier_mangling.rb +180 -0
- data/lib/sequel/extensions/implicit_subquery.rb +48 -0
- data/lib/sequel/extensions/index_caching.rb +109 -0
- data/lib/sequel/extensions/inflector.rb +8 -4
- data/lib/sequel/extensions/integer64.rb +32 -0
- data/lib/sequel/extensions/looser_typecasting.rb +19 -9
- data/lib/sequel/extensions/migration.rb +132 -80
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +4 -0
- data/lib/sequel/extensions/named_timezones.rb +88 -23
- data/lib/sequel/extensions/no_auto_literal_strings.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +12 -8
- data/lib/sequel/extensions/pagination.rb +35 -28
- data/lib/sequel/extensions/pg_array.rb +227 -316
- data/lib/sequel/extensions/pg_array_ops.rb +19 -7
- data/lib/sequel/extensions/pg_enum.rb +69 -24
- data/lib/sequel/extensions/pg_extended_date_support.rb +250 -0
- data/lib/sequel/extensions/pg_hstore.rb +50 -59
- data/lib/sequel/extensions/pg_hstore_ops.rb +9 -3
- data/lib/sequel/extensions/pg_inet.rb +34 -15
- data/lib/sequel/extensions/pg_inet_ops.rb +5 -1
- data/lib/sequel/extensions/pg_interval.rb +26 -26
- data/lib/sequel/extensions/pg_json.rb +422 -141
- data/lib/sequel/extensions/pg_json_ops.rb +248 -9
- data/lib/sequel/extensions/pg_loose_count.rb +5 -1
- data/lib/sequel/extensions/pg_range.rb +162 -146
- data/lib/sequel/extensions/pg_range_ops.rb +10 -5
- data/lib/sequel/extensions/pg_row.rb +53 -87
- data/lib/sequel/extensions/pg_row_ops.rb +36 -13
- data/lib/sequel/extensions/pg_static_cache_updater.rb +6 -2
- data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +12 -7
- data/lib/sequel/extensions/round_timestamps.rb +6 -9
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +59 -0
- data/lib/sequel/extensions/schema_caching.rb +14 -1
- data/lib/sequel/extensions/schema_dumper.rb +83 -55
- data/lib/sequel/extensions/select_remove.rb +8 -4
- data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
- data/lib/sequel/extensions/server_block.rb +50 -17
- data/lib/sequel/extensions/server_logging.rb +61 -0
- data/lib/sequel/extensions/split_array_nil.rb +8 -4
- data/lib/sequel/extensions/sql_comments.rb +96 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -1
- data/lib/sequel/extensions/string_agg.rb +181 -0
- data/lib/sequel/extensions/string_date_time.rb +2 -0
- data/lib/sequel/extensions/symbol_aref.rb +53 -0
- data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
- data/lib/sequel/extensions/symbol_as.rb +23 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
- data/lib/sequel/extensions/synchronize_sql.rb +45 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +4 -0
- data/lib/sequel/extensions/to_dot.rb +15 -5
- data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
- data/lib/sequel/model.rb +36 -126
- data/lib/sequel/model/associations.rb +850 -257
- data/lib/sequel/model/base.rb +652 -764
- data/lib/sequel/model/dataset_module.rb +13 -10
- data/lib/sequel/model/default_inflections.rb +3 -1
- data/lib/sequel/model/errors.rb +3 -3
- data/lib/sequel/model/exceptions.rb +12 -12
- data/lib/sequel/model/inflections.rb +8 -19
- data/lib/sequel/model/plugins.rb +111 -0
- data/lib/sequel/plugins/accessed_columns.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +32 -7
- data/lib/sequel/plugins/after_initialize.rb +3 -1
- data/lib/sequel/plugins/association_dependencies.rb +27 -18
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
- data/lib/sequel/plugins/association_pks.rb +181 -83
- data/lib/sequel/plugins/association_proxies.rb +33 -9
- data/lib/sequel/plugins/auto_validations.rb +58 -23
- data/lib/sequel/plugins/before_after_save.rb +8 -0
- data/lib/sequel/plugins/blacklist_security.rb +23 -12
- data/lib/sequel/plugins/boolean_readers.rb +9 -6
- data/lib/sequel/plugins/boolean_subsets.rb +64 -0
- data/lib/sequel/plugins/caching.rb +27 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +192 -94
- data/lib/sequel/plugins/column_conflicts.rb +18 -3
- data/lib/sequel/plugins/column_select.rb +9 -5
- data/lib/sequel/plugins/columns_updated.rb +42 -0
- data/lib/sequel/plugins/composition.rb +36 -24
- data/lib/sequel/plugins/constraint_validations.rb +37 -16
- data/lib/sequel/plugins/csv_serializer.rb +58 -35
- data/lib/sequel/plugins/dataset_associations.rb +60 -18
- data/lib/sequel/plugins/def_dataset_method.rb +90 -0
- data/lib/sequel/plugins/defaults_setter.rb +74 -13
- data/lib/sequel/plugins/delay_add_association.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +65 -24
- data/lib/sequel/plugins/eager_each.rb +27 -3
- data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
- data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
- data/lib/sequel/plugins/error_splitter.rb +19 -12
- data/lib/sequel/plugins/finder.rb +246 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/force_encoding.rb +9 -12
- data/lib/sequel/plugins/hook_class_methods.rb +39 -54
- data/lib/sequel/plugins/input_transformer.rb +20 -10
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/insert_returning_select.rb +4 -2
- data/lib/sequel/plugins/instance_filters.rb +12 -8
- data/lib/sequel/plugins/instance_hooks.rb +36 -17
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/inverted_subsets.rb +24 -13
- data/lib/sequel/plugins/json_serializer.rb +123 -47
- data/lib/sequel/plugins/lazy_attributes.rb +20 -14
- data/lib/sequel/plugins/list.rb +40 -26
- data/lib/sequel/plugins/many_through_many.rb +28 -12
- data/lib/sequel/plugins/modification_detection.rb +17 -5
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -5
- data/lib/sequel/plugins/nested_attributes.rb +55 -28
- data/lib/sequel/plugins/optimistic_locking.rb +5 -3
- data/lib/sequel/plugins/pg_array_associations.rb +52 -18
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +348 -0
- data/lib/sequel/plugins/pg_row.rb +7 -51
- data/lib/sequel/plugins/prepared_statements.rb +53 -72
- data/lib/sequel/plugins/prepared_statements_safe.rb +13 -5
- data/lib/sequel/plugins/rcte_tree.rb +43 -63
- data/lib/sequel/plugins/serialization.rb +37 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +3 -1
- data/lib/sequel/plugins/sharding.rb +17 -10
- data/lib/sequel/plugins/single_table_inheritance.rb +62 -28
- data/lib/sequel/plugins/singular_table_names.rb +2 -0
- data/lib/sequel/plugins/skip_create_refresh.rb +5 -3
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/split_values.rb +13 -6
- data/lib/sequel/plugins/static_cache.rb +79 -53
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/string_stripper.rb +5 -3
- data/lib/sequel/plugins/subclasses.rb +20 -2
- data/lib/sequel/plugins/subset_conditions.rb +48 -0
- data/lib/sequel/plugins/table_select.rb +4 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +120 -6
- data/lib/sequel/plugins/throw_failures.rb +110 -0
- data/lib/sequel/plugins/timestamps.rb +22 -8
- data/lib/sequel/plugins/touch.rb +21 -8
- data/lib/sequel/plugins/tree.rb +57 -30
- data/lib/sequel/plugins/typecast_on_load.rb +14 -4
- data/lib/sequel/plugins/unlimited_update.rb +3 -7
- data/lib/sequel/plugins/update_or_create.rb +6 -4
- data/lib/sequel/plugins/update_primary_key.rb +3 -1
- data/lib/sequel/plugins/update_refresh.rb +28 -15
- data/lib/sequel/plugins/uuid.rb +70 -0
- data/lib/sequel/plugins/validate_associated.rb +20 -0
- data/lib/sequel/plugins/validation_class_methods.rb +40 -19
- data/lib/sequel/plugins/validation_contexts.rb +49 -0
- data/lib/sequel/plugins/validation_helpers.rb +49 -31
- data/lib/sequel/plugins/whitelist_security.rb +122 -0
- data/lib/sequel/plugins/xml_serializer.rb +31 -30
- data/lib/sequel/sql.rb +479 -329
- data/lib/sequel/timezones.rb +62 -32
- data/lib/sequel/version.rb +10 -3
- metadata +177 -477
- data/Rakefile +0 -165
- data/doc/active_record.rdoc +0 -912
- data/doc/release_notes/1.0.txt +0 -38
- data/doc/release_notes/1.1.txt +0 -143
- data/doc/release_notes/1.3.txt +0 -101
- data/doc/release_notes/1.4.0.txt +0 -53
- data/doc/release_notes/1.5.0.txt +0 -155
- data/doc/release_notes/2.0.0.txt +0 -298
- data/doc/release_notes/2.1.0.txt +0 -271
- data/doc/release_notes/2.10.0.txt +0 -328
- data/doc/release_notes/2.11.0.txt +0 -215
- data/doc/release_notes/2.12.0.txt +0 -534
- data/doc/release_notes/2.2.0.txt +0 -253
- data/doc/release_notes/2.3.0.txt +0 -88
- data/doc/release_notes/2.4.0.txt +0 -106
- data/doc/release_notes/2.5.0.txt +0 -137
- data/doc/release_notes/2.6.0.txt +0 -157
- data/doc/release_notes/2.7.0.txt +0 -166
- data/doc/release_notes/2.8.0.txt +0 -171
- data/doc/release_notes/2.9.0.txt +0 -97
- data/doc/release_notes/3.0.0.txt +0 -221
- data/doc/release_notes/3.1.0.txt +0 -406
- data/doc/release_notes/3.10.0.txt +0 -286
- data/doc/release_notes/3.11.0.txt +0 -254
- data/doc/release_notes/3.12.0.txt +0 -304
- data/doc/release_notes/3.13.0.txt +0 -210
- data/doc/release_notes/3.14.0.txt +0 -118
- data/doc/release_notes/3.15.0.txt +0 -78
- data/doc/release_notes/3.16.0.txt +0 -45
- data/doc/release_notes/3.17.0.txt +0 -58
- data/doc/release_notes/3.18.0.txt +0 -120
- data/doc/release_notes/3.19.0.txt +0 -67
- data/doc/release_notes/3.2.0.txt +0 -268
- data/doc/release_notes/3.20.0.txt +0 -41
- data/doc/release_notes/3.21.0.txt +0 -87
- data/doc/release_notes/3.22.0.txt +0 -39
- data/doc/release_notes/3.23.0.txt +0 -172
- data/doc/release_notes/3.24.0.txt +0 -420
- data/doc/release_notes/3.25.0.txt +0 -88
- data/doc/release_notes/3.26.0.txt +0 -88
- data/doc/release_notes/3.27.0.txt +0 -82
- data/doc/release_notes/3.28.0.txt +0 -304
- data/doc/release_notes/3.29.0.txt +0 -459
- data/doc/release_notes/3.3.0.txt +0 -192
- data/doc/release_notes/3.30.0.txt +0 -135
- data/doc/release_notes/3.31.0.txt +0 -146
- data/doc/release_notes/3.32.0.txt +0 -202
- data/doc/release_notes/3.33.0.txt +0 -157
- data/doc/release_notes/3.34.0.txt +0 -671
- data/doc/release_notes/3.35.0.txt +0 -144
- data/doc/release_notes/3.36.0.txt +0 -245
- data/doc/release_notes/3.37.0.txt +0 -338
- data/doc/release_notes/3.38.0.txt +0 -234
- data/doc/release_notes/3.39.0.txt +0 -237
- data/doc/release_notes/3.4.0.txt +0 -325
- data/doc/release_notes/3.40.0.txt +0 -73
- data/doc/release_notes/3.41.0.txt +0 -155
- data/doc/release_notes/3.42.0.txt +0 -74
- data/doc/release_notes/3.43.0.txt +0 -105
- data/doc/release_notes/3.44.0.txt +0 -152
- data/doc/release_notes/3.45.0.txt +0 -179
- data/doc/release_notes/3.46.0.txt +0 -122
- data/doc/release_notes/3.47.0.txt +0 -270
- data/doc/release_notes/3.48.0.txt +0 -477
- data/doc/release_notes/3.5.0.txt +0 -510
- data/doc/release_notes/3.6.0.txt +0 -366
- data/doc/release_notes/3.7.0.txt +0 -179
- data/doc/release_notes/3.8.0.txt +0 -151
- data/doc/release_notes/3.9.0.txt +0 -233
- data/doc/release_notes/4.0.0.txt +0 -262
- data/doc/release_notes/4.1.0.txt +0 -85
- data/doc/release_notes/4.10.0.txt +0 -226
- data/doc/release_notes/4.11.0.txt +0 -147
- data/doc/release_notes/4.12.0.txt +0 -105
- data/doc/release_notes/4.13.0.txt +0 -169
- data/doc/release_notes/4.14.0.txt +0 -68
- data/doc/release_notes/4.15.0.txt +0 -56
- data/doc/release_notes/4.16.0.txt +0 -36
- data/doc/release_notes/4.17.0.txt +0 -38
- data/doc/release_notes/4.18.0.txt +0 -36
- data/doc/release_notes/4.19.0.txt +0 -45
- data/doc/release_notes/4.2.0.txt +0 -129
- data/doc/release_notes/4.20.0.txt +0 -79
- data/doc/release_notes/4.21.0.txt +0 -94
- data/doc/release_notes/4.22.0.txt +0 -72
- data/doc/release_notes/4.23.0.txt +0 -65
- data/doc/release_notes/4.24.0.txt +0 -99
- data/doc/release_notes/4.25.0.txt +0 -181
- data/doc/release_notes/4.26.0.txt +0 -44
- data/doc/release_notes/4.3.0.txt +0 -40
- data/doc/release_notes/4.4.0.txt +0 -92
- data/doc/release_notes/4.5.0.txt +0 -34
- data/doc/release_notes/4.6.0.txt +0 -30
- data/doc/release_notes/4.7.0.txt +0 -103
- data/doc/release_notes/4.8.0.txt +0 -175
- data/doc/release_notes/4.9.0.txt +0 -190
- data/lib/sequel/adapters/cubrid.rb +0 -142
- data/lib/sequel/adapters/do.rb +0 -156
- data/lib/sequel/adapters/do/mysql.rb +0 -64
- data/lib/sequel/adapters/do/postgres.rb +0 -42
- data/lib/sequel/adapters/do/sqlite3.rb +0 -40
- data/lib/sequel/adapters/jdbc/as400.rb +0 -82
- data/lib/sequel/adapters/jdbc/cubrid.rb +0 -62
- data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -34
- data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -31
- data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -31
- data/lib/sequel/adapters/odbc/progress.rb +0 -8
- data/lib/sequel/adapters/shared/cubrid.rb +0 -243
- data/lib/sequel/adapters/shared/firebird.rb +0 -245
- data/lib/sequel/adapters/shared/informix.rb +0 -52
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -150
- data/lib/sequel/adapters/shared/progress.rb +0 -38
- data/lib/sequel/adapters/swift.rb +0 -158
- data/lib/sequel/adapters/swift/mysql.rb +0 -47
- data/lib/sequel/adapters/swift/postgres.rb +0 -45
- data/lib/sequel/adapters/swift/sqlite.rb +0 -47
- data/lib/sequel/adapters/utils/pg_types.rb +0 -68
- data/lib/sequel/dataset/mutation.rb +0 -109
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -3
- data/lib/sequel/extensions/filter_having.rb +0 -59
- data/lib/sequel/extensions/hash_aliases.rb +0 -45
- data/lib/sequel/extensions/meta_def.rb +0 -31
- data/lib/sequel/extensions/query_literals.rb +0 -80
- data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -22
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -118
- data/lib/sequel/extensions/set_overrides.rb +0 -72
- data/lib/sequel/no_core_ext.rb +0 -1
- data/lib/sequel/plugins/association_autoreloading.rb +0 -7
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -7
- data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -78
- data/lib/sequel/plugins/prepared_statements_associations.rb +0 -117
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -59
- data/lib/sequel/plugins/schema.rb +0 -80
- data/lib/sequel/plugins/scissors.rb +0 -33
- data/spec/adapters/db2_spec.rb +0 -160
- data/spec/adapters/firebird_spec.rb +0 -411
- data/spec/adapters/informix_spec.rb +0 -100
- data/spec/adapters/mssql_spec.rb +0 -706
- data/spec/adapters/mysql_spec.rb +0 -1287
- data/spec/adapters/oracle_spec.rb +0 -313
- data/spec/adapters/postgres_spec.rb +0 -3725
- data/spec/adapters/spec_helper.rb +0 -43
- data/spec/adapters/sqlanywhere_spec.rb +0 -170
- data/spec/adapters/sqlite_spec.rb +0 -653
- data/spec/bin_spec.rb +0 -254
- data/spec/core/connection_pool_spec.rb +0 -1016
- data/spec/core/database_spec.rb +0 -2531
- data/spec/core/dataset_spec.rb +0 -5098
- data/spec/core/deprecated_spec.rb +0 -70
- data/spec/core/expression_filters_spec.rb +0 -1243
- data/spec/core/mock_adapter_spec.rb +0 -462
- data/spec/core/object_graph_spec.rb +0 -303
- data/spec/core/placeholder_literalizer_spec.rb +0 -163
- data/spec/core/schema_generator_spec.rb +0 -179
- data/spec/core/schema_spec.rb +0 -1659
- data/spec/core/spec_helper.rb +0 -34
- data/spec/core/version_spec.rb +0 -7
- data/spec/core_extensions_spec.rb +0 -699
- data/spec/extensions/accessed_columns_spec.rb +0 -51
- data/spec/extensions/active_model_spec.rb +0 -123
- data/spec/extensions/after_initialize_spec.rb +0 -24
- data/spec/extensions/arbitrary_servers_spec.rb +0 -109
- data/spec/extensions/association_dependencies_spec.rb +0 -117
- data/spec/extensions/association_pks_spec.rb +0 -365
- data/spec/extensions/association_proxies_spec.rb +0 -86
- data/spec/extensions/auto_validations_spec.rb +0 -192
- data/spec/extensions/blacklist_security_spec.rb +0 -88
- data/spec/extensions/blank_spec.rb +0 -69
- data/spec/extensions/boolean_readers_spec.rb +0 -93
- data/spec/extensions/caching_spec.rb +0 -270
- data/spec/extensions/class_table_inheritance_spec.rb +0 -420
- data/spec/extensions/column_conflicts_spec.rb +0 -60
- data/spec/extensions/column_select_spec.rb +0 -108
- data/spec/extensions/columns_introspection_spec.rb +0 -91
- data/spec/extensions/composition_spec.rb +0 -242
- data/spec/extensions/connection_validator_spec.rb +0 -120
- data/spec/extensions/constraint_validations_plugin_spec.rb +0 -274
- data/spec/extensions/constraint_validations_spec.rb +0 -325
- data/spec/extensions/core_refinements_spec.rb +0 -519
- data/spec/extensions/csv_serializer_spec.rb +0 -173
- data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
- data/spec/extensions/dataset_associations_spec.rb +0 -311
- data/spec/extensions/dataset_source_alias_spec.rb +0 -51
- data/spec/extensions/date_arithmetic_spec.rb +0 -150
- data/spec/extensions/defaults_setter_spec.rb +0 -101
- data/spec/extensions/delay_add_association_spec.rb +0 -52
- data/spec/extensions/dirty_spec.rb +0 -180
- data/spec/extensions/eager_each_spec.rb +0 -42
- data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
- data/spec/extensions/error_splitter_spec.rb +0 -18
- data/spec/extensions/error_sql_spec.rb +0 -20
- data/spec/extensions/eval_inspect_spec.rb +0 -73
- data/spec/extensions/filter_having_spec.rb +0 -40
- data/spec/extensions/force_encoding_spec.rb +0 -114
- data/spec/extensions/from_block_spec.rb +0 -21
- data/spec/extensions/graph_each_spec.rb +0 -109
- data/spec/extensions/hash_aliases_spec.rb +0 -24
- data/spec/extensions/hook_class_methods_spec.rb +0 -429
- data/spec/extensions/inflector_spec.rb +0 -183
- data/spec/extensions/input_transformer_spec.rb +0 -54
- data/spec/extensions/insert_returning_select_spec.rb +0 -46
- data/spec/extensions/instance_filters_spec.rb +0 -79
- data/spec/extensions/instance_hooks_spec.rb +0 -276
- data/spec/extensions/inverted_subsets_spec.rb +0 -33
- data/spec/extensions/json_serializer_spec.rb +0 -291
- data/spec/extensions/lazy_attributes_spec.rb +0 -170
- data/spec/extensions/list_spec.rb +0 -267
- data/spec/extensions/looser_typecasting_spec.rb +0 -43
- data/spec/extensions/many_through_many_spec.rb +0 -2172
- data/spec/extensions/meta_def_spec.rb +0 -21
- data/spec/extensions/migration_spec.rb +0 -712
- data/spec/extensions/modification_detection_spec.rb +0 -80
- data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
- data/spec/extensions/named_timezones_spec.rb +0 -108
- data/spec/extensions/nested_attributes_spec.rb +0 -697
- data/spec/extensions/null_dataset_spec.rb +0 -85
- data/spec/extensions/optimistic_locking_spec.rb +0 -128
- data/spec/extensions/pagination_spec.rb +0 -118
- data/spec/extensions/pg_array_associations_spec.rb +0 -736
- data/spec/extensions/pg_array_ops_spec.rb +0 -143
- data/spec/extensions/pg_array_spec.rb +0 -395
- data/spec/extensions/pg_enum_spec.rb +0 -92
- data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
- data/spec/extensions/pg_hstore_spec.rb +0 -206
- data/spec/extensions/pg_inet_ops_spec.rb +0 -101
- data/spec/extensions/pg_inet_spec.rb +0 -52
- data/spec/extensions/pg_interval_spec.rb +0 -76
- data/spec/extensions/pg_json_ops_spec.rb +0 -229
- data/spec/extensions/pg_json_spec.rb +0 -218
- data/spec/extensions/pg_loose_count_spec.rb +0 -17
- data/spec/extensions/pg_range_ops_spec.rb +0 -58
- data/spec/extensions/pg_range_spec.rb +0 -404
- data/spec/extensions/pg_row_ops_spec.rb +0 -60
- data/spec/extensions/pg_row_plugin_spec.rb +0 -62
- data/spec/extensions/pg_row_spec.rb +0 -360
- data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
- data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
- data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
- data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
- data/spec/extensions/prepared_statements_spec.rb +0 -103
- data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
- data/spec/extensions/pretty_table_spec.rb +0 -92
- data/spec/extensions/query_literals_spec.rb +0 -183
- data/spec/extensions/query_spec.rb +0 -102
- data/spec/extensions/rcte_tree_spec.rb +0 -392
- data/spec/extensions/round_timestamps_spec.rb +0 -43
- data/spec/extensions/schema_caching_spec.rb +0 -41
- data/spec/extensions/schema_dumper_spec.rb +0 -789
- data/spec/extensions/schema_spec.rb +0 -117
- data/spec/extensions/scissors_spec.rb +0 -26
- data/spec/extensions/select_remove_spec.rb +0 -38
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
- data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
- data/spec/extensions/serialization_spec.rb +0 -362
- data/spec/extensions/server_block_spec.rb +0 -90
- data/spec/extensions/set_overrides_spec.rb +0 -61
- data/spec/extensions/sharding_spec.rb +0 -198
- data/spec/extensions/shared_caching_spec.rb +0 -175
- data/spec/extensions/single_table_inheritance_spec.rb +0 -297
- data/spec/extensions/singular_table_names_spec.rb +0 -22
- data/spec/extensions/skip_create_refresh_spec.rb +0 -17
- data/spec/extensions/spec_helper.rb +0 -71
- data/spec/extensions/split_array_nil_spec.rb +0 -24
- data/spec/extensions/split_values_spec.rb +0 -22
- data/spec/extensions/sql_expr_spec.rb +0 -60
- data/spec/extensions/static_cache_spec.rb +0 -361
- data/spec/extensions/string_date_time_spec.rb +0 -95
- data/spec/extensions/string_stripper_spec.rb +0 -68
- data/spec/extensions/subclasses_spec.rb +0 -66
- data/spec/extensions/table_select_spec.rb +0 -71
- data/spec/extensions/tactical_eager_loading_spec.rb +0 -82
- data/spec/extensions/thread_local_timezones_spec.rb +0 -67
- data/spec/extensions/timestamps_spec.rb +0 -175
- data/spec/extensions/to_dot_spec.rb +0 -154
- data/spec/extensions/touch_spec.rb +0 -203
- data/spec/extensions/tree_spec.rb +0 -274
- data/spec/extensions/typecast_on_load_spec.rb +0 -80
- data/spec/extensions/unlimited_update_spec.rb +0 -20
- data/spec/extensions/update_or_create_spec.rb +0 -87
- data/spec/extensions/update_primary_key_spec.rb +0 -100
- data/spec/extensions/update_refresh_spec.rb +0 -53
- data/spec/extensions/validate_associated_spec.rb +0 -52
- data/spec/extensions/validation_class_methods_spec.rb +0 -1027
- data/spec/extensions/validation_helpers_spec.rb +0 -541
- data/spec/extensions/xml_serializer_spec.rb +0 -207
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
- data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
- data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
- data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/reversible_migrations/001_reversible.rb +0 -5
- data/spec/files/reversible_migrations/002_reversible.rb +0 -5
- data/spec/files/reversible_migrations/003_reversible.rb +0 -5
- data/spec/files/reversible_migrations/004_reversible.rb +0 -5
- data/spec/files/reversible_migrations/005_reversible.rb +0 -10
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
- data/spec/guards_helper.rb +0 -55
- data/spec/integration/associations_test.rb +0 -2454
- data/spec/integration/database_test.rb +0 -113
- data/spec/integration/dataset_test.rb +0 -1808
- data/spec/integration/eager_loader_test.rb +0 -687
- data/spec/integration/migrator_test.rb +0 -240
- data/spec/integration/model_test.rb +0 -226
- data/spec/integration/plugin_test.rb +0 -2240
- data/spec/integration/prepared_statement_test.rb +0 -467
- data/spec/integration/schema_test.rb +0 -817
- data/spec/integration/spec_helper.rb +0 -48
- data/spec/integration/timezone_test.rb +0 -86
- data/spec/integration/transaction_test.rb +0 -374
- data/spec/integration/type_test.rb +0 -133
- data/spec/model/association_reflection_spec.rb +0 -525
- data/spec/model/associations_spec.rb +0 -4426
- data/spec/model/base_spec.rb +0 -759
- data/spec/model/class_dataset_methods_spec.rb +0 -146
- data/spec/model/dataset_methods_spec.rb +0 -149
- data/spec/model/eager_loading_spec.rb +0 -2137
- data/spec/model/hooks_spec.rb +0 -604
- data/spec/model/inflector_spec.rb +0 -26
- data/spec/model/model_spec.rb +0 -982
- data/spec/model/plugins_spec.rb +0 -299
- data/spec/model/record_spec.rb +0 -2147
- data/spec/model/spec_helper.rb +0 -46
- data/spec/model/validations_spec.rb +0 -193
- data/spec/sequel_coverage.rb +0 -15
- data/spec/spec_config.rb +0 -10
data/lib/sequel/model/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
1
3
|
module Sequel
|
2
4
|
class Model
|
3
5
|
extend Enumerable
|
@@ -5,18 +7,33 @@ module Sequel
|
|
5
7
|
|
6
8
|
# Class methods for Sequel::Model that implement basic model functionality.
|
7
9
|
#
|
8
|
-
# * All of the
|
9
|
-
# the
|
10
|
+
# * All of the following methods have class methods created that send the method
|
11
|
+
# to the model's dataset: all, as_hash, avg, count, cross_join, distinct, each,
|
12
|
+
# each_server, empty?, except, exclude, exclude_having, fetch_rows,
|
13
|
+
# filter, first, first!, for_update, from, from_self, full_join, full_outer_join,
|
14
|
+
# get, graph, grep, group, group_and_count, group_append, group_by, having, import,
|
15
|
+
# inner_join, insert, intersect, invert, join, join_table, last, left_join,
|
16
|
+
# left_outer_join, limit, lock_style, map, max, min, multi_insert, naked, natural_full_join,
|
17
|
+
# natural_join, natural_left_join, natural_right_join, offset, order, order_append, order_by,
|
18
|
+
# order_more, order_prepend, paged_each, qualify, reverse, reverse_order, right_join,
|
19
|
+
# right_outer_join, select, select_all, select_append, select_group, select_hash,
|
20
|
+
# select_hash_groups, select_map, select_more, select_order_map, server,
|
21
|
+
# single_record, single_record!, single_value, single_value!, sum, to_hash, to_hash_groups,
|
22
|
+
# truncate, unfiltered, ungraphed, ungrouped, union, unlimited, unordered, where, where_all,
|
23
|
+
# where_each, where_single_value, with, with_recursive, with_sql
|
10
24
|
module ClassMethods
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
25
|
+
# Whether to cache the anonymous models created by Sequel::Model(), true by default. This is
|
26
|
+
# required for reloading them correctly (avoiding the superclass mismatch).
|
27
|
+
attr_accessor :cache_anonymous_models
|
14
28
|
|
15
29
|
# Array of modules that extend this model's dataset. Stored
|
16
30
|
# so that if the model's dataset is changed, it will be extended
|
17
31
|
# with all of these modules.
|
18
32
|
attr_reader :dataset_method_modules
|
19
33
|
|
34
|
+
# The Module subclass to use for dataset_module blocks.
|
35
|
+
attr_reader :dataset_module_class
|
36
|
+
|
20
37
|
# The default options to use for Model#set_fields. These are merged with
|
21
38
|
# the options given to set_fields.
|
22
39
|
attr_accessor :default_set_fields_options
|
@@ -25,6 +42,10 @@ module Sequel
|
|
25
42
|
# model instances, or nil if the optimization should not be used. For internal use only.
|
26
43
|
attr_reader :fast_instance_delete_sql
|
27
44
|
|
45
|
+
# SQL string fragment used for faster lookups by primary key, or nil if the optimization
|
46
|
+
# should not be used. For internal use only.
|
47
|
+
attr_reader :fast_pk_lookup_sql
|
48
|
+
|
28
49
|
# The dataset that instance datasets (#this) are based on. Generally a naked version of
|
29
50
|
# the model's dataset limited to one row. For internal use only.
|
30
51
|
attr_reader :instance_dataset
|
@@ -46,11 +67,8 @@ module Sequel
|
|
46
67
|
attr_accessor :raise_on_save_failure
|
47
68
|
|
48
69
|
# Whether to raise an error when unable to typecast data for a column
|
49
|
-
# (default:
|
50
|
-
#
|
51
|
-
# web applications). You can use the validates_schema_types validation
|
52
|
-
# (from the validation_helpers plugin) in connection with this setting to
|
53
|
-
# check for typecast failures during validation.
|
70
|
+
# (default: false). This should be set to true if you want to have model
|
71
|
+
# setter methods raise errors if the argument cannot be typecast properly.
|
54
72
|
attr_accessor :raise_on_typecast_failure
|
55
73
|
|
56
74
|
# Whether to raise an error if an UPDATE or DELETE query related to
|
@@ -58,6 +76,12 @@ module Sequel
|
|
58
76
|
# Sequel will not check the number of rows modified (default: true).
|
59
77
|
attr_accessor :require_modification
|
60
78
|
|
79
|
+
# If true (the default), requires that all models have valid tables,
|
80
|
+
# raising exceptions if creating a model without a valid table backing it.
|
81
|
+
# Setting this to false will allow the creation of model classes where the
|
82
|
+
# underlying table doesn't exist.
|
83
|
+
attr_accessor :require_valid_table
|
84
|
+
|
61
85
|
# Should be the literal primary key column name if this Model's table has a simple primary key, or
|
62
86
|
# nil if the model has a compound primary key or no primary key.
|
63
87
|
attr_reader :simple_pk
|
@@ -66,7 +90,7 @@ module Sequel
|
|
66
90
|
# or nil otherwise. This and simple_pk are used for an optimization in Model.[].
|
67
91
|
attr_reader :simple_table
|
68
92
|
|
69
|
-
# Whether new
|
93
|
+
# Whether mass assigning via .create/.new/#set/#update should raise an error
|
70
94
|
# if an invalid key is used. A key is invalid if no setter method exists
|
71
95
|
# for that key or the access to the setter method is restricted (e.g. due to it
|
72
96
|
# being a primary key field). If set to false, silently skip
|
@@ -85,15 +109,95 @@ module Sequel
|
|
85
109
|
# database to typecast the value correctly.
|
86
110
|
attr_accessor :typecast_on_assignment
|
87
111
|
|
88
|
-
# Whether to enable the after_commit and after_rollback hooks when saving/destroying
|
89
|
-
# instances. On by default, can be turned off for performance reasons or when using
|
90
|
-
# prepared transactions (which aren't compatible with after commit/rollback).
|
91
|
-
attr_accessor :use_after_commit_rollback
|
92
|
-
|
93
112
|
# Whether to use a transaction by default when saving/deleting records (default: true).
|
94
113
|
# If you are sending database queries in before_* or after_* hooks, you shouldn't change
|
95
114
|
# the default setting without a good reason.
|
96
115
|
attr_accessor :use_transactions
|
116
|
+
|
117
|
+
# Define a Model method on the given module that calls the Model
|
118
|
+
# method on the receiver. This is how the Sequel::Model() method is
|
119
|
+
# defined, and allows you to define Model() methods on other modules,
|
120
|
+
# making it easier to have custom model settings for all models under
|
121
|
+
# a namespace. Example:
|
122
|
+
#
|
123
|
+
# module Foo
|
124
|
+
# Model = Class.new(Sequel::Model)
|
125
|
+
# Model.def_Model(self)
|
126
|
+
# DB = Model.db = Sequel.connect(ENV['FOO_DATABASE_URL'])
|
127
|
+
# Model.plugin :prepared_statements
|
128
|
+
#
|
129
|
+
# class Bar < Model
|
130
|
+
# # Uses Foo::DB[:bars]
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# class Baz < Model(:my_baz)
|
134
|
+
# # Uses Foo::DB[:my_baz]
|
135
|
+
# end
|
136
|
+
# end
|
137
|
+
def def_Model(mod)
|
138
|
+
model = self
|
139
|
+
mod.define_singleton_method(:Model) do |source|
|
140
|
+
model.Model(source)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Lets you create a Model subclass with its dataset already set.
|
145
|
+
# +source+ should be an instance of one of the following classes:
|
146
|
+
#
|
147
|
+
# Database :: Sets the database for this model to +source+.
|
148
|
+
# Generally only useful when subclassing directly
|
149
|
+
# from the returned class, where the name of the
|
150
|
+
# subclass sets the table name (which is combined
|
151
|
+
# with the +Database+ in +source+ to create the
|
152
|
+
# dataset to use)
|
153
|
+
# Dataset :: Sets the dataset for this model to +source+.
|
154
|
+
# other :: Sets the table name for this model to +source+. The
|
155
|
+
# class will use the default database for model
|
156
|
+
# classes in order to create the dataset.
|
157
|
+
#
|
158
|
+
# The purpose of this method is to set the dataset/database automatically
|
159
|
+
# for a model class, if the table name doesn't match the default table
|
160
|
+
# name that Sequel would use.
|
161
|
+
#
|
162
|
+
# When creating subclasses of Sequel::Model itself, this method is usually
|
163
|
+
# called on Sequel itself, using <tt>Sequel::Model(:something)</tt>.
|
164
|
+
#
|
165
|
+
# # Using a symbol
|
166
|
+
# class Comment < Sequel::Model(:something)
|
167
|
+
# table_name # => :something
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# # Using a dataset
|
171
|
+
# class Comment < Sequel::Model(DB1[:something])
|
172
|
+
# dataset # => DB1[:something]
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# # Using a database
|
176
|
+
# class Comment < Sequel::Model(DB1)
|
177
|
+
# dataset # => DB1[:comments]
|
178
|
+
# end
|
179
|
+
def Model(source)
|
180
|
+
if cache_anonymous_models
|
181
|
+
cache = Sequel.synchronize{@Model_cache ||= {}}
|
182
|
+
if klass = Sequel.synchronize{cache[source]}
|
183
|
+
return klass
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
klass = Class.new(self)
|
188
|
+
|
189
|
+
if source.is_a?(::Sequel::Database)
|
190
|
+
klass.db = source
|
191
|
+
else
|
192
|
+
klass.set_dataset(source)
|
193
|
+
end
|
194
|
+
|
195
|
+
if cache_anonymous_models
|
196
|
+
Sequel.synchronize{cache[source] = klass}
|
197
|
+
end
|
198
|
+
|
199
|
+
klass
|
200
|
+
end
|
97
201
|
|
98
202
|
# Returns the first record from the database matching the conditions.
|
99
203
|
# If a hash is given, it is used as the conditions. If another
|
@@ -103,11 +207,11 @@ module Sequel
|
|
103
207
|
# Artist[1] # SELECT * FROM artists WHERE id = 1
|
104
208
|
# # => #<Artist {:id=>1, ...}>
|
105
209
|
#
|
106
|
-
# Artist[:
|
210
|
+
# Artist[name: 'Bob'] # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
107
211
|
# # => #<Artist {:name=>'Bob', ...}>
|
108
212
|
def [](*args)
|
109
213
|
args = args.first if args.size <= 1
|
110
|
-
args.is_a?(Hash) ?
|
214
|
+
args.is_a?(Hash) ? first(args) : (primary_key_lookup(args) unless args.nil?)
|
111
215
|
end
|
112
216
|
|
113
217
|
# Initializes a model instance as an existing record. This constructor is
|
@@ -122,7 +226,7 @@ module Sequel
|
|
122
226
|
|
123
227
|
# Clear the setter_methods cache
|
124
228
|
def clear_setter_methods_cache
|
125
|
-
@setter_methods = nil
|
229
|
+
@setter_methods = nil unless frozen?
|
126
230
|
end
|
127
231
|
|
128
232
|
# Returns the columns in the result set in their original order.
|
@@ -133,18 +237,20 @@ module Sequel
|
|
133
237
|
# Artist.columns
|
134
238
|
# # => [:id, :name]
|
135
239
|
def columns
|
136
|
-
@columns
|
240
|
+
return @columns if @columns
|
241
|
+
return nil if frozen?
|
242
|
+
set_columns(dataset.naked.columns)
|
137
243
|
end
|
138
244
|
|
139
245
|
# Creates instance using new with the given values and block, and saves it.
|
140
246
|
#
|
141
|
-
# Artist.create(:
|
247
|
+
# Artist.create(name: 'Bob')
|
142
248
|
# # INSERT INTO artists (name) VALUES ('Bob')
|
143
249
|
#
|
144
250
|
# Artist.create do |a|
|
145
251
|
# a.name = 'Jim'
|
146
252
|
# end # INSERT INTO artists (name) VALUES ('Jim')
|
147
|
-
def create(values =
|
253
|
+
def create(values = OPTS, &block)
|
148
254
|
new(values, &block).save
|
149
255
|
end
|
150
256
|
|
@@ -167,49 +273,70 @@ module Sequel
|
|
167
273
|
# a plugin with the methods defined in DatasetMethods.
|
168
274
|
# This is the recommended way to add methods to model datasets.
|
169
275
|
#
|
170
|
-
# If an argument, it should be a module, and is used to extend
|
276
|
+
# If given an argument, it should be a module, and is used to extend
|
171
277
|
# the underlying dataset. Otherwise an anonymous module is created, and
|
172
278
|
# if a block is given, it is module_evaled, allowing you do define
|
173
279
|
# dataset methods directly using the standard ruby def syntax.
|
174
280
|
# Returns the module given or the anonymous module created.
|
175
281
|
#
|
176
282
|
# # Usage with existing module
|
177
|
-
#
|
283
|
+
# Album.dataset_module Sequel::ColumnsIntrospection
|
178
284
|
#
|
179
285
|
# # Usage with anonymous module
|
180
|
-
#
|
286
|
+
# Album.dataset_module do
|
181
287
|
# def foo
|
182
288
|
# :bar
|
183
289
|
# end
|
184
290
|
# end
|
185
|
-
#
|
291
|
+
# Album.dataset.foo
|
186
292
|
# # => :bar
|
187
|
-
#
|
293
|
+
# Album.foo
|
188
294
|
# # => :bar
|
189
295
|
#
|
190
296
|
# Any anonymous modules created are actually instances of Sequel::Model::DatasetModule
|
191
|
-
# (a Module subclass), which allows you to call the subset method on them
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
297
|
+
# (a Module subclass), which allows you to call the subset method on them, which
|
298
|
+
# defines a dataset method that adds a filter. There are also a number of other
|
299
|
+
# methods with the same names as the dataset methods, which can use to define
|
300
|
+
# named dataset methods:
|
301
|
+
#
|
302
|
+
# Album.dataset_module do
|
303
|
+
# where(:released, Sequel[:release_date] <= Sequel::CURRENT_DATE)
|
304
|
+
# order :by_release_date, :release_date
|
305
|
+
# select :for_select_options, :id, :name, :release_date
|
195
306
|
# end
|
307
|
+
# Album.released.sql
|
308
|
+
# # => "SELECT * FROM artists WHERE (release_date <= CURRENT_DATE)"
|
309
|
+
# Album.by_release_date.sql
|
310
|
+
# # => "SELECT * FROM artists ORDER BY release_date"
|
311
|
+
# Album.for_select_options.sql
|
312
|
+
# # => "SELECT id, name, release_date FROM artists"
|
313
|
+
# Album.released.by_release_date.for_select_options.sql
|
314
|
+
# # => "SELECT id, name, release_date FROM artists WHERE (release_date <= CURRENT_DATE) ORDER BY release_date"
|
315
|
+
#
|
316
|
+
# The following methods are supported: distinct, eager, exclude, exclude_having, grep, group, group_and_count,
|
317
|
+
# group_append, having, limit, offset, order, order_append, order_prepend, select, select_all,
|
318
|
+
# select_append, select_group, where, and server.
|
319
|
+
#
|
320
|
+
# The advantage of using these DatasetModule methods to define your dataset
|
321
|
+
# methods is that they can take advantage of dataset caching to improve
|
322
|
+
# performance.
|
196
323
|
#
|
197
324
|
# Any public methods in the dataset module will have class methods created that
|
198
325
|
# call the method on the dataset, assuming that the class method is not already
|
199
326
|
# defined.
|
200
|
-
def dataset_module(mod = nil)
|
327
|
+
def dataset_module(mod = nil, &block)
|
201
328
|
if mod
|
202
|
-
raise Error, "can't provide both argument and block to Model.dataset_module" if
|
329
|
+
raise Error, "can't provide both argument and block to Model.dataset_module" if block
|
203
330
|
dataset_extend(mod)
|
204
331
|
mod
|
205
332
|
else
|
206
|
-
@dataset_module ||=
|
207
|
-
@dataset_module.module_eval(&
|
333
|
+
@dataset_module ||= dataset_module_class.new(self)
|
334
|
+
@dataset_module.module_eval(&block) if block
|
208
335
|
dataset_extend(@dataset_module)
|
209
336
|
@dataset_module
|
210
337
|
end
|
211
338
|
end
|
212
|
-
|
339
|
+
|
213
340
|
# Returns the database associated with the Model class.
|
214
341
|
# If this model doesn't have a database associated with it,
|
215
342
|
# assumes the superclass's database, or the first object in
|
@@ -217,7 +344,7 @@ module Sequel
|
|
217
344
|
# been created, raises an error.
|
218
345
|
#
|
219
346
|
# Artist.db.transaction do # BEGIN
|
220
|
-
# Artist.create(:
|
347
|
+
# Artist.create(name: 'Bob')
|
221
348
|
# # INSERT INTO artists (name) VALUES ('Bob')
|
222
349
|
# end # COMMIT
|
223
350
|
def db
|
@@ -227,23 +354,23 @@ module Sequel
|
|
227
354
|
@db
|
228
355
|
end
|
229
356
|
|
230
|
-
# Sets the database associated with the Model class.
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
# Sequel::Model to set the default database to be used
|
235
|
-
# by subclasses, or to override the database used for specific
|
236
|
-
# models:
|
357
|
+
# Sets the database associated with the Model class.
|
358
|
+
# Should only be used if the Model class currently does not
|
359
|
+
# have a dataset defined.
|
360
|
+
#
|
361
|
+
# This can be used directly on Sequel::Model to set the default database to be used
|
362
|
+
# by subclasses, or to override the database used for specific models:
|
237
363
|
#
|
238
364
|
# Sequel::Model.db = DB1
|
365
|
+
# Artist = Class.new(Sequel::Model)
|
239
366
|
# Artist.db = DB2
|
240
367
|
#
|
241
368
|
# Note that you should not use this to change the model's database
|
242
369
|
# at runtime. If you have that need, you should look into Sequel's
|
243
|
-
# sharding support.
|
370
|
+
# sharding support, or consider using separate model classes per Database.
|
244
371
|
def db=(db)
|
372
|
+
raise Error, "Cannot use Sequel::Model.db= on model with existing dataset. Use Sequel::Model.dataset= instead." if @dataset
|
245
373
|
@db = db
|
246
|
-
set_dataset(db.dataset.clone(@dataset.opts)) if @dataset
|
247
374
|
end
|
248
375
|
|
249
376
|
# Returns the cached schema information if available or gets it
|
@@ -255,7 +382,9 @@ module Sequel
|
|
255
382
|
# # {:id=>{:type=>:integer, :primary_key=>true, ...},
|
256
383
|
# # :name=>{:type=>:string, :primary_key=>false, ...}}
|
257
384
|
def db_schema
|
258
|
-
@db_schema
|
385
|
+
return @db_schema if @db_schema
|
386
|
+
return nil if frozen?
|
387
|
+
@db_schema = get_db_schema
|
259
388
|
end
|
260
389
|
|
261
390
|
# Create a column alias, where the column methods have one name, but the underlying storage uses a
|
@@ -268,52 +397,16 @@ module Sequel
|
|
268
397
|
end
|
269
398
|
end
|
270
399
|
|
271
|
-
# If a block is given, define a method on the dataset (if the model currently has an dataset) with the given argument name using
|
272
|
-
# the given block. Also define a class method on the model that calls the
|
273
|
-
# dataset method. Stores the method name and block so that it can be reapplied if the model's
|
274
|
-
# dataset changes.
|
275
|
-
#
|
276
|
-
# If a block is not given, just define a class method on the model for each argument
|
277
|
-
# that calls the dataset method of the same argument name.
|
278
|
-
#
|
279
|
-
# It is recommended that you define methods inside a block passed to #dataset_module
|
280
|
-
# instead of using this method, as #dataset_module allows you to use normal
|
281
|
-
# ruby def syntax.
|
282
|
-
#
|
283
|
-
# # Add new dataset method and class method that calls it
|
284
|
-
# Artist.def_dataset_method(:by_name){order(:name)}
|
285
|
-
# Artist.filter(:name.like('A%')).by_name
|
286
|
-
# Artist.by_name.filter(:name.like('A%'))
|
287
|
-
#
|
288
|
-
# # Just add a class method that calls an existing dataset method
|
289
|
-
# Artist.def_dataset_method(:server!)
|
290
|
-
# Artist.server!(:server1)
|
291
|
-
def def_dataset_method(*args, &block)
|
292
|
-
raise(Error, "No arguments given") if args.empty?
|
293
|
-
|
294
|
-
if block
|
295
|
-
raise(Error, "Defining a dataset method using a block requires only one argument") if args.length > 1
|
296
|
-
dataset_module{define_method(args.first, &block)}
|
297
|
-
else
|
298
|
-
args.each{|arg| def_model_dataset_method(arg)}
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
400
|
# Finds a single record according to the supplied filter.
|
303
401
|
# You are encouraged to use Model.[] or Model.first instead of this method.
|
304
402
|
#
|
305
|
-
# Artist.find(:
|
403
|
+
# Artist.find(name: 'Bob')
|
306
404
|
# # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
307
405
|
#
|
308
406
|
# Artist.find{name > 'M'}
|
309
407
|
# # SELECT * FROM artists WHERE (name > 'M') LIMIT 1
|
310
408
|
def find(*args, &block)
|
311
|
-
|
312
|
-
# Use optimized finder
|
313
|
-
first_where(args.first)
|
314
|
-
else
|
315
|
-
filter(*args, &block).first
|
316
|
-
end
|
409
|
+
first(*args, &block)
|
317
410
|
end
|
318
411
|
|
319
412
|
# Like +find+ but invokes create with given conditions when record does not
|
@@ -321,154 +414,43 @@ module Sequel
|
|
321
414
|
# to +find+, but instead is passed to +create+ only if +find+ does not
|
322
415
|
# return an object.
|
323
416
|
#
|
324
|
-
# Artist.find_or_create(:
|
417
|
+
# Artist.find_or_create(name: 'Bob')
|
325
418
|
# # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
|
326
419
|
# # INSERT INTO artists (name) VALUES ('Bob')
|
327
420
|
#
|
328
|
-
# Artist.find_or_create(:
|
421
|
+
# Artist.find_or_create(name: 'Jim'){|a| a.hometown = 'Sactown'}
|
329
422
|
# # SELECT * FROM artists WHERE (name = 'Jim') LIMIT 1
|
330
423
|
# # INSERT INTO artists (name, hometown) VALUES ('Jim', 'Sactown')
|
331
424
|
def find_or_create(cond, &block)
|
332
425
|
find(cond) || create(cond, &block)
|
333
426
|
end
|
334
|
-
|
335
427
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
# end
|
347
|
-
#
|
348
|
-
# Artist.finder :by_name
|
349
|
-
#
|
350
|
-
# This creates an optimized first_by_name method, which you can call normally:
|
351
|
-
#
|
352
|
-
# Artist.first_by_name("Joe")
|
353
|
-
#
|
354
|
-
# The alternative way to use this to pass your own block:
|
355
|
-
#
|
356
|
-
# Artist.finder(:name=>:first_by_name){|pl, ds| ds.where(:name=>pl.arg).limit(1)}
|
357
|
-
#
|
358
|
-
# Note that if you pass your own block, you are responsible for manually setting
|
359
|
-
# limits if necessary (as shown above).
|
360
|
-
#
|
361
|
-
# Options:
|
362
|
-
# :arity :: When using a symbol method name, this specifies the arity of the method.
|
363
|
-
# This should be used if if the method accepts an arbitrary number of arguments,
|
364
|
-
# or the method has default argument values. Note that if the method is defined
|
365
|
-
# as a dataset method, the class method Sequel creates accepts an arbitrary number
|
366
|
-
# of arguments, so you should use this option in that case. If you want to handle
|
367
|
-
# multiple possible arities, you need to call the finder method multiple times with
|
368
|
-
# unique :arity and :name methods each time.
|
369
|
-
# :name :: The name of the method to create. This must be given if you pass a block.
|
370
|
-
# If you use a symbol, this defaults to the symbol prefixed by the type.
|
371
|
-
# :mod :: The module in which to create the finder method. Defaults to the singleton
|
372
|
-
# class of the model.
|
373
|
-
# :type :: The type of query to run. Can be :first, :each, :all, or :get, defaults to
|
374
|
-
# :first.
|
375
|
-
#
|
376
|
-
# Caveats:
|
377
|
-
#
|
378
|
-
# This doesn't handle all possible cases. For example, if you have a method such as:
|
379
|
-
#
|
380
|
-
# def Artist.by_name(name)
|
381
|
-
# name ? where(:name=>name) : exclude(:name=>nil)
|
382
|
-
# end
|
383
|
-
#
|
384
|
-
# Then calling a finder without an argument will not work as you expect.
|
385
|
-
#
|
386
|
-
# Artist.finder :by_name
|
387
|
-
# Artist.by_name(nil).first
|
388
|
-
# # WHERE (name IS NOT NULL)
|
389
|
-
# Artist.first_by_name(nil)
|
390
|
-
# # WHERE (name IS NULL)
|
391
|
-
#
|
392
|
-
# See Dataset::PlaceholderLiteralizer for additional caveats.
|
393
|
-
def finder(meth=OPTS, opts=OPTS, &block)
|
394
|
-
if block
|
395
|
-
raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash)
|
396
|
-
raise Error, "cannot pass two option hashes to Model.finder" unless opts.equal?(OPTS)
|
397
|
-
opts = meth
|
398
|
-
raise Error, "must provide method name via :name option when passing block to Model.finder" unless meth_name = opts[:name]
|
399
|
-
end
|
400
|
-
|
401
|
-
type = opts.fetch(:type, :first)
|
402
|
-
unless prepare = opts[:prepare]
|
403
|
-
raise Error, ":type option to Model.finder must be :first, :all, :each, or :get" unless FINDER_TYPES.include?(type)
|
404
|
-
end
|
405
|
-
limit1 = type == :first || type == :get
|
406
|
-
meth_name ||= opts[:name] || :"#{type}_#{meth}"
|
407
|
-
|
408
|
-
argn = lambda do |model|
|
409
|
-
if arity = opts[:arity]
|
410
|
-
arity
|
411
|
-
else
|
412
|
-
method = block || model.method(meth)
|
413
|
-
(method.arity < 0 ? method.arity.abs - 1 : method.arity)
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
loader_proc = if prepare
|
418
|
-
proc do |model|
|
419
|
-
args = prepare_method_args('$a', argn.call(model))
|
420
|
-
ds = if block
|
421
|
-
model.instance_exec(*args, &block)
|
422
|
-
else
|
423
|
-
model.send(meth, *args)
|
424
|
-
end
|
425
|
-
ds = ds.limit(1) if limit1
|
426
|
-
model_name = model.name
|
427
|
-
if model_name.to_s.empty?
|
428
|
-
model_name = model.object_id
|
429
|
-
else
|
430
|
-
model_name = model_name.gsub(/\W/, '_')
|
431
|
-
end
|
432
|
-
ds.prepare(type, :"#{model_name}_#{meth_name}")
|
433
|
-
end
|
428
|
+
# Freeze a model class, disallowing any further changes to it.
|
429
|
+
def freeze
|
430
|
+
return self if frozen?
|
431
|
+
dataset_module.freeze
|
432
|
+
overridable_methods_module.freeze
|
433
|
+
|
434
|
+
if @dataset
|
435
|
+
db_schema.freeze.each_value(&:freeze)
|
436
|
+
columns.freeze
|
437
|
+
setter_methods.freeze
|
434
438
|
else
|
435
|
-
|
436
|
-
n = argn.call(model)
|
437
|
-
block ||= lambda do |pl, model2|
|
438
|
-
args = (0...n).map{pl.arg}
|
439
|
-
ds = model2.send(meth, *args)
|
440
|
-
ds = ds.limit(1) if limit1
|
441
|
-
ds
|
442
|
-
end
|
443
|
-
|
444
|
-
Sequel::Dataset::PlaceholderLiteralizer.loader(model, &block)
|
445
|
-
end
|
439
|
+
@setter_methods = [].freeze
|
446
440
|
end
|
447
441
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
def_prepare_method(mod, meth_name)
|
452
|
-
else
|
453
|
-
def_finder_method(mod, meth_name, type)
|
454
|
-
end
|
455
|
-
end
|
442
|
+
@dataset_method_modules.freeze
|
443
|
+
@default_set_fields_options.freeze
|
444
|
+
@plugins.freeze
|
456
445
|
|
457
|
-
|
458
|
-
# optimized handling of the single argument case.
|
459
|
-
def first(*args, &block)
|
460
|
-
if args.length == 1 && !block && !args.first.is_a?(Integer)
|
461
|
-
# Use optimized finder
|
462
|
-
first_where(args.first)
|
463
|
-
else
|
464
|
-
dataset.first(*args, &block)
|
465
|
-
end
|
446
|
+
super
|
466
447
|
end
|
467
448
|
|
468
|
-
#
|
469
|
-
#
|
470
|
-
|
471
|
-
|
449
|
+
# Whether the model has a dataset. True for most model classes,
|
450
|
+
# but can be false if the model class is an abstract model class
|
451
|
+
# designed for subclassing, such as Sequel::Model itself.
|
452
|
+
def has_dataset?
|
453
|
+
!@dataset.nil?
|
472
454
|
end
|
473
455
|
|
474
456
|
# Clear the setter_methods cache when a module is included, as it
|
@@ -478,47 +460,6 @@ module Sequel
|
|
478
460
|
super
|
479
461
|
end
|
480
462
|
|
481
|
-
# If possible, set the dataset for the model subclass as soon as it
|
482
|
-
# is created. Also, make sure the inherited class instance variables
|
483
|
-
# are copied into the subclass.
|
484
|
-
#
|
485
|
-
# Sequel queries the database to get schema information as soon as
|
486
|
-
# a model class is created:
|
487
|
-
#
|
488
|
-
# class Artist < Sequel::Model # Causes schema query
|
489
|
-
# end
|
490
|
-
def inherited(subclass)
|
491
|
-
super
|
492
|
-
ivs = subclass.instance_variables.collect(&:to_s)
|
493
|
-
inherited_instance_variables.each do |iv, dup|
|
494
|
-
next if ivs.include?(iv.to_s)
|
495
|
-
if (sup_class_value = instance_variable_get(iv)) && dup
|
496
|
-
sup_class_value = case dup
|
497
|
-
when :dup
|
498
|
-
sup_class_value.dup
|
499
|
-
when :hash_dup
|
500
|
-
h = {}
|
501
|
-
sup_class_value.each{|k,v| h[k] = v.dup}
|
502
|
-
h
|
503
|
-
when Proc
|
504
|
-
dup.call(sup_class_value)
|
505
|
-
else
|
506
|
-
raise Error, "bad inherited instance variable type: #{dup.inspect}"
|
507
|
-
end
|
508
|
-
end
|
509
|
-
subclass.instance_variable_set(iv, sup_class_value)
|
510
|
-
end
|
511
|
-
|
512
|
-
unless ivs.include?("@dataset")
|
513
|
-
if @dataset && self != Model
|
514
|
-
subclass.set_dataset(@dataset.clone, :inherited=>true) rescue nil
|
515
|
-
elsif (n = subclass.name) && !n.to_s.empty?
|
516
|
-
db
|
517
|
-
subclass.set_dataset(subclass.implicit_table_name) rescue nil
|
518
|
-
end
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
463
|
# Returns the implicit table name for the model class, which is the demodulized,
|
523
464
|
# underscored, pluralized name of the class.
|
524
465
|
#
|
@@ -528,17 +469,11 @@ module Sequel
|
|
528
469
|
pluralize(underscore(demodulize(name))).to_sym
|
529
470
|
end
|
530
471
|
|
531
|
-
# Calls #call with the values hash.
|
472
|
+
# Calls #call with the values hash.
|
532
473
|
def load(values)
|
533
474
|
call(values)
|
534
475
|
end
|
535
476
|
|
536
|
-
# Clear the setter_methods cache when a setter method is added
|
537
|
-
def method_added(meth)
|
538
|
-
clear_setter_methods_cache if meth.to_s =~ SETTER_METHOD_REGEXP
|
539
|
-
super
|
540
|
-
end
|
541
|
-
|
542
477
|
# Mark the model as not having a primary key. Not having a primary key
|
543
478
|
# can cause issues, among which is that you won't be able to update records.
|
544
479
|
#
|
@@ -552,22 +487,28 @@ module Sequel
|
|
552
487
|
|
553
488
|
# Loads a plugin for use with the model class, passing optional arguments
|
554
489
|
# to the plugin. If the plugin is a module, load it directly. Otherwise,
|
555
|
-
# require the plugin from
|
556
|
-
#
|
557
|
-
# the camelized plugin name under Sequel::Plugins.
|
490
|
+
# require the plugin from sequel/plugins/#{plugin} and then attempt to load
|
491
|
+
# the module using a the camelized plugin name under Sequel::Plugins.
|
558
492
|
def plugin(plugin, *args, &block)
|
559
493
|
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
494
|
+
|
495
|
+
if !m.respond_to?(:apply) && !m.respond_to?(:configure) && (!args.empty? || block)
|
496
|
+
Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
|
497
|
+
end
|
498
|
+
|
560
499
|
unless @plugins.include?(m)
|
561
500
|
@plugins << m
|
562
501
|
m.apply(self, *args, &block) if m.respond_to?(:apply)
|
563
|
-
extend(m::ClassMethods) if
|
564
|
-
include(m::InstanceMethods) if
|
565
|
-
if
|
502
|
+
extend(m::ClassMethods) if m.const_defined?(:ClassMethods, false)
|
503
|
+
include(m::InstanceMethods) if m.const_defined?(:InstanceMethods, false)
|
504
|
+
if m.const_defined?(:DatasetMethods, false)
|
566
505
|
dataset_extend(m::DatasetMethods, :create_class_methods=>false)
|
567
506
|
end
|
568
507
|
end
|
508
|
+
|
569
509
|
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
570
510
|
end
|
511
|
+
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
571
512
|
|
572
513
|
# Returns primary key attribute hash. If using a composite primary key
|
573
514
|
# value such be an array with values for each primary key in the correct
|
@@ -595,7 +536,7 @@ module Sequel
|
|
595
536
|
# plan to join other tables to this table and you want the column references
|
596
537
|
# to be qualified.
|
597
538
|
#
|
598
|
-
# Artist.
|
539
|
+
# Artist.where(Artist.qualified_primary_key_hash(1))
|
599
540
|
# # SELECT * FROM artists WHERE (artists.id = 1)
|
600
541
|
def qualified_primary_key_hash(value, qualifier=table_name)
|
601
542
|
case key = @primary_key
|
@@ -610,27 +551,6 @@ module Sequel
|
|
610
551
|
end
|
611
552
|
end
|
612
553
|
|
613
|
-
# Similar to finder, but uses a prepared statement instead of a placeholder
|
614
|
-
# literalizer. This makes the SQL used static (cannot vary per call), but
|
615
|
-
# allows binding argument values instead of literalizing them into the SQL
|
616
|
-
# query string.
|
617
|
-
#
|
618
|
-
# If a block is used with this method, it is instance_execed by the model,
|
619
|
-
# and should accept the desired number of placeholder arguments.
|
620
|
-
#
|
621
|
-
# The options are the same as the options for finder, with the following
|
622
|
-
# exception:
|
623
|
-
# :type :: Specifies the type of prepared statement to create
|
624
|
-
def prepared_finder(meth=OPTS, opts=OPTS, &block)
|
625
|
-
if block
|
626
|
-
raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash)
|
627
|
-
meth = meth.merge(:prepare=>true)
|
628
|
-
else
|
629
|
-
opts = opts.merge(:prepare=>true)
|
630
|
-
end
|
631
|
-
finder(meth, opts, &block)
|
632
|
-
end
|
633
|
-
|
634
554
|
# Restrict the setting of the primary key(s) when using mass assignment (e.g. +set+). Because
|
635
555
|
# this is the default, this only make sense to use in a subclass where the
|
636
556
|
# parent class has used +unrestrict_primary_key+.
|
@@ -645,22 +565,6 @@ module Sequel
|
|
645
565
|
@restrict_primary_key
|
646
566
|
end
|
647
567
|
|
648
|
-
# Set the columns to allow when using mass assignment (e.g. +set+). Using this means that
|
649
|
-
# any columns not listed here will not be modified. If you have any virtual
|
650
|
-
# setter methods (methods that end in =) that you want to be used during
|
651
|
-
# mass assignment, they need to be listed here as well (without the =).
|
652
|
-
#
|
653
|
-
# It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
|
654
|
-
# the allowed fields per call.
|
655
|
-
#
|
656
|
-
# Artist.set_allowed_columns(:name, :hometown)
|
657
|
-
# Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
|
658
|
-
# Artist.set(:name=>'Bob', :records_sold=>30000) # Error
|
659
|
-
def set_allowed_columns(*cols)
|
660
|
-
clear_setter_methods_cache
|
661
|
-
@allowed_columns = cols
|
662
|
-
end
|
663
|
-
|
664
568
|
# Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
|
665
569
|
# +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
|
666
570
|
# <tt>SQL::AliasedExpression</tt>
|
@@ -670,36 +574,43 @@ module Sequel
|
|
670
574
|
# database with the table name given. Other arguments raise an +Error+.
|
671
575
|
# Returns self.
|
672
576
|
#
|
673
|
-
# This changes the row_proc of the dataset to return
|
674
|
-
# model objects and extends the dataset with the dataset_method_modules.
|
675
577
|
# It also attempts to determine the database schema for the model,
|
676
578
|
# based on the given dataset.
|
677
579
|
#
|
678
|
-
# Artist.set_dataset(:tbl_artists)
|
679
|
-
# Artist.set_dataset(DB[:artists])
|
680
|
-
#
|
681
580
|
# Note that you should not use this to change the model's dataset
|
682
581
|
# at runtime. If you have that need, you should look into Sequel's
|
683
|
-
# sharding support
|
582
|
+
# sharding support, or creating a separate Model class per dataset
|
583
|
+
#
|
584
|
+
# You should avoid calling this method directly if possible. Instead you should
|
585
|
+
# set the table name or dataset when creating the model class:
|
586
|
+
#
|
587
|
+
# # table name
|
588
|
+
# class Artist < Sequel::Model(:tbl_artists)
|
589
|
+
# end
|
590
|
+
#
|
591
|
+
# # dataset
|
592
|
+
# class Artist < Sequel::Model(DB[:tbl_artists])
|
593
|
+
# end
|
684
594
|
def set_dataset(ds, opts=OPTS)
|
685
595
|
inherited = opts[:inherited]
|
686
596
|
@dataset = convert_input_dataset(ds)
|
687
|
-
@require_modification =
|
597
|
+
@require_modification = @dataset.provides_accurate_rows_matched? if require_modification.nil?
|
688
598
|
if inherited
|
689
599
|
self.simple_table = superclass.simple_table
|
690
|
-
@columns =
|
600
|
+
@columns = superclass.instance_variable_get(:@columns)
|
601
|
+
@db_schema = superclass.instance_variable_get(:@db_schema)
|
691
602
|
else
|
692
|
-
@
|
603
|
+
@dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
|
604
|
+
@db_schema = get_db_schema
|
693
605
|
end
|
694
|
-
|
695
|
-
check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
|
606
|
+
|
696
607
|
reset_instance_dataset
|
697
608
|
self
|
698
609
|
end
|
699
610
|
|
700
611
|
# Sets the primary key for this model. You can use either a regular
|
701
612
|
# or a composite primary key. To not use a primary key, set to nil
|
702
|
-
# or use +no_primary_key+.
|
613
|
+
# or use +no_primary_key+. On most adapters, Sequel can automatically
|
703
614
|
# determine the primary key to use, so this method is not needed often.
|
704
615
|
#
|
705
616
|
# class Person < Sequel::Model
|
@@ -721,48 +632,22 @@ module Sequel
|
|
721
632
|
end
|
722
633
|
end
|
723
634
|
self.simple_pk = if key && !key.is_a?(Array)
|
724
|
-
(@dataset || db).literal(key)
|
635
|
+
(@dataset || db).literal(key).freeze
|
725
636
|
end
|
726
637
|
@primary_key = key
|
727
638
|
end
|
728
639
|
|
729
|
-
# Cache of setter methods to allow by default, in order to speed up
|
640
|
+
# Cache of setter methods to allow by default, in order to speed up mass assignment.
|
730
641
|
def setter_methods
|
731
|
-
@setter_methods
|
642
|
+
@setter_methods || (@setter_methods = get_setter_methods)
|
732
643
|
end
|
733
644
|
|
734
|
-
# Sets up a dataset method that returns a filtered dataset.
|
735
|
-
# Sometimes thought of as a scope, and like most dataset methods,
|
736
|
-
# they can be chained.
|
737
|
-
# For example:
|
738
|
-
#
|
739
|
-
# Topic.subset(:joes, :username.like('%joe%'))
|
740
|
-
# Topic.subset(:popular){num_posts > 100}
|
741
|
-
# Topic.subset(:recent){created_on > Date.today - 7}
|
742
|
-
#
|
743
|
-
# Allows you to do:
|
744
|
-
#
|
745
|
-
# Topic.joes.recent.popular
|
746
|
-
#
|
747
|
-
# to get topics with a username that includes joe that
|
748
|
-
# have more than 100 posts and were created less than
|
749
|
-
# 7 days ago.
|
750
|
-
#
|
751
|
-
# Both the args given and the block are passed to <tt>Dataset#filter</tt>.
|
752
|
-
#
|
753
|
-
# This method creates dataset methods that do not accept arguments. To create
|
754
|
-
# dataset methods that accept arguments, you should use define a
|
755
|
-
# method directly inside a #dataset_module block.
|
756
|
-
def subset(name, *args, &block)
|
757
|
-
def_dataset_method(name){filter(*args, &block)}
|
758
|
-
end
|
759
|
-
|
760
645
|
# Returns name of primary table for the dataset. If the table for the dataset
|
761
646
|
# is aliased, returns the aliased name.
|
762
647
|
#
|
763
648
|
# Artist.table_name # => :artists
|
764
649
|
# Sequel::Model(:foo).table_name # => :foo
|
765
|
-
# Sequel::Model(:
|
650
|
+
# Sequel::Model(Sequel[:foo].as(:bar)).table_name # => :bar
|
766
651
|
def table_name
|
767
652
|
dataset.first_source_alias
|
768
653
|
end
|
@@ -770,9 +655,9 @@ module Sequel
|
|
770
655
|
# Allow the setting of the primary key(s) when using the mass assignment methods.
|
771
656
|
# Using this method can open up security issues, be very careful before using it.
|
772
657
|
#
|
773
|
-
# Artist.set(:
|
658
|
+
# Artist.set(id: 1) # Error
|
774
659
|
# Artist.unrestrict_primary_key
|
775
|
-
# Artist.set(:
|
660
|
+
# Artist.set(id: 1) # No Error
|
776
661
|
def unrestrict_primary_key
|
777
662
|
clear_setter_methods_cache
|
778
663
|
@restrict_primary_key = false
|
@@ -789,18 +674,18 @@ module Sequel
|
|
789
674
|
end
|
790
675
|
|
791
676
|
# Add model methods that call dataset methods
|
792
|
-
Plugins.def_dataset_methods(self,
|
677
|
+
Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
|
793
678
|
|
794
679
|
private
|
795
680
|
|
796
|
-
# Yield to the passed block and swallow all errors other than DatabaseConnectionErrors.
|
797
|
-
def check_non_connection_error
|
681
|
+
# Yield to the passed block and if do_raise is false, swallow all errors other than DatabaseConnectionErrors.
|
682
|
+
def check_non_connection_error(do_raise=require_valid_table)
|
798
683
|
begin
|
799
|
-
yield
|
684
|
+
db.transaction(:savepoint=>:only){yield}
|
800
685
|
rescue Sequel::DatabaseConnectionError
|
801
686
|
raise
|
802
|
-
rescue
|
803
|
-
|
687
|
+
rescue Sequel::Error
|
688
|
+
raise if do_raise
|
804
689
|
end
|
805
690
|
end
|
806
691
|
|
@@ -809,25 +694,27 @@ module Sequel
|
|
809
694
|
def convert_input_dataset(ds)
|
810
695
|
case ds
|
811
696
|
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
812
|
-
self.simple_table = db.literal(ds)
|
697
|
+
self.simple_table = db.literal(ds).freeze
|
813
698
|
ds = db.from(ds)
|
814
699
|
when Dataset
|
700
|
+
ds = ds.from_self(:alias=>ds.first_source) if ds.joined_dataset?
|
701
|
+
|
815
702
|
self.simple_table = if ds.send(:simple_select_all?)
|
816
|
-
ds.literal(ds.first_source_table)
|
703
|
+
ds.literal(ds.first_source_table).freeze
|
817
704
|
end
|
818
705
|
@db = ds.db
|
819
706
|
else
|
820
707
|
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
821
708
|
end
|
822
|
-
|
823
|
-
ds
|
709
|
+
|
710
|
+
set_dataset_row_proc(ds.clone(:model=>self))
|
824
711
|
end
|
825
712
|
|
826
713
|
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
827
714
|
# module if the model has a dataset. Add dataset methods to the class for all
|
828
715
|
# public dataset methods.
|
829
716
|
def dataset_extend(mod, opts=OPTS)
|
830
|
-
@dataset.
|
717
|
+
@dataset = @dataset.with_extend(mod) if @dataset
|
831
718
|
reset_instance_dataset
|
832
719
|
dataset_method_modules << mod
|
833
720
|
unless opts[:create_class_methods] == false
|
@@ -837,9 +724,11 @@ module Sequel
|
|
837
724
|
|
838
725
|
# Create a column accessor for a column with a method name that is hard to use in ruby code.
|
839
726
|
def def_bad_column_accessor(column)
|
727
|
+
im = instance_methods
|
840
728
|
overridable_methods_module.module_eval do
|
841
|
-
|
842
|
-
define_method(
|
729
|
+
meth = :"#{column}="
|
730
|
+
define_method(column){self[column]} unless im.include?(column)
|
731
|
+
define_method(meth){|v| self[column] = v} unless im.include?(meth)
|
843
732
|
end
|
844
733
|
end
|
845
734
|
|
@@ -847,51 +736,28 @@ module Sequel
|
|
847
736
|
# use a string to define the method for speed. For other columns names, use a block.
|
848
737
|
def def_column_accessor(*columns)
|
849
738
|
clear_setter_methods_cache
|
850
|
-
columns, bad_columns = columns.partition{|x|
|
739
|
+
columns, bad_columns = columns.partition{|x| /\A[A-Za-z_][A-Za-z0-9_]*\z/.match(x.to_s)}
|
851
740
|
bad_columns.each{|x| def_bad_column_accessor(x)}
|
852
|
-
im = instance_methods
|
741
|
+
im = instance_methods
|
853
742
|
columns.each do |column|
|
854
|
-
meth = "#{column}="
|
855
|
-
overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__) unless im.include?(column
|
743
|
+
meth = :"#{column}="
|
744
|
+
overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__) unless im.include?(column)
|
856
745
|
overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__) unless im.include?(meth)
|
857
746
|
end
|
858
747
|
end
|
859
748
|
|
860
749
|
# Define a model method that calls the dataset method with the same name,
|
861
|
-
# only used for methods with names that can't be
|
750
|
+
# only used for methods with names that can't be represented directly in
|
862
751
|
# ruby code.
|
863
752
|
def def_model_dataset_method(meth)
|
864
753
|
return if respond_to?(meth, true)
|
865
754
|
|
866
|
-
if meth.to_s =~
|
755
|
+
if meth.to_s =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
|
867
756
|
instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
|
868
757
|
else
|
869
|
-
(
|
758
|
+
define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
|
870
759
|
end
|
871
|
-
|
872
|
-
|
873
|
-
# Define a finder method in the given module with the given method name that
|
874
|
-
# load rows using the finder with the given name.
|
875
|
-
def def_finder_method(mod, meth, type)
|
876
|
-
mod.send(:define_method, meth){|*args, &block| finder_for(meth).send(type, *args, &block)}
|
877
|
-
end
|
878
|
-
|
879
|
-
# Define a prepared_finder method in the given module that will call the associated prepared
|
880
|
-
# statement.
|
881
|
-
def def_prepare_method(mod, meth)
|
882
|
-
mod.send(:define_method, meth){|*args, &block| finder_for(meth).call(prepare_method_arg_hash(args), &block)}
|
883
|
-
end
|
884
|
-
|
885
|
-
# Find the finder to use for the give method. If a finder has not been loaded
|
886
|
-
# for the method, load the finder and set correctly in the finders hash, then
|
887
|
-
# return the finder.
|
888
|
-
def finder_for(meth)
|
889
|
-
unless finder = Sequel.synchronize{@finders[meth]}
|
890
|
-
finder_loader = @finder_loaders.fetch(meth)
|
891
|
-
finder = finder_loader.call(self)
|
892
|
-
Sequel.synchronize{@finders[meth] = finder}
|
893
|
-
end
|
894
|
-
finder
|
760
|
+
singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
895
761
|
end
|
896
762
|
|
897
763
|
# Get the schema from the database, fall back on checking the columns
|
@@ -903,14 +769,14 @@ module Sequel
|
|
903
769
|
schema_hash = {}
|
904
770
|
ds_opts = dataset.opts
|
905
771
|
get_columns = proc{check_non_connection_error{columns} || []}
|
906
|
-
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
|
772
|
+
schema_array = check_non_connection_error(false){db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
|
907
773
|
if schema_array
|
908
774
|
schema_array.each{|k,v| schema_hash[k] = v}
|
909
775
|
|
910
776
|
# Set the primary key(s) based on the schema information,
|
911
777
|
# if the schema information includes primary key information
|
912
778
|
if schema_array.all?{|k,v| v.has_key?(:primary_key)}
|
913
|
-
pks = schema_array.
|
779
|
+
pks = schema_array.map{|k,v| k if v[:primary_key]}.compact
|
914
780
|
pks.length > 0 ? set_primary_key(pks) : no_primary_key
|
915
781
|
end
|
916
782
|
|
@@ -925,11 +791,11 @@ module Sequel
|
|
925
791
|
# Dataset is for a single table with all columns,
|
926
792
|
# so set the columns based on the order they were
|
927
793
|
# returned by the schema.
|
928
|
-
cols = schema_array.
|
794
|
+
cols = schema_array.map{|k,v| k}
|
929
795
|
set_columns(cols)
|
930
796
|
# Also set the columns for the dataset, so the dataset
|
931
797
|
# doesn't have to do a query to get them.
|
932
|
-
dataset.
|
798
|
+
dataset.send(:columns=, cols)
|
933
799
|
end
|
934
800
|
else
|
935
801
|
# If the dataset uses multiple tables or custom sql or getting
|
@@ -943,21 +809,81 @@ module Sequel
|
|
943
809
|
# Uncached version of setter_methods, to be overridden by plugins
|
944
810
|
# that want to modify the methods used.
|
945
811
|
def get_setter_methods
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
meths = instance_methods.collect(&:to_s).grep(SETTER_METHOD_REGEXP) - RESTRICTED_SETTER_METHODS
|
950
|
-
meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && restrict_primary_key?
|
951
|
-
meths
|
952
|
-
end
|
812
|
+
meths = instance_methods.map(&:to_s).select{|l| l.end_with?('=')} - RESTRICTED_SETTER_METHODS
|
813
|
+
meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && restrict_primary_key?
|
814
|
+
meths
|
953
815
|
end
|
954
816
|
|
817
|
+
# If possible, set the dataset for the model subclass as soon as it
|
818
|
+
# is created. Also, make sure the inherited class instance variables
|
819
|
+
# are copied into the subclass.
|
820
|
+
#
|
821
|
+
# Sequel queries the database to get schema information as soon as
|
822
|
+
# a model class is created:
|
823
|
+
#
|
824
|
+
# class Artist < Sequel::Model # Causes schema query
|
825
|
+
# end
|
826
|
+
def inherited(subclass)
|
827
|
+
super
|
828
|
+
ivs = subclass.instance_variables
|
829
|
+
inherited_instance_variables.each do |iv, dup|
|
830
|
+
if (sup_class_value = instance_variable_get(iv)) && dup
|
831
|
+
sup_class_value = case dup
|
832
|
+
when :dup
|
833
|
+
sup_class_value.dup
|
834
|
+
when :hash_dup
|
835
|
+
h = {}
|
836
|
+
sup_class_value.each{|k,v| h[k] = v.dup}
|
837
|
+
h
|
838
|
+
when Proc
|
839
|
+
dup.call(sup_class_value)
|
840
|
+
else
|
841
|
+
raise Error, "bad inherited instance variable type: #{dup.inspect}"
|
842
|
+
end
|
843
|
+
end
|
844
|
+
subclass.instance_variable_set(iv, sup_class_value)
|
845
|
+
end
|
846
|
+
|
847
|
+
unless ivs.include?(:@dataset)
|
848
|
+
if @dataset && self != Model
|
849
|
+
subclass.set_dataset(@dataset.clone, :inherited=>true)
|
850
|
+
elsif (n = subclass.name) && !n.to_s.empty?
|
851
|
+
db
|
852
|
+
subclass.set_dataset(subclass.implicit_table_name)
|
853
|
+
end
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
955
857
|
# A hash of instance variables to automatically set up in subclasses.
|
956
|
-
#
|
957
|
-
#
|
958
|
-
#
|
858
|
+
# Keys are instance variable symbols, values should be:
|
859
|
+
# nil :: Assign directly from superclass to subclass (frozen objects)
|
860
|
+
# :dup :: Dup object when assigning from superclass to subclass (mutable objects)
|
861
|
+
# :hash_dup :: Assign hash with same keys, but dup all the values
|
862
|
+
# Proc :: Call with subclass to do the assignment
|
959
863
|
def inherited_instance_variables
|
960
|
-
|
864
|
+
{
|
865
|
+
:@cache_anonymous_models=>nil,
|
866
|
+
:@dataset_method_modules=>:dup,
|
867
|
+
:@dataset_module_class=>nil,
|
868
|
+
:@db=>nil,
|
869
|
+
:@default_set_fields_options=>:dup,
|
870
|
+
:@fast_instance_delete_sql=>nil,
|
871
|
+
:@fast_pk_lookup_sql=>nil,
|
872
|
+
:@plugins=>:dup,
|
873
|
+
:@primary_key=>nil,
|
874
|
+
:@raise_on_save_failure=>nil,
|
875
|
+
:@raise_on_typecast_failure=>nil,
|
876
|
+
:@require_modification=>nil,
|
877
|
+
:@require_valid_table=>nil,
|
878
|
+
:@restrict_primary_key=>nil,
|
879
|
+
:@setter_methods=>nil,
|
880
|
+
:@simple_pk=>nil,
|
881
|
+
:@simple_table=>nil,
|
882
|
+
:@strict_param_setting=>nil,
|
883
|
+
:@typecast_empty_string_to_nil=>nil,
|
884
|
+
:@typecast_on_assignment=>nil,
|
885
|
+
:@use_transactions=>nil
|
886
|
+
}
|
961
887
|
end
|
962
888
|
|
963
889
|
# For the given opts hash and default name or :class option, add a
|
@@ -968,11 +894,24 @@ module Sequel
|
|
968
894
|
case opts[:class]
|
969
895
|
when String, Symbol
|
970
896
|
# Delete :class to allow late binding
|
971
|
-
|
897
|
+
class_name = opts.delete(:class).to_s
|
898
|
+
|
899
|
+
if (namespace = opts[:class_namespace]) && !class_name.start_with?('::')
|
900
|
+
class_name = "::#{namespace}::#{class_name}"
|
901
|
+
end
|
902
|
+
|
903
|
+
opts[:class_name] ||= class_name
|
972
904
|
when Class
|
973
905
|
opts[:class_name] ||= opts[:class].name
|
974
906
|
end
|
975
|
-
|
907
|
+
|
908
|
+
opts[:class_name] ||= '::' + ((name || '').split("::")[0..-2] + [camelize(default)]).join('::')
|
909
|
+
end
|
910
|
+
|
911
|
+
# Clear the setter_methods cache when a setter method is added.
|
912
|
+
def method_added(meth)
|
913
|
+
clear_setter_methods_cache if meth.to_s.end_with?('=')
|
914
|
+
super
|
976
915
|
end
|
977
916
|
|
978
917
|
# Module that the class includes that holds methods the class adds for column accessors and
|
@@ -986,51 +925,12 @@ module Sequel
|
|
986
925
|
# defined, the corresponding plugin required.
|
987
926
|
def plugin_module(plugin)
|
988
927
|
module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
989
|
-
|
990
|
-
|
991
|
-
Sequel::Plugins.const_get(module_name) == Sequel.const_get(module_name))
|
992
|
-
begin
|
993
|
-
require "sequel/plugins/#{plugin}"
|
994
|
-
rescue LoadError => e
|
995
|
-
begin
|
996
|
-
require "sequel_#{plugin}"
|
997
|
-
rescue LoadError => e2
|
998
|
-
e.message << "; #{e2.message}"
|
999
|
-
raise e
|
1000
|
-
end
|
1001
|
-
end
|
928
|
+
unless Sequel::Plugins.const_defined?(module_name, false)
|
929
|
+
require "sequel/plugins/#{plugin}"
|
1002
930
|
end
|
1003
931
|
Sequel::Plugins.const_get(module_name)
|
1004
932
|
end
|
1005
933
|
|
1006
|
-
# Check if the plugin module +plugin+ defines the constant named by +submod+.
|
1007
|
-
def plugin_module_defined?(plugin, submod)
|
1008
|
-
if RUBY_VERSION >= '1.9'
|
1009
|
-
plugin.const_defined?(submod, false)
|
1010
|
-
else
|
1011
|
-
# :nocov:
|
1012
|
-
plugin.const_defined?(submod)
|
1013
|
-
# :nocov:
|
1014
|
-
end
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
# An hash of prepared argument values for the given arguments, with keys
|
1018
|
-
# starting at a. Used by the methods created by prepared_finder.
|
1019
|
-
def prepare_method_arg_hash(args)
|
1020
|
-
h = {}
|
1021
|
-
prepare_method_args('a', args.length).zip(args).each{|k, v| h[k] = v}
|
1022
|
-
h
|
1023
|
-
end
|
1024
|
-
|
1025
|
-
# An array of prepared statement argument names, of length n and starting with base.
|
1026
|
-
def prepare_method_args(base, n)
|
1027
|
-
(0...n).map do
|
1028
|
-
s = base.to_sym
|
1029
|
-
base = base.next
|
1030
|
-
s
|
1031
|
-
end
|
1032
|
-
end
|
1033
|
-
|
1034
934
|
# Find the row in the dataset that matches the primary key. Uses
|
1035
935
|
# a static SQL optimization if the table and primary key are simple.
|
1036
936
|
#
|
@@ -1044,10 +944,8 @@ module Sequel
|
|
1044
944
|
ds.literal_append(sql, pk)
|
1045
945
|
ds.fetch_rows(sql){|r| return ds.row_proc.call(r)}
|
1046
946
|
nil
|
1047
|
-
elsif dataset.joined_dataset?
|
1048
|
-
first_where(qualified_primary_key_hash(pk))
|
1049
947
|
else
|
1050
|
-
|
948
|
+
dataset.first(primary_key_hash(pk))
|
1051
949
|
end
|
1052
950
|
end
|
1053
951
|
|
@@ -1060,18 +958,17 @@ module Sequel
|
|
1060
958
|
# are used, or set it to nil if not used.
|
1061
959
|
def reset_fast_pk_lookup_sql
|
1062
960
|
@fast_pk_lookup_sql = if @simple_table && @simple_pk
|
1063
|
-
"SELECT * FROM
|
961
|
+
"SELECT * FROM #{@simple_table} WHERE #{@simple_pk} = ".freeze
|
1064
962
|
end
|
1065
963
|
@fast_instance_delete_sql = if @simple_table && @simple_pk
|
1066
|
-
"DELETE FROM
|
964
|
+
"DELETE FROM #{@simple_table} WHERE #{@simple_pk} = ".freeze
|
1067
965
|
end
|
1068
966
|
end
|
1069
967
|
|
1070
968
|
# Reset the instance dataset to a modified copy of the current dataset,
|
1071
969
|
# should be used whenever the model's dataset is modified.
|
1072
970
|
def reset_instance_dataset
|
1073
|
-
@
|
1074
|
-
@instance_dataset = @dataset.limit(1).naked if @dataset
|
971
|
+
@instance_dataset = @dataset.limit(1).naked.skip_limit_check if @dataset
|
1075
972
|
end
|
1076
973
|
|
1077
974
|
# Set the columns for this model and create accessor methods for each column.
|
@@ -1083,7 +980,7 @@ module Sequel
|
|
1083
980
|
|
1084
981
|
# Set the dataset's row_proc to the current model.
|
1085
982
|
def set_dataset_row_proc(ds)
|
1086
|
-
ds.
|
983
|
+
ds.with_row_proc(self)
|
1087
984
|
end
|
1088
985
|
|
1089
986
|
# Reset the fast primary key lookup SQL when the simple_pk value changes.
|
@@ -1107,36 +1004,43 @@ module Sequel
|
|
1107
1004
|
|
1108
1005
|
# Sequel::Model instance methods that implement basic model functionality.
|
1109
1006
|
#
|
1110
|
-
# * All of the
|
1007
|
+
# * All of the model before/after/around hooks are implemented as instance methods that are called
|
1111
1008
|
# by Sequel when the appropriate action occurs. For example, when destroying
|
1112
1009
|
# a model object, Sequel will call +around_destroy+, which will call +before_destroy+, do
|
1113
1010
|
# the destroy, and then call +after_destroy+.
|
1114
1011
|
# * The following instance_methods all call the class method of the same
|
1115
1012
|
# name: columns, db, primary_key, db_schema.
|
1116
|
-
# *
|
1117
|
-
#
|
1118
|
-
#
|
1119
|
-
#
|
1013
|
+
# * The following accessor methods are defined via metaprogramming:
|
1014
|
+
# raise_on_save_failure, raise_on_typecast_failure, require_modification,
|
1015
|
+
# strict_param_setting, typecast_empty_string_to_nil, typecast_on_assignment,
|
1016
|
+
# and use_transactions. The setter methods will change the setting for the
|
1017
|
+
# instance, and the getter methods will check for an instance setting, then
|
1018
|
+
# try the class setting if no instance setting has been set.
|
1120
1019
|
module InstanceMethods
|
1121
1020
|
HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
|
1122
|
-
|
1021
|
+
[:around_create, :around_update, :around_save, :around_destroy, :around_validation].each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
|
1123
1022
|
|
1124
1023
|
# Define instance method(s) that calls class method(s) of the
|
1125
1024
|
# same name. Replaces the construct:
|
1126
1025
|
#
|
1127
|
-
# define_method(meth){self.class.
|
1026
|
+
# define_method(meth){self.class.public_send(meth)}
|
1128
1027
|
[:columns, :db, :primary_key, :db_schema].each{|meth| class_eval("def #{meth}; self.class.#{meth} end", __FILE__, __LINE__)}
|
1129
1028
|
|
1130
1029
|
# Define instance method(s) that calls class method(s) of the
|
1131
1030
|
# same name, caching the result in an instance variable. Define
|
1132
1031
|
# standard attr_writer method for modifying that instance variable.
|
1133
|
-
|
1134
|
-
|
1032
|
+
[:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting,
|
1033
|
+
:raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_transactions].each do |meth|
|
1034
|
+
class_eval("def #{meth}; !defined?(@#{meth}) ? (frozen? ? self.class.#{meth} : (@#{meth} = self.class.#{meth})) : @#{meth} end", __FILE__, __LINE__)
|
1035
|
+
attr_writer(meth)
|
1036
|
+
end
|
1135
1037
|
|
1136
1038
|
# The hash of attribute values. Keys are symbols with the names of the
|
1137
|
-
# underlying database columns.
|
1039
|
+
# underlying database columns. The returned hash is a reference to the
|
1040
|
+
# receiver's values hash, and modifying it will also modify the receiver's
|
1041
|
+
# values.
|
1138
1042
|
#
|
1139
|
-
# Artist.new(:
|
1043
|
+
# Artist.new(name: 'Bob').values # => {:name=>'Bob'}
|
1140
1044
|
# Artist[1].values # => {:id=>1, :name=>'Jim', ...}
|
1141
1045
|
attr_reader :values
|
1142
1046
|
alias to_hash values
|
@@ -1147,7 +1051,7 @@ module Sequel
|
|
1147
1051
|
# method names.
|
1148
1052
|
alias get_column_value send
|
1149
1053
|
|
1150
|
-
# Set the value of the column. Takes two
|
1054
|
+
# Set the value of the column. Takes two arguments. The first is a
|
1151
1055
|
# symbol or string argument for the column name, suffixed with =. The
|
1152
1056
|
# second is the value to set for the column. By default it calls send
|
1153
1057
|
# with the argument to set the value. This can be overridden if you have
|
@@ -1161,17 +1065,17 @@ module Sequel
|
|
1161
1065
|
# Arguments:
|
1162
1066
|
# values :: should be a hash to pass to set.
|
1163
1067
|
#
|
1164
|
-
# Artist.new(:
|
1068
|
+
# Artist.new(name: 'Bob')
|
1165
1069
|
#
|
1166
1070
|
# Artist.new do |a|
|
1167
1071
|
# a.name = 'Bob'
|
1168
1072
|
# end
|
1169
|
-
def initialize(values =
|
1073
|
+
def initialize(values = OPTS)
|
1170
1074
|
@values = {}
|
1171
1075
|
@new = true
|
1172
1076
|
@modified = true
|
1173
1077
|
initialize_set(values)
|
1174
|
-
|
1078
|
+
_clear_changed_columns(:initialize)
|
1175
1079
|
yield self if block_given?
|
1176
1080
|
end
|
1177
1081
|
|
@@ -1207,16 +1111,33 @@ module Sequel
|
|
1207
1111
|
eql?(obj)
|
1208
1112
|
end
|
1209
1113
|
|
1210
|
-
#
|
1211
|
-
#
|
1114
|
+
# Case equality. By default, checks equality of the primary key value, see
|
1115
|
+
# pk_equal?.
|
1212
1116
|
#
|
1213
|
-
# Artist[1] === Artist[1] # true
|
1214
|
-
# Artist.new === Artist.new # false
|
1215
|
-
# Artist[1].set(:name=>'Bob')
|
1117
|
+
# Artist[1] === Artist[1] # => true
|
1118
|
+
# Artist.new === Artist.new # => false
|
1119
|
+
# Artist[1].set(:name=>'Bob') === Artist[1] # => true
|
1216
1120
|
def ===(obj)
|
1217
|
-
|
1121
|
+
case pkv = pk
|
1122
|
+
when nil
|
1123
|
+
return false
|
1124
|
+
when Array
|
1125
|
+
return false if pkv.any?(&:nil?)
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
(obj.class == model) && (obj.pk == pkv)
|
1218
1129
|
end
|
1219
|
-
|
1130
|
+
|
1131
|
+
# If the receiver has a primary key value, returns true if the objects have
|
1132
|
+
# the same class and primary key value.
|
1133
|
+
# If the receiver's primary key value is nil or is an array containing
|
1134
|
+
# nil, returns false.
|
1135
|
+
#
|
1136
|
+
# Artist[1].pk_equal?(Artist[1]) # => true
|
1137
|
+
# Artist.new.pk_equal?(Artist.new) # => false
|
1138
|
+
# Artist[1].set(:name=>'Bob').pk_equal?(Artist[1]) # => true
|
1139
|
+
alias pk_equal? ===
|
1140
|
+
|
1220
1141
|
# class is defined in Object, but it is also a keyword,
|
1221
1142
|
# and since a lot of instance methods call class methods,
|
1222
1143
|
# this alias makes it so you can use model instead of
|
@@ -1248,9 +1169,9 @@ module Sequel
|
|
1248
1169
|
# a.name = 'Bob'
|
1249
1170
|
# a.changed_columns # => [:name]
|
1250
1171
|
def changed_columns
|
1251
|
-
|
1172
|
+
_changed_columns
|
1252
1173
|
end
|
1253
|
-
|
1174
|
+
|
1254
1175
|
# Deletes and returns +self+. Does not run destroy hooks.
|
1255
1176
|
# Look into using +destroy+ instead.
|
1256
1177
|
#
|
@@ -1263,11 +1184,8 @@ module Sequel
|
|
1263
1184
|
end
|
1264
1185
|
|
1265
1186
|
# Like delete but runs hooks before and after delete.
|
1266
|
-
#
|
1267
|
-
#
|
1268
|
-
# the item from the database and returns self. Uses a transaction
|
1269
|
-
# if use_transactions is true or if the :transaction option is given and
|
1270
|
-
# true.
|
1187
|
+
# Uses a transaction if use_transactions is true or if the
|
1188
|
+
# :transaction option is given and true.
|
1271
1189
|
#
|
1272
1190
|
# Artist[1].destroy # BEGIN; DELETE FROM artists WHERE (id = 1); COMMIT;
|
1273
1191
|
# # => #<Artist {:id=>1, ...}>
|
@@ -1326,12 +1244,12 @@ module Sequel
|
|
1326
1244
|
# errors, or dataset.
|
1327
1245
|
def freeze
|
1328
1246
|
values.freeze
|
1329
|
-
|
1247
|
+
_changed_columns.freeze
|
1330
1248
|
unless errors.frozen?
|
1331
1249
|
validate
|
1332
1250
|
errors.freeze
|
1333
1251
|
end
|
1334
|
-
this
|
1252
|
+
this if !new? && model.primary_key
|
1335
1253
|
super
|
1336
1254
|
end
|
1337
1255
|
|
@@ -1339,9 +1257,9 @@ module Sequel
|
|
1339
1257
|
# the same class and values (if pk is nil).
|
1340
1258
|
#
|
1341
1259
|
# Artist[1].hash == Artist[1].hash # true
|
1342
|
-
# Artist[1].set(:
|
1260
|
+
# Artist[1].set(name: 'Bob').hash == Artist[1].hash # true
|
1343
1261
|
# Artist.new.hash == Artist.new.hash # true
|
1344
|
-
# Artist.new(:
|
1262
|
+
# Artist.new(name: 'Bob').hash == Artist.new.hash # false
|
1345
1263
|
def hash
|
1346
1264
|
case primary_key
|
1347
1265
|
when Array
|
@@ -1370,23 +1288,37 @@ module Sequel
|
|
1370
1288
|
# Returns the keys in +values+. May not include all column names.
|
1371
1289
|
#
|
1372
1290
|
# Artist.new.keys # => []
|
1373
|
-
# Artist.new(:
|
1291
|
+
# Artist.new(name: 'Bob').keys # => [:name]
|
1374
1292
|
# Artist[1].keys # => [:id, :name]
|
1375
1293
|
def keys
|
1376
1294
|
@values.keys
|
1377
1295
|
end
|
1378
1296
|
|
1379
|
-
# Refresh this record using +for_update+
|
1380
|
-
# This can be used to make sure no other
|
1381
|
-
# same time.
|
1297
|
+
# Refresh this record using +for_update+ (by default, or the specified style when given)
|
1298
|
+
# unless this is a new record. Returns self. This can be used to make sure no other
|
1299
|
+
# process is updating the record at the same time.
|
1300
|
+
#
|
1301
|
+
# If style is a string, it will be used directly. You should never pass a string
|
1302
|
+
# to this method that is derived from user input, as that can lead to
|
1303
|
+
# SQL injection.
|
1304
|
+
#
|
1305
|
+
# A symbol may be used for database independent locking behavior, but
|
1306
|
+
# all supported symbols have separate methods (e.g. for_update).
|
1307
|
+
#
|
1382
1308
|
#
|
1383
1309
|
# a = Artist[1]
|
1384
1310
|
# Artist.db.transaction do
|
1385
1311
|
# a.lock!
|
1386
1312
|
# a.update(:name=>'A')
|
1387
1313
|
# end
|
1388
|
-
|
1389
|
-
|
1314
|
+
#
|
1315
|
+
# a = Artist[2]
|
1316
|
+
# Artist.db.transaction do
|
1317
|
+
# a.lock!('FOR NO KEY UPDATE')
|
1318
|
+
# a.update(:name=>'B')
|
1319
|
+
# end
|
1320
|
+
def lock!(style=:update)
|
1321
|
+
_refresh(this.lock_style(style)) unless new?
|
1390
1322
|
self
|
1391
1323
|
end
|
1392
1324
|
|
@@ -1416,9 +1348,7 @@ module Sequel
|
|
1416
1348
|
# a.modified!(:name)
|
1417
1349
|
# a.name.gsub!(/[aeou]/, 'i')
|
1418
1350
|
def modified!(column=nil)
|
1419
|
-
|
1420
|
-
changed_columns << column
|
1421
|
-
end
|
1351
|
+
_add_changed_column(column) if column
|
1422
1352
|
@modified = true
|
1423
1353
|
end
|
1424
1354
|
|
@@ -1428,7 +1358,7 @@ module Sequel
|
|
1428
1358
|
#
|
1429
1359
|
# a = Artist[1]
|
1430
1360
|
# a.modified? # => false
|
1431
|
-
# a.set(:
|
1361
|
+
# a.set(name: 'Jim')
|
1432
1362
|
# a.modified? # => true
|
1433
1363
|
#
|
1434
1364
|
# If a column is given, specifically check if the given column has
|
@@ -1477,12 +1407,12 @@ module Sequel
|
|
1477
1407
|
model.primary_key_hash(pk)
|
1478
1408
|
end
|
1479
1409
|
|
1480
|
-
# Returns a hash mapping the receivers primary key column(s) to their values.
|
1410
|
+
# Returns a hash mapping the receivers qualified primary key column(s) to their values.
|
1481
1411
|
#
|
1482
1412
|
# Artist[1].qualified_pk_hash
|
1483
|
-
# # => {Sequel
|
1413
|
+
# # => {Sequel[:artists][:id]=>1}
|
1484
1414
|
# Artist[[1, 2]].qualified_pk_hash
|
1485
|
-
# # => {Sequel
|
1415
|
+
# # => {Sequel[:artists][:id1]=>1, Sequel[:artists][:id2]=>2}
|
1486
1416
|
def qualified_pk_hash(qualifier=model.table_name)
|
1487
1417
|
model.qualified_primary_key_hash(pk, qualifier)
|
1488
1418
|
end
|
@@ -1510,9 +1440,9 @@ module Sequel
|
|
1510
1440
|
# is valid and before hooks execute successfully. Fails if:
|
1511
1441
|
#
|
1512
1442
|
# * the record is not valid, or
|
1513
|
-
# * before_save
|
1514
|
-
# * the record is new and before_create
|
1515
|
-
# * the record is not new and before_update
|
1443
|
+
# * before_save calls cancel_action, or
|
1444
|
+
# * the record is new and before_create calls cancel_action, or
|
1445
|
+
# * the record is not new and before_update calls cancel_action.
|
1516
1446
|
#
|
1517
1447
|
# If +save+ fails and either raise_on_save_failure or the
|
1518
1448
|
# :raise_on_failure option is true, it raises ValidationFailed
|
@@ -1520,9 +1450,6 @@ module Sequel
|
|
1520
1450
|
#
|
1521
1451
|
# If it succeeds, it returns self.
|
1522
1452
|
#
|
1523
|
-
# You can provide an optional list of columns to update, in which
|
1524
|
-
# case it only updates those columns, or a options hash.
|
1525
|
-
#
|
1526
1453
|
# Takes the following options:
|
1527
1454
|
#
|
1528
1455
|
# :changed :: save all changed columns, instead of all columns or the columns given
|
@@ -1537,12 +1464,9 @@ module Sequel
|
|
1537
1464
|
def save(opts=OPTS)
|
1538
1465
|
raise Sequel::Error, "can't save frozen object" if frozen?
|
1539
1466
|
set_server(opts[:server]) if opts[:server]
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
raise(ValidationFailed.new(self)) if raise_on_failure?(opts)
|
1544
|
-
return
|
1545
|
-
end
|
1467
|
+
unless _save_valid?(opts)
|
1468
|
+
raise(validation_failed_error) if raise_on_failure?(opts)
|
1469
|
+
return
|
1546
1470
|
end
|
1547
1471
|
checked_save_failure(opts){checked_transaction(opts){_save(opts)}}
|
1548
1472
|
end
|
@@ -1565,22 +1489,12 @@ module Sequel
|
|
1565
1489
|
# a setter method (or ignoring it if <tt>strict_param_setting = false</tt>).
|
1566
1490
|
# Does not save the record.
|
1567
1491
|
#
|
1568
|
-
# artist.set(:
|
1492
|
+
# artist.set(name: 'Jim')
|
1569
1493
|
# artist.name # => 'Jim'
|
1570
1494
|
def set(hash)
|
1571
1495
|
set_restricted(hash, :default)
|
1572
1496
|
end
|
1573
1497
|
|
1574
|
-
# Set all values using the entries in the hash, ignoring any setting of
|
1575
|
-
# allowed_columns in the model.
|
1576
|
-
#
|
1577
|
-
# Artist.set_allowed_columns(:num_albums)
|
1578
|
-
# artist.set_all(:name=>'Jim')
|
1579
|
-
# artist.name # => 'Jim'
|
1580
|
-
def set_all(hash)
|
1581
|
-
set_restricted(hash, :all)
|
1582
|
-
end
|
1583
|
-
|
1584
1498
|
# For each of the fields in the given array +fields+, call the setter
|
1585
1499
|
# method with the value of that +hash+ entry for the field. Returns self.
|
1586
1500
|
#
|
@@ -1593,43 +1507,36 @@ module Sequel
|
|
1593
1507
|
#
|
1594
1508
|
# Examples:
|
1595
1509
|
#
|
1596
|
-
# artist.set_fields({:
|
1510
|
+
# artist.set_fields({name: 'Jim'}, [:name])
|
1597
1511
|
# artist.name # => 'Jim'
|
1598
1512
|
#
|
1599
|
-
# artist.set_fields({:
|
1513
|
+
# artist.set_fields({hometown: 'LA'}, [:name])
|
1600
1514
|
# artist.name # => nil
|
1601
1515
|
# artist.hometown # => 'Sac'
|
1602
1516
|
#
|
1603
1517
|
# artist.name # => 'Jim'
|
1604
|
-
# artist.set_fields({}, [:name], :
|
1518
|
+
# artist.set_fields({}, [:name], missing: :skip)
|
1605
1519
|
# artist.name # => 'Jim'
|
1606
1520
|
#
|
1607
1521
|
# artist.name # => 'Jim'
|
1608
|
-
# artist.set_fields({}, [:name], :
|
1522
|
+
# artist.set_fields({}, [:name], missing: :raise)
|
1609
1523
|
# # Sequel::Error raised
|
1610
1524
|
def set_fields(hash, fields, opts=nil)
|
1611
1525
|
opts = if opts
|
1612
|
-
|
1526
|
+
model.default_set_fields_options.merge(opts)
|
1613
1527
|
else
|
1614
1528
|
model.default_set_fields_options
|
1615
1529
|
end
|
1616
1530
|
|
1617
|
-
case opts[:missing]
|
1618
|
-
when :skip
|
1531
|
+
case missing = opts[:missing]
|
1532
|
+
when :skip, :raise
|
1533
|
+
do_raise = true if missing == :raise
|
1619
1534
|
fields.each do |f|
|
1620
1535
|
if hash.has_key?(f)
|
1621
1536
|
set_column_value("#{f}=", hash[f])
|
1622
1537
|
elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
|
1623
1538
|
set_column_value("#{sf}=", hash[sf])
|
1624
|
-
|
1625
|
-
end
|
1626
|
-
when :raise
|
1627
|
-
fields.each do |f|
|
1628
|
-
if hash.has_key?(f)
|
1629
|
-
set_column_value("#{f}=", hash[f])
|
1630
|
-
elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
|
1631
|
-
set_column_value("#{sf}=", hash[sf])
|
1632
|
-
else
|
1539
|
+
elsif do_raise
|
1633
1540
|
raise(Sequel::Error, "missing field in hash: #{f.inspect} not in #{hash.inspect}")
|
1634
1541
|
end
|
1635
1542
|
end
|
@@ -1639,31 +1546,28 @@ module Sequel
|
|
1639
1546
|
self
|
1640
1547
|
end
|
1641
1548
|
|
1642
|
-
# Set the values using the entries in the hash, only if the key
|
1643
|
-
# is included in only. It may be a better idea to use +set_fields+
|
1644
|
-
# instead of this method.
|
1645
|
-
#
|
1646
|
-
# artist.set_only({:name=>'Jim'}, :name)
|
1647
|
-
# artist.name # => 'Jim'
|
1648
|
-
#
|
1649
|
-
# artist.set_only({:hometown=>'LA'}, :name) # Raise Error
|
1650
|
-
def set_only(hash, *only)
|
1651
|
-
set_restricted(hash, only.flatten)
|
1652
|
-
end
|
1653
|
-
|
1654
1549
|
# Set the shard that this object is tied to. Returns self.
|
1655
1550
|
def set_server(s)
|
1656
1551
|
@server = s
|
1657
|
-
@this
|
1552
|
+
@this = @this.server(s) if @this
|
1658
1553
|
self
|
1659
1554
|
end
|
1660
1555
|
|
1661
1556
|
# Clear the setter_methods cache when a method is added
|
1662
1557
|
def singleton_method_added(meth)
|
1663
|
-
@singleton_setter_added = true if meth.to_s
|
1558
|
+
@singleton_setter_added = true if meth.to_s.end_with?('=')
|
1664
1559
|
super
|
1665
1560
|
end
|
1666
1561
|
|
1562
|
+
# Skip all validation of the object on the next call to #save,
|
1563
|
+
# including the running of validation hooks. This is designed for
|
1564
|
+
# and should only be used in cases where #valid? is called before
|
1565
|
+
# saving and the <tt>validate: false</tt> option cannot be passed to
|
1566
|
+
# #save.
|
1567
|
+
def skip_validation_on_next_save!
|
1568
|
+
@skip_validation_on_next_save = true
|
1569
|
+
end
|
1570
|
+
|
1667
1571
|
# Returns (naked) dataset that should return only this instance.
|
1668
1572
|
#
|
1669
1573
|
# Artist[1].this
|
@@ -1671,57 +1575,29 @@ module Sequel
|
|
1671
1575
|
def this
|
1672
1576
|
return @this if @this
|
1673
1577
|
raise Error, "No dataset for model #{model}" unless ds = model.instance_dataset
|
1674
|
-
|
1675
|
-
cond = if ds.joined_dataset?
|
1676
|
-
qualified_pk_hash
|
1677
|
-
else
|
1678
|
-
pk_hash
|
1679
|
-
end
|
1680
|
-
|
1681
|
-
@this = use_server(ds.where(cond))
|
1578
|
+
@this = use_server(ds.where(pk_hash))
|
1682
1579
|
end
|
1683
1580
|
|
1684
1581
|
# Runs #set with the passed hash and then runs save_changes.
|
1685
1582
|
#
|
1686
|
-
# artist.update(:
|
1583
|
+
# artist.update(name: 'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1687
1584
|
def update(hash)
|
1688
1585
|
update_restricted(hash, :default)
|
1689
1586
|
end
|
1690
1587
|
|
1691
|
-
# Update
|
1692
|
-
#
|
1693
|
-
#
|
1694
|
-
# Artist.set_allowed_columns(:num_albums)
|
1695
|
-
# artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1696
|
-
def update_all(hash)
|
1697
|
-
update_restricted(hash, :all)
|
1698
|
-
end
|
1699
|
-
|
1700
|
-
# Update the instances values by calling +set_fields+ with the arguments, then
|
1701
|
-
# saves any changes to the record. Returns self.
|
1588
|
+
# Update the instance's values by calling set_fields with the arguments, then
|
1589
|
+
# calls save_changes.
|
1702
1590
|
#
|
1703
|
-
# artist.update_fields({:
|
1591
|
+
# artist.update_fields({name: 'Jim'}, [:name])
|
1704
1592
|
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1705
1593
|
#
|
1706
|
-
# artist.update_fields({:
|
1594
|
+
# artist.update_fields({hometown: 'LA'}, [:name])
|
1707
1595
|
# # UPDATE artists SET name = NULL WHERE (id = 1)
|
1708
1596
|
def update_fields(hash, fields, opts=nil)
|
1709
1597
|
set_fields(hash, fields, opts)
|
1710
1598
|
save_changes
|
1711
1599
|
end
|
1712
1600
|
|
1713
|
-
# Update the values using the entries in the hash, only if the key
|
1714
|
-
# is included in only. It may be a better idea to use +update_fields+
|
1715
|
-
# instead of this method.
|
1716
|
-
#
|
1717
|
-
# artist.update_only({:name=>'Jim'}, :name)
|
1718
|
-
# # UPDATE artists SET name = 'Jim' WHERE (id = 1)
|
1719
|
-
#
|
1720
|
-
# artist.update_only({:hometown=>'LA'}, :name) # Raise Error
|
1721
|
-
def update_only(hash, *only)
|
1722
|
-
update_restricted(hash, only.flatten)
|
1723
|
-
end
|
1724
|
-
|
1725
1601
|
# Validates the object. If the object is invalid, errors should be added
|
1726
1602
|
# to the errors attribute. By default, does nothing, as all models
|
1727
1603
|
# are valid by default. See the {"Model Validations" guide}[rdoc-ref:doc/validations.rdoc].
|
@@ -1733,25 +1609,37 @@ module Sequel
|
|
1733
1609
|
|
1734
1610
|
# Validates the object and returns true if no errors are reported.
|
1735
1611
|
#
|
1736
|
-
# artist(:
|
1737
|
-
# artist(:
|
1612
|
+
# artist.set(name: 'Valid').valid? # => true
|
1613
|
+
# artist.set(name: 'Invalid').valid? # => false
|
1738
1614
|
# artist.errors.full_messages # => ['name cannot be Invalid']
|
1739
1615
|
def valid?(opts = OPTS)
|
1740
|
-
|
1741
|
-
|
1616
|
+
begin
|
1617
|
+
_valid?(opts)
|
1618
|
+
rescue HookFailed
|
1619
|
+
false
|
1620
|
+
end
|
1742
1621
|
end
|
1743
1622
|
|
1744
1623
|
private
|
1745
1624
|
|
1746
|
-
#
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1625
|
+
# Add a column as a changed column.
|
1626
|
+
def _add_changed_column(column)
|
1627
|
+
cc = _changed_columns
|
1628
|
+
cc << column unless cc.include?(column)
|
1629
|
+
end
|
1630
|
+
|
1631
|
+
# Internal changed_columns method that just returns stored array.
|
1632
|
+
def _changed_columns
|
1633
|
+
@changed_columns ||= []
|
1753
1634
|
end
|
1754
1635
|
|
1636
|
+
# Clear the changed columns. Reason is the reason for clearing
|
1637
|
+
# the columns, and should be one of: :initialize, :refresh, :create
|
1638
|
+
# or :update.
|
1639
|
+
def _clear_changed_columns(_reason)
|
1640
|
+
_changed_columns.clear
|
1641
|
+
end
|
1642
|
+
|
1755
1643
|
# Do the deletion of the object's dataset, and check that the row
|
1756
1644
|
# was actually deleted.
|
1757
1645
|
def _delete
|
@@ -1760,7 +1648,7 @@ module Sequel
|
|
1760
1648
|
n
|
1761
1649
|
end
|
1762
1650
|
|
1763
|
-
# The dataset to use when deleting the object.
|
1651
|
+
# The dataset to use when deleting the object. The same as the object's
|
1764
1652
|
# dataset by default.
|
1765
1653
|
def _delete_dataset
|
1766
1654
|
this
|
@@ -1782,18 +1670,14 @@ module Sequel
|
|
1782
1670
|
# Internal destroy method, separted from destroy to
|
1783
1671
|
# allow running inside a transaction
|
1784
1672
|
def _destroy(opts)
|
1785
|
-
sh = {:server=>this_server}
|
1786
|
-
db.after_rollback(sh){after_destroy_rollback} if uacr = use_after_commit_rollback
|
1787
1673
|
called = false
|
1788
1674
|
around_destroy do
|
1789
1675
|
called = true
|
1790
|
-
|
1676
|
+
before_destroy
|
1791
1677
|
_destroy_delete
|
1792
1678
|
after_destroy
|
1793
|
-
true
|
1794
1679
|
end
|
1795
1680
|
raise_hook_failure(:around_destroy) unless called
|
1796
|
-
db.after_commit(sh){after_destroy_commit} if uacr
|
1797
1681
|
self
|
1798
1682
|
end
|
1799
1683
|
|
@@ -1808,8 +1692,8 @@ module Sequel
|
|
1808
1692
|
# the record should be refreshed from the database.
|
1809
1693
|
def _insert
|
1810
1694
|
ds = _insert_dataset
|
1811
|
-
if _use_insert_select?(ds) && (h = _insert_select_raw(ds))
|
1812
|
-
_save_set_values(h)
|
1695
|
+
if _use_insert_select?(ds) && !(h = _insert_select_raw(ds)).nil?
|
1696
|
+
_save_set_values(h) if h
|
1813
1697
|
nil
|
1814
1698
|
else
|
1815
1699
|
iid = _insert_raw(ds)
|
@@ -1830,27 +1714,38 @@ module Sequel
|
|
1830
1714
|
|
1831
1715
|
# Insert into the given dataset and return the primary key created (if any).
|
1832
1716
|
def _insert_raw(ds)
|
1833
|
-
ds.insert(
|
1717
|
+
ds.insert(_insert_values)
|
1834
1718
|
end
|
1835
1719
|
|
1836
1720
|
# Insert into the given dataset and return the hash of column values.
|
1837
1721
|
def _insert_select_raw(ds)
|
1838
|
-
ds.insert_select(
|
1722
|
+
ds.insert_select(_insert_values)
|
1839
1723
|
end
|
1724
|
+
|
1725
|
+
# The values hash to use when inserting a new record.
|
1726
|
+
alias _insert_values values
|
1727
|
+
private :_insert_values
|
1840
1728
|
|
1841
1729
|
# Refresh using a particular dataset, used inside save to make sure the same server
|
1842
1730
|
# is used for reading newly inserted values from the database
|
1843
1731
|
def _refresh(dataset)
|
1844
|
-
_refresh_set_values(_refresh_get(dataset) || raise(
|
1845
|
-
|
1732
|
+
_refresh_set_values(_refresh_get(dataset) || raise(NoExistingObject, "Record not found"))
|
1733
|
+
_clear_changed_columns(:refresh)
|
1846
1734
|
end
|
1847
1735
|
|
1848
1736
|
# Get the row of column data from the database.
|
1849
1737
|
def _refresh_get(dataset)
|
1850
|
-
dataset.
|
1738
|
+
if (sql = model.fast_pk_lookup_sql) && !dataset.opts[:lock]
|
1739
|
+
sql = sql.dup
|
1740
|
+
ds = use_server(dataset)
|
1741
|
+
ds.literal_append(sql, pk)
|
1742
|
+
ds.with_sql_first(sql)
|
1743
|
+
else
|
1744
|
+
dataset.first
|
1745
|
+
end
|
1851
1746
|
end
|
1852
1747
|
|
1853
|
-
# Set the
|
1748
|
+
# Set the values to the given hash after refreshing.
|
1854
1749
|
def _refresh_set_values(h)
|
1855
1750
|
@values = h
|
1856
1751
|
end
|
@@ -1858,24 +1753,22 @@ module Sequel
|
|
1858
1753
|
# Internal version of save, split from save to allow running inside
|
1859
1754
|
# it's own transaction.
|
1860
1755
|
def _save(opts)
|
1861
|
-
sh = {:server=>this_server}
|
1862
|
-
db.after_rollback(sh){after_rollback} if uacr = use_after_commit_rollback
|
1863
|
-
was_new = false
|
1864
1756
|
pk = nil
|
1865
1757
|
called_save = false
|
1866
1758
|
called_cu = false
|
1867
1759
|
around_save do
|
1868
1760
|
called_save = true
|
1869
|
-
|
1761
|
+
before_save
|
1762
|
+
|
1870
1763
|
if new?
|
1871
|
-
was_new = true
|
1872
1764
|
around_create do
|
1873
1765
|
called_cu = true
|
1874
|
-
|
1766
|
+
before_create
|
1875
1767
|
pk = _insert
|
1876
1768
|
@this = nil
|
1877
1769
|
@new = false
|
1878
|
-
@
|
1770
|
+
@modified = false
|
1771
|
+
pk ? _save_refresh : _clear_changed_columns(:create)
|
1879
1772
|
after_create
|
1880
1773
|
true
|
1881
1774
|
end
|
@@ -1883,22 +1776,23 @@ module Sequel
|
|
1883
1776
|
else
|
1884
1777
|
around_update do
|
1885
1778
|
called_cu = true
|
1886
|
-
|
1779
|
+
before_update
|
1887
1780
|
columns = opts[:columns]
|
1888
1781
|
if columns.nil?
|
1889
|
-
|
1890
|
-
|
1782
|
+
columns_updated = if opts[:changed]
|
1783
|
+
_save_update_changed_colums_hash
|
1891
1784
|
else
|
1892
1785
|
_save_update_all_columns_hash
|
1893
1786
|
end
|
1894
|
-
|
1787
|
+
_clear_changed_columns(:update)
|
1895
1788
|
else # update only the specified columns
|
1896
1789
|
columns = Array(columns)
|
1897
|
-
|
1898
|
-
|
1790
|
+
columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
1791
|
+
_changed_columns.reject!{|c| columns.include?(c)}
|
1899
1792
|
end
|
1900
|
-
_update_columns(
|
1793
|
+
_update_columns(columns_updated)
|
1901
1794
|
@this = nil
|
1795
|
+
@modified = false
|
1902
1796
|
after_update
|
1903
1797
|
true
|
1904
1798
|
end
|
@@ -1908,14 +1802,6 @@ module Sequel
|
|
1908
1802
|
true
|
1909
1803
|
end
|
1910
1804
|
raise_hook_failure(:around_save) unless called_save
|
1911
|
-
if was_new
|
1912
|
-
@was_new = nil
|
1913
|
-
pk ? _save_refresh : changed_columns.clear
|
1914
|
-
else
|
1915
|
-
@columns_updated = nil
|
1916
|
-
end
|
1917
|
-
@modified = false
|
1918
|
-
db.after_commit(sh){after_commit} if uacr
|
1919
1805
|
self
|
1920
1806
|
end
|
1921
1807
|
|
@@ -1923,8 +1809,8 @@ module Sequel
|
|
1923
1809
|
# default values of all columns. Separated from _save so it
|
1924
1810
|
# can be overridden to avoid the refresh.
|
1925
1811
|
def _save_refresh
|
1926
|
-
_save_set_values(_refresh_get(this.server?(:default)) || raise(
|
1927
|
-
|
1812
|
+
_save_set_values(_refresh_get(this.server?(:default)) || raise(NoExistingObject, "Record not found"))
|
1813
|
+
_clear_changed_columns(:create)
|
1928
1814
|
end
|
1929
1815
|
|
1930
1816
|
# Set values to the provided hash. Called after a create,
|
@@ -1941,10 +1827,32 @@ module Sequel
|
|
1941
1827
|
# to their existing values.
|
1942
1828
|
def _save_update_all_columns_hash
|
1943
1829
|
v = Hash[@values]
|
1944
|
-
|
1830
|
+
cc = changed_columns
|
1831
|
+
Array(primary_key).each{|x| v.delete(x) unless cc.include?(x)}
|
1945
1832
|
v
|
1946
1833
|
end
|
1947
1834
|
|
1835
|
+
# Return a hash of values used when saving changed columns of an
|
1836
|
+
# existing object. Defaults to all of the objects current values
|
1837
|
+
# that are recorded as modified.
|
1838
|
+
def _save_update_changed_colums_hash
|
1839
|
+
cc = changed_columns
|
1840
|
+
@values.reject{|k,v| !cc.include?(k)}
|
1841
|
+
end
|
1842
|
+
|
1843
|
+
# Validate the object if validating on save. Skips validation
|
1844
|
+
# completely (including validation hooks) if
|
1845
|
+
# skip_validation_on_save! has been called on the object,
|
1846
|
+
# resetting the flag so that future saves will validate.
|
1847
|
+
def _save_valid?(opts)
|
1848
|
+
if @skip_validation_on_next_save
|
1849
|
+
@skip_validation_on_next_save = false
|
1850
|
+
return true
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
checked_save_failure(opts){_valid?(opts)}
|
1854
|
+
end
|
1855
|
+
|
1948
1856
|
# Call _update with the given columns, if any are present.
|
1949
1857
|
# Plugins can override this method in order to update with
|
1950
1858
|
# additional columns, even when the column hash is initially empty.
|
@@ -1976,38 +1884,25 @@ module Sequel
|
|
1976
1884
|
(!ds.opts[:select] || ds.opts[:returning]) && ds.supports_insert_select?
|
1977
1885
|
end
|
1978
1886
|
|
1979
|
-
# Internal validation method
|
1980
|
-
|
1981
|
-
# +false+, +false+ will be returned instead.
|
1982
|
-
def _valid?(raise_errors, opts)
|
1887
|
+
# Internal validation method, running validation hooks.
|
1888
|
+
def _valid?(opts)
|
1983
1889
|
return errors.empty? if frozen?
|
1984
1890
|
errors.clear
|
1985
1891
|
called = false
|
1986
|
-
|
1892
|
+
skip_validate = opts[:validate] == false
|
1987
1893
|
around_validation do
|
1988
1894
|
called = true
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
else
|
1993
|
-
error = true
|
1994
|
-
end
|
1995
|
-
false
|
1996
|
-
else
|
1997
|
-
validate
|
1998
|
-
after_validation
|
1999
|
-
errors.empty?
|
2000
|
-
end
|
1895
|
+
before_validation
|
1896
|
+
validate unless skip_validate
|
1897
|
+
after_validation
|
2001
1898
|
end
|
2002
|
-
|
2003
|
-
if
|
2004
|
-
|
2005
|
-
|
2006
|
-
else
|
2007
|
-
false
|
2008
|
-
end
|
2009
|
-
else
|
1899
|
+
|
1900
|
+
return true if skip_validate
|
1901
|
+
|
1902
|
+
if called
|
2010
1903
|
errors.empty?
|
1904
|
+
else
|
1905
|
+
raise_hook_failure(:around_validation)
|
2011
1906
|
end
|
2012
1907
|
end
|
2013
1908
|
|
@@ -2032,8 +1927,7 @@ module Sequel
|
|
2032
1927
|
|
2033
1928
|
# Change the value of the column to given value, recording the change.
|
2034
1929
|
def change_column_value(column, value)
|
2035
|
-
|
2036
|
-
cc << column unless cc.include?(column)
|
1930
|
+
_add_changed_column(column)
|
2037
1931
|
@values[column] = value
|
2038
1932
|
end
|
2039
1933
|
|
@@ -2042,24 +1936,17 @@ module Sequel
|
|
2042
1936
|
Errors
|
2043
1937
|
end
|
2044
1938
|
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
def clone
|
2057
|
-
o = dup
|
2058
|
-
o.freeze if frozen?
|
2059
|
-
o
|
2060
|
-
end
|
2061
|
-
public :clone
|
2062
|
-
# :nocov:
|
1939
|
+
# A HookFailed exception for the given message tied to the current instance.
|
1940
|
+
def hook_failed_error(msg)
|
1941
|
+
HookFailed.new(msg, self)
|
1942
|
+
end
|
1943
|
+
|
1944
|
+
# Clone constructor -- freeze internal data structures if the original's
|
1945
|
+
# are frozen.
|
1946
|
+
def initialize_clone(other)
|
1947
|
+
super
|
1948
|
+
freeze if other.frozen?
|
1949
|
+
self
|
2063
1950
|
end
|
2064
1951
|
|
2065
1952
|
# Copy constructor -- Duplicate internal data structures.
|
@@ -2068,7 +1955,6 @@ module Sequel
|
|
2068
1955
|
@values = Hash[@values]
|
2069
1956
|
@changed_columns = @changed_columns.dup if @changed_columns
|
2070
1957
|
@errors = @errors.dup if @errors
|
2071
|
-
@this = @this.dup if @this
|
2072
1958
|
self
|
2073
1959
|
end
|
2074
1960
|
|
@@ -2104,9 +1990,9 @@ module Sequel
|
|
2104
1990
|
"a hook failed"
|
2105
1991
|
end
|
2106
1992
|
|
2107
|
-
raise
|
1993
|
+
raise hook_failed_error(msg)
|
2108
1994
|
end
|
2109
|
-
|
1995
|
+
|
2110
1996
|
# Get the ruby class or classes related to the given column's type.
|
2111
1997
|
def schema_type_class(column)
|
2112
1998
|
if (sch = db_schema[column]) && (type = sch[:type])
|
@@ -2147,19 +2033,13 @@ module Sequel
|
|
2147
2033
|
# :all :: Allow setting all setters, except those specifically restricted (such as ==).
|
2148
2034
|
# Array :: Only allow setting of columns in the given array.
|
2149
2035
|
def setter_methods(type)
|
2150
|
-
if type == :default
|
2151
|
-
|
2152
|
-
return model.setter_methods
|
2153
|
-
end
|
2036
|
+
if type == :default && !@singleton_setter_added
|
2037
|
+
return model.setter_methods
|
2154
2038
|
end
|
2155
2039
|
|
2156
|
-
|
2157
|
-
|
2158
|
-
|
2159
|
-
meths = methods.collect(&:to_s).grep(SETTER_METHOD_REGEXP) - RESTRICTED_SETTER_METHODS
|
2160
|
-
meths -= Array(primary_key).map{|x| "#{x}="} if type != :all && primary_key && model.restrict_primary_key?
|
2161
|
-
meths
|
2162
|
-
end
|
2040
|
+
meths = methods.map(&:to_s).select{|l| l.end_with?('=')} - RESTRICTED_SETTER_METHODS
|
2041
|
+
meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && model.restrict_primary_key?
|
2042
|
+
meths
|
2163
2043
|
end
|
2164
2044
|
|
2165
2045
|
# The server/shard that the model object's dataset uses, or :default if the
|
@@ -2205,22 +2085,28 @@ module Sequel
|
|
2205
2085
|
def use_transaction?(opts = OPTS)
|
2206
2086
|
opts.fetch(:transaction, use_transactions)
|
2207
2087
|
end
|
2088
|
+
|
2089
|
+
# An ValidationFailed exception instance to raise for this instance.
|
2090
|
+
def validation_failed_error
|
2091
|
+
ValidationFailed.new(self)
|
2092
|
+
end
|
2208
2093
|
end
|
2209
2094
|
|
2210
|
-
#
|
2211
|
-
# the call to set_dataset.
|
2095
|
+
# DatasetMethods contains methods that all model datasets have.
|
2212
2096
|
module DatasetMethods
|
2213
2097
|
# The model class associated with this dataset
|
2214
2098
|
#
|
2215
2099
|
# Artist.dataset.model # => Artist
|
2216
|
-
|
2100
|
+
def model
|
2101
|
+
@opts[:model]
|
2102
|
+
end
|
2217
2103
|
|
2218
2104
|
# Assume if a single integer is given that it is a lookup by primary
|
2219
2105
|
# key, and call with_pk with the argument.
|
2220
2106
|
#
|
2221
2107
|
# Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
2222
2108
|
def [](*args)
|
2223
|
-
if args.length == 1 && (i = args
|
2109
|
+
if args.length == 1 && (i = args[0]) && i.is_a?(Integer)
|
2224
2110
|
with_pk(i)
|
2225
2111
|
else
|
2226
2112
|
super
|
@@ -2241,58 +2127,14 @@ module Sequel
|
|
2241
2127
|
model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
|
2242
2128
|
end
|
2243
2129
|
|
2244
|
-
# Allow Sequel::Model classes to be used as dataset arguments when graphing:
|
2245
|
-
#
|
2246
|
-
# Artist.graph(Album, :artist_id=>id)
|
2247
|
-
# # SELECT artists.id, artists.name, albums.id AS albums_id, albums.artist_id, albums.name AS albums_name
|
2248
|
-
# # FROM artists LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
2249
|
-
def graph(table, *args, &block)
|
2250
|
-
if table.is_a?(Class) && table < Sequel::Model
|
2251
|
-
super(table.dataset, *args, &block)
|
2252
|
-
else
|
2253
|
-
super
|
2254
|
-
end
|
2255
|
-
end
|
2256
|
-
|
2257
|
-
# Handle Sequel::Model instances when inserting, using the model instance's
|
2258
|
-
# values for the insert, unless the model instance can be used directly in
|
2259
|
-
# SQL.
|
2260
|
-
#
|
2261
|
-
# Album.insert(Album.load(:name=>'A'))
|
2262
|
-
# # INSERT INTO albums (name) VALUES ('A')
|
2263
|
-
def insert_sql(*values)
|
2264
|
-
if values.size == 1 && (v = values.at(0)).is_a?(Sequel::Model) && !v.respond_to?(:sql_literal_append)
|
2265
|
-
super(v.to_hash)
|
2266
|
-
else
|
2267
|
-
super
|
2268
|
-
end
|
2269
|
-
end
|
2270
|
-
|
2271
|
-
# Allow Sequel::Model classes to be used as table name arguments in dataset
|
2272
|
-
# join methods:
|
2273
|
-
#
|
2274
|
-
# Artist.join(Album, :artist_id=>id)
|
2275
|
-
# # SELECT * FROM artists INNER JOIN albums ON (albums.artist_id = artists.id)
|
2276
|
-
def join_table(type, table, *args, &block)
|
2277
|
-
if table.is_a?(Class) && table < Sequel::Model
|
2278
|
-
if table.dataset.simple_select_all?
|
2279
|
-
super(type, table.table_name, *args, &block)
|
2280
|
-
else
|
2281
|
-
super(type, table.dataset, *args, &block)
|
2282
|
-
end
|
2283
|
-
else
|
2284
|
-
super
|
2285
|
-
end
|
2286
|
-
end
|
2287
|
-
|
2288
2130
|
# If there is no order already defined on this dataset, order it by
|
2289
2131
|
# the primary key and call last.
|
2290
2132
|
#
|
2291
2133
|
# Album.last
|
2292
2134
|
# # SELECT * FROM albums ORDER BY id DESC LIMIT 1
|
2293
2135
|
def last(*a, &block)
|
2294
|
-
if
|
2295
|
-
|
2136
|
+
if ds = _primary_key_order
|
2137
|
+
ds.last(*a, &block)
|
2296
2138
|
else
|
2297
2139
|
super
|
2298
2140
|
end
|
@@ -2307,30 +2149,35 @@ module Sequel
|
|
2307
2149
|
# # SELECT * FROM albums ORDER BY id LIMIT 1000 OFFSET 2000
|
2308
2150
|
# # ...
|
2309
2151
|
def paged_each(*a, &block)
|
2310
|
-
if
|
2311
|
-
|
2152
|
+
if ds = _primary_key_order
|
2153
|
+
ds.paged_each(*a, &block)
|
2312
2154
|
else
|
2313
2155
|
super
|
2314
2156
|
end
|
2315
2157
|
end
|
2316
2158
|
|
2317
|
-
# This allows you to call +
|
2159
|
+
# This allows you to call +as_hash+ without any arguments, which will
|
2318
2160
|
# result in a hash with the primary key value being the key and the
|
2319
2161
|
# model object being the value.
|
2320
2162
|
#
|
2321
|
-
# Artist.dataset.
|
2163
|
+
# Artist.dataset.as_hash # SELECT * FROM artists
|
2322
2164
|
# # => {1=>#<Artist {:id=>1, ...}>,
|
2323
2165
|
# # 2=>#<Artist {:id=>2, ...}>,
|
2324
2166
|
# # ...}
|
2325
|
-
def
|
2167
|
+
def as_hash(key_column=nil, value_column=nil, opts=OPTS)
|
2326
2168
|
if key_column
|
2327
2169
|
super
|
2328
2170
|
else
|
2329
2171
|
raise(Sequel::Error, "No primary key for model") unless model && (pk = model.primary_key)
|
2330
|
-
super(pk, value_column)
|
2172
|
+
super(pk, value_column, opts)
|
2331
2173
|
end
|
2332
2174
|
end
|
2333
2175
|
|
2176
|
+
# Alias of as_hash for backwards compatibility.
|
2177
|
+
def to_hash(*a)
|
2178
|
+
as_hash(*a)
|
2179
|
+
end
|
2180
|
+
|
2334
2181
|
# Given a primary key value, return the first record in the dataset with that primary key
|
2335
2182
|
# value. If no records matches, returns nil.
|
2336
2183
|
#
|
@@ -2342,7 +2189,11 @@ module Sequel
|
|
2342
2189
|
# Artist.dataset.with_pk([1, 2])
|
2343
2190
|
# # SELECT * FROM artists WHERE ((artists.id1 = 1) AND (artists.id2 = 2)) LIMIT 1
|
2344
2191
|
def with_pk(pk)
|
2345
|
-
|
2192
|
+
if pk && (loader = _with_pk_loader)
|
2193
|
+
loader.first(*pk)
|
2194
|
+
else
|
2195
|
+
first(model.qualified_primary_key_hash(pk))
|
2196
|
+
end
|
2346
2197
|
end
|
2347
2198
|
|
2348
2199
|
# Same as with_pk, but raises NoMatchingRow instead of returning nil if no
|
@@ -2350,10 +2201,47 @@ module Sequel
|
|
2350
2201
|
def with_pk!(pk)
|
2351
2202
|
with_pk(pk) || raise(NoMatchingRow.new(self))
|
2352
2203
|
end
|
2204
|
+
|
2205
|
+
private
|
2206
|
+
|
2207
|
+
# If the dataset is not already ordered, and the model has a primary key,
|
2208
|
+
# return a clone ordered by the primary key.
|
2209
|
+
def _primary_key_order
|
2210
|
+
if @opts[:order].nil? && model && (pk = model.primary_key)
|
2211
|
+
cached_dataset(:_pk_order_ds){order(*pk)}
|
2212
|
+
end
|
2213
|
+
end
|
2214
|
+
|
2215
|
+
# A cached placeholder literalizer, if one exists for the current dataset.
|
2216
|
+
def _with_pk_loader
|
2217
|
+
cached_placeholder_literalizer(:_with_pk_loader) do |pl|
|
2218
|
+
table = model.table_name
|
2219
|
+
cond = case primary_key = model.primary_key
|
2220
|
+
when Array
|
2221
|
+
primary_key.map{|key| [SQL::QualifiedIdentifier.new(table, key), pl.arg]}
|
2222
|
+
when Symbol
|
2223
|
+
{SQL::QualifiedIdentifier.new(table, primary_key)=>pl.arg}
|
2224
|
+
else
|
2225
|
+
raise(Error, "#{model} does not have a primary key")
|
2226
|
+
end
|
2227
|
+
|
2228
|
+
where(cond).limit(1)
|
2229
|
+
end
|
2230
|
+
end
|
2231
|
+
|
2232
|
+
def non_sql_option?(key)
|
2233
|
+
super || key == :model
|
2234
|
+
end
|
2353
2235
|
end
|
2354
2236
|
|
2355
2237
|
extend ClassMethods
|
2356
2238
|
plugin self
|
2357
|
-
|
2239
|
+
|
2240
|
+
singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
|
2241
|
+
# :nocov:
|
2242
|
+
if RUBY_VERSION >= '1.9.3'
|
2243
|
+
# :nocov:
|
2244
|
+
singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
|
2245
|
+
end
|
2358
2246
|
end
|
2359
2247
|
end
|