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.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +114 -0
  3. data/Rakefile +10 -7
  4. data/doc/association_basics.rdoc +25 -23
  5. data/doc/code_order.rdoc +7 -0
  6. data/doc/core_extensions.rdoc +0 -10
  7. data/doc/object_model.rdoc +4 -1
  8. data/doc/querying.rdoc +3 -3
  9. data/doc/release_notes/4.0.0.txt +262 -0
  10. data/doc/security.rdoc +0 -28
  11. data/doc/testing.rdoc +8 -14
  12. data/lib/sequel/adapters/ado.rb +7 -11
  13. data/lib/sequel/adapters/ado/access.rb +8 -8
  14. data/lib/sequel/adapters/ado/mssql.rb +4 -4
  15. data/lib/sequel/adapters/amalgalite.rb +6 -6
  16. data/lib/sequel/adapters/cubrid.rb +7 -7
  17. data/lib/sequel/adapters/db2.rb +5 -9
  18. data/lib/sequel/adapters/dbi.rb +2 -6
  19. data/lib/sequel/adapters/do.rb +4 -4
  20. data/lib/sequel/adapters/firebird.rb +4 -4
  21. data/lib/sequel/adapters/ibmdb.rb +8 -8
  22. data/lib/sequel/adapters/informix.rb +2 -10
  23. data/lib/sequel/adapters/jdbc.rb +17 -17
  24. data/lib/sequel/adapters/jdbc/as400.rb +2 -2
  25. data/lib/sequel/adapters/jdbc/cubrid.rb +1 -1
  26. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  27. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  29. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  30. data/lib/sequel/adapters/jdbc/informix.rb +1 -1
  31. data/lib/sequel/adapters/jdbc/mssql.rb +2 -2
  32. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  33. data/lib/sequel/adapters/jdbc/oracle.rb +5 -1
  34. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -3
  35. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  36. data/lib/sequel/adapters/jdbc/transactions.rb +3 -3
  37. data/lib/sequel/adapters/mock.rb +7 -7
  38. data/lib/sequel/adapters/mysql.rb +3 -3
  39. data/lib/sequel/adapters/mysql2.rb +4 -4
  40. data/lib/sequel/adapters/odbc.rb +2 -6
  41. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  42. data/lib/sequel/adapters/openbase.rb +1 -5
  43. data/lib/sequel/adapters/oracle.rb +13 -17
  44. data/lib/sequel/adapters/postgres.rb +20 -25
  45. data/lib/sequel/adapters/shared/cubrid.rb +3 -3
  46. data/lib/sequel/adapters/shared/db2.rb +2 -2
  47. data/lib/sequel/adapters/shared/firebird.rb +7 -7
  48. data/lib/sequel/adapters/shared/mssql.rb +9 -9
  49. data/lib/sequel/adapters/shared/mysql.rb +29 -13
  50. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +7 -7
  51. data/lib/sequel/adapters/shared/oracle.rb +22 -13
  52. data/lib/sequel/adapters/shared/postgres.rb +61 -46
  53. data/lib/sequel/adapters/shared/sqlite.rb +9 -9
  54. data/lib/sequel/adapters/sqlite.rb +17 -11
  55. data/lib/sequel/adapters/swift.rb +3 -3
  56. data/lib/sequel/adapters/swift/mysql.rb +1 -1
  57. data/lib/sequel/adapters/swift/sqlite.rb +1 -1
  58. data/lib/sequel/adapters/tinytds.rb +8 -8
  59. data/lib/sequel/ast_transformer.rb +3 -1
  60. data/lib/sequel/connection_pool.rb +4 -2
  61. data/lib/sequel/connection_pool/sharded_single.rb +2 -2
  62. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -5
  63. data/lib/sequel/connection_pool/threaded.rb +7 -7
  64. data/lib/sequel/core.rb +4 -67
  65. data/lib/sequel/database.rb +1 -0
  66. data/lib/sequel/database/connecting.rb +2 -8
  67. data/lib/sequel/database/dataset.rb +2 -7
  68. data/lib/sequel/database/dataset_defaults.rb +0 -18
  69. data/lib/sequel/database/features.rb +4 -4
  70. data/lib/sequel/database/misc.rb +6 -8
  71. data/lib/sequel/database/query.rb +5 -61
  72. data/lib/sequel/database/schema_generator.rb +22 -20
  73. data/lib/sequel/database/schema_methods.rb +48 -20
  74. data/lib/sequel/database/transactions.rb +7 -17
  75. data/lib/sequel/dataset.rb +2 -0
  76. data/lib/sequel/dataset/actions.rb +23 -91
  77. data/lib/sequel/dataset/features.rb +1 -4
  78. data/lib/sequel/dataset/graph.rb +3 -47
  79. data/lib/sequel/dataset/misc.rb +4 -33
  80. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  81. data/lib/sequel/dataset/query.rb +116 -240
  82. data/lib/sequel/dataset/sql.rb +19 -97
  83. data/lib/sequel/deprecated.rb +0 -16
  84. data/lib/sequel/exceptions.rb +0 -3
  85. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  86. data/lib/sequel/extensions/columns_introspection.rb +1 -12
  87. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  88. data/lib/sequel/extensions/core_extensions.rb +0 -9
  89. data/lib/sequel/extensions/date_arithmetic.rb +1 -2
  90. data/lib/sequel/extensions/graph_each.rb +11 -0
  91. data/lib/sequel/extensions/migration.rb +5 -5
  92. data/lib/sequel/extensions/null_dataset.rb +11 -13
  93. data/lib/sequel/extensions/pagination.rb +3 -6
  94. data/lib/sequel/extensions/pg_array.rb +6 -4
  95. data/lib/sequel/extensions/pg_array_ops.rb +35 -1
  96. data/lib/sequel/extensions/pg_json.rb +12 -2
  97. data/lib/sequel/extensions/pg_json_ops.rb +266 -0
  98. data/lib/sequel/extensions/pg_range.rb +2 -2
  99. data/lib/sequel/extensions/pg_range_ops.rb +0 -8
  100. data/lib/sequel/extensions/pg_row.rb +2 -2
  101. data/lib/sequel/extensions/pretty_table.rb +0 -4
  102. data/lib/sequel/extensions/query.rb +3 -8
  103. data/lib/sequel/extensions/schema_caching.rb +0 -7
  104. data/lib/sequel/extensions/schema_dumper.rb +10 -17
  105. data/lib/sequel/extensions/select_remove.rb +0 -4
  106. data/lib/sequel/extensions/set_overrides.rb +28 -0
  107. data/lib/sequel/extensions/to_dot.rb +6 -10
  108. data/lib/sequel/model.rb +6 -7
  109. data/lib/sequel/model/associations.rb +127 -182
  110. data/lib/sequel/model/base.rb +88 -211
  111. data/lib/sequel/model/errors.rb +0 -13
  112. data/lib/sequel/model/plugins.rb +2 -2
  113. data/lib/sequel/no_core_ext.rb +0 -1
  114. data/lib/sequel/plugins/after_initialize.rb +11 -17
  115. data/lib/sequel/plugins/association_autoreloading.rb +1 -47
  116. data/lib/sequel/plugins/association_dependencies.rb +2 -2
  117. data/lib/sequel/plugins/auto_validations.rb +2 -8
  118. data/lib/sequel/plugins/blacklist_security.rb +32 -2
  119. data/lib/sequel/plugins/caching.rb +1 -1
  120. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  121. data/lib/sequel/plugins/composition.rb +10 -8
  122. data/lib/sequel/plugins/constraint_validations.rb +2 -2
  123. data/lib/sequel/plugins/dataset_associations.rb +4 -0
  124. data/lib/sequel/plugins/defaults_setter.rb +8 -6
  125. data/lib/sequel/plugins/dirty.rb +6 -6
  126. data/lib/sequel/plugins/force_encoding.rb +13 -8
  127. data/lib/sequel/plugins/hook_class_methods.rb +1 -7
  128. data/lib/sequel/plugins/json_serializer.rb +13 -74
  129. data/lib/sequel/plugins/lazy_attributes.rb +2 -4
  130. data/lib/sequel/plugins/list.rb +1 -1
  131. data/lib/sequel/plugins/many_through_many.rb +4 -11
  132. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +1 -49
  133. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  134. data/lib/sequel/plugins/optimistic_locking.rb +3 -5
  135. data/lib/sequel/plugins/pg_array_associations.rb +453 -0
  136. data/lib/sequel/plugins/pg_typecast_on_load.rb +23 -7
  137. data/lib/sequel/plugins/prepared_statements.rb +1 -1
  138. data/lib/sequel/plugins/prepared_statements_associations.rb +20 -14
  139. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -2
  140. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  141. data/lib/sequel/plugins/serialization.rb +5 -4
  142. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  143. data/lib/sequel/plugins/sharding.rb +7 -1
  144. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  145. data/lib/sequel/plugins/timestamps.rb +1 -1
  146. data/lib/sequel/plugins/touch.rb +2 -2
  147. data/lib/sequel/plugins/tree.rb +1 -1
  148. data/lib/sequel/plugins/typecast_on_load.rb +19 -4
  149. data/lib/sequel/plugins/validation_class_methods.rb +0 -30
  150. data/lib/sequel/plugins/validation_helpers.rb +13 -31
  151. data/lib/sequel/plugins/xml_serializer.rb +18 -57
  152. data/lib/sequel/sql.rb +20 -22
  153. data/lib/sequel/version.rb +2 -2
  154. data/spec/adapters/db2_spec.rb +14 -23
  155. data/spec/adapters/firebird_spec.rb +25 -29
  156. data/spec/adapters/informix_spec.rb +11 -14
  157. data/spec/adapters/mssql_spec.rb +71 -77
  158. data/spec/adapters/mysql_spec.rb +165 -172
  159. data/spec/adapters/oracle_spec.rb +36 -39
  160. data/spec/adapters/postgres_spec.rb +175 -100
  161. data/spec/adapters/spec_helper.rb +13 -11
  162. data/spec/adapters/sqlite_spec.rb +36 -44
  163. data/spec/core/connection_pool_spec.rb +2 -1
  164. data/spec/core/database_spec.rb +55 -55
  165. data/spec/core/dataset_spec.rb +45 -249
  166. data/spec/core/deprecated_spec.rb +0 -8
  167. data/spec/core/expression_filters_spec.rb +23 -5
  168. data/spec/core/object_graph_spec.rb +4 -66
  169. data/spec/core/schema_spec.rb +35 -12
  170. data/spec/core/spec_helper.rb +3 -2
  171. data/spec/core_extensions_spec.rb +17 -19
  172. data/spec/extensions/arbitrary_servers_spec.rb +2 -3
  173. data/spec/extensions/association_dependencies_spec.rb +14 -14
  174. data/spec/extensions/auto_validations_spec.rb +7 -0
  175. data/spec/extensions/blacklist_security_spec.rb +5 -5
  176. data/spec/extensions/blank_spec.rb +2 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  178. data/spec/extensions/columns_introspection_spec.rb +2 -29
  179. data/spec/extensions/composition_spec.rb +10 -17
  180. data/spec/extensions/core_refinements_spec.rb +5 -1
  181. data/spec/extensions/dataset_associations_spec.rb +18 -0
  182. data/spec/extensions/date_arithmetic_spec.rb +2 -2
  183. data/spec/extensions/defaults_setter_spec.rb +9 -9
  184. data/spec/extensions/dirty_spec.rb +0 -5
  185. data/spec/extensions/eval_inspect_spec.rb +2 -0
  186. data/spec/extensions/force_encoding_spec.rb +2 -18
  187. data/spec/extensions/hash_aliases_spec.rb +8 -0
  188. data/spec/extensions/hook_class_methods_spec.rb +39 -58
  189. data/spec/extensions/inflector_spec.rb +2 -0
  190. data/spec/extensions/instance_filters_spec.rb +8 -8
  191. data/spec/extensions/json_serializer_spec.rb +1 -41
  192. data/spec/extensions/list_spec.rb +1 -1
  193. data/spec/extensions/many_through_many_spec.rb +106 -109
  194. data/spec/extensions/migration_spec.rb +2 -0
  195. data/spec/extensions/named_timezones_spec.rb +1 -0
  196. data/spec/extensions/pg_array_associations_spec.rb +603 -0
  197. data/spec/extensions/pg_array_ops_spec.rb +25 -0
  198. data/spec/extensions/pg_array_spec.rb +9 -1
  199. data/spec/extensions/pg_hstore_ops_spec.rb +13 -0
  200. data/spec/extensions/pg_hstore_spec.rb +1 -0
  201. data/spec/extensions/pg_json_ops_spec.rb +131 -0
  202. data/spec/extensions/pg_json_spec.rb +10 -4
  203. data/spec/extensions/pg_range_ops_spec.rb +2 -5
  204. data/spec/extensions/pg_range_spec.rb +6 -2
  205. data/spec/extensions/pg_row_ops_spec.rb +2 -0
  206. data/spec/extensions/prepared_statements_associations_spec.rb +26 -5
  207. data/spec/extensions/rcte_tree_spec.rb +15 -15
  208. data/spec/extensions/schema_dumper_spec.rb +0 -1
  209. data/spec/extensions/schema_spec.rb +9 -9
  210. data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
  211. data/spec/extensions/serialization_spec.rb +18 -29
  212. data/spec/extensions/set_overrides_spec.rb +4 -0
  213. data/spec/extensions/{many_to_one_pk_lookup_spec.rb → shared_caching_spec.rb} +1 -4
  214. data/spec/extensions/single_table_inheritance_spec.rb +4 -4
  215. data/spec/extensions/spec_helper.rb +8 -9
  216. data/spec/extensions/sql_expr_spec.rb +2 -0
  217. data/spec/extensions/string_date_time_spec.rb +2 -0
  218. data/spec/extensions/string_stripper_spec.rb +2 -0
  219. data/spec/extensions/tactical_eager_loading_spec.rb +12 -12
  220. data/spec/extensions/thread_local_timezones_spec.rb +2 -0
  221. data/spec/extensions/timestamps_spec.rb +1 -1
  222. data/spec/extensions/to_dot_spec.rb +1 -1
  223. data/spec/extensions/touch_spec.rb +24 -24
  224. data/spec/extensions/tree_spec.rb +7 -7
  225. data/spec/extensions/typecast_on_load_spec.rb +8 -1
  226. data/spec/extensions/update_primary_key_spec.rb +10 -10
  227. data/spec/extensions/validation_class_methods_spec.rb +10 -39
  228. data/spec/extensions/validation_helpers_spec.rb +29 -47
  229. data/spec/extensions/xml_serializer_spec.rb +1 -23
  230. data/spec/integration/associations_test.rb +231 -40
  231. data/spec/integration/database_test.rb +1 -1
  232. data/spec/integration/dataset_test.rb +64 -64
  233. data/spec/integration/eager_loader_test.rb +28 -28
  234. data/spec/integration/migrator_test.rb +1 -1
  235. data/spec/integration/model_test.rb +2 -2
  236. data/spec/integration/plugin_test.rb +21 -21
  237. data/spec/integration/prepared_statement_test.rb +7 -7
  238. data/spec/integration/schema_test.rb +115 -110
  239. data/spec/integration/spec_helper.rb +17 -27
  240. data/spec/integration/timezone_test.rb +1 -1
  241. data/spec/integration/transaction_test.rb +10 -10
  242. data/spec/integration/type_test.rb +2 -2
  243. data/spec/model/association_reflection_spec.rb +2 -28
  244. data/spec/model/associations_spec.rb +239 -188
  245. data/spec/model/base_spec.rb +27 -68
  246. data/spec/model/dataset_methods_spec.rb +4 -4
  247. data/spec/model/eager_loading_spec.rb +160 -172
  248. data/spec/model/hooks_spec.rb +62 -79
  249. data/spec/model/model_spec.rb +36 -51
  250. data/spec/model/plugins_spec.rb +5 -19
  251. data/spec/model/record_spec.rb +125 -151
  252. data/spec/model/spec_helper.rb +8 -6
  253. data/spec/model/validations_spec.rb +4 -17
  254. data/spec/spec_config.rb +2 -10
  255. metadata +50 -56
  256. data/lib/sequel/deprecated_core_extensions.rb +0 -135
  257. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -185
  258. data/lib/sequel/extensions/pg_statement_cache.rb +0 -318
  259. data/lib/sequel/plugins/identity_map.rb +0 -260
  260. data/lib/sequel_core.rb +0 -2
  261. data/lib/sequel_model.rb +0 -2
  262. data/spec/extensions/association_autoreloading_spec.rb +0 -102
  263. data/spec/extensions/identity_map_spec.rb +0 -337
  264. data/spec/extensions/pg_auto_parameterize_spec.rb +0 -70
  265. data/spec/extensions/pg_statement_cache_spec.rb +0 -208
  266. data/spec/rcov.opts +0 -8
  267. 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
@@ -1,2 +0,0 @@
1
- require 'sequel/core'
2
- Sequel::Deprecation.deprecate('require "sequel_core"', 'Switch to require "sequel/core"')
@@ -1,2 +0,0 @@
1
- require 'sequel/model'
2
- Sequel::Deprecation.deprecate('require "sequel_model"', 'Switch to require "sequel"')
@@ -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