sequel 3.21.0 → 3.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +576 -0
- data/doc/opening_databases.rdoc +121 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- data/lib/sequel/adapters/ado/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -0
- data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/connection_validator.rb +109 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +539 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +334 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +137 -16
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +134 -15
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +77 -6
- data/lib/sequel/plugins/list.rb +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/prepared_statements.rb +168 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/touch.rb +18 -6
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- data/lib/sequel/timezones.rb +67 -40
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +227 -33
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/connection_validator_spec.rb +118 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +268 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +112 -0
- data/spec/extensions/pg_array_spec.rb +324 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +200 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +340 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +86 -48
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +65 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +8 -11
- data/spec/extensions/touch_spec.rb +53 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -37
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +223 -81
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -11,8 +11,17 @@ module Sequel
|
|
|
11
11
|
# set the @deserialized_values entry. This plugin adds a before_save hook
|
|
12
12
|
# that serializes all @deserialized_values to @values.
|
|
13
13
|
#
|
|
14
|
-
# You can
|
|
15
|
-
#
|
|
14
|
+
# You can specify the serialization format as a pair of serializer/deserializer
|
|
15
|
+
# callable objects. You can also specify the serialization format as a single
|
|
16
|
+
# symbol, if such a symbol has a registered serializer/deserializer pair in the
|
|
17
|
+
# plugin. By default, the plugin registers the :marshal, :yaml, and :json
|
|
18
|
+
# serialization formats. To register your own serialization formats, use
|
|
19
|
+
# Sequel::Plugins::Serialization.register_format.
|
|
20
|
+
# If you use yaml or json format, you need to require the libraries, Sequel
|
|
21
|
+
# does not do the requiring for you.
|
|
22
|
+
#
|
|
23
|
+
# You can specify the columns to serialize when loading the plugin, or later
|
|
24
|
+
# using the serialize_attributes class method.
|
|
16
25
|
#
|
|
17
26
|
# Because of how this plugin works, it must be used inside each model class
|
|
18
27
|
# that needs serialization, after any set_dataset method calls in that class.
|
|
@@ -22,56 +31,96 @@ module Sequel
|
|
|
22
31
|
# == Example
|
|
23
32
|
#
|
|
24
33
|
# require 'sequel'
|
|
34
|
+
# # Require json, as the plugin doesn't require it for you.
|
|
25
35
|
# require 'json'
|
|
36
|
+
#
|
|
37
|
+
# # Register custom serializer/deserializer pair
|
|
38
|
+
# Sequel::Plugins::Serialization.register_format(:reverse,
|
|
39
|
+
# lambda{|v| v.reverse},
|
|
40
|
+
# lambda{|v| v.reverse})
|
|
41
|
+
#
|
|
26
42
|
# class User < Sequel::Model
|
|
43
|
+
# # Built-in format support when loading the plugin
|
|
27
44
|
# plugin :serialization, :json, :permissions
|
|
28
|
-
#
|
|
45
|
+
#
|
|
46
|
+
# # Built-in format support after loading the plugin using serialize_attributes
|
|
29
47
|
# plugin :serialization
|
|
30
|
-
# serialize_attributes :marshal, :permissions
|
|
48
|
+
# serialize_attributes :marshal, :permissions
|
|
49
|
+
#
|
|
50
|
+
# # Use custom registered serialization format just like built-in format
|
|
51
|
+
# serialize_attributes :reverse, :password
|
|
52
|
+
#
|
|
53
|
+
# # Use a custom serializer/deserializer pair without registering
|
|
54
|
+
# serialize_attributes [lambda{|v| v.reverse}, lambda{|v| v.reverse}], :password
|
|
31
55
|
# end
|
|
32
56
|
# user = User.create
|
|
33
57
|
# user.permissions = { :global => 'read-only' }
|
|
34
58
|
# user.save
|
|
35
59
|
module Serialization
|
|
60
|
+
# The default serializers supported by the serialization module.
|
|
61
|
+
# Use register_format to add serializers to this hash.
|
|
62
|
+
REGISTERED_FORMATS = {}
|
|
63
|
+
|
|
36
64
|
# Set up the column readers to do deserialization and the column writers
|
|
37
65
|
# to save the value in deserialized_values.
|
|
38
66
|
def self.apply(model, *args)
|
|
39
|
-
model.instance_eval
|
|
67
|
+
model.instance_eval do
|
|
68
|
+
@deserialization_map = {}
|
|
69
|
+
@serialization_map = {}
|
|
70
|
+
end
|
|
40
71
|
end
|
|
41
72
|
|
|
73
|
+
# Automatically call serialize_attributes with the format and columns unless
|
|
74
|
+
# no columns were provided.
|
|
42
75
|
def self.configure(model, format=nil, *columns)
|
|
43
76
|
model.serialize_attributes(format, *columns) unless columns.empty?
|
|
44
77
|
end
|
|
45
78
|
|
|
79
|
+
# Register a serializer/deserializer pair with a format symbol, to allow
|
|
80
|
+
# models to pick this format by name. Both serializer and deserializer
|
|
81
|
+
# should be callable objects.
|
|
82
|
+
def self.register_format(format, serializer, deserializer)
|
|
83
|
+
REGISTERED_FORMATS[format] = [serializer, deserializer]
|
|
84
|
+
end
|
|
85
|
+
register_format(:marshal, lambda{|v| [Marshal.dump(v)].pack('m')}, lambda{|v| Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)})
|
|
86
|
+
register_format(:yaml, lambda{|v| v.to_yaml}, lambda{|v| YAML.load(v)})
|
|
87
|
+
register_format(:json, lambda{|v| v.to_json}, lambda{|v| JSON.parse(v)})
|
|
88
|
+
|
|
46
89
|
module ClassMethods
|
|
47
|
-
# A
|
|
48
|
-
#
|
|
90
|
+
# A hash with column name symbols and callable values, with the value
|
|
91
|
+
# called to deserialize the column.
|
|
92
|
+
attr_reader :deserialization_map
|
|
93
|
+
|
|
94
|
+
# A hash with column name symbols and callable values, with the value
|
|
95
|
+
# called to serialize the column.
|
|
49
96
|
attr_reader :serialization_map
|
|
50
97
|
|
|
51
98
|
# Module to store the serialized column accessor methods, so they can
|
|
52
99
|
# call be overridden and call super to get the serialization behavior
|
|
53
100
|
attr_accessor :serialization_module
|
|
54
101
|
|
|
55
|
-
# Copy the
|
|
102
|
+
# Copy the serialization_map and deserialization map into the subclass.
|
|
56
103
|
def inherited(subclass)
|
|
57
104
|
super
|
|
58
105
|
sm = serialization_map.dup
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def serialization_format
|
|
65
|
-
serialization_map.values.first
|
|
106
|
+
dsm = deserialization_map.dup
|
|
107
|
+
subclass.instance_eval do
|
|
108
|
+
@deserialization_map = dsm
|
|
109
|
+
@serialization_map = sm
|
|
110
|
+
end
|
|
66
111
|
end
|
|
67
112
|
|
|
68
113
|
# Create instance level reader that deserializes column values on request,
|
|
69
|
-
# and instance level writer that stores new deserialized
|
|
70
|
-
# columns
|
|
114
|
+
# and instance level writer that stores new deserialized values.
|
|
71
115
|
def serialize_attributes(format, *columns)
|
|
72
|
-
|
|
116
|
+
if format.is_a?(Symbol)
|
|
117
|
+
unless format = REGISTERED_FORMATS[format]
|
|
118
|
+
raise(Error, "Unsupported serialization format: #{format} (valid formats: #{REGISTERED_FORMATS.keys.map{|k| k.inspect}.join})")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
serializer, deserializer = format
|
|
73
122
|
raise(Error, "No columns given. The serialization plugin requires you specify which columns to serialize") if columns.empty?
|
|
74
|
-
define_serialized_attribute_accessor(
|
|
123
|
+
define_serialized_attribute_accessor(serializer, deserializer, *columns)
|
|
75
124
|
end
|
|
76
125
|
|
|
77
126
|
# The columns that will be serialized. This is only for
|
|
@@ -83,12 +132,13 @@ module Sequel
|
|
|
83
132
|
private
|
|
84
133
|
|
|
85
134
|
# Add serializated attribute acessor methods to the serialization_module
|
|
86
|
-
def define_serialized_attribute_accessor(
|
|
135
|
+
def define_serialized_attribute_accessor(serializer, deserializer, *columns)
|
|
87
136
|
m = self
|
|
88
137
|
include(self.serialization_module ||= Module.new) unless serialization_module
|
|
89
138
|
serialization_module.class_eval do
|
|
90
139
|
columns.each do |column|
|
|
91
|
-
m.serialization_map[column] =
|
|
140
|
+
m.serialization_map[column] = serializer
|
|
141
|
+
m.deserialization_map[column] = deserializer
|
|
92
142
|
define_method(column) do
|
|
93
143
|
if deserialized_values.has_key?(column)
|
|
94
144
|
deserialized_values[column]
|
|
@@ -110,52 +160,51 @@ module Sequel
|
|
|
110
160
|
attr_reader :deserialized_values
|
|
111
161
|
|
|
112
162
|
# Set @deserialized_values to the empty hash
|
|
113
|
-
def
|
|
163
|
+
def initialize_set(values)
|
|
114
164
|
@deserialized_values = {}
|
|
115
165
|
super
|
|
116
166
|
end
|
|
117
167
|
|
|
118
|
-
# Serialize
|
|
168
|
+
# Serialize deserialized values before saving
|
|
119
169
|
def before_save
|
|
120
|
-
|
|
170
|
+
serialize_deserialized_values
|
|
121
171
|
super
|
|
122
172
|
end
|
|
123
173
|
|
|
124
|
-
#
|
|
125
|
-
def
|
|
126
|
-
@deserialized_values
|
|
174
|
+
# Initialization the deserialized values for objects retrieved from the database.
|
|
175
|
+
def set_values(*)
|
|
176
|
+
@deserialized_values ||= {}
|
|
127
177
|
super
|
|
128
178
|
end
|
|
129
179
|
|
|
130
180
|
private
|
|
131
181
|
|
|
132
|
-
#
|
|
182
|
+
# Empty the deserialized values when refreshing.
|
|
183
|
+
def _refresh(*)
|
|
184
|
+
@deserialized_values = {}
|
|
185
|
+
super
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Deserialize the column value. Called when the model column accessor is called to
|
|
189
|
+
# return a deserialized value.
|
|
133
190
|
def deserialize_value(column, v)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)
|
|
138
|
-
when :yaml
|
|
139
|
-
YAML.load v if v
|
|
140
|
-
when :json
|
|
141
|
-
JSON.parse v if v
|
|
142
|
-
else
|
|
143
|
-
raise Error, "Bad serialization format (#{model.serialization_map[column].inspect}) for column #{column.inspect}"
|
|
191
|
+
unless v.nil?
|
|
192
|
+
raise Sequel::Error, "no entry in deserialization_map for #{column.inspect}" unless callable = model.deserialization_map[column]
|
|
193
|
+
callable.call(v)
|
|
144
194
|
end
|
|
145
195
|
end
|
|
146
196
|
|
|
147
|
-
# Serialize
|
|
197
|
+
# Serialize all deserialized values
|
|
198
|
+
def serialize_deserialized_values
|
|
199
|
+
deserialized_values.each{|k,v| @values[k] = serialize_value(k, v)}
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Serialize the column value. Called before saving to ensure the serialized value
|
|
203
|
+
# is saved in the database.
|
|
148
204
|
def serialize_value(column, v)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
[Marshal.dump(v)].pack('m')
|
|
153
|
-
when :yaml
|
|
154
|
-
v.to_yaml
|
|
155
|
-
when :json
|
|
156
|
-
v.to_json
|
|
157
|
-
else
|
|
158
|
-
raise Error, "Bad serialization format (#{model.serialization_map[column].inspect}) for column #{column.inspect}"
|
|
205
|
+
unless v.nil?
|
|
206
|
+
raise Sequel::Error, "no entry in serialization_map for #{column.inspect}" unless callable = model.serialization_map[column]
|
|
207
|
+
callable.call(v)
|
|
159
208
|
end
|
|
160
209
|
end
|
|
161
210
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# This plugin extends the serialization plugin and enables it to detect
|
|
4
|
+
# changes in serialized values by checking whether the current
|
|
5
|
+
# deserialized value is the same as the original deserialized value.
|
|
6
|
+
# The serialization plugin does not do such checks by default, as they
|
|
7
|
+
# often aren't needed and can hurt performance.
|
|
8
|
+
#
|
|
9
|
+
# Note that for this plugin to work correctly, the values you are
|
|
10
|
+
# serializing must roundtrip correctly (i.e. deserialize(serialize(value))
|
|
11
|
+
# should equal value). This is true in most cases, but not in all. For
|
|
12
|
+
# example, ruby symbols round trip through yaml, but not json (as they get
|
|
13
|
+
# turned into strings in json).
|
|
14
|
+
#
|
|
15
|
+
# == Example
|
|
16
|
+
#
|
|
17
|
+
# require 'sequel'
|
|
18
|
+
# require 'json'
|
|
19
|
+
# class User < Sequel::Model
|
|
20
|
+
# plugin :serialization, :json, :permissions
|
|
21
|
+
# plugin :serialization_modification_detection
|
|
22
|
+
# end
|
|
23
|
+
# user = User.create(:permissions => {})
|
|
24
|
+
# user.permissions[:global] = 'read-only'
|
|
25
|
+
# user.save_changes
|
|
26
|
+
module SerializationModificationDetection
|
|
27
|
+
# Load the serialization plugin automatically.
|
|
28
|
+
def self.apply(model)
|
|
29
|
+
model.plugin :serialization
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module InstanceMethods
|
|
33
|
+
# Clear the cache of original deserialized values after saving so that it doesn't
|
|
34
|
+
# show the column is modified after saving.
|
|
35
|
+
def after_save
|
|
36
|
+
super
|
|
37
|
+
@original_deserialized_values = {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Detect which serialized columns have changed.
|
|
41
|
+
def changed_columns
|
|
42
|
+
cc = super
|
|
43
|
+
deserialized_values.each{|c, v| cc << c if !cc.include?(c) && original_deserialized_value(c) != v}
|
|
44
|
+
cc
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# For new objects, serialize any existing deserialized values so that changes can
|
|
50
|
+
# be detected.
|
|
51
|
+
def initialize_set(values)
|
|
52
|
+
super
|
|
53
|
+
serialize_deserialized_values
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Return the original deserialized value of the column, caching it to improve performance.
|
|
57
|
+
def original_deserialized_value(column)
|
|
58
|
+
(@original_deserialized_values ||= {})[column] ||= deserialize_value(column, self[column])
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
# The sharding plugin
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
3
|
+
# The sharding plugin augments Sequel's default model sharding support
|
|
4
|
+
# in the following ways:
|
|
5
|
+
#
|
|
6
|
+
# 1) It automatically sets model instances to be saved back to the
|
|
7
|
+
# shard they were retreived from.
|
|
8
|
+
# 2) It makes model associations use the same shard as the model
|
|
9
|
+
# object.
|
|
10
|
+
# 3) It adds a slightly nicer API for creating model instances on
|
|
11
|
+
# specific shards.
|
|
11
12
|
#
|
|
12
13
|
# Usage:
|
|
13
14
|
#
|
|
@@ -40,15 +41,21 @@ module Sequel
|
|
|
40
41
|
def new_using_server(s, values={}, &block)
|
|
41
42
|
new(values, &block).set_server(s)
|
|
42
43
|
end
|
|
43
|
-
end
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Set the server for each graphed dataset to the current server
|
|
48
|
+
# unless the graphed dataset already has a server set.
|
|
49
|
+
def eager_graph_dataset(opts, eager_options)
|
|
50
|
+
ds = super
|
|
51
|
+
if s = eager_options[:self].opts[:server]
|
|
52
|
+
ds = ds.server(s) unless ds.opts[:server]
|
|
53
|
+
end
|
|
54
|
+
ds
|
|
50
55
|
end
|
|
56
|
+
end
|
|
51
57
|
|
|
58
|
+
module InstanceMethods
|
|
52
59
|
# Set the server that this object is tied to, unless it has
|
|
53
60
|
# already been set. Returns self.
|
|
54
61
|
def set_server?(s)
|
|
@@ -56,11 +63,6 @@ module Sequel
|
|
|
56
63
|
self
|
|
57
64
|
end
|
|
58
65
|
|
|
59
|
-
# Ensure that the instance dataset is tied to the correct shard.
|
|
60
|
-
def this
|
|
61
|
-
use_server(super)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
66
|
private
|
|
65
67
|
|
|
66
68
|
# Ensure that association datasets are tied to the correct shard.
|
|
@@ -68,21 +70,11 @@ module Sequel
|
|
|
68
70
|
use_server(super)
|
|
69
71
|
end
|
|
70
72
|
|
|
71
|
-
# Ensure that the object is inserted into the correct shard.
|
|
72
|
-
def _insert_dataset
|
|
73
|
-
use_server(super)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
73
|
# Ensure that the join table for many_to_many associations uses the correct shard.
|
|
77
74
|
def _join_table_dataset(opts)
|
|
78
75
|
use_server(super)
|
|
79
76
|
end
|
|
80
77
|
|
|
81
|
-
# Make sure to use the correct shard when using a transaction
|
|
82
|
-
def checked_transaction(opts={}, &block)
|
|
83
|
-
super(@server ? {:server=>@server}.merge(opts) : opts, &block)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
78
|
# If creating the object by doing <tt>add_association</tt> for a
|
|
87
79
|
# +many_to_many+ association, make sure the associated object is created on the
|
|
88
80
|
# current object's shard, unless the passed object already has an assigned shard.
|
|
@@ -90,11 +82,6 @@ module Sequel
|
|
|
90
82
|
o.set_server?(@server) if o.respond_to?(:set_server?)
|
|
91
83
|
super
|
|
92
84
|
end
|
|
93
|
-
|
|
94
|
-
# Set the given dataset to use the current object's shard.
|
|
95
|
-
def use_server(ds)
|
|
96
|
-
@server ? ds.server(@server) : ds
|
|
97
|
-
end
|
|
98
85
|
end
|
|
99
86
|
|
|
100
87
|
module DatasetMethods
|
|
@@ -109,26 +96,6 @@ module Sequel
|
|
|
109
96
|
end
|
|
110
97
|
ds
|
|
111
98
|
end
|
|
112
|
-
|
|
113
|
-
private
|
|
114
|
-
|
|
115
|
-
# Set the shard of all retrieved objects to the shard of
|
|
116
|
-
# the table's dataset, or the current shard if the table's
|
|
117
|
-
# dataset does not have a shard.
|
|
118
|
-
def graph_each
|
|
119
|
-
ta = @opts[:graph][:table_aliases]
|
|
120
|
-
s = @opts[:server]
|
|
121
|
-
super do |r|
|
|
122
|
-
r.each do |k, v|
|
|
123
|
-
if ds = ta[k]
|
|
124
|
-
dss = ds.opts[:server]
|
|
125
|
-
end
|
|
126
|
-
vs = dss || s
|
|
127
|
-
v.set_server(vs) if vs && v.respond_to?(:set_server)
|
|
128
|
-
end
|
|
129
|
-
yield r
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
99
|
end
|
|
133
100
|
end
|
|
134
101
|
end
|
|
@@ -35,6 +35,17 @@ module Sequel
|
|
|
35
35
|
# Employee.plugin :single_table_inheritance, :type,
|
|
36
36
|
# :model_map=>{'line staff'=>:Staff, 'supervisor'=>:Manager}
|
|
37
37
|
#
|
|
38
|
+
# # By default the plugin sets the respective column value
|
|
39
|
+
# # when a new instance is created.
|
|
40
|
+
# Staff.create.type == 'line staff'
|
|
41
|
+
# Manager.create.type == 'supervisor'
|
|
42
|
+
#
|
|
43
|
+
# # You can customize this behavior with the :key_chooser option.
|
|
44
|
+
# # This is most useful when using a non-bijective mapping.
|
|
45
|
+
# Employee.plugin :single_table_inheritance, :type,
|
|
46
|
+
# :model_map=>{'line staff'=>:Staff, 'supervisor'=>:Manager},
|
|
47
|
+
# :key_chooser=>proc{|instance| instance.model.sti_key_map[instance.model.to_s].first || 'stranger' }
|
|
48
|
+
#
|
|
38
49
|
# # Using custom procs, with :model_map taking column values
|
|
39
50
|
# # and yielding either a class, string, symbol, or nil,
|
|
40
51
|
# # and :key_map taking a class object and returning the column
|
|
@@ -43,6 +54,15 @@ module Sequel
|
|
|
43
54
|
# :model_map=>proc{|v| v.reverse},
|
|
44
55
|
# :key_map=>proc{|klass| klass.name.reverse}
|
|
45
56
|
#
|
|
57
|
+
# # You can use the same class for multiple values.
|
|
58
|
+
# # This is mainly useful when the sti_key column contains multiple values
|
|
59
|
+
# # which are different but do not require different code.
|
|
60
|
+
# Employee.plugin :single_table_inheritance, :type,
|
|
61
|
+
# :model_map=>{'staff' => "Staff",
|
|
62
|
+
# 'manager => "Manager",
|
|
63
|
+
# 'overpayed staff => "Staff",
|
|
64
|
+
# 'underpayed staff' => "Staff"}
|
|
65
|
+
#
|
|
46
66
|
# One minor issue to note is that if you specify the <tt>:key_map</tt>
|
|
47
67
|
# option as a hash, instead of having it inferred from the <tt>:model_map</tt>,
|
|
48
68
|
# you should only use class name strings as keys, you should not use symbols
|
|
@@ -57,20 +77,38 @@ module Sequel
|
|
|
57
77
|
@sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
|
|
58
78
|
@sti_key_map = if km = opts[:key_map]
|
|
59
79
|
if km.is_a?(Hash)
|
|
60
|
-
h = Hash.new
|
|
61
|
-
|
|
80
|
+
h = Hash.new do |h,k|
|
|
81
|
+
unless k.is_a?(String)
|
|
82
|
+
h[k.to_s]
|
|
83
|
+
else
|
|
84
|
+
[]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
km.each do |k,v|
|
|
88
|
+
h[k.to_s] = [ ] unless h.key?(k.to_s)
|
|
89
|
+
h[k.to_s].push( *Array(v) )
|
|
90
|
+
end
|
|
91
|
+
h
|
|
62
92
|
else
|
|
63
93
|
km
|
|
64
94
|
end
|
|
65
95
|
elsif sti_model_map.is_a?(Hash)
|
|
66
|
-
h = Hash.new
|
|
96
|
+
h = Hash.new do |h,k|
|
|
97
|
+
unless k.is_a?(String)
|
|
98
|
+
h[k.to_s]
|
|
99
|
+
else
|
|
100
|
+
[]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
67
103
|
sti_model_map.each do |k,v|
|
|
68
|
-
h[v.to_s] =
|
|
104
|
+
h[v.to_s] = [ ] unless h.key?(v.to_s)
|
|
105
|
+
h[v.to_s] << k
|
|
69
106
|
end
|
|
70
107
|
h
|
|
71
108
|
else
|
|
72
109
|
lambda{|klass| klass.name.to_s}
|
|
73
110
|
end
|
|
111
|
+
@sti_key_chooser = opts[:key_chooser] || lambda{|inst| Array(inst.model.sti_key_map[inst.model]).last }
|
|
74
112
|
dataset.row_proc = lambda{|r| model.sti_load(r)}
|
|
75
113
|
end
|
|
76
114
|
end
|
|
@@ -97,6 +135,10 @@ module Sequel
|
|
|
97
135
|
# the value of the sti_key column to the appropriate class to use.
|
|
98
136
|
attr_reader :sti_model_map
|
|
99
137
|
|
|
138
|
+
# A proc which returns the value to use for new instances.
|
|
139
|
+
# This defaults to a lookup in the key map.
|
|
140
|
+
attr_reader :sti_key_chooser
|
|
141
|
+
|
|
100
142
|
# Copy the necessary attributes to the subclasses, and filter the
|
|
101
143
|
# subclass's dataset based on the sti_kep_map entry for the class.
|
|
102
144
|
def inherited(subclass)
|
|
@@ -105,31 +147,34 @@ module Sequel
|
|
|
105
147
|
sd = sti_dataset
|
|
106
148
|
skm = sti_key_map
|
|
107
149
|
smm = sti_model_map
|
|
108
|
-
|
|
150
|
+
skc = sti_key_chooser
|
|
151
|
+
key = Array(skm[subclass]).dup
|
|
109
152
|
sti_subclass_added(key)
|
|
110
|
-
|
|
111
|
-
subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>
|
|
153
|
+
rp = dataset.row_proc
|
|
154
|
+
subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>key), :inherited=>true)
|
|
112
155
|
subclass.instance_eval do
|
|
156
|
+
dataset.row_proc = rp
|
|
113
157
|
@sti_key = sk
|
|
114
|
-
@sti_key_array =
|
|
158
|
+
@sti_key_array = key
|
|
115
159
|
@sti_dataset = sd
|
|
116
160
|
@sti_key_map = skm
|
|
117
161
|
@sti_model_map = smm
|
|
118
|
-
@
|
|
162
|
+
@sti_key_chooser = skc
|
|
163
|
+
self.simple_table = nil
|
|
119
164
|
end
|
|
120
165
|
end
|
|
121
166
|
|
|
122
167
|
# Return an instance of the class specified by sti_key,
|
|
123
168
|
# used by the row_proc.
|
|
124
169
|
def sti_load(r)
|
|
125
|
-
sti_class(sti_model_map[r[sti_key]]).
|
|
170
|
+
sti_class(sti_model_map[r[sti_key]]).call(r)
|
|
126
171
|
end
|
|
127
172
|
|
|
128
173
|
# Make sure that all subclasses of the parent class correctly include
|
|
129
174
|
# keys for all of their descendant classes.
|
|
130
175
|
def sti_subclass_added(key)
|
|
131
176
|
if sti_key_array
|
|
132
|
-
sti_key_array
|
|
177
|
+
Sequel.synchronize{sti_key_array.push(*Array(key))}
|
|
133
178
|
superclass.sti_subclass_added(key)
|
|
134
179
|
end
|
|
135
180
|
end
|
|
@@ -157,7 +202,7 @@ module Sequel
|
|
|
157
202
|
module InstanceMethods
|
|
158
203
|
# Set the sti_key column based on the sti_key_map.
|
|
159
204
|
def before_create
|
|
160
|
-
send("#{model.sti_key}=", model.
|
|
205
|
+
send("#{model.sti_key}=", model.sti_key_chooser.call(self)) unless self[model.sti_key]
|
|
161
206
|
super
|
|
162
207
|
end
|
|
163
208
|
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The static_cache plugin is designed for models that are not modified at all
|
|
4
|
+
# in production use cases, or at least where modifications to them would usually
|
|
5
|
+
# coincide with an application restart. When loaded into a model class, it
|
|
6
|
+
# retrieves all rows in the database and staticly caches a ruby array and hash
|
|
7
|
+
# keyed on primary key containing all of the model instances. All of these instances
|
|
8
|
+
# are frozen so they won't be modified unexpectedly.
|
|
9
|
+
#
|
|
10
|
+
# The caches this plugin creates are used for the following things:
|
|
11
|
+
#
|
|
12
|
+
# * Primary key lookups (e.g. Model[1])
|
|
13
|
+
# * Model.all calls
|
|
14
|
+
# * Model.each calls
|
|
15
|
+
# * Model.map calls without an argument
|
|
16
|
+
# * Model.to_hash calls without an argument
|
|
17
|
+
#
|
|
18
|
+
# Usage:
|
|
19
|
+
#
|
|
20
|
+
# # Cache the AlbumType class staticly
|
|
21
|
+
# AlbumType.plugin :static_cache
|
|
22
|
+
module StaticCache
|
|
23
|
+
# Populate the static caches when loading the plugin.
|
|
24
|
+
def self.configure(model)
|
|
25
|
+
model.send(:load_cache)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
# A frozen ruby hash holding all of the model's frozen instances, keyed by frozen primary key.
|
|
30
|
+
attr_reader :cache
|
|
31
|
+
|
|
32
|
+
# An array of all of the model's frozen instances, without issuing a database
|
|
33
|
+
# query.
|
|
34
|
+
def all
|
|
35
|
+
@all.dup
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Return the frozen object with the given pk, or nil if no such object exists
|
|
39
|
+
# in the cache, without issuing a database query.
|
|
40
|
+
def cache_get_pk(pk)
|
|
41
|
+
cache[pk]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Yield each of the model's frozen instances to the block, without issuing a database
|
|
45
|
+
# query.
|
|
46
|
+
def each(&block)
|
|
47
|
+
@all.each(&block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# If no arguments are given, yield each of the model's frozen instances to the block.
|
|
51
|
+
# and return a new array, without issuing a database query. If any arguments are
|
|
52
|
+
# given, use the default Sequel behavior.
|
|
53
|
+
def map(*a)
|
|
54
|
+
if a.empty?
|
|
55
|
+
@all.map(&(Proc.new if block_given?))
|
|
56
|
+
else
|
|
57
|
+
super
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Reload the cache when the dataset changes.
|
|
62
|
+
def set_dataset(*)
|
|
63
|
+
s = super
|
|
64
|
+
load_cache
|
|
65
|
+
s
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# If no arguments are given, yield an identity map for the model with frozen primary keys
|
|
69
|
+
# and instances, without issuing a database query. If any arguments are
|
|
70
|
+
# given, use the default Sequel behavior.
|
|
71
|
+
def to_hash(*a)
|
|
72
|
+
if a.empty?
|
|
73
|
+
cache.dup
|
|
74
|
+
else
|
|
75
|
+
super
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
# Return the frozen object with the given pk, or nil if no such object exists
|
|
82
|
+
# in the cache, without issuing a database query.
|
|
83
|
+
def primary_key_lookup(pk)
|
|
84
|
+
cache[pk]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Reload the cache for this model by retrieving all of the instances in the dataset
|
|
88
|
+
# freezing them, and populating the cached array and hash.
|
|
89
|
+
def load_cache
|
|
90
|
+
a = dataset.all
|
|
91
|
+
h = {}
|
|
92
|
+
a.each{|o| h[o.pk.freeze] = o.freeze}
|
|
93
|
+
@all = a.freeze
|
|
94
|
+
@cache = h.freeze
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|