sequel 3.48.0 → 4.0.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 +114 -0
- data/Rakefile +10 -7
- data/doc/association_basics.rdoc +25 -23
- data/doc/code_order.rdoc +7 -0
- data/doc/core_extensions.rdoc +0 -10
- data/doc/object_model.rdoc +4 -1
- data/doc/querying.rdoc +3 -3
- data/doc/release_notes/4.0.0.txt +262 -0
- data/doc/security.rdoc +0 -28
- data/doc/testing.rdoc +8 -14
- data/lib/sequel/adapters/ado.rb +7 -11
- data/lib/sequel/adapters/ado/access.rb +8 -8
- data/lib/sequel/adapters/ado/mssql.rb +4 -4
- data/lib/sequel/adapters/amalgalite.rb +6 -6
- data/lib/sequel/adapters/cubrid.rb +7 -7
- data/lib/sequel/adapters/db2.rb +5 -9
- data/lib/sequel/adapters/dbi.rb +2 -6
- data/lib/sequel/adapters/do.rb +4 -4
- data/lib/sequel/adapters/firebird.rb +4 -4
- data/lib/sequel/adapters/ibmdb.rb +8 -8
- data/lib/sequel/adapters/informix.rb +2 -10
- data/lib/sequel/adapters/jdbc.rb +17 -17
- data/lib/sequel/adapters/jdbc/as400.rb +2 -2
- data/lib/sequel/adapters/jdbc/cubrid.rb +1 -1
- data/lib/sequel/adapters/jdbc/db2.rb +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
- data/lib/sequel/adapters/jdbc/informix.rb +1 -1
- data/lib/sequel/adapters/jdbc/mssql.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +5 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -3
- data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
- data/lib/sequel/adapters/jdbc/transactions.rb +3 -3
- data/lib/sequel/adapters/mock.rb +7 -7
- data/lib/sequel/adapters/mysql.rb +3 -3
- data/lib/sequel/adapters/mysql2.rb +4 -4
- data/lib/sequel/adapters/odbc.rb +2 -6
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +1 -5
- data/lib/sequel/adapters/oracle.rb +13 -17
- data/lib/sequel/adapters/postgres.rb +20 -25
- data/lib/sequel/adapters/shared/cubrid.rb +3 -3
- data/lib/sequel/adapters/shared/db2.rb +2 -2
- data/lib/sequel/adapters/shared/firebird.rb +7 -7
- data/lib/sequel/adapters/shared/mssql.rb +9 -9
- data/lib/sequel/adapters/shared/mysql.rb +29 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +7 -7
- data/lib/sequel/adapters/shared/oracle.rb +22 -13
- data/lib/sequel/adapters/shared/postgres.rb +61 -46
- data/lib/sequel/adapters/shared/sqlite.rb +9 -9
- data/lib/sequel/adapters/sqlite.rb +17 -11
- data/lib/sequel/adapters/swift.rb +3 -3
- data/lib/sequel/adapters/swift/mysql.rb +1 -1
- data/lib/sequel/adapters/swift/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +8 -8
- data/lib/sequel/ast_transformer.rb +3 -1
- data/lib/sequel/connection_pool.rb +4 -2
- data/lib/sequel/connection_pool/sharded_single.rb +2 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -5
- data/lib/sequel/connection_pool/threaded.rb +7 -7
- data/lib/sequel/core.rb +4 -67
- data/lib/sequel/database.rb +1 -0
- data/lib/sequel/database/connecting.rb +2 -8
- data/lib/sequel/database/dataset.rb +2 -7
- data/lib/sequel/database/dataset_defaults.rb +0 -18
- data/lib/sequel/database/features.rb +4 -4
- data/lib/sequel/database/misc.rb +6 -8
- data/lib/sequel/database/query.rb +5 -61
- data/lib/sequel/database/schema_generator.rb +22 -20
- data/lib/sequel/database/schema_methods.rb +48 -20
- data/lib/sequel/database/transactions.rb +7 -17
- data/lib/sequel/dataset.rb +2 -0
- data/lib/sequel/dataset/actions.rb +23 -91
- data/lib/sequel/dataset/features.rb +1 -4
- data/lib/sequel/dataset/graph.rb +3 -47
- data/lib/sequel/dataset/misc.rb +4 -33
- data/lib/sequel/dataset/prepared_statements.rb +3 -1
- data/lib/sequel/dataset/query.rb +116 -240
- data/lib/sequel/dataset/sql.rb +19 -97
- data/lib/sequel/deprecated.rb +0 -16
- data/lib/sequel/exceptions.rb +0 -3
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/columns_introspection.rb +1 -12
- data/lib/sequel/extensions/constraint_validations.rb +3 -3
- data/lib/sequel/extensions/core_extensions.rb +0 -9
- data/lib/sequel/extensions/date_arithmetic.rb +1 -2
- data/lib/sequel/extensions/graph_each.rb +11 -0
- data/lib/sequel/extensions/migration.rb +5 -5
- data/lib/sequel/extensions/null_dataset.rb +11 -13
- data/lib/sequel/extensions/pagination.rb +3 -6
- data/lib/sequel/extensions/pg_array.rb +6 -4
- data/lib/sequel/extensions/pg_array_ops.rb +35 -1
- data/lib/sequel/extensions/pg_json.rb +12 -2
- data/lib/sequel/extensions/pg_json_ops.rb +266 -0
- data/lib/sequel/extensions/pg_range.rb +2 -2
- data/lib/sequel/extensions/pg_range_ops.rb +0 -8
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/pretty_table.rb +0 -4
- data/lib/sequel/extensions/query.rb +3 -8
- data/lib/sequel/extensions/schema_caching.rb +0 -7
- data/lib/sequel/extensions/schema_dumper.rb +10 -17
- data/lib/sequel/extensions/select_remove.rb +0 -4
- data/lib/sequel/extensions/set_overrides.rb +28 -0
- data/lib/sequel/extensions/to_dot.rb +6 -10
- data/lib/sequel/model.rb +6 -7
- data/lib/sequel/model/associations.rb +127 -182
- data/lib/sequel/model/base.rb +88 -211
- data/lib/sequel/model/errors.rb +0 -13
- data/lib/sequel/model/plugins.rb +2 -2
- data/lib/sequel/no_core_ext.rb +0 -1
- data/lib/sequel/plugins/after_initialize.rb +11 -17
- data/lib/sequel/plugins/association_autoreloading.rb +1 -47
- data/lib/sequel/plugins/association_dependencies.rb +2 -2
- data/lib/sequel/plugins/auto_validations.rb +2 -8
- data/lib/sequel/plugins/blacklist_security.rb +32 -2
- data/lib/sequel/plugins/caching.rb +1 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/composition.rb +10 -8
- data/lib/sequel/plugins/constraint_validations.rb +2 -2
- data/lib/sequel/plugins/dataset_associations.rb +4 -0
- data/lib/sequel/plugins/defaults_setter.rb +8 -6
- data/lib/sequel/plugins/dirty.rb +6 -6
- data/lib/sequel/plugins/force_encoding.rb +13 -8
- data/lib/sequel/plugins/hook_class_methods.rb +1 -7
- data/lib/sequel/plugins/json_serializer.rb +13 -74
- data/lib/sequel/plugins/lazy_attributes.rb +2 -4
- data/lib/sequel/plugins/list.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +4 -11
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +1 -49
- data/lib/sequel/plugins/nested_attributes.rb +1 -1
- data/lib/sequel/plugins/optimistic_locking.rb +3 -5
- data/lib/sequel/plugins/pg_array_associations.rb +453 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +23 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +20 -14
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +5 -4
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/sharding.rb +7 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/touch.rb +2 -2
- data/lib/sequel/plugins/tree.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +19 -4
- data/lib/sequel/plugins/validation_class_methods.rb +0 -30
- data/lib/sequel/plugins/validation_helpers.rb +13 -31
- data/lib/sequel/plugins/xml_serializer.rb +18 -57
- data/lib/sequel/sql.rb +20 -22
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/db2_spec.rb +14 -23
- data/spec/adapters/firebird_spec.rb +25 -29
- data/spec/adapters/informix_spec.rb +11 -14
- data/spec/adapters/mssql_spec.rb +71 -77
- data/spec/adapters/mysql_spec.rb +165 -172
- data/spec/adapters/oracle_spec.rb +36 -39
- data/spec/adapters/postgres_spec.rb +175 -100
- data/spec/adapters/spec_helper.rb +13 -11
- data/spec/adapters/sqlite_spec.rb +36 -44
- data/spec/core/connection_pool_spec.rb +2 -1
- data/spec/core/database_spec.rb +55 -55
- data/spec/core/dataset_spec.rb +45 -249
- data/spec/core/deprecated_spec.rb +0 -8
- data/spec/core/expression_filters_spec.rb +23 -5
- data/spec/core/object_graph_spec.rb +4 -66
- data/spec/core/schema_spec.rb +35 -12
- data/spec/core/spec_helper.rb +3 -2
- data/spec/core_extensions_spec.rb +17 -19
- data/spec/extensions/arbitrary_servers_spec.rb +2 -3
- data/spec/extensions/association_dependencies_spec.rb +14 -14
- data/spec/extensions/auto_validations_spec.rb +7 -0
- data/spec/extensions/blacklist_security_spec.rb +5 -5
- data/spec/extensions/blank_spec.rb +2 -0
- data/spec/extensions/class_table_inheritance_spec.rb +2 -2
- data/spec/extensions/columns_introspection_spec.rb +2 -29
- data/spec/extensions/composition_spec.rb +10 -17
- data/spec/extensions/core_refinements_spec.rb +5 -1
- data/spec/extensions/dataset_associations_spec.rb +18 -0
- data/spec/extensions/date_arithmetic_spec.rb +2 -2
- data/spec/extensions/defaults_setter_spec.rb +9 -9
- data/spec/extensions/dirty_spec.rb +0 -5
- data/spec/extensions/eval_inspect_spec.rb +2 -0
- data/spec/extensions/force_encoding_spec.rb +2 -18
- data/spec/extensions/hash_aliases_spec.rb +8 -0
- data/spec/extensions/hook_class_methods_spec.rb +39 -58
- data/spec/extensions/inflector_spec.rb +2 -0
- data/spec/extensions/instance_filters_spec.rb +8 -8
- data/spec/extensions/json_serializer_spec.rb +1 -41
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +106 -109
- data/spec/extensions/migration_spec.rb +2 -0
- data/spec/extensions/named_timezones_spec.rb +1 -0
- data/spec/extensions/pg_array_associations_spec.rb +603 -0
- data/spec/extensions/pg_array_ops_spec.rb +25 -0
- data/spec/extensions/pg_array_spec.rb +9 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +13 -0
- data/spec/extensions/pg_hstore_spec.rb +1 -0
- data/spec/extensions/pg_json_ops_spec.rb +131 -0
- data/spec/extensions/pg_json_spec.rb +10 -4
- data/spec/extensions/pg_range_ops_spec.rb +2 -5
- data/spec/extensions/pg_range_spec.rb +6 -2
- data/spec/extensions/pg_row_ops_spec.rb +2 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +26 -5
- data/spec/extensions/rcte_tree_spec.rb +15 -15
- data/spec/extensions/schema_dumper_spec.rb +0 -1
- data/spec/extensions/schema_spec.rb +9 -9
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +18 -29
- data/spec/extensions/set_overrides_spec.rb +4 -0
- data/spec/extensions/{many_to_one_pk_lookup_spec.rb → shared_caching_spec.rb} +1 -4
- data/spec/extensions/single_table_inheritance_spec.rb +4 -4
- data/spec/extensions/spec_helper.rb +8 -9
- data/spec/extensions/sql_expr_spec.rb +2 -0
- data/spec/extensions/string_date_time_spec.rb +2 -0
- data/spec/extensions/string_stripper_spec.rb +2 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +12 -12
- data/spec/extensions/thread_local_timezones_spec.rb +2 -0
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/to_dot_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +24 -24
- data/spec/extensions/tree_spec.rb +7 -7
- data/spec/extensions/typecast_on_load_spec.rb +8 -1
- data/spec/extensions/update_primary_key_spec.rb +10 -10
- data/spec/extensions/validation_class_methods_spec.rb +10 -39
- data/spec/extensions/validation_helpers_spec.rb +29 -47
- data/spec/extensions/xml_serializer_spec.rb +1 -23
- data/spec/integration/associations_test.rb +231 -40
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +64 -64
- data/spec/integration/eager_loader_test.rb +28 -28
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +21 -21
- data/spec/integration/prepared_statement_test.rb +7 -7
- data/spec/integration/schema_test.rb +115 -110
- data/spec/integration/spec_helper.rb +17 -27
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +10 -10
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/association_reflection_spec.rb +2 -28
- data/spec/model/associations_spec.rb +239 -188
- data/spec/model/base_spec.rb +27 -68
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +160 -172
- data/spec/model/hooks_spec.rb +62 -79
- data/spec/model/model_spec.rb +36 -51
- data/spec/model/plugins_spec.rb +5 -19
- data/spec/model/record_spec.rb +125 -151
- data/spec/model/spec_helper.rb +8 -6
- data/spec/model/validations_spec.rb +4 -17
- data/spec/spec_config.rb +2 -10
- metadata +50 -56
- data/lib/sequel/deprecated_core_extensions.rb +0 -135
- data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -185
- data/lib/sequel/extensions/pg_statement_cache.rb +0 -318
- data/lib/sequel/plugins/identity_map.rb +0 -260
- data/lib/sequel_core.rb +0 -2
- data/lib/sequel_model.rb +0 -2
- data/spec/extensions/association_autoreloading_spec.rb +0 -102
- data/spec/extensions/identity_map_spec.rb +0 -337
- data/spec/extensions/pg_auto_parameterize_spec.rb +0 -70
- data/spec/extensions/pg_statement_cache_spec.rb +0 -208
- data/spec/rcov.opts +0 -8
- data/spec/spec_config.rb.example +0 -10
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
module Sequel
|
|
2
|
-
module Plugins
|
|
3
|
-
# The identity_map plugin allows the user to create temporary identity maps
|
|
4
|
-
# via the with_identity_map method, which takes a block. Inside the block,
|
|
5
|
-
# objects have a 1-1 correspondence with rows in the database.
|
|
6
|
-
#
|
|
7
|
-
# For example, the following is true, and wouldn't be true if you weren't
|
|
8
|
-
# using the identity map:
|
|
9
|
-
# Sequel::Model.with_identity_map do
|
|
10
|
-
# Album.filter{(id > 0) & (id < 2)}.first.object_id == Album.first(:id=>1).object_id
|
|
11
|
-
# end
|
|
12
|
-
#
|
|
13
|
-
# In addition to providing a 1-1 correspondence, the identity_map plugin
|
|
14
|
-
# also provides a cached looked up of records in two cases:
|
|
15
|
-
# * Model.[] (e.g. Album[1])
|
|
16
|
-
# * Model.many_to_one accessor methods (e.g. album.artist)
|
|
17
|
-
#
|
|
18
|
-
# If the object you are looking up, using one of those two methods, is already
|
|
19
|
-
# in the identity map, the record is returned without a database query being
|
|
20
|
-
# issued.
|
|
21
|
-
#
|
|
22
|
-
# Identity maps are thread-local and only persist for the duration of the block,
|
|
23
|
-
# so they should only be considered as a possible performance enhancer.
|
|
24
|
-
#
|
|
25
|
-
# The identity_map plugin is not compatible with the eager loading in the +rcte_tree+ plugin.
|
|
26
|
-
#
|
|
27
|
-
# Usage:
|
|
28
|
-
#
|
|
29
|
-
# # Use an identity map that will affect all model classes (called before loading subclasses)
|
|
30
|
-
# Sequel::Model.plugin :identity_map
|
|
31
|
-
#
|
|
32
|
-
# # Use an identity map just for the Album class
|
|
33
|
-
# Album.plugin :identity_map
|
|
34
|
-
# # would need to do Album.with_identity_map{} to use the identity map
|
|
35
|
-
module IdentityMap
|
|
36
|
-
def self.apply(mod)
|
|
37
|
-
Sequel::Deprecation.deprecate('The identity_map plugin', 'Please stop loading it') unless defined?(SEQUEL_EXTENSIONS_NO_DEPRECATION_WARNING)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
module ClassMethods
|
|
41
|
-
# Override the default :eager_loader option for many_*_many associations to work
|
|
42
|
-
# with an identity_map. If the :eager_graph association option is used, you'll probably have to use
|
|
43
|
-
# :uniq=>true on the current association and :cartesian_product_number=>2 on the association
|
|
44
|
-
# mentioned by :eager_graph, otherwise you'll end up with duplicates because the row proc will be
|
|
45
|
-
# getting called multiple times for the same object. If you do have duplicates and you use :eager_graph,
|
|
46
|
-
# they'll probably be lost. Making that work correctly would require changing a lot of the core
|
|
47
|
-
# architecture, such as how graphing and eager graphing work.
|
|
48
|
-
def associate(type, name, opts = {}, &block)
|
|
49
|
-
if opts[:eager_loader]
|
|
50
|
-
super
|
|
51
|
-
elsif type == :many_to_many
|
|
52
|
-
opts = super
|
|
53
|
-
el = opts[:eager_loader]
|
|
54
|
-
model = self
|
|
55
|
-
left_pk = opts[:left_primary_key]
|
|
56
|
-
uses_lcks = opts[:uses_left_composite_keys]
|
|
57
|
-
uses_rcks = opts[:uses_right_composite_keys]
|
|
58
|
-
right = opts[:right_key]
|
|
59
|
-
rcks = opts[:right_keys]
|
|
60
|
-
join_table = opts[:join_table]
|
|
61
|
-
left = opts[:left_key]
|
|
62
|
-
lcks = opts[:left_keys]
|
|
63
|
-
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
|
64
|
-
opts[:eager_loader] = lambda do |eo|
|
|
65
|
-
return el.call(eo) unless model.identity_map
|
|
66
|
-
h = eo[:id_map]
|
|
67
|
-
eo[:rows].each{|object| object.associations[name] = []}
|
|
68
|
-
r = uses_rcks ? rcks.zip(opts.right_primary_keys) : [[right, opts.right_primary_key]]
|
|
69
|
-
l = uses_lcks ? [[lcks.map{|k| SQL::QualifiedIdentifier.new(join_table, k)}, h.keys]] : [[left, h.keys]]
|
|
70
|
-
|
|
71
|
-
# Replace the row proc to remove the left key alias before calling the previous row proc.
|
|
72
|
-
# Associate the value of the left key alias with the associated object (through its object_id).
|
|
73
|
-
# When loading the associated objects, lookup the left key alias value and associate the
|
|
74
|
-
# associated objects to the main objects if the left key alias value matches the left primary key
|
|
75
|
-
# value of the main object.
|
|
76
|
-
#
|
|
77
|
-
# The deleting of the left key alias from the hash before calling the previous row proc
|
|
78
|
-
# is necessary when an identity map is used, otherwise if the same associated object is returned more than
|
|
79
|
-
# once for the association, it won't know which of current objects to associate it to.
|
|
80
|
-
ds = opts.associated_class.inner_join(join_table, r + l)
|
|
81
|
-
pr = ds.row_proc
|
|
82
|
-
h2 = {}
|
|
83
|
-
ds.row_proc = proc do |hash|
|
|
84
|
-
hash_key = if uses_lcks
|
|
85
|
-
left_key_alias.map{|k| hash.delete(k)}
|
|
86
|
-
else
|
|
87
|
-
hash.delete(left_key_alias)
|
|
88
|
-
end
|
|
89
|
-
obj = pr.call(hash)
|
|
90
|
-
(h2[obj.object_id] ||= []) << hash_key
|
|
91
|
-
obj
|
|
92
|
-
end
|
|
93
|
-
model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo) .all do |assoc_record|
|
|
94
|
-
if hash_keys = h2.delete(assoc_record.object_id)
|
|
95
|
-
hash_keys.each do |hash_key|
|
|
96
|
-
if objects = h[hash_key]
|
|
97
|
-
objects.each{|object| object.associations[name].push(assoc_record)}
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
opts
|
|
104
|
-
elsif type == :many_through_many
|
|
105
|
-
opts = super
|
|
106
|
-
el = opts[:eager_loader]
|
|
107
|
-
model = self
|
|
108
|
-
left_pk = opts[:left_primary_key]
|
|
109
|
-
left_key = opts[:left_key]
|
|
110
|
-
uses_lcks = opts[:uses_left_composite_keys]
|
|
111
|
-
left_keys = Array(left_key)
|
|
112
|
-
left_key_alias = opts[:left_key_alias]
|
|
113
|
-
opts[:eager_loader] = lambda do |eo|
|
|
114
|
-
return el.call(eo) unless model.identity_map
|
|
115
|
-
h = eo[:id_map]
|
|
116
|
-
eo[:rows].each{|object| object.associations[name] = []}
|
|
117
|
-
ds = opts.associated_class
|
|
118
|
-
opts.reverse_edges.each{|t| ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
|
119
|
-
ft = opts.final_reverse_edge
|
|
120
|
-
conds = uses_lcks ? [[left_keys.map{|k| SQL::QualifiedIdentifier.new(ft[:table], k)}, h.keys]] : [[left_key, h.keys]]
|
|
121
|
-
|
|
122
|
-
# See above comment in many_to_many eager_loader
|
|
123
|
-
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + conds, :table_alias=>ft[:alias])
|
|
124
|
-
pr = ds.row_proc
|
|
125
|
-
h2 = {}
|
|
126
|
-
ds.row_proc = proc do |hash|
|
|
127
|
-
hash_key = if uses_lcks
|
|
128
|
-
left_key_alias.map{|k| hash.delete(k)}
|
|
129
|
-
else
|
|
130
|
-
hash.delete(left_key_alias)
|
|
131
|
-
end
|
|
132
|
-
obj = pr.call(hash)
|
|
133
|
-
(h2[obj.object_id] ||= []) << hash_key
|
|
134
|
-
obj
|
|
135
|
-
end
|
|
136
|
-
model.eager_loading_dataset(opts, ds, Array(opts.select), eo[:associations], eo).all do |assoc_record|
|
|
137
|
-
if hash_keys = h2.delete(assoc_record.object_id)
|
|
138
|
-
hash_keys.each do |hash_key|
|
|
139
|
-
if objects = h[hash_key]
|
|
140
|
-
objects.each{|object| object.associations[name].push(assoc_record)}
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
opts
|
|
147
|
-
else
|
|
148
|
-
super
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Returns the current thread-local identity map. Should be a hash if
|
|
153
|
-
# there is an active identity map, and nil otherwise.
|
|
154
|
-
def identity_map
|
|
155
|
-
Thread.current[:sequel_identity_map]
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# The identity map key for an object of the current class with the given pk.
|
|
159
|
-
# May not always be correct for a class which uses STI.
|
|
160
|
-
def identity_map_key(pk)
|
|
161
|
-
pk = Array(pk)
|
|
162
|
-
"#{self}:#{pk.join(',')}" unless pk.compact.empty?
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# If the identity map is in use, check it for a current copy of the object.
|
|
166
|
-
# If a copy does not exist, create a new object and add it to the identity map.
|
|
167
|
-
# If a copy exists, add any values in the given row that aren't currently
|
|
168
|
-
# in the object to the object's values. This allows you to only request
|
|
169
|
-
# certain fields in an initial query, make modifications to some of those
|
|
170
|
-
# fields and request other, potentially overlapping fields in a new query,
|
|
171
|
-
# and not have the second query override fields you modified.
|
|
172
|
-
def call(row)
|
|
173
|
-
return super unless (idm = identity_map) && (pk = primary_key)
|
|
174
|
-
if (k = identity_map_key(Array(pk).map{|x| row[x]})) && (o = idm[k])
|
|
175
|
-
o.merge_db_update(row)
|
|
176
|
-
else
|
|
177
|
-
o = super
|
|
178
|
-
if (k = identity_map_key(o.pk))
|
|
179
|
-
idm[k] = o
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
o
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Take a block and inside that block use an identity map to ensure a 1-1
|
|
186
|
-
# correspondence of objects to the database row they represent.
|
|
187
|
-
def with_identity_map
|
|
188
|
-
return yield if identity_map
|
|
189
|
-
begin
|
|
190
|
-
self.identity_map = {}
|
|
191
|
-
yield
|
|
192
|
-
ensure
|
|
193
|
-
self.identity_map = nil
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
private
|
|
198
|
-
|
|
199
|
-
# Set the thread local identity map to the given value.
|
|
200
|
-
def identity_map=(v)
|
|
201
|
-
Thread.current[:sequel_identity_map] = v
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# Check the current identity map if it exists for the object with
|
|
205
|
-
# the matching pk. If one is found, return it, otherwise call super.
|
|
206
|
-
def primary_key_lookup(pk)
|
|
207
|
-
((idm = identity_map) && (k = identity_map_key(pk)) && (o = idm[k])) ? o : super
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
module InstanceMethods
|
|
212
|
-
# Remove instances from the identity map cache if they are deleted.
|
|
213
|
-
def delete
|
|
214
|
-
super
|
|
215
|
-
if (idm = model.identity_map) && (k = model.identity_map_key(pk))
|
|
216
|
-
idm.delete(k)
|
|
217
|
-
end
|
|
218
|
-
self
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
# Merge the current values into the values provided in the row, ensuring
|
|
222
|
-
# that current values are not overridden by new values.
|
|
223
|
-
def merge_db_update(row)
|
|
224
|
-
@values = row.merge(@values)
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
private
|
|
228
|
-
|
|
229
|
-
# The primary keys values of the associated object, given the foreign
|
|
230
|
-
# key columns(s).
|
|
231
|
-
def _associated_object_pk(fk)
|
|
232
|
-
fk.is_a?(Array) ? fk.map{|c| send(c)} : send(fk)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# If the association is a many_to_one and it has a :key option and the
|
|
236
|
-
# key option has a value and the association uses the primary key of
|
|
237
|
-
# the associated class as the :primary_key option, check the identity
|
|
238
|
-
# map for the associated object and return it if present.
|
|
239
|
-
def _load_associated_object(opts, dynamic_opts)
|
|
240
|
-
klass = opts.associated_class
|
|
241
|
-
cache_lookup = opts.fetch(:idm_cache_lookup) do
|
|
242
|
-
opts[:idm_cache_lookup] = klass.respond_to?(:identity_map) &&
|
|
243
|
-
opts[:type] == :many_to_one &&
|
|
244
|
-
opts[:key] &&
|
|
245
|
-
opts.primary_key == klass.primary_key
|
|
246
|
-
end
|
|
247
|
-
if cache_lookup &&
|
|
248
|
-
!dynamic_opts[:callback] &&
|
|
249
|
-
(idm = klass.identity_map) &&
|
|
250
|
-
(k = klass.identity_map_key(_associated_object_pk(opts[:key]))) &&
|
|
251
|
-
(o = idm[k])
|
|
252
|
-
o
|
|
253
|
-
else
|
|
254
|
-
super
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
end
|
data/lib/sequel_core.rb
DELETED
data/lib/sequel_model.rb
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
-
|
|
3
|
-
describe "AssociationAutoreloading plugin" do
|
|
4
|
-
before do
|
|
5
|
-
@c = Class.new(Sequel::Model)
|
|
6
|
-
@c.plugin :association_autoreloading
|
|
7
|
-
@Artist = Class.new(@c).set_dataset(:artists)
|
|
8
|
-
@Artist.dataset._fetch = {:id=>2, :name=>'Ar'}
|
|
9
|
-
@Album = Class.new(@c).set_dataset(:albums)
|
|
10
|
-
@Artist.columns :id, :name
|
|
11
|
-
@Album.columns :id, :name, :artist_id
|
|
12
|
-
@Album.db_schema[:artist_id][:type] = :integer
|
|
13
|
-
@Album.many_to_one :artist, :class=>@Artist
|
|
14
|
-
MODEL_DB.reset
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
specify "should reload many_to_one association when foreign key is modified" do
|
|
18
|
-
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
19
|
-
album.artist
|
|
20
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
|
|
21
|
-
|
|
22
|
-
album.artist_id = 1
|
|
23
|
-
album.artist
|
|
24
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
specify "should handle multiple many_to_one association with the same foreign key" do
|
|
28
|
-
@Album.many_to_one :artist2, :key=>:artist_id, :class=>@Artist
|
|
29
|
-
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
30
|
-
album.artist
|
|
31
|
-
album.artist2
|
|
32
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1'] * 2
|
|
33
|
-
|
|
34
|
-
album.artist
|
|
35
|
-
album.artist2
|
|
36
|
-
MODEL_DB.sqls.should == []
|
|
37
|
-
|
|
38
|
-
album.artist_id = 1
|
|
39
|
-
album.artist
|
|
40
|
-
album.artist2
|
|
41
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1'] * 2
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
specify "should not reload when value has not changed" do
|
|
45
|
-
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
46
|
-
album.artist
|
|
47
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
|
|
48
|
-
|
|
49
|
-
album.artist_id = 2
|
|
50
|
-
album.artist
|
|
51
|
-
MODEL_DB.sqls.should == []
|
|
52
|
-
|
|
53
|
-
album.artist_id = "2"
|
|
54
|
-
album.artist
|
|
55
|
-
MODEL_DB.sqls.should == []
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
specify "should reload all associations which use the foreign key" do
|
|
59
|
-
@Album.many_to_one :other_artist, :key => :artist_id, :foreign_key => :id, :class => @Artist
|
|
60
|
-
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
61
|
-
album.artist
|
|
62
|
-
album.other_artist
|
|
63
|
-
MODEL_DB.reset
|
|
64
|
-
|
|
65
|
-
album.artist_id = 1
|
|
66
|
-
album.artist
|
|
67
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
|
|
68
|
-
|
|
69
|
-
album.other_artist
|
|
70
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
specify "should work with composite keys" do
|
|
74
|
-
@Album.many_to_one :composite_artist, :key => [:artist_id, :name], :primary_key => [:id, :name], :class => @Artist
|
|
75
|
-
album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
76
|
-
album.composite_artist
|
|
77
|
-
MODEL_DB.reset
|
|
78
|
-
|
|
79
|
-
album.artist_id = 1
|
|
80
|
-
album.composite_artist
|
|
81
|
-
MODEL_DB.sqls.should == ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al')) LIMIT 1"]
|
|
82
|
-
|
|
83
|
-
album.name = 'Al2'
|
|
84
|
-
album.composite_artist
|
|
85
|
-
MODEL_DB.sqls.should == ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al2')) LIMIT 1"]
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
specify "should work with subclasses" do
|
|
89
|
-
salbum = Class.new(@Album)
|
|
90
|
-
oartist = Class.new(@c).set_dataset(:oartist)
|
|
91
|
-
oartist.columns :id, :name
|
|
92
|
-
salbum.many_to_one :artist2, :class=>oartist, :key=>:artist_id
|
|
93
|
-
album = salbum.load(:id => 1, :name=>'Al', :artist_id=>2)
|
|
94
|
-
album.artist
|
|
95
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
|
|
96
|
-
|
|
97
|
-
album.artist_id = 1
|
|
98
|
-
album.artist
|
|
99
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
end
|
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
-
|
|
3
|
-
describe "Sequel::Plugins::IdentityMap" do
|
|
4
|
-
before do
|
|
5
|
-
class ::IdentityMapModel < Sequel::Model
|
|
6
|
-
plugin :identity_map
|
|
7
|
-
attr_accessor :foo
|
|
8
|
-
dataset._fetch = proc do |sql|
|
|
9
|
-
sql =~ /WHERE \(?(\w+\.)?(\w+) = (\d)\)?/
|
|
10
|
-
{$2.to_sym=>$3.to_i}
|
|
11
|
-
end
|
|
12
|
-
def self.waw_identity_map(&block) # with and without
|
|
13
|
-
with_identity_map(&block)
|
|
14
|
-
db.reset
|
|
15
|
-
yield
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
class ::IdentityMapAlbum < ::IdentityMapModel
|
|
19
|
-
columns :artist_id
|
|
20
|
-
end
|
|
21
|
-
class ::IdentityMapArtist < ::IdentityMapModel
|
|
22
|
-
end
|
|
23
|
-
@c = ::IdentityMapModel
|
|
24
|
-
@c1 = ::IdentityMapAlbum
|
|
25
|
-
@c2 = ::IdentityMapArtist
|
|
26
|
-
MODEL_DB.reset
|
|
27
|
-
end
|
|
28
|
-
after do
|
|
29
|
-
Object.send(:remove_const, :IdentityMapAlbum)
|
|
30
|
-
Object.send(:remove_const, :IdentityMapArtist)
|
|
31
|
-
Object.send(:remove_const, :IdentityMapModel)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
it "#identity_map should return a hash if an identity map is currently being used" do
|
|
35
|
-
@c.with_identity_map{@c.identity_map.should == {}}
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it "#identity_map should return nil if an identity map is not currently being used" do
|
|
39
|
-
@c.identity_map.should == nil
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "#identity_map_key should be the same for the same class and pk" do
|
|
43
|
-
@c.identity_map_key(1).should == @c.identity_map_key(1)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it "#identity_map_key should be different for a different class" do
|
|
47
|
-
@c1.identity_map_key(1).should_not == @c2.identity_map_key(1)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "#identity_map_key should be different for different anonymous classes" do
|
|
51
|
-
Class.new(@c).identity_map_key(1).should_not == Class.new(@c).identity_map_key(1)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it "#identity_map_key should be different for a different pk" do
|
|
55
|
-
@c.identity_map_key(1).should_not == @c.identity_map_key(2)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "#identity_map_key should be nil for an empty pk values" do
|
|
59
|
-
@c.identity_map_key(nil).should == nil
|
|
60
|
-
@c.identity_map_key([]).should == nil
|
|
61
|
-
@c.identity_map_key([nil]).should == nil
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it "#load should work even if model doesn't have a primary key" do
|
|
65
|
-
c = Class.new(@c)
|
|
66
|
-
c.no_primary_key
|
|
67
|
-
proc{c.with_identity_map{c.load({})}}.should_not raise_error
|
|
68
|
-
c.with_identity_map{c.load({}).should_not equal(c.load({}))}
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it "#load should return an object if there is no current identity map" do
|
|
72
|
-
o = @c.load(:id=>1)
|
|
73
|
-
o.should be_a_kind_of(@c)
|
|
74
|
-
o.values.should == {:id=>1}
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
it "#load should return an object if there is a current identity map" do
|
|
78
|
-
@c.with_identity_map do
|
|
79
|
-
o = @c.load(:id=>1)
|
|
80
|
-
o.should be_a_kind_of(@c)
|
|
81
|
-
o.values.should == {:id=>1}
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
it "#load should store the object in the current identity map if it isn't already there" do
|
|
86
|
-
@c.with_identity_map do
|
|
87
|
-
@c.identity_map[@c.identity_map_key(1)].should == nil
|
|
88
|
-
o = @c.load(:id=>1)
|
|
89
|
-
@c.identity_map[@c.identity_map_key(1)].should == o
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
it "#load should update the record in the current identity map if new fields if it is already there" do
|
|
94
|
-
@c.with_identity_map do
|
|
95
|
-
o = @c.load(:id=>1, :a=>2)
|
|
96
|
-
o.values.should == {:id=>1, :a=>2}
|
|
97
|
-
o = @c.load(:id=>1, :b=>3)
|
|
98
|
-
o.values.should == {:id=>1, :a=>2, :b=>3}
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it "#load should not update existing fields in the record if the record is in the current identity map" do
|
|
103
|
-
@c.with_identity_map do
|
|
104
|
-
o = @c.load(:id=>1, :a=>2)
|
|
105
|
-
o.values.should == {:id=>1, :a=>2}
|
|
106
|
-
o = @c.load(:id=>1, :a=>4)
|
|
107
|
-
o.values.should == {:id=>1, :a=>2}
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it "should use the identity map as a lookup cache in Model.[] to save on database queries" do
|
|
112
|
-
@c.with_identity_map do
|
|
113
|
-
MODEL_DB.sqls.length.should == 0
|
|
114
|
-
o = @c[1]
|
|
115
|
-
MODEL_DB.sqls.length.should == 1
|
|
116
|
-
o.foo = 1
|
|
117
|
-
@c[1].foo.should == o.foo
|
|
118
|
-
MODEL_DB.sqls.length.should == 0
|
|
119
|
-
@c[2].foo.should_not == o.foo
|
|
120
|
-
MODEL_DB.sqls.length.should == 1
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it "should remove instances from the identity map if they are deleted or destroyed" do
|
|
125
|
-
@c.with_identity_map do
|
|
126
|
-
MODEL_DB.sqls.length.should == 0
|
|
127
|
-
o = @c[1]
|
|
128
|
-
MODEL_DB.sqls.length.should == 1
|
|
129
|
-
o.foo = 1
|
|
130
|
-
@c[1].should == o
|
|
131
|
-
MODEL_DB.sqls.length.should == 0
|
|
132
|
-
o.destroy
|
|
133
|
-
MODEL_DB.sqls.length.should == 1
|
|
134
|
-
@c[1].foo.should_not == o.foo
|
|
135
|
-
MODEL_DB.sqls.length.should == 1
|
|
136
|
-
|
|
137
|
-
MODEL_DB.reset
|
|
138
|
-
o = @c[2]
|
|
139
|
-
MODEL_DB.sqls.length.should == 1
|
|
140
|
-
o.foo = 1
|
|
141
|
-
@c[2].should == o
|
|
142
|
-
MODEL_DB.sqls.length.should == 0
|
|
143
|
-
o.delete
|
|
144
|
-
MODEL_DB.sqls.length.should == 1
|
|
145
|
-
@c[2].foo.should_not == o.foo
|
|
146
|
-
MODEL_DB.sqls.length.should == 1
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
it "should use the identity map as a lookup cache when retrieving many_to_one associated records via a composite key" do
|
|
151
|
-
@c1.columns :another_id
|
|
152
|
-
@c1.many_to_one :artist, :class=>@c2, :key=>[:id, :another_id]
|
|
153
|
-
@c.with_identity_map do
|
|
154
|
-
MODEL_DB.sqls.length.should == 0
|
|
155
|
-
o = @c1.load(:id=>1, :another_id=>1, :artist_id=>2)
|
|
156
|
-
a = o.artist
|
|
157
|
-
a.should be_a_kind_of(@c2)
|
|
158
|
-
MODEL_DB.sqls.length.should == 1
|
|
159
|
-
o = @c1.load(:id=>1, :another_id=>2, :artist_id=>2)
|
|
160
|
-
o.artist.should == a
|
|
161
|
-
MODEL_DB.sqls.length.should == 0
|
|
162
|
-
o = @c1.load(:id=>3, :another_id=>3, :artist_id=>3)
|
|
163
|
-
o.artist.should_not == a
|
|
164
|
-
MODEL_DB.sqls.length.should == 1
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
it "should use the identity map as a lookup cache when retrieving many_to_one associated records" do
|
|
169
|
-
@c1.many_to_one :artist, :class=>@c2
|
|
170
|
-
@c.with_identity_map do
|
|
171
|
-
MODEL_DB.sqls.length.should == 0
|
|
172
|
-
o = @c1.load(:id=>1, :artist_id=>2)
|
|
173
|
-
a = o.artist
|
|
174
|
-
a.should be_a_kind_of(@c2)
|
|
175
|
-
MODEL_DB.sqls.length.should == 1
|
|
176
|
-
o = @c1.load(:id=>2, :artist_id=>2)
|
|
177
|
-
o.artist.should == a
|
|
178
|
-
MODEL_DB.sqls.length.should == 0
|
|
179
|
-
o = @c1.load(:id=>3, :artist_id=>3)
|
|
180
|
-
o.artist.should_not == a
|
|
181
|
-
MODEL_DB.sqls.length.should == 1
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
it "should not use the identity map as a lookup cache for a one_to_one association" do
|
|
186
|
-
@c2.one_to_one :artist, :class=>@c1, :key=>:artist_id
|
|
187
|
-
@c.with_identity_map do
|
|
188
|
-
MODEL_DB.sqls.length.should == 0
|
|
189
|
-
o = @c2.load(:id=>2)
|
|
190
|
-
a = o.artist
|
|
191
|
-
a.should be_a_kind_of(@c1)
|
|
192
|
-
MODEL_DB.sqls.length.should == 1
|
|
193
|
-
o.reload
|
|
194
|
-
MODEL_DB.sqls.length.should == 1
|
|
195
|
-
o.artist.should == a
|
|
196
|
-
MODEL_DB.sqls.length.should == 1
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
it "should not use the identity map as a lookup cache if the assocation has a nil :key option" do
|
|
201
|
-
c = @c2
|
|
202
|
-
@c1.many_to_one :artist, :class=>@c2, :key=>nil, :dataset=>proc{c.filter(:artist_id=>artist_id)}
|
|
203
|
-
@c.with_identity_map do
|
|
204
|
-
MODEL_DB.sqls.length.should == 0
|
|
205
|
-
o = @c1.load(:id=>1, :artist_id=>2)
|
|
206
|
-
a = o.artist
|
|
207
|
-
a.should be_a_kind_of(@c2)
|
|
208
|
-
MODEL_DB.sqls.length.should == 1
|
|
209
|
-
o = @c1.load(:id=>2, :artist_id=>2)
|
|
210
|
-
o.artist.should == a
|
|
211
|
-
MODEL_DB.sqls.length.should == 1
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
it "should not use the identity map as a lookup cache if the assocation's :primary_key option doesn't match the primary key of the associated class" do
|
|
216
|
-
@c1.many_to_one :artist, :class=>@c2, :primary_key=>:artist_id
|
|
217
|
-
@c.with_identity_map do
|
|
218
|
-
MODEL_DB.sqls.length.should == 0
|
|
219
|
-
o = @c1.load(:id=>1, :artist_id=>2)
|
|
220
|
-
a = o.artist
|
|
221
|
-
a.should be_a_kind_of(@c2)
|
|
222
|
-
MODEL_DB.sqls.length.should == 1
|
|
223
|
-
o = @c1.load(:id=>2, :artist_id=>2)
|
|
224
|
-
o.artist.should == a
|
|
225
|
-
MODEL_DB.sqls.length.should == 1
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
it "should not use the identity map as a lookup cache if a dynamic callback is used" do
|
|
230
|
-
@c1.many_to_one :artist, :class=>@c2
|
|
231
|
-
@c.with_identity_map do
|
|
232
|
-
MODEL_DB.sqls.length.should == 0
|
|
233
|
-
o = @c1.load(:id=>1, :artist_id=>2)
|
|
234
|
-
a = o.artist
|
|
235
|
-
a.should be_a_kind_of(@c2)
|
|
236
|
-
MODEL_DB.sqls.length.should == 1
|
|
237
|
-
o = @c1.load(:id=>2, :artist_id=>2)
|
|
238
|
-
o.artist.should == a
|
|
239
|
-
MODEL_DB.sqls.length.should == 0
|
|
240
|
-
o = @c1.load(:id=>3, :artist_id=>3)
|
|
241
|
-
o.artist.should_not == a
|
|
242
|
-
MODEL_DB.sqls.length.should == 1
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
it "should not override custom :eager_loaders for many_to_many associations" do
|
|
247
|
-
@c1.columns :id
|
|
248
|
-
@c2.columns :id
|
|
249
|
-
c = @c2
|
|
250
|
-
@c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa, :eager_loader=>(proc do |eo|
|
|
251
|
-
eo[:rows].each{|object| object.associations[:artists] = [c.load(:id=>object.id)]}
|
|
252
|
-
end)
|
|
253
|
-
@c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
|
|
254
|
-
|
|
255
|
-
@c.waw_identity_map do
|
|
256
|
-
MODEL_DB.sqls.length.should == 0
|
|
257
|
-
a = @c1.eager(:artists).all
|
|
258
|
-
MODEL_DB.sqls.length.should == 1
|
|
259
|
-
a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
|
|
260
|
-
a.map{|x| x.artists}.should == [[@c2.load(:id=>1)], [@c2.load(:id=>2)], [@c2.load(:id=>3)]]
|
|
261
|
-
MODEL_DB.sqls.length.should == 0
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
it "should work correctly when eagerly loading many_to_many associations" do
|
|
266
|
-
@c1.columns :id
|
|
267
|
-
@c2.columns :id
|
|
268
|
-
@c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa
|
|
269
|
-
@c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
|
|
270
|
-
@c2.dataset._fetch = [{:id=>1, :x_foreign_key_x=>1}, {:id=>1, :x_foreign_key_x=>2}, {:id=>2, :x_foreign_key_x=>1}, {:id=>2, :x_foreign_key_x=>2}, {:id=>3, :x_foreign_key_x=>1}, {:id=>3, :x_foreign_key_x=>1}]
|
|
271
|
-
|
|
272
|
-
@c.waw_identity_map do
|
|
273
|
-
MODEL_DB.sqls.length.should == 0
|
|
274
|
-
a = @c1.eager(:artists).all
|
|
275
|
-
MODEL_DB.sqls.length.should == 2
|
|
276
|
-
a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
|
|
277
|
-
a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
|
|
278
|
-
MODEL_DB.sqls.length.should == 0
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
it "should work correctly when eagerly loading many_to_many associations with composite keys" do
|
|
283
|
-
@c1.columns :id, :id2
|
|
284
|
-
@c2.columns :id, :id2
|
|
285
|
-
@c1.set_primary_key [:id, :id2]
|
|
286
|
-
@c2.set_primary_key [:id, :id2]
|
|
287
|
-
@c1.many_to_many :artists, :class=>@c2, :left_key=>[:album_id1, :album_id2], :right_key=>[:artist_id1, :artist_id2], :join_table=>:aa
|
|
288
|
-
@c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
|
|
289
|
-
@c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
|
|
290
|
-
|
|
291
|
-
@c.waw_identity_map do
|
|
292
|
-
MODEL_DB.sqls.length.should == 0
|
|
293
|
-
a = @c1.eager(:artists).all
|
|
294
|
-
MODEL_DB.sqls.length.should == 2
|
|
295
|
-
a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
|
|
296
|
-
a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
|
|
297
|
-
MODEL_DB.sqls.length.should == 0
|
|
298
|
-
end
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
it "should work correctly when eagerly loading many_through_many associations" do
|
|
302
|
-
@c1.columns :id
|
|
303
|
-
@c2.columns :id
|
|
304
|
-
@c1.plugin :many_through_many
|
|
305
|
-
@c1.many_through_many :artists, [[:aa, :album_id, :artist_id]], :class=>@c2
|
|
306
|
-
@c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
|
|
307
|
-
@c2.dataset._fetch = [{:id=>1, :x_foreign_key_x=>1}, {:id=>1, :x_foreign_key_x=>2}, {:id=>2, :x_foreign_key_x=>1}, {:id=>2, :x_foreign_key_x=>2}, {:id=>3, :x_foreign_key_x=>1}, {:id=>3, :x_foreign_key_x=>1}]
|
|
308
|
-
|
|
309
|
-
@c.waw_identity_map do
|
|
310
|
-
MODEL_DB.sqls.length.should == 0
|
|
311
|
-
a = @c1.eager(:artists).all
|
|
312
|
-
MODEL_DB.sqls.length.should == 2
|
|
313
|
-
a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
|
|
314
|
-
a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
|
|
315
|
-
MODEL_DB.sqls.length.should == 0
|
|
316
|
-
end
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
it "should work correctly when eagerly loading many_to_many associations with composite keys" do
|
|
320
|
-
@c1.columns :id, :id2
|
|
321
|
-
@c2.columns :id
|
|
322
|
-
@c1.set_primary_key [:id, :id2]
|
|
323
|
-
@c1.plugin :many_through_many
|
|
324
|
-
@c1.many_through_many :artists, [[:aa, [:album_id1, :album_id2], :artist_id]], :class=>@c2
|
|
325
|
-
@c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
|
|
326
|
-
@c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
|
|
327
|
-
|
|
328
|
-
@c.waw_identity_map do
|
|
329
|
-
MODEL_DB.sqls.length.should == 0
|
|
330
|
-
a = @c1.eager(:artists).all
|
|
331
|
-
MODEL_DB.sqls.length.should == 2
|
|
332
|
-
a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
|
|
333
|
-
a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
|
|
334
|
-
MODEL_DB.sqls.length.should == 0
|
|
335
|
-
end
|
|
336
|
-
end
|
|
337
|
-
end
|