sequel 3.21.0 → 3.39.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 +1061 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +103 -63
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +67 -51
- data/doc/advanced_associations.rdoc +189 -75
- data/doc/association_basics.rdoc +327 -79
- 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 +547 -0
- data/doc/opening_databases.rdoc +114 -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/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/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +56 -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 +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +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 +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +279 -97
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +125 -222
- data/lib/sequel/adapters/mysql2.rb +65 -56
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +368 -138
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +343 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +401 -116
- data/lib/sequel/adapters/shared/mysql.rb +424 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +257 -45
- data/lib/sequel/adapters/shared/postgres.rb +675 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +285 -96
- data/lib/sequel/adapters/sqlite.rb +101 -39
- 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 +28 -19
- data/lib/sequel/adapters/tinytds.rb +174 -22
- 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/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +43 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +190 -46
- data/lib/sequel/database/query.rb +272 -110
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +300 -67
- 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 +305 -54
- data/lib/sequel/dataset/sql.rb +720 -292
- 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/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 +537 -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 +313 -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 +208 -0
- data/lib/sequel/extensions/pg_range.rb +512 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +572 -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 +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/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 +58 -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 +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +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 +5 -3
- 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/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 +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +55 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +649 -122
- 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 +1 -1
- data/spec/adapters/mssql_spec.rb +194 -66
- data/spec/adapters/mysql_spec.rb +475 -306
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +1982 -325
- data/spec/adapters/spec_helper.rb +9 -6
- data/spec/adapters/sqlite_spec.rb +203 -75
- data/spec/core/connection_pool_spec.rb +218 -93
- data/spec/core/database_spec.rb +956 -522
- data/spec/core/dataset_spec.rb +1631 -1242
- data/spec/core/expression_filters_spec.rb +690 -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 +66 -32
- 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/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 +320 -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 +194 -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 +323 -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 +23 -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 +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +58 -67
- data/spec/extensions/validation_helpers_spec.rb +31 -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 +11 -5
- data/spec/integration/dataset_test.rb +717 -129
- 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 +604 -147
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +367 -57
- data/spec/integration/spec_helper.rb +42 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +31 -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 +209 -80
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements_associations plugin modifies the regular association
|
|
4
|
+
# load method to use a cached prepared statement to load the associations.
|
|
5
|
+
# It will not work on all associations, but it should skip the use of prepared
|
|
6
|
+
# statements for associations where it will not work, assuming you load the
|
|
7
|
+
# plugin before defining the associations.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# # Make all model subclasses more safe when using prepared statements (called before loading subclasses)
|
|
12
|
+
# Sequel::Model.plugin :prepared_statements_associations
|
|
13
|
+
#
|
|
14
|
+
# # Make the Album class more safe when using prepared statements
|
|
15
|
+
# Album.plugin :prepared_statements_associations
|
|
16
|
+
module PreparedStatementsAssociations
|
|
17
|
+
# Synchronize access to the integer sequence so that no two calls get the same integer.
|
|
18
|
+
MUTEX = Mutex.new
|
|
19
|
+
|
|
20
|
+
i = 0
|
|
21
|
+
# This plugin names prepared statements uniquely using an integer sequence, this
|
|
22
|
+
# lambda returns the next integer to use.
|
|
23
|
+
NEXT = lambda{MUTEX.synchronize{i += 1}}
|
|
24
|
+
|
|
25
|
+
module ClassMethods
|
|
26
|
+
# Disable prepared statement use if a block is given, or the :dataset or :conditions
|
|
27
|
+
# options are used, or you are cloning an association.
|
|
28
|
+
def associate(type, name, opts = {}, &block)
|
|
29
|
+
if block || opts[:dataset] || opts[:conditions] || (opts[:clone] && association_reflection(opts[:clone])[:prepared_statement] == false)
|
|
30
|
+
opts = opts.merge(:prepared_statement=>false)
|
|
31
|
+
end
|
|
32
|
+
super(type, name, opts, &block)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module InstanceMethods
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Return a bound variable hash that maps the keys in +ks+ (qualified by the +table+)
|
|
40
|
+
# to the values of the results of sending the methods in +vs+.
|
|
41
|
+
def association_bound_variable_hash(table, ks, vs)
|
|
42
|
+
Hash[*ks.zip(vs).map{|k, v| [:"#{table}.#{k}", send(v)]}.flatten]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Given an association reflection, return a bound variable hash for the given
|
|
46
|
+
# association for this instance's values.
|
|
47
|
+
def association_bound_variables(opts)
|
|
48
|
+
case opts[:type]
|
|
49
|
+
when :many_to_one
|
|
50
|
+
association_bound_variable_hash(opts.associated_class.table_name, opts.primary_keys, opts[:keys])
|
|
51
|
+
when :one_to_many
|
|
52
|
+
association_bound_variable_hash(opts.associated_class.table_name, opts[:keys], opts[:primary_keys])
|
|
53
|
+
when :many_to_many
|
|
54
|
+
association_bound_variable_hash(opts.join_table_alias, opts[:left_keys], opts[:left_primary_keys])
|
|
55
|
+
when :many_through_many
|
|
56
|
+
association_bound_variable_hash(opts.final_reverse_edge[:alias], Array(opts[:left_key]), opts[:left_primary_keys])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Given an association reflection, return and cache a prepared statement for this association such
|
|
61
|
+
# that, given appropriate bound variables, the prepared statement will work correctly for any
|
|
62
|
+
# instance.
|
|
63
|
+
def association_prepared_statement(opts)
|
|
64
|
+
opts.send(:cached_fetch, :prepared_statement) do
|
|
65
|
+
ps = _associated_dataset(opts, {}).unbind.first.prepare(opts.returns_array? ? :select : :first, :"smpsap_#{NEXT.call}")
|
|
66
|
+
ps.log_sql = true
|
|
67
|
+
ps
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# If a prepared statement can be used to load the associated objects, execute it to retrieve them. Otherwise,
|
|
72
|
+
# fall back to the default implementation.
|
|
73
|
+
def _load_associated_objects(opts, dynamic_opts={})
|
|
74
|
+
if !opts.can_have_associated_objects?(self) || dynamic_opts[:callback] || (ps = opts[:prepared_statement]) == false
|
|
75
|
+
super
|
|
76
|
+
else
|
|
77
|
+
if bv = association_bound_variables(opts)
|
|
78
|
+
(ps || association_prepared_statement(opts)).call(bv)
|
|
79
|
+
else
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements_safe plugin modifies the model to reduce the number of
|
|
4
|
+
# prepared statements that can be created, by setting as many columns as possible
|
|
5
|
+
# before creating, and by changing +save_changes+ to save all columns instead of
|
|
6
|
+
# just the changed ones.
|
|
7
|
+
#
|
|
8
|
+
# This plugin depends on the +prepared_statements+ plugin.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
#
|
|
12
|
+
# # Make all model subclasses more safe when using prepared statements (called before loading subclasses)
|
|
13
|
+
# Sequel::Model.plugin :prepared_statements_safe
|
|
14
|
+
#
|
|
15
|
+
# # Make the Album class more safe when using prepared statements
|
|
16
|
+
# Album.plugin :prepared_statements_safe
|
|
17
|
+
module PreparedStatementsSafe
|
|
18
|
+
# Depend on the prepared_statements plugin
|
|
19
|
+
def self.apply(model)
|
|
20
|
+
model.plugin(:prepared_statements)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Set the column defaults to use when creating on the model.
|
|
24
|
+
def self.configure(model)
|
|
25
|
+
model.send(:set_prepared_statements_column_defaults)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
# A hash with column symbol keys and default values. Instance's
|
|
30
|
+
# values are merged into this hash before creating to reduce the
|
|
31
|
+
# number of free columns (columns that may or may not be present
|
|
32
|
+
# in the INSERT statement), as the number of prepared statements
|
|
33
|
+
# that can be created is 2^N (where N is the number of free columns).
|
|
34
|
+
attr_reader :prepared_statements_column_defaults
|
|
35
|
+
|
|
36
|
+
def inherited(subclass)
|
|
37
|
+
super
|
|
38
|
+
subclass.instance_variable_set(:@prepared_statements_column_defaults, @prepared_statements_column_defaults) if @prepared_statements_column_defaults && !subclass.prepared_statements_column_defaults
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Set the column defaults to use when creating on the subclass.
|
|
42
|
+
def set_dataset(*)
|
|
43
|
+
x = super
|
|
44
|
+
set_prepared_statements_column_defaults
|
|
45
|
+
x
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Set the column defaults based on the database schema. All columns
|
|
51
|
+
# are set to a default value unless they are a primary key column or
|
|
52
|
+
# they don't have a parseable default.
|
|
53
|
+
def set_prepared_statements_column_defaults
|
|
54
|
+
if db_schema
|
|
55
|
+
h = {}
|
|
56
|
+
db_schema.each do |k, v|
|
|
57
|
+
h[k] = v[:ruby_default] if (v[:ruby_default] || !v[:default]) && !v[:primary_key]
|
|
58
|
+
end
|
|
59
|
+
@prepared_statements_column_defaults = h
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module InstanceMethods
|
|
65
|
+
# Merge the current values into the default values to reduce the number
|
|
66
|
+
# of free columns.
|
|
67
|
+
def before_create
|
|
68
|
+
if v = model.prepared_statements_column_defaults
|
|
69
|
+
set_values(v.merge(values))
|
|
70
|
+
end
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Always do a full save of all columns to reduce the number of prepared
|
|
75
|
+
# statements that can be used.
|
|
76
|
+
def save_changes(opts={})
|
|
77
|
+
save(opts) || false if modified?
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements_with_pk plugin allows Dataset#with_pk for model datasets
|
|
4
|
+
# to use prepared statements by extract the values of previously bound variables
|
|
5
|
+
# using <tt>Dataset#unbind</tt>, and attempting to use a prepared statement if the
|
|
6
|
+
# variables can be unbound correctly. See +Unbinder+ for details about what types of
|
|
7
|
+
# dataset filters can be unbound correctly.
|
|
8
|
+
#
|
|
9
|
+
# This plugin depends on the +prepared_statements+ plugin and should be considered unsafe.
|
|
10
|
+
# Unbinding dataset values cannot be done correctly in all cases, and use of this plugin
|
|
11
|
+
# in cases where not there are variables that are not unbound can lead to an denial of
|
|
12
|
+
# service attack by allocating an arbitrary number of prepared statements. You have been
|
|
13
|
+
# warned.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
#
|
|
17
|
+
# # Make all model subclasses use prepared statements for Dataset#with_pk (called before loading subclasses)
|
|
18
|
+
# Sequel::Model.plugin :prepared_statements_with_pk
|
|
19
|
+
#
|
|
20
|
+
# # Make the Album class use prepared statements for Dataset#with_pk
|
|
21
|
+
# Album.plugin :prepared_statements_with_pk
|
|
22
|
+
module PreparedStatementsWithPk
|
|
23
|
+
# Depend on the prepared_statements plugin
|
|
24
|
+
def self.apply(model)
|
|
25
|
+
model.plugin(:prepared_statements)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Return a prepared statement that can be used to lookup a row given a dataset for the row matching
|
|
32
|
+
# the primary key.
|
|
33
|
+
def prepared_lookup_dataset(ds)
|
|
34
|
+
cached_prepared_statement(:lookup_sql, ds.sql){prepare_statement(ds.filter(prepared_statement_key_array(primary_key).map{|k, v| [SQL::QualifiedIdentifier.new(ds.model.table_name, k), v]}), :first)}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module DatasetMethods
|
|
39
|
+
# Use a prepared statement to find a row with the matching primary key
|
|
40
|
+
# inside this dataset.
|
|
41
|
+
def with_pk(pk)
|
|
42
|
+
begin
|
|
43
|
+
ds, bv = unbind
|
|
44
|
+
rescue UnbindDuplicate
|
|
45
|
+
super
|
|
46
|
+
else
|
|
47
|
+
begin
|
|
48
|
+
bv = bv.merge!(model.primary_key_hash(pk)){|k, v1, v2| ((v1 == v2) ? v1 : raise(UnbindDuplicate))}
|
|
49
|
+
rescue UnbindDuplicate
|
|
50
|
+
super
|
|
51
|
+
else
|
|
52
|
+
model.send(:prepared_lookup_dataset, ds).call(bv)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -109,7 +109,15 @@ module Sequel
|
|
|
109
109
|
ka = opts[:key_alias] ||= :x_root_x
|
|
110
110
|
t = opts[:cte_name] ||= :t
|
|
111
111
|
opts[:reciprocal] = nil
|
|
112
|
-
c_all =
|
|
112
|
+
c_all = if model.dataset.recursive_cte_requires_column_aliases?
|
|
113
|
+
# Work around Oracle/ruby-oci8 bug that returns integers as BigDecimals in recursive queries.
|
|
114
|
+
conv_bd = model.db.database_type == :oracle
|
|
115
|
+
col_aliases = model.dataset.columns
|
|
116
|
+
model_table = model.table_name
|
|
117
|
+
col_aliases.map{|c| SQL::QualifiedIdentifier.new(model_table, c)}
|
|
118
|
+
else
|
|
119
|
+
[SQL::ColumnAll.new(model.table_name)]
|
|
120
|
+
end
|
|
113
121
|
|
|
114
122
|
a = opts.merge(opts.fetch(:ancestors, {}))
|
|
115
123
|
ancestors = a.fetch(:name, :ancestors)
|
|
@@ -125,9 +133,9 @@ module Sequel
|
|
|
125
133
|
end
|
|
126
134
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
|
127
135
|
model.from(t => table_alias).
|
|
128
|
-
with_recursive(t, base_ds.select_all,
|
|
129
|
-
recursive_ds.
|
|
130
|
-
|
|
136
|
+
with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
|
|
137
|
+
recursive_ds.select(*c_all),
|
|
138
|
+
:args=>col_aliases)
|
|
131
139
|
end
|
|
132
140
|
aal = Array(a[:after_load])
|
|
133
141
|
aal << proc do |m, ancs|
|
|
@@ -152,7 +160,7 @@ module Sequel
|
|
|
152
160
|
end
|
|
153
161
|
a[:after_load] ||= aal
|
|
154
162
|
a[:eager_loader] ||= proc do |eo|
|
|
155
|
-
id_map = eo[:
|
|
163
|
+
id_map = eo[:id_map]
|
|
156
164
|
parent_map = {}
|
|
157
165
|
children_map = {}
|
|
158
166
|
eo[:rows].each do |obj|
|
|
@@ -163,9 +171,9 @@ module Sequel
|
|
|
163
171
|
end
|
|
164
172
|
r = model.association_reflection(ancestors)
|
|
165
173
|
base_case = model.filter(prkey=>id_map.keys).
|
|
166
|
-
select(SQL::AliasedExpression.new(prkey, ka), c_all)
|
|
174
|
+
select(SQL::AliasedExpression.new(prkey, ka), *c_all)
|
|
167
175
|
recursive_case = model.join(t, key=>prkey).
|
|
168
|
-
select(SQL::QualifiedIdentifier.new(t, ka), c_all)
|
|
176
|
+
select(SQL::QualifiedIdentifier.new(t, ka), *c_all)
|
|
169
177
|
if c = r[:conditions]
|
|
170
178
|
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
|
171
179
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
|
@@ -175,7 +183,8 @@ module Sequel
|
|
|
175
183
|
elds = model.eager_loading_dataset(r,
|
|
176
184
|
model.from(t => table_alias).
|
|
177
185
|
with_recursive(t, base_case,
|
|
178
|
-
recursive_case
|
|
186
|
+
recursive_case,
|
|
187
|
+
:args=>(([ka] + col_aliases) if col_aliases)),
|
|
179
188
|
r.select,
|
|
180
189
|
eo[:associations], eo)
|
|
181
190
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
|
@@ -192,7 +201,9 @@ module Sequel
|
|
|
192
201
|
(children_map[obj[key]] ||= []) << obj
|
|
193
202
|
end
|
|
194
203
|
|
|
195
|
-
|
|
204
|
+
kv = obj.values.delete(ka)
|
|
205
|
+
kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
|
|
206
|
+
if roots = id_map[kv]
|
|
196
207
|
roots.each do |root|
|
|
197
208
|
root.associations[ancestors] << obj
|
|
198
209
|
end
|
|
@@ -222,9 +233,9 @@ module Sequel
|
|
|
222
233
|
end
|
|
223
234
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
|
224
235
|
model.from(t => table_alias).
|
|
225
|
-
with_recursive(t, base_ds.select_all,
|
|
226
|
-
recursive_ds.
|
|
227
|
-
|
|
236
|
+
with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
|
|
237
|
+
recursive_ds.select(*c_all),
|
|
238
|
+
:args=>col_aliases)
|
|
228
239
|
end
|
|
229
240
|
dal = Array(d[:after_load])
|
|
230
241
|
dal << proc do |m, descs|
|
|
@@ -248,7 +259,7 @@ module Sequel
|
|
|
248
259
|
end
|
|
249
260
|
d[:after_load] = dal
|
|
250
261
|
d[:eager_loader] ||= proc do |eo|
|
|
251
|
-
id_map = eo[:
|
|
262
|
+
id_map = eo[:id_map]
|
|
252
263
|
associations = eo[:associations]
|
|
253
264
|
parent_map = {}
|
|
254
265
|
children_map = {}
|
|
@@ -259,9 +270,9 @@ module Sequel
|
|
|
259
270
|
end
|
|
260
271
|
r = model.association_reflection(descendants)
|
|
261
272
|
base_case = model.filter(key=>id_map.keys).
|
|
262
|
-
select(SQL::AliasedExpression.new(key, ka), c_all)
|
|
273
|
+
select(SQL::AliasedExpression.new(key, ka), *c_all)
|
|
263
274
|
recursive_case = model.join(t, prkey=>key).
|
|
264
|
-
select(SQL::QualifiedIdentifier.new(t, ka), c_all)
|
|
275
|
+
select(SQL::QualifiedIdentifier.new(t, ka), *c_all)
|
|
265
276
|
if c = r[:conditions]
|
|
266
277
|
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
|
267
278
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
|
@@ -276,7 +287,8 @@ module Sequel
|
|
|
276
287
|
end
|
|
277
288
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
|
278
289
|
elds = model.eager_loading_dataset(r,
|
|
279
|
-
model.from(t => table_alias).with_recursive(t, base_case, recursive_case
|
|
290
|
+
model.from(t => table_alias).with_recursive(t, base_case, recursive_case,
|
|
291
|
+
:args=>(([ka] + col_aliases + (level ? [la] : [])) if col_aliases)),
|
|
280
292
|
r.select,
|
|
281
293
|
associations, eo)
|
|
282
294
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
|
@@ -296,7 +308,9 @@ module Sequel
|
|
|
296
308
|
parent_map[opk] = obj
|
|
297
309
|
end
|
|
298
310
|
|
|
299
|
-
|
|
311
|
+
kv = obj.values.delete(ka)
|
|
312
|
+
kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
|
|
313
|
+
if root = id_map[kv].first
|
|
300
314
|
root.associations[descendants] << obj
|
|
301
315
|
end
|
|
302
316
|
|
|
@@ -29,7 +29,7 @@ module Sequel
|
|
|
29
29
|
# Drops the table if it exists and then runs create_table. Should probably
|
|
30
30
|
# not be used except in testing.
|
|
31
31
|
def create_table!(*args, &block)
|
|
32
|
-
drop_table
|
|
32
|
+
drop_table?
|
|
33
33
|
create_table(*args, &block)
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -38,11 +38,16 @@ module Sequel
|
|
|
38
38
|
create_table(*args, &block) unless table_exists?
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
# Drops table.
|
|
41
|
+
# Drops table. If the table doesn't exist, this will probably raise an error.
|
|
42
42
|
def drop_table
|
|
43
43
|
db.drop_table(table_name)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Drops table if it already exists, do nothing if it doesn't exist.
|
|
47
|
+
def drop_table?
|
|
48
|
+
db.drop_table?(table_name)
|
|
49
|
+
end
|
|
50
|
+
|
|
46
51
|
# Returns table schema created with set_schema for direct descendant of Model.
|
|
47
52
|
# Does not retreive schema information from the database, see db_schema if you
|
|
48
53
|
# want that.
|
|
@@ -61,7 +66,7 @@ module Sequel
|
|
|
61
66
|
# test code or simple examples.
|
|
62
67
|
def set_schema(name = nil, &block)
|
|
63
68
|
set_dataset(db[name]) if name
|
|
64
|
-
@schema =
|
|
69
|
+
@schema = db.create_table_generator(&block)
|
|
65
70
|
set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
|
|
66
71
|
end
|
|
67
72
|
|
|
@@ -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
|