sequel 3.46.0 → 3.47.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.
- checksums.yaml +4 -4
- data/CHANGELOG +96 -0
- data/Rakefile +7 -1
- data/bin/sequel +6 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +14 -35
- data/doc/association_basics.rdoc +66 -4
- data/doc/migration.rdoc +4 -0
- data/doc/opening_databases.rdoc +6 -0
- data/doc/postgresql.rdoc +302 -0
- data/doc/release_notes/3.47.0.txt +270 -0
- data/doc/security.rdoc +6 -0
- data/lib/sequel/adapters/ibmdb.rb +9 -9
- data/lib/sequel/adapters/jdbc.rb +22 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
- data/lib/sequel/adapters/mock.rb +2 -0
- data/lib/sequel/adapters/postgres.rb +44 -13
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +94 -55
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/utils/pg_types.rb +1 -14
- data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/features.rb +5 -0
- data/lib/sequel/database/misc.rb +47 -5
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/dataset/actions.rb +4 -2
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +8 -6
- data/lib/sequel/dataset/sql.rb +8 -6
- data/lib/sequel/extensions/constraint_validations.rb +5 -2
- data/lib/sequel/extensions/migration.rb +10 -8
- data/lib/sequel/extensions/pagination.rb +3 -0
- data/lib/sequel/extensions/pg_array.rb +85 -25
- data/lib/sequel/extensions/pg_hstore.rb +8 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
- data/lib/sequel/extensions/pg_inet.rb +16 -13
- data/lib/sequel/extensions/pg_interval.rb +6 -2
- data/lib/sequel/extensions/pg_json.rb +18 -11
- data/lib/sequel/extensions/pg_range.rb +17 -2
- data/lib/sequel/extensions/pg_range_ops.rb +7 -5
- data/lib/sequel/extensions/pg_row.rb +29 -12
- data/lib/sequel/extensions/pretty_table.rb +3 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/schema_caching.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -1
- data/lib/sequel/extensions/select_remove.rb +3 -0
- data/lib/sequel/model.rb +8 -2
- data/lib/sequel/model/associations.rb +39 -27
- data/lib/sequel/model/base.rb +99 -38
- data/lib/sequel/model/plugins.rb +25 -0
- data/lib/sequel/plugins/association_autoreloading.rb +27 -22
- data/lib/sequel/plugins/association_dependencies.rb +1 -7
- data/lib/sequel/plugins/auto_validations.rb +110 -0
- data/lib/sequel/plugins/boolean_readers.rb +1 -6
- data/lib/sequel/plugins/caching.rb +6 -13
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +14 -7
- data/lib/sequel/plugins/constraint_validations.rb +2 -13
- data/lib/sequel/plugins/defaults_setter.rb +1 -6
- data/lib/sequel/plugins/dirty.rb +8 -0
- data/lib/sequel/plugins/error_splitter.rb +54 -0
- data/lib/sequel/plugins/force_encoding.rb +1 -5
- data/lib/sequel/plugins/hook_class_methods.rb +1 -6
- data/lib/sequel/plugins/input_transformer.rb +79 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -1
- data/lib/sequel/plugins/instance_hooks.rb +7 -1
- data/lib/sequel/plugins/json_serializer.rb +5 -10
- data/lib/sequel/plugins/lazy_attributes.rb +20 -7
- data/lib/sequel/plugins/list.rb +1 -6
- data/lib/sequel/plugins/many_through_many.rb +1 -2
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
- data/lib/sequel/plugins/optimistic_locking.rb +1 -5
- data/lib/sequel/plugins/pg_row.rb +4 -2
- data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -5
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/serialization.rb +11 -13
- data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/static_cache.rb +67 -19
- data/lib/sequel/plugins/string_stripper.rb +7 -27
- data/lib/sequel/plugins/subclasses.rb +3 -5
- data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -7
- data/lib/sequel/plugins/touch.rb +5 -8
- data/lib/sequel/plugins/tree.rb +1 -6
- data/lib/sequel/plugins/typecast_on_load.rb +1 -5
- data/lib/sequel/plugins/update_primary_key.rb +26 -14
- data/lib/sequel/plugins/validation_class_methods.rb +31 -16
- data/lib/sequel/plugins/validation_helpers.rb +50 -26
- data/lib/sequel/plugins/xml_serializer.rb +3 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +131 -15
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +16 -17
- data/spec/core/database_spec.rb +111 -40
- data/spec/core/dataset_spec.rb +65 -74
- data/spec/core/expression_filters_spec.rb +6 -5
- data/spec/core/object_graph_spec.rb +0 -1
- data/spec/core/schema_spec.rb +23 -23
- data/spec/core/spec_helper.rb +5 -1
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_validations_spec.rb +90 -0
- data/spec/extensions/caching_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -1
- data/spec/extensions/composition_spec.rb +12 -5
- data/spec/extensions/constraint_validations_spec.rb +4 -4
- data/spec/extensions/core_refinements_spec.rb +29 -79
- data/spec/extensions/dirty_spec.rb +14 -0
- data/spec/extensions/error_splitter_spec.rb +18 -0
- data/spec/extensions/identity_map_spec.rb +0 -1
- data/spec/extensions/input_transformer_spec.rb +54 -0
- data/spec/extensions/instance_filters_spec.rb +6 -0
- data/spec/extensions/instance_hooks_spec.rb +12 -1
- data/spec/extensions/json_serializer_spec.rb +0 -1
- data/spec/extensions/lazy_attributes_spec.rb +64 -55
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
- data/spec/extensions/migration_spec.rb +16 -0
- data/spec/extensions/null_dataset_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +48 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
- data/spec/extensions/pg_hstore_spec.rb +5 -0
- data/spec/extensions/pg_inet_spec.rb +5 -0
- data/spec/extensions/pg_interval_spec.rb +7 -3
- data/spec/extensions/pg_json_spec.rb +6 -1
- data/spec/extensions/pg_range_ops_spec.rb +4 -1
- data/spec/extensions/pg_range_spec.rb +5 -0
- data/spec/extensions/pg_row_plugin_spec.rb +13 -0
- data/spec/extensions/pg_row_spec.rb +28 -19
- data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
- data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
- data/spec/extensions/query_literals_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
- data/spec/extensions/serialization_spec.rb +15 -1
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +59 -9
- data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
- data/spec/extensions/update_primary_key_spec.rb +17 -1
- data/spec/extensions/validation_class_methods_spec.rb +25 -0
- data/spec/extensions/validation_helpers_spec.rb +59 -3
- data/spec/integration/associations_test.rb +5 -5
- data/spec/integration/eager_loader_test.rb +32 -63
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +88 -56
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/timezone_test.rb +0 -1
- data/spec/integration/transaction_test.rb +0 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +106 -84
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +8 -8
- data/spec/model/model_spec.rb +27 -9
- data/spec/model/plugins_spec.rb +71 -0
- data/spec/model/record_spec.rb +99 -13
- metadata +12 -2
|
@@ -190,7 +190,7 @@ module Sequel
|
|
|
190
190
|
opts[:eager_loader_key] = left_pk unless opts.has_key?(:eager_loader_key)
|
|
191
191
|
left_pks = opts[:left_primary_keys] = Array(left_pk)
|
|
192
192
|
lpkc = opts[:left_primary_key_column] ||= left_pk
|
|
193
|
-
|
|
193
|
+
opts[:left_primary_key_columns] ||= Array(lpkc)
|
|
194
194
|
opts[:dataset] ||= lambda do
|
|
195
195
|
ds = opts.associated_dataset
|
|
196
196
|
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias], :qualify=>:deep)}
|
|
@@ -267,7 +267,6 @@ module Sequel
|
|
|
267
267
|
lpks = ref.qualify(model.table_name, lpks)
|
|
268
268
|
edges = ref.edges
|
|
269
269
|
first, rest = edges.first, edges[1..-1]
|
|
270
|
-
last = edges.last
|
|
271
270
|
ds = model.db[first[:table]].select(*Array(ref.qualify(first[:table], first[:right])))
|
|
272
271
|
rest.each{|e| ds = ds.join(e[:table], e.fetch(:only_conditions, (Array(e[:right]).zip(Array(e[:left])) + e[:conditions])), :table_alias=>ds.unused_table_alias(e[:table]), :qualify=>:deep, &e[:block])}
|
|
273
272
|
last_alias = if rest.empty?
|
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
#
|
|
3
|
+
# The ManyToOnePkLookup plugin that modifies the internal association loading logic
|
|
4
4
|
# for many_to_one associations to use a simple primary key lookup on the associated
|
|
5
5
|
# class, which is generally faster as it uses mostly static SQL. Additional, if the
|
|
6
6
|
# associated class is caching primary key lookups, you get the benefit of a cached
|
|
7
7
|
# lookup.
|
|
8
8
|
#
|
|
9
|
-
# This plugin is generally not as fast as the prepared_statements_associations plugin
|
|
10
|
-
# in the case where the model is not caching primary key lookups, however, it is
|
|
11
|
-
# probably significantly faster if the model is caching primary key lookups. If
|
|
12
|
-
# the prepared_statements_associations plugin has been loaded first, this
|
|
13
|
-
# plugin will only use the primary key lookup code if the associated model is
|
|
14
|
-
# caching primary key lookups.
|
|
15
|
-
#
|
|
16
9
|
# This plugin attempts to determine cases where the primary key lookup would have
|
|
17
|
-
# different results than the regular lookup, and use the regular lookup in that case
|
|
18
|
-
#
|
|
19
|
-
#
|
|
10
|
+
# different results than the regular lookup, and use the regular lookup in that case.
|
|
11
|
+
# If you want to explicitly force whether or not to use primary key lookups for
|
|
12
|
+
# a given association, set the :many_to_one_pk_lookup association option.
|
|
20
13
|
#
|
|
21
|
-
# You can disable primary key lookups on a per association basis with this
|
|
22
|
-
# plugin using the :many_to_one_pk_lookup=>false association option.
|
|
23
|
-
#
|
|
24
14
|
# Usage:
|
|
25
15
|
#
|
|
26
16
|
# # Make all model subclass instances use primary key lookups for many_to_one
|
|
@@ -30,37 +20,31 @@ module Sequel
|
|
|
30
20
|
# # Do so for just the album class.
|
|
31
21
|
# Album.plugin :many_to_one_pk_lookup
|
|
32
22
|
module ManyToOnePkLookup
|
|
23
|
+
module ClassMethods
|
|
24
|
+
# Disable primary key lookup in cases where it will result in a different
|
|
25
|
+
# query than the association query.
|
|
26
|
+
def def_many_to_one(opts)
|
|
27
|
+
if !opts.has_key?(:many_to_one_pk_lookup) &&
|
|
28
|
+
(opts[:dataset] || opts[:conditions] || opts[:block] || opts[:select] ||
|
|
29
|
+
(opts.has_key?(:key) && opts[:key] == nil))
|
|
30
|
+
opts[:many_to_one_pk_lookup] = false
|
|
31
|
+
end
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
33
36
|
module InstanceMethods
|
|
34
37
|
private
|
|
35
38
|
|
|
36
|
-
# If the current association is a
|
|
39
|
+
# If the current association is a simple many_to_one association, use
|
|
37
40
|
# a simple primary key lookup on the associated model, which can benefit from
|
|
38
41
|
# caching if the associated model is using caching.
|
|
39
|
-
def _load_associated_object(opts, dynamic_opts)
|
|
40
|
-
klass = opts.associated_class
|
|
41
|
-
cache_lookup = opts.send(:cached_fetch, :many_to_one_pk_lookup) do
|
|
42
|
-
opts[:type] == :many_to_one &&
|
|
43
|
-
opts[:key] &&
|
|
44
|
-
opts.primary_key == klass.primary_key
|
|
45
|
-
end
|
|
46
|
-
if cache_lookup &&
|
|
47
|
-
!dynamic_opts[:callback] &&
|
|
48
|
-
(o = klass.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| send(c)} : send(fk))))
|
|
49
|
-
o
|
|
50
|
-
else
|
|
51
|
-
super
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Deal with the situation where the prepared_statements_associations plugin is
|
|
56
|
-
# loaded first, by using a primary key lookup for many_to_one associations if
|
|
57
|
-
# the associated class is using caching, and using the default code otherwise.
|
|
58
|
-
# This is done because the prepared_statements_associations code is probably faster
|
|
59
|
-
# than the primary key lookup this plugin uses if the model is not caching lookups,
|
|
60
|
-
# but probably slower if the model is caching lookups.
|
|
61
42
|
def _load_associated_objects(opts, dynamic_opts={})
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
return super unless opts.can_have_associated_objects?(self) && opts[:type] == :many_to_one
|
|
44
|
+
klass = opts.associated_class
|
|
45
|
+
if !dynamic_opts[:callback] &&
|
|
46
|
+
opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == klass.primary_key}
|
|
47
|
+
klass.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| send(c)} : send(fk)))
|
|
64
48
|
else
|
|
65
49
|
super
|
|
66
50
|
end
|
|
@@ -38,11 +38,7 @@ module Sequel
|
|
|
38
38
|
# The column holding the version of the lock
|
|
39
39
|
attr_accessor :lock_column
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
def inherited(subclass)
|
|
43
|
-
super
|
|
44
|
-
subclass.lock_column = lock_column
|
|
45
|
-
end
|
|
41
|
+
Plugins.inherited_instance_variables(self, :@lock_column=>nil)
|
|
46
42
|
end
|
|
47
43
|
|
|
48
44
|
module InstanceMethods
|
|
@@ -100,7 +100,9 @@ module Sequel
|
|
|
100
100
|
module ClassMethods
|
|
101
101
|
# Register the model's row type with the database.
|
|
102
102
|
def register_row_type
|
|
103
|
-
|
|
103
|
+
table = dataset.first_source_table
|
|
104
|
+
db.register_row_type(table, :converter=>self, :typecaster=>method(:new))
|
|
105
|
+
db.instance_variable_get(:@schema_type_classes)[:"pg_row_#{table}"] = self
|
|
104
106
|
end
|
|
105
107
|
end
|
|
106
108
|
|
|
@@ -113,7 +115,7 @@ module Sequel
|
|
|
113
115
|
sql << ROW
|
|
114
116
|
ds.literal_append(sql, values.values_at(*columns))
|
|
115
117
|
sql << CAST
|
|
116
|
-
ds.quote_schema_table_append(sql, model.
|
|
118
|
+
ds.quote_schema_table_append(sql, model.dataset.first_source_table)
|
|
117
119
|
end
|
|
118
120
|
end
|
|
119
121
|
end
|
|
@@ -41,11 +41,7 @@ module Sequel
|
|
|
41
41
|
@pg_typecast_on_load_columns.concat(columns)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
def inherited(subclass)
|
|
46
|
-
super
|
|
47
|
-
subclass.instance_variable_set(:@pg_typecast_on_load_columns, pg_typecast_on_load_columns.dup)
|
|
48
|
-
end
|
|
44
|
+
Plugins.inherited_instance_variables(self, :@pg_typecast_on_load_columns=>:dup)
|
|
49
45
|
end
|
|
50
46
|
|
|
51
47
|
module InstanceMethods
|
|
@@ -53,8 +49,8 @@ module Sequel
|
|
|
53
49
|
# object, and use it to convert the value.
|
|
54
50
|
def set_values(values)
|
|
55
51
|
model.pg_typecast_on_load_columns.each do |c|
|
|
56
|
-
if (v = values[c]).is_a?(String) && (oid = db_schema[c][:oid])
|
|
57
|
-
values[c] =
|
|
52
|
+
if (v = values[c]).is_a?(String) && (oid = db_schema[c][:oid]) && (pr = db.conversion_procs[oid])
|
|
53
|
+
values[c] = pr.call(v)
|
|
58
54
|
end
|
|
59
55
|
end
|
|
60
56
|
super
|
|
@@ -36,11 +36,7 @@ module Sequel
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
module ClassMethods
|
|
39
|
-
|
|
40
|
-
def inherited(subclass)
|
|
41
|
-
super
|
|
42
|
-
subclass.instance_variable_set(:@prepared_statements, :insert=>{}, :insert_select=>{}, :update=>{}, :lookup_sql=>{}, :fixed=>{})
|
|
43
|
-
end
|
|
39
|
+
Plugins.inherited_instance_variables(self, :@prepared_statements=>lambda{|v| {:insert=>{}, :insert_select=>{}, :update=>{}, :lookup_sql=>{}, :fixed=>{}}})
|
|
44
40
|
|
|
45
41
|
private
|
|
46
42
|
|
|
@@ -33,17 +33,8 @@ module Sequel
|
|
|
33
33
|
# that can be created is 2^N (where N is the number of free columns).
|
|
34
34
|
attr_reader :prepared_statements_column_defaults
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
36
|
+
Plugins.inherited_instance_variables(self, :@prepared_statements_column_defaults=>:dup)
|
|
37
|
+
Plugins.after_set_dataset(self, :set_prepared_statements_column_defaults)
|
|
47
38
|
|
|
48
39
|
private
|
|
49
40
|
|
|
@@ -190,7 +190,7 @@ module Sequel
|
|
|
190
190
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
|
191
191
|
elds.all do |obj|
|
|
192
192
|
opk = obj[prkey]
|
|
193
|
-
if
|
|
193
|
+
if parent_map.has_key?(opk)
|
|
194
194
|
if idm_obj = parent_map[opk]
|
|
195
195
|
idm_obj.values[ka] = obj.values[ka]
|
|
196
196
|
obj = idm_obj
|
|
@@ -298,7 +298,7 @@ module Sequel
|
|
|
298
298
|
end
|
|
299
299
|
|
|
300
300
|
opk = obj[prkey]
|
|
301
|
-
if
|
|
301
|
+
if parent_map.has_key?(opk)
|
|
302
302
|
if idm_obj = parent_map[opk]
|
|
303
303
|
idm_obj.values[ka] = obj.values[ka]
|
|
304
304
|
obj = idm_obj
|
|
@@ -30,11 +30,11 @@ module Sequel
|
|
|
30
30
|
#
|
|
31
31
|
# == Example
|
|
32
32
|
#
|
|
33
|
-
# require
|
|
34
|
-
# # Require json, as the plugin doesn't require it for you.
|
|
33
|
+
# # Require json if you plan to use it, as the plugin doesn't require it for you.
|
|
35
34
|
# require 'json'
|
|
36
35
|
#
|
|
37
|
-
# # Register custom serializer/deserializer pair
|
|
36
|
+
# # Register custom serializer/deserializer pair, if desired
|
|
37
|
+
# require 'sequel/plugins/serialization'
|
|
38
38
|
# Sequel::Plugins::Serialization.register_format(:reverse,
|
|
39
39
|
# lambda{|v| v.reverse},
|
|
40
40
|
# lambda{|v| v.reverse})
|
|
@@ -111,16 +111,7 @@ module Sequel
|
|
|
111
111
|
# call be overridden and call super to get the serialization behavior
|
|
112
112
|
attr_accessor :serialization_module
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
def inherited(subclass)
|
|
116
|
-
super
|
|
117
|
-
sm = serialization_map.dup
|
|
118
|
-
dsm = deserialization_map.dup
|
|
119
|
-
subclass.instance_eval do
|
|
120
|
-
@deserialization_map = dsm
|
|
121
|
-
@serialization_map = sm
|
|
122
|
-
end
|
|
123
|
-
end
|
|
114
|
+
Plugins.inherited_instance_variables(self, :@deserialization_map=>:dup, :@serialization_map=>:dup)
|
|
124
115
|
|
|
125
116
|
# Create instance level reader that deserializes column values on request,
|
|
126
117
|
# and instance level writer that stores new deserialized values.
|
|
@@ -154,6 +145,8 @@ module Sequel
|
|
|
154
145
|
define_method(column) do
|
|
155
146
|
if deserialized_values.has_key?(column)
|
|
156
147
|
deserialized_values[column]
|
|
148
|
+
elsif frozen?
|
|
149
|
+
deserialize_value(column, super())
|
|
157
150
|
else
|
|
158
151
|
deserialized_values[column] = deserialize_value(column, super())
|
|
159
152
|
end
|
|
@@ -179,6 +172,11 @@ module Sequel
|
|
|
179
172
|
@deserialized_values ||= {}
|
|
180
173
|
end
|
|
181
174
|
|
|
175
|
+
def freeze
|
|
176
|
+
deserialized_values.freeze
|
|
177
|
+
super
|
|
178
|
+
end
|
|
179
|
+
|
|
182
180
|
# Initialization the deserialized values for objects retrieved from the database.
|
|
183
181
|
def set_values(hash)
|
|
184
182
|
@deserialized_values.clear if @deserialized_values
|
|
@@ -40,10 +40,18 @@ module Sequel
|
|
|
40
40
|
# Detect which serialized columns have changed.
|
|
41
41
|
def changed_columns
|
|
42
42
|
cc = super
|
|
43
|
+
cc = cc.dup if frozen?
|
|
43
44
|
deserialized_values.each{|c, v| cc << c if !cc.include?(c) && original_deserialized_value(c) != v}
|
|
44
45
|
cc
|
|
45
46
|
end
|
|
46
47
|
|
|
48
|
+
# Freeze the original deserialized values when freezing the instance.
|
|
49
|
+
def freeze
|
|
50
|
+
@original_deserialized_values ||= {}
|
|
51
|
+
@original_deserialized_values.freeze
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
private
|
|
48
56
|
|
|
49
57
|
# For new objects, serialize any existing deserialized values so that changes can
|
|
@@ -55,7 +63,11 @@ module Sequel
|
|
|
55
63
|
|
|
56
64
|
# Return the original deserialized value of the column, caching it to improve performance.
|
|
57
65
|
def original_deserialized_value(column)
|
|
58
|
-
|
|
66
|
+
if frozen?
|
|
67
|
+
@original_deserialized_values[column] || deserialize_value(column, self[column])
|
|
68
|
+
else
|
|
69
|
+
(@original_deserialized_values ||= {})[column] ||= deserialize_value(column, self[column])
|
|
70
|
+
end
|
|
59
71
|
end
|
|
60
72
|
end
|
|
61
73
|
end
|
|
@@ -77,9 +77,9 @@ module Sequel
|
|
|
77
77
|
@sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
|
|
78
78
|
@sti_key_map = if km = opts[:key_map]
|
|
79
79
|
if km.is_a?(Hash)
|
|
80
|
-
h = Hash.new do |
|
|
80
|
+
h = Hash.new do |h1,k|
|
|
81
81
|
unless k.is_a?(String)
|
|
82
|
-
|
|
82
|
+
h1[k.to_s]
|
|
83
83
|
else
|
|
84
84
|
[]
|
|
85
85
|
end
|
|
@@ -93,9 +93,9 @@ module Sequel
|
|
|
93
93
|
km
|
|
94
94
|
end
|
|
95
95
|
elsif sti_model_map.is_a?(Hash)
|
|
96
|
-
h = Hash.new do |
|
|
96
|
+
h = Hash.new do |h1,k|
|
|
97
97
|
unless k.is_a?(String)
|
|
98
|
-
|
|
98
|
+
h1[k.to_s]
|
|
99
99
|
else
|
|
100
100
|
[]
|
|
101
101
|
end
|
|
@@ -35,6 +35,15 @@ module Sequel
|
|
|
35
35
|
@all.dup
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# Get the number of records in the cache, without issuing a database query.
|
|
39
|
+
def count(*a, &block)
|
|
40
|
+
if a.empty? && !block
|
|
41
|
+
@all.size
|
|
42
|
+
else
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
38
47
|
# Return the frozen object with the given pk, or nil if no such object exists
|
|
39
48
|
# in the cache, without issuing a database query.
|
|
40
49
|
def cache_get_pk(pk)
|
|
@@ -47,33 +56,72 @@ module Sequel
|
|
|
47
56
|
@all.each(&block)
|
|
48
57
|
end
|
|
49
58
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
# Use the cache instead of a query to get the results.
|
|
60
|
+
def map(column=nil, &block)
|
|
61
|
+
if column
|
|
62
|
+
raise(Error, "Cannot provide both column and block to map") if block
|
|
63
|
+
if column.is_a?(Array)
|
|
64
|
+
@all.map{|r| r.values.values_at(*column)}
|
|
65
|
+
else
|
|
66
|
+
@all.map{|r| r[column]}
|
|
67
|
+
end
|
|
56
68
|
else
|
|
57
|
-
|
|
69
|
+
@all.map(&(Proc.new if block_given?))
|
|
58
70
|
end
|
|
59
71
|
end
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
Plugins.after_set_dataset(self, :load_cache)
|
|
74
|
+
|
|
75
|
+
# Use the cache instead of a query to get the results.
|
|
76
|
+
def to_hash(key_column = nil, value_column = nil)
|
|
77
|
+
return cache.dup if key_column.nil? && value_column.nil?
|
|
78
|
+
|
|
79
|
+
h = {}
|
|
80
|
+
if value_column
|
|
81
|
+
if value_column.is_a?(Array)
|
|
82
|
+
if key_column.is_a?(Array)
|
|
83
|
+
each{|r| h[r.values.values_at(*key_column)] = r.values.values_at(*value_column)}
|
|
84
|
+
else
|
|
85
|
+
each{|r| h[r[key_column]] = r.values.values_at(*value_column)}
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
if key_column.is_a?(Array)
|
|
89
|
+
each{|r| h[r.values.values_at(*key_column)] = r[value_column]}
|
|
90
|
+
else
|
|
91
|
+
each{|r| h[r[key_column]] = r[value_column]}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
elsif key_column.is_a?(Array)
|
|
95
|
+
each{|r| h[r.values.values_at(*key_column)] = r}
|
|
96
|
+
else
|
|
97
|
+
each{|r| h[r[key_column]] = r}
|
|
98
|
+
end
|
|
99
|
+
h
|
|
66
100
|
end
|
|
67
101
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
102
|
+
# Use the cache instead of a query to get the results
|
|
103
|
+
def to_hash_groups(key_column, value_column = nil)
|
|
104
|
+
h = {}
|
|
105
|
+
if value_column
|
|
106
|
+
if value_column.is_a?(Array)
|
|
107
|
+
if key_column.is_a?(Array)
|
|
108
|
+
each{|r| (h[r.values.values_at(*key_column)] ||= []) << r.values.values_at(*value_column)}
|
|
109
|
+
else
|
|
110
|
+
each{|r| (h[r[key_column]] ||= []) << r.values.values_at(*value_column)}
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
if key_column.is_a?(Array)
|
|
114
|
+
each{|r| (h[r.values.values_at(*key_column)] ||= []) << r[value_column]}
|
|
115
|
+
else
|
|
116
|
+
each{|r| (h[r[key_column]] ||= []) << r[value_column]}
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
elsif key_column.is_a?(Array)
|
|
120
|
+
each{|r| (h[r.values.values_at(*key_column)] ||= []) << r}
|
|
74
121
|
else
|
|
75
|
-
|
|
122
|
+
each{|r| (h[r[key_column]] ||= []) << r}
|
|
76
123
|
end
|
|
124
|
+
h
|
|
77
125
|
end
|
|
78
126
|
|
|
79
127
|
private
|
|
@@ -22,34 +22,24 @@ module Sequel
|
|
|
22
22
|
# # Make the Album class strip strings
|
|
23
23
|
# Album.plugin :string_stripper
|
|
24
24
|
module StringStripper
|
|
25
|
-
|
|
25
|
+
def self.apply(model)
|
|
26
|
+
model.plugin(:input_transformer, :string_stripper){|v| (v.is_a?(String) && !v.is_a?(SQL::Blob)) ? v.strip : v}
|
|
27
|
+
end
|
|
26
28
|
def self.configure(model)
|
|
27
|
-
model.
|
|
28
|
-
model.send(:set_skipped_string_stripping_columns)
|
|
29
|
+
model.instance_eval{set_skipped_string_stripping_columns if @dataset}
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
module ClassMethods
|
|
32
|
-
|
|
33
|
-
def inherited(subclass)
|
|
34
|
-
subclass.instance_variable_set(:@skipped_string_stripping_columns, @skipped_string_stripping_columns.dup)
|
|
35
|
-
super
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Set blob columns as skipping stripping when plugin is loaded.
|
|
39
|
-
def set_dataset(*)
|
|
40
|
-
res = super
|
|
41
|
-
set_skipped_string_stripping_columns
|
|
42
|
-
res
|
|
43
|
-
end
|
|
33
|
+
Plugins.after_set_dataset(self, :set_skipped_string_stripping_columns)
|
|
44
34
|
|
|
45
35
|
# Skip stripping for the given columns.
|
|
46
36
|
def skip_string_stripping(*columns)
|
|
47
|
-
|
|
37
|
+
skip_input_transformer(:string_stripper, *columns)
|
|
48
38
|
end
|
|
49
39
|
|
|
50
40
|
# Return true if the column should not have values stripped.
|
|
51
41
|
def skip_string_stripping?(column)
|
|
52
|
-
|
|
42
|
+
skip_input_transformer?(:string_stripper, column)
|
|
53
43
|
end
|
|
54
44
|
|
|
55
45
|
private
|
|
@@ -62,16 +52,6 @@ module Sequel
|
|
|
62
52
|
end
|
|
63
53
|
end
|
|
64
54
|
end
|
|
65
|
-
|
|
66
|
-
module InstanceMethods
|
|
67
|
-
# Strip value if it is a non-blob string and the model hasn't been set
|
|
68
|
-
# to skip stripping for the column, before attempting to assign
|
|
69
|
-
# it to the model's values.
|
|
70
|
-
def []=(k, v)
|
|
71
|
-
v = v.strip if v.is_a?(String) && !v.is_a?(SQL::Blob) && !model.skip_string_stripping?(k)
|
|
72
|
-
super(k, v)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
55
|
end
|
|
76
56
|
end
|
|
77
57
|
end
|