sequel 3.21.0 → 3.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -2,42 +2,47 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "associate" do
4
4
  it "should use explicit class if given a class, symbol, or string" do
5
- MODEL_DB.reset
6
- klass = Class.new(Sequel::Model(:nodes))
7
- class ::ParParent < Sequel::Model
5
+ begin
6
+ klass = Class.new(Sequel::Model(:nodes))
7
+ class ::ParParent < Sequel::Model; end
8
+
9
+ klass.associate :many_to_one, :par_parent0, :class=>ParParent
10
+ klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
11
+ klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
12
+
13
+ klass.association_reflection(:"par_parent0").associated_class.should == ParParent
14
+ klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
15
+ klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
16
+ ensure
17
+ Object.send(:remove_const, :ParParent)
8
18
  end
9
-
10
- klass.associate :many_to_one, :par_parent0, :class=>ParParent
11
- klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
12
- klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
13
-
14
- klass.association_reflection(:"par_parent0").associated_class.should == ParParent
15
- klass.association_reflection(:"par_parent1s").associated_class.should == ParParent
16
- klass.association_reflection(:"par_parent2s").associated_class.should == ParParent
17
19
  end
18
20
 
19
21
  it "should default to associating to other models in the same scope" do
20
- class ::AssociationModuleTest
21
- class Album < Sequel::Model
22
- many_to_one :artist
23
- many_to_many :tags
24
- end
25
- class Artist< Sequel::Model
26
- one_to_many :albums
27
- end
28
- class Tag < Sequel::Model
29
- many_to_many :albums
22
+ begin
23
+ class ::AssociationModuleTest
24
+ class Album < Sequel::Model
25
+ many_to_one :artist
26
+ many_to_many :tags
27
+ end
28
+ class Artist< Sequel::Model
29
+ one_to_many :albums
30
+ end
31
+ class Tag < Sequel::Model
32
+ many_to_many :albums
33
+ end
30
34
  end
35
+
36
+ ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
37
+ ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
38
+ ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
39
+ ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
40
+ ensure
41
+ Object.send(:remove_const, :AssociationModuleTest)
31
42
  end
32
-
33
- ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.should == ::AssociationModuleTest::Artist
34
- ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.should == ::AssociationModuleTest::Tag
35
- ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
36
- ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.should == ::AssociationModuleTest::Album
37
43
  end
38
44
 
39
45
  it "should add a model_object and association_reflection accessors to the dataset, and return it with the current model object" do
40
- MODEL_DB.reset
41
46
  klass = Class.new(Sequel::Model(:nodes)) do
42
47
  columns :id, :a_id
43
48
  end
@@ -64,7 +69,6 @@ describe Sequel::Model, "associate" do
64
69
  end
65
70
 
66
71
  it "should allow extending the dataset with :extend option" do
67
- MODEL_DB.reset
68
72
  klass = Class.new(Sequel::Model(:nodes)) do
69
73
  columns :id, :a_id
70
74
  end
@@ -91,44 +95,58 @@ describe Sequel::Model, "associate" do
91
95
  end
92
96
 
93
97
  it "should clone an existing association with the :clone option" do
94
- MODEL_DB.reset
95
- klass = Class.new(Sequel::Model(:nodes))
96
-
97
- klass.many_to_one(:par_parent, :order=>:a){1}
98
- klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
99
- klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
98
+ begin
99
+ class ::ParParent < Sequel::Model; end
100
+ klass = Class.new(Sequel::Model(:nodes))
101
+
102
+ klass.many_to_one(:par_parent, :order=>:a){1}
103
+ klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
104
+ klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
100
105
 
101
- klass.many_to_one :par, :clone=>:par_parent, :select=>:b
102
- klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
103
- klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
104
-
105
- klass.association_reflection(:par).associated_class.should == ParParent
106
- klass.association_reflection(:par1s).associated_class.should == ParParent
107
- klass.association_reflection(:par2s).associated_class.should == ParParent
108
-
109
- klass.association_reflection(:par)[:order].should == :a
110
- klass.association_reflection(:par).select.should == :b
111
- klass.association_reflection(:par)[:block].call.should == 1
112
- klass.association_reflection(:par1s)[:limit].should == 10
113
- klass.association_reflection(:par1s)[:order].should == :b
114
- klass.association_reflection(:par1s)[:block].should == nil
115
- klass.association_reflection(:par2s)[:after_load].length.should == 1
116
- klass.association_reflection(:par2s)[:order].should == :c
117
- klass.association_reflection(:par2s)[:block].call.should == 3
106
+ klass.many_to_one :par, :clone=>:par_parent, :select=>:b
107
+ klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
108
+ klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
109
+
110
+ klass.association_reflection(:par).associated_class.should == ParParent
111
+ klass.association_reflection(:par1s).associated_class.should == ParParent
112
+ klass.association_reflection(:par2s).associated_class.should == ParParent
113
+
114
+ klass.association_reflection(:par)[:order].should == :a
115
+ klass.association_reflection(:par).select.should == :b
116
+ klass.association_reflection(:par)[:block].call.should == 1
117
+ klass.association_reflection(:par1s)[:limit].should == 10
118
+ klass.association_reflection(:par1s)[:order].should == :b
119
+ klass.association_reflection(:par1s)[:block].should == nil
120
+ klass.association_reflection(:par2s)[:after_load].length.should == 1
121
+ klass.association_reflection(:par2s)[:order].should == :c
122
+ klass.association_reflection(:par2s)[:block].call.should == 3
123
+ ensure
124
+ Object.send(:remove_const, :ParParent)
125
+ end
126
+ end
127
+
128
+ it "should raise an error if attempting to clone an association of differing type" do
129
+ c = Class.new(Sequel::Model(:c))
130
+ c.many_to_one :c
131
+ proc{c.one_to_many :cs, :clone=>:c}.should raise_error(Sequel::Error)
118
132
  end
119
133
 
134
+ it "should allow cloning of one_to_many to one_to_one associations and vice-versa" do
135
+ c = Class.new(Sequel::Model(:c))
136
+ c.one_to_one :c
137
+ proc{c.one_to_many :cs, :clone=>:c}.should_not raise_error(Sequel::Error)
138
+ proc{c.one_to_one :c2, :clone=>:cs}.should_not raise_error(Sequel::Error)
139
+ end
120
140
  end
121
141
 
122
142
  describe Sequel::Model, "many_to_one" do
123
143
  before do
124
- MODEL_DB.reset
125
-
126
144
  @c2 = Class.new(Sequel::Model(:nodes)) do
127
145
  unrestrict_primary_key
128
146
  columns :id, :parent_id, :par_parent_id, :blah
129
147
  end
130
-
131
148
  @dataset = @c2.dataset
149
+ MODEL_DB.reset
132
150
  end
133
151
 
134
152
  it "should use implicit key if omitted" do
@@ -142,32 +160,43 @@ describe Sequel::Model, "many_to_one" do
142
160
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
143
161
  end
144
162
 
163
+ it "should allow association with the same name as the key if :key_alias is given" do
164
+ @c2.def_column_alias(:parent_id_id, :parent_id)
165
+ @c2.many_to_one :parent_id, :key_column=>:parent_id, :class => @c2
166
+ d = @c2.load(:id => 1, :parent_id => 234)
167
+ d.parent_id_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"
168
+ d.parent_id.should == @c2.load(:x => 1, :id => 1)
169
+ d.parent_id_id.should == 234
170
+ d[:parent_id].should == 234
171
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
172
+
173
+ d.parent_id_id = 3
174
+ d.parent_id_id.should == 3
175
+ d[:parent_id].should == 3
176
+ end
177
+
145
178
  it "should use implicit class if omitted" do
146
- class ::ParParent < Sequel::Model
179
+ begin
180
+ class ::ParParent < Sequel::Model; end
181
+ @c2.many_to_one :par_parent
182
+ @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.should == ParParent
183
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
184
+ ensure
185
+ Object.send(:remove_const, :ParParent)
147
186
  end
148
-
149
- @c2.many_to_one :par_parent
150
-
151
- d = @c2.new(:id => 1, :par_parent_id => 234)
152
- p = d.par_parent
153
- p.class.should == ParParent
154
-
155
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.id = 234) LIMIT 1"]
156
187
  end
157
188
 
158
189
  it "should use class inside module if given as a string" do
159
- module ::Par
160
- class Parent < Sequel::Model
190
+ begin
191
+ module ::Par
192
+ class Parent < Sequel::Model; end
161
193
  end
194
+ @c2.many_to_one :par_parent, :class=>"Par::Parent"
195
+ @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.should == Par::Parent
196
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
197
+ ensure
198
+ Object.send(:remove_const, :Par)
162
199
  end
163
-
164
- @c2.many_to_one :par_parent, :class=>"Par::Parent"
165
-
166
- d = @c2.new(:id => 1, :par_parent_id => 234)
167
- p = d.par_parent
168
- p.class.should == Par::Parent
169
-
170
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.id = 234) LIMIT 1"]
171
200
  end
172
201
 
173
202
  it "should use explicit key if given" do
@@ -181,6 +210,12 @@ describe Sequel::Model, "many_to_one" do
181
210
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
182
211
  end
183
212
 
213
+ it "should respect :qualify => false option" do
214
+ @c2.many_to_one :parent, :class => @c2, :key => :blah, :qualify=>false
215
+ @c2.new(:id => 1, :blah => 567).parent
216
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (id = 567) LIMIT 1"]
217
+ end
218
+
184
219
  it "should use :primary_key option if given" do
185
220
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :primary_key => :pk
186
221
  @c2.new(:id => 1, :blah => 567).parent
@@ -218,15 +253,13 @@ describe Sequel::Model, "many_to_one" do
218
253
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND (a = 32)) LIMIT 1"]
219
254
 
220
255
  @c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>:a
221
- MODEL_DB.sqls.clear
222
256
  @c2.new(:id => 1, :blah => 567).parent
223
257
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 567) AND a) LIMIT 1"]
224
258
  end
225
259
 
226
260
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
227
- c2 = @c2
228
- @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
229
- ds.filter(:x.sql_number > 1)
261
+ @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
262
+ ds.filter{x > 1}
230
263
  end
231
264
  @c2.load(:id => 100).child_20
232
265
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
@@ -234,24 +267,19 @@ describe Sequel::Model, "many_to_one" do
234
267
 
235
268
  it "should return nil if key value is nil" do
236
269
  @c2.many_to_one :parent, :class => @c2
237
-
238
- d = @c2.new(:id => 1)
239
- d.parent.should == nil
270
+ @c2.new(:id => 1).parent.should == nil
271
+ MODEL_DB.sqls.should == []
240
272
  end
241
273
 
242
274
  it "should cache negative lookup" do
243
275
  @c2.many_to_one :parent, :class => @c2
244
- ds = @c2.dataset
245
- def ds.fetch_rows(sql, &block)
246
- MODEL_DB.sqls << sql
247
- end
248
-
276
+ @c2.dataset._fetch = []
249
277
  d = @c2.new(:id => 1, :parent_id=>555)
250
278
  MODEL_DB.sqls.should == []
251
279
  d.parent.should == nil
252
280
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
253
281
  d.parent.should == nil
254
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.id = 555) LIMIT 1']
282
+ MODEL_DB.sqls.should == []
255
283
  end
256
284
 
257
285
  it "should define a setter method" do
@@ -314,11 +342,9 @@ describe Sequel::Model, "many_to_one" do
314
342
  @c2.many_to_one :parent, :class => @c2
315
343
 
316
344
  d = @c2.load(:id => 1)
317
- MODEL_DB.reset
318
345
  d.parent_id = 234
319
346
  d.associations[:parent].should == nil
320
- ds = @c2.dataset
321
- def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
347
+ @c2.dataset._fetch = {:id=>234}
322
348
  e = d.parent
323
349
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
324
350
  d.associations[:parent].should == e
@@ -357,13 +383,32 @@ describe Sequel::Model, "many_to_one" do
357
383
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
358
384
  end
359
385
 
386
+ it "should use a callback if given one as the argument" do
387
+ @c2.many_to_one :parent, :class => @c2
388
+
389
+ d = @c2.create(:id => 1)
390
+ MODEL_DB.reset
391
+ d.parent_id = 234
392
+ d.associations[:parent] = 42
393
+ d.parent(proc{|ds| ds.filter{name > 'M'}}).should_not == 42
394
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
395
+ end
396
+
397
+ it "should use a block given to the association method as a callback" do
398
+ @c2.many_to_one :parent, :class => @c2
399
+
400
+ d = @c2.create(:id => 1)
401
+ MODEL_DB.reset
402
+ d.parent_id = 234
403
+ d.associations[:parent] = 42
404
+ d.parent{|ds| ds.filter{name > 'M'}}.should_not == 42
405
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
406
+ end
407
+
360
408
  it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
361
409
  @c2.many_to_one :parent, :class => @c2
362
410
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
363
- ds = @c2.dataset
364
- def ds.fetch_rows(sql, &block)
365
- MODEL_DB.sqls << sql
366
- end
411
+ @c2.dataset._fetch = []
367
412
 
368
413
  d = @c2.new(:id => 1)
369
414
  e = @c2.new(:id => 2)
@@ -372,14 +417,13 @@ describe Sequel::Model, "many_to_one" do
372
417
  e.children.should_not(include(d))
373
418
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
374
419
 
375
- MODEL_DB.reset
376
420
  d = @c2.new(:id => 1)
377
421
  e = @c2.new(:id => 2)
378
422
  e.children.should_not(include(d))
379
423
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
380
424
  d.parent = e
381
425
  e.children.should(include(d))
382
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
426
+ MODEL_DB.sqls.should == []
383
427
  end
384
428
 
385
429
  it "should have many_to_one setter deal with a one_to_one reciprocal" do
@@ -407,10 +451,7 @@ describe Sequel::Model, "many_to_one" do
407
451
  it "should have the setter remove the object from the previous associated object's reciprocal one_to_many cached association list if it exists" do
408
452
  @c2.many_to_one :parent, :class => @c2
409
453
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
410
- ds = @c2.dataset
411
- def ds.fetch_rows(sql, &block)
412
- MODEL_DB.sqls << sql
413
- end
454
+ @c2.dataset._fetch = []
414
455
 
415
456
  d = @c2.new(:id => 1)
416
457
  e = @c2.new(:id => 2)
@@ -428,18 +469,24 @@ describe Sequel::Model, "many_to_one" do
428
469
  MODEL_DB.sqls.should == []
429
470
  end
430
471
 
472
+ it "should have the setter not modify the reciprocal if set to same value as current" do
473
+ @c2.many_to_one :parent, :class => @c2
474
+ @c2.one_to_many :children, :class => @c2, :key=>:parent_id
475
+
476
+ c1 = @c2.load(:id => 1, :parent_id=>nil)
477
+ c2 = @c2.load(:id => 2, :parent_id=>1)
478
+ c3 = @c2.load(:id => 3, :parent_id=>1)
479
+ c1.associations[:children] = [c2, c3]
480
+ c2.associations[:parent] = c1
481
+ c2.parent = c1
482
+ c1.children.should == [c2, c3]
483
+ MODEL_DB.sqls.should == []
484
+ end
485
+
431
486
  it "should get all matching records and only return the first if :key option is set to nil" do
432
- c2 = @c2
433
487
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
434
- @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{c2.filter(:children_id=>parent_id)}
435
- ds = @c2.dataset
436
- def ds.columns
437
- [:id, :parent_id, :par_parent_id, :blah]
438
- end
439
- def ds.fetch_rows(sql, &block)
440
- MODEL_DB.sqls << sql
441
- yield({:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6})
442
- end
488
+ @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{model.filter(:children_id=>parent_id)}
489
+ @c2.dataset.columns(:id, :parent_id, :par_parent_id, :blah)._fetch = [{:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6}, {}]
443
490
  p = @c2.new(:parent_id=>2)
444
491
  fgp = p.first_grand_parent
445
492
  MODEL_DB.sqls.should == ["SELECT nodes.id, nodes.parent_id, nodes.par_parent_id, nodes.blah, children.id AS children_id, children.parent_id AS children_parent_id, children.par_parent_id AS children_par_parent_id, children.blah AS children_blah FROM nodes LEFT OUTER JOIN nodes AS children ON (children.parent_id = nodes.id) WHERE (children_id = 2)"]
@@ -500,15 +547,15 @@ describe Sequel::Model, "many_to_one" do
500
547
  h = []
501
548
  @c2.many_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
502
549
  @c2.class_eval do
503
- class_variable_set(:@@blah, h)
550
+ self::Foo = h
504
551
  def []=(a, v)
505
- a == :parent_id ? (self.class.send(:class_variable_get, :@@blah) << (v ? 4 : 5)) : super
552
+ a == :parent_id ? (model::Foo << (v ? 4 : 5)) : super
506
553
  end
507
554
  def blah(x)
508
- self.class.send(:class_variable_get, :@@blah) << (x ? x.pk : :x)
555
+ model::Foo << (x ? x.pk : :x)
509
556
  end
510
557
  def blahr(x)
511
- self.class.send(:class_variable_get, :@@blah) << 6
558
+ model::Foo << 6
512
559
  end
513
560
  end
514
561
  p = @c2.load(:id=>10)
@@ -524,13 +571,11 @@ describe Sequel::Model, "many_to_one" do
524
571
  h = []
525
572
  @c2.many_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
526
573
  @c2.class_eval do
527
- class_variable_set(:@@blah, h)
574
+ self::Foo = h
528
575
  def al(v)
529
- self.class.send(:class_variable_get, :@@blah) << v.pk
530
- end
531
- def @dataset.fetch_rows(sql)
532
- yield({:id=>20})
576
+ model::Foo << v.pk
533
577
  end
578
+ dataset._fetch = {:id=>20}
534
579
  end
535
580
  p = @c2.load(:id=>10, :parent_id=>20)
536
581
  parent = p.parent
@@ -538,6 +583,18 @@ describe Sequel::Model, "many_to_one" do
538
583
  parent.pk.should == 20
539
584
  end
540
585
 
586
+ it "should support after_load association callback that changes the cached object" do
587
+ @c2.many_to_one :parent, :class => @c2, :after_load=>:al
588
+ @c2.class_eval do
589
+ def al(v)
590
+ associations[:parent] = :foo
591
+ end
592
+ end
593
+ p = @c2.load(:id=>10, :parent_id=>20)
594
+ p.parent.should == :foo
595
+ p.associations[:parent].should == :foo
596
+ end
597
+
541
598
  it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
542
599
  # The reason for this is that assignment in ruby always returns the argument instead of the result
543
600
  # of the method, so we can't return nil to signal that the association callback prevented the modification
@@ -565,35 +622,32 @@ describe Sequel::Model, "many_to_one" do
565
622
  d = @c2.load(:id=>321)
566
623
  p = @c2.new
567
624
  p.associations[:parent] = d
568
- h = []
569
625
  @c2.many_to_one :parent, :class => @c2, :before_set=>:bs, :after_set=>:as
570
626
  @c2.class_eval do
571
- class_variable_set(:@@blah, h)
627
+ self::Foo = []
572
628
  def []=(a, v)
573
- a == :parent_id ? (self.class.send(:class_variable_get, :@@blah) << 5) : super
629
+ a == :parent_id ? (model::Foo << 5) : super
574
630
  end
575
631
  def bs(x)
576
- self.class.send(:class_variable_get, :@@blah) << x.pk
632
+ model::Foo << x.pk
577
633
  end
578
634
  def as(x)
579
- self.class.send(:class_variable_get, :@@blah) << x.pk * 2
635
+ model::Foo << x.pk * 2
580
636
  end
581
637
  end
582
638
  p.parent = c
583
- h.should == [123, 5, 246]
639
+ @c2::Foo.should == [123, 5, 246]
584
640
  end
585
641
  end
586
642
 
587
643
  describe Sequel::Model, "one_to_one" do
588
644
  before do
589
645
  @c1 = Class.new(Sequel::Model(:attributes)) do
590
- def _refresh(ds); end
591
646
  unrestrict_primary_key
592
647
  columns :id, :node_id, :y
593
648
  end
594
649
 
595
650
  @c2 = Class.new(Sequel::Model(:nodes)) do
596
- def _refresh(ds); end
597
651
  unrestrict_primary_key
598
652
  attr_accessor :xxx
599
653
 
@@ -602,24 +656,8 @@ describe Sequel::Model, "one_to_one" do
602
656
  columns :id, :x, :parent_id, :par_parent_id, :blah, :node_id
603
657
  end
604
658
  @dataset = @c2.dataset
605
-
606
- @c2.dataset.extend(Module.new {
607
- def empty?; false; end
608
- def fetch_rows(sql)
609
- @db << sql
610
- yield Hash.new
611
- end
612
- })
613
-
614
- @c1.dataset.extend(Module.new {
615
- def empty?; opts.has_key?(:empty) ? (super; true) : false; end
616
- def fetch_rows(sql)
617
- @db << sql
618
- yield Hash.new
619
- end
620
- })
621
-
622
- @dataset = @c2.dataset
659
+ @dataset._fetch = {}
660
+ @c1.dataset._fetch = {}
623
661
  MODEL_DB.reset
624
662
  end
625
663
 
@@ -641,26 +679,24 @@ describe Sequel::Model, "one_to_one" do
641
679
  it "should add a setter method" do
642
680
  @c2.one_to_one :attribute, :class => @c1
643
681
  attrib = @c1.new(:id=>3)
644
- d = @c1.dataset
645
- @c1.class_eval{remove_method :_refresh}
646
- def d.fetch_rows(s); yield({:id=>3}) end
682
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:id=>3}
647
683
  @c2.new(:id => 1234).attribute = attrib
684
+ sqls = MODEL_DB.sqls
648
685
  ['INSERT INTO attributes (node_id, id) VALUES (1234, 3)',
649
- 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(MODEL_DB.sqls.last))
650
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
651
- MODEL_DB.sqls.length.should == 2
686
+ 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)'].should(include(sqls.slice! 1))
687
+ sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)', "SELECT * FROM attributes WHERE (id = 3) LIMIT 1"]
688
+
652
689
  @c2.new(:id => 1234).attribute.should == attrib
653
- MODEL_DB.sqls.clear
654
690
  attrib = @c1.load(:id=>3)
655
691
  @c2.new(:id => 1234).attribute = attrib
656
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
692
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 1",
693
+ 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
657
694
  "UPDATE attributes SET node_id = 1234 WHERE (id = 3)"]
658
695
  end
659
696
 
660
697
  it "should use a transaction in the setter method" do
661
698
  @c2.one_to_one :attribute, :class => @c1
662
699
  @c2.use_transactions = true
663
- MODEL_DB.sqls.clear
664
700
  attrib = @c1.load(:id=>3)
665
701
  @c2.new(:id => 1234).attribute = attrib
666
702
  MODEL_DB.sqls.should == ['BEGIN',
@@ -673,7 +709,6 @@ describe Sequel::Model, "one_to_one" do
673
709
  @c2.one_to_one :attribute, :class => @c1, :conditions=>{:a=>1} do |ds|
674
710
  ds.filter(:b=>2)
675
711
  end
676
- MODEL_DB.sqls.clear
677
712
  attrib = @c1.load(:id=>3)
678
713
  @c2.new(:id => 1234).attribute = attrib
679
714
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (a = 1) AND (b = 2) AND (id != 3))',
@@ -683,30 +718,30 @@ describe Sequel::Model, "one_to_one" do
683
718
  it "should have the setter method respect the :primary_key option" do
684
719
  @c2.one_to_one :attribute, :class => @c1, :primary_key=>:xxx
685
720
  attrib = @c1.new(:id=>3)
686
- d = @c1.dataset
687
- @c1.class_eval{remove_method :_refresh}
688
- def d.fetch_rows(s); yield({:id=>3}) end
721
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:id=>3}
689
722
  @c2.new(:id => 1234, :xxx=>5).attribute = attrib
723
+ sqls = MODEL_DB.sqls
690
724
  ['INSERT INTO attributes (node_id, id) VALUES (5, 3)',
691
- 'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(MODEL_DB.sqls.last))
692
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
693
- MODEL_DB.sqls.length.should == 2
725
+ 'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(sqls.slice! 1))
726
+ sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 5)', "SELECT * FROM attributes WHERE (id = 3) LIMIT 1"]
727
+
694
728
  @c2.new(:id => 321, :xxx=>5).attribute.should == attrib
695
- MODEL_DB.sqls.clear
696
729
  attrib = @c1.load(:id=>3)
697
730
  @c2.new(:id => 621, :xxx=>5).attribute = attrib
698
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
731
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE (attributes.node_id = 5) LIMIT 1",
732
+ 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
699
733
  'UPDATE attributes SET node_id = 5 WHERE (id = 3)']
700
734
  end
701
735
 
702
736
  it "should have the setter method respect composite keys" do
703
737
  @c2.one_to_one :attribute, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
704
738
  attrib = @c1.load(:id=>3, :y=>6)
705
- d = @c1.dataset
706
- def d.fetch_rows(s); yield({:id=>3, :y=>6}) end
739
+ @c1.dataset._fetch = {:id=>3, :y=>6}
707
740
  @c2.load(:id => 1234, :x=>5).attribute = attrib
708
- MODEL_DB.sqls.last.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 3\)/
709
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
741
+ sqls = MODEL_DB.sqls
742
+ sqls.last.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 3\)/
743
+ sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\) AND \(id != 3\)\)/
744
+ sqls.length.should == 2
710
745
  end
711
746
 
712
747
  it "should use implicit key if omitted" do
@@ -721,31 +756,27 @@ describe Sequel::Model, "one_to_one" do
721
756
  end
722
757
 
723
758
  it "should use implicit class if omitted" do
724
- class ::ParParent < Sequel::Model
759
+ begin
760
+ class ::ParParent < Sequel::Model; end
761
+ @c2.one_to_one :par_parent
762
+ @c2.new(:id => 234).par_parent.class.should == ParParent
763
+ MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
764
+ ensure
765
+ Object.send(:remove_const, :ParParent)
725
766
  end
726
-
727
- @c2.one_to_one :par_parent
728
-
729
- d = @c2.new(:id => 234)
730
- p = d.par_parent
731
- p.class.should == ParParent
732
-
733
- MODEL_DB.sqls.should == ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
734
767
  end
735
768
 
736
769
  it "should use class inside module if given as a string" do
737
- module ::Par
738
- class Parent < Sequel::Model
770
+ begin
771
+ module ::Par
772
+ class Parent < Sequel::Model; end
739
773
  end
774
+ @c2.one_to_one :par_parent, :class=>"Par::Parent"
775
+ @c2.new(:id => 234).par_parent.class.should == Par::Parent
776
+ MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
777
+ ensure
778
+ Object.send(:remove_const, :Par)
740
779
  end
741
-
742
- @c2.one_to_one :par_parent, :class=>"Par::Parent"
743
-
744
- d = @c2.new(:id => 234)
745
- p = d.par_parent
746
- p.class.should == Par::Parent
747
-
748
- MODEL_DB.sqls.should == ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
749
780
  end
750
781
 
751
782
  it "should use explicit key if given" do
@@ -796,15 +827,13 @@ describe Sequel::Model, "one_to_one" do
796
827
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.node_id = 567) AND (a = 32)) LIMIT 1"]
797
828
 
798
829
  @c2.one_to_one :parent, :class => @c2, :conditions=>:a
799
- MODEL_DB.sqls.clear
800
830
  @c2.new(:id => 567).parent
801
831
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.node_id = 567) AND a) LIMIT 1"]
802
832
  end
803
833
 
804
834
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
805
- c2 = @c2
806
- @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
807
- ds.filter(:x.sql_number > 1)
835
+ @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
836
+ ds.filter{x > 1}
808
837
  end
809
838
  @c2.load(:id => 100).child_20
810
839
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
@@ -813,53 +842,32 @@ describe Sequel::Model, "one_to_one" do
813
842
  it "should return nil if primary_key value is nil" do
814
843
  @c2.one_to_one :parent, :class => @c2, :primary_key=>:node_id
815
844
 
816
- d = @c2.new(:id => 1)
817
- d.parent.should == nil
845
+ @c2.new(:id => 1).parent.should be_nil
818
846
  MODEL_DB.sqls.should == []
819
847
  end
820
848
 
821
849
  it "should cache negative lookup" do
822
850
  @c2.one_to_one :parent, :class => @c2
823
- ds = @c2.dataset
824
- def ds.fetch_rows(sql, &block)
825
- MODEL_DB.sqls << sql
826
- end
827
-
851
+ @c2.dataset._fetch = []
828
852
  d = @c2.new(:id => 555)
829
853
  MODEL_DB.sqls.should == []
830
854
  d.parent.should == nil
831
855
  MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
832
856
  d.parent.should == nil
833
- MODEL_DB.sqls.should == ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
857
+ MODEL_DB.sqls.should == []
834
858
  end
835
859
 
836
- it "should define a setter method" do
837
- @c2.one_to_one :parent, :class => @c2
838
-
839
- d = @c2.new(:id => 1)
840
- f = @c2.new(:id => 3, :node_id=> 4321)
841
- d.parent = f
842
- f.values.should == {:id => 3, :node_id=>1}
843
- d.parent.should == f
844
-
845
- d.parent = nil
846
- d.parent.should == nil
847
- end
848
-
849
- it "should have the setter method respect the :primary_key option" do
850
- @c2.one_to_one :parent, :class => @c2, :primary_key=>:blah
851
- d = @c2.new(:id => 1, :blah => 3)
852
- e = @c2.new(:id => 4321, :node_id=>444)
853
- d.parent = e
854
- e.values.should == {:id => 4321, :node_id => 3}
855
- end
856
-
857
860
  it "should have the setter method respect the :key option" do
858
861
  @c2.one_to_one :parent, :class => @c2, :key=>:blah
859
862
  d = @c2.new(:id => 3)
860
863
  e = @c2.new(:id => 4321, :blah=>444)
864
+ @c2.dataset._fetch = @c2.instance_dataset._fetch = {:id => 4321, :blah => 3}
861
865
  d.parent = e
862
866
  e.values.should == {:id => 4321, :blah => 3}
867
+ sqls = MODEL_DB.sqls
868
+ ["INSERT INTO nodes (blah, id) VALUES (3, 4321)",
869
+ "INSERT INTO nodes (id, blah) VALUES (4321, 3)"].should include(sqls.slice! 1)
870
+ sqls.should == ["UPDATE nodes SET blah = NULL WHERE (blah = 3)", "SELECT * FROM nodes WHERE (id = 4321) LIMIT 1"]
863
871
  end
864
872
 
865
873
  it "should persist changes to associated object when the setter is called" do
@@ -875,12 +883,11 @@ describe Sequel::Model, "one_to_one" do
875
883
 
876
884
  d = @c2.load(:id => 1)
877
885
  d.associations[:parent].should == nil
878
- ds = @c2.dataset
879
- def ds.fetch_rows(sql, &block); MODEL_DB.sqls << sql; yield({:id=>234}) end
886
+ @c2.dataset._fetch = {:id=>234}
880
887
  e = d.parent
881
888
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
882
889
  d.parent
883
- MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
890
+ MODEL_DB.sqls.should == []
884
891
  d.associations[:parent].should == e
885
892
  end
886
893
 
@@ -922,7 +929,6 @@ describe Sequel::Model, "one_to_one" do
922
929
  e.child.should == d
923
930
  MODEL_DB.sqls.should == ["UPDATE nodes SET parent_id = NULL WHERE ((parent_id = 1) AND (id != 2))",
924
931
  "UPDATE nodes SET parent_id = 1 WHERE (id = 2)"]
925
- MODEL_DB.reset
926
932
  d.parent = nil
927
933
  e.child.should == nil
928
934
  MODEL_DB.sqls.should == ["UPDATE nodes SET parent_id = NULL WHERE (parent_id = 1)"]
@@ -931,17 +937,13 @@ describe Sequel::Model, "one_to_one" do
931
937
  it "should have the setter remove the object from the previous associated object's reciprocal many_to_one cached association list if it exists" do
932
938
  @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
933
939
  @c2.many_to_one :child, :class => @c2, :key=>:parent_id
934
- ds = @c2.dataset
935
- def ds.fetch_rows(sql, &block)
936
- MODEL_DB.sqls << sql
937
- end
940
+ @c2.dataset._fetch = []
938
941
 
939
942
  d = @c2.load(:id => 1)
940
943
  e = @c2.load(:id => 2)
941
944
  f = @c2.load(:id => 3)
942
945
  e.child.should == nil
943
946
  f.child.should == nil
944
- MODEL_DB.reset
945
947
  d.parent = e
946
948
  e.child.should == d
947
949
  d.parent = f
@@ -951,6 +953,19 @@ describe Sequel::Model, "one_to_one" do
951
953
  f.child.should == nil
952
954
  end
953
955
 
956
+ it "should have the setter not modify the reciprocal if set to same value as current" do
957
+ @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
958
+ @c2.many_to_one :child, :class => @c2, :key=>:parent_id
959
+
960
+ c1 = @c2.load(:id => 1, :parent_id=>nil)
961
+ c2 = @c2.load(:id => 2, :parent_id=>1)
962
+ c1.associations[:child] = c2
963
+ c2.associations[:parent] = c1
964
+ c2.parent = c1
965
+ c1.child.should == c2
966
+ MODEL_DB.sqls.should == []
967
+ end
968
+
954
969
  it "should not add associations methods directly to class" do
955
970
  @c2.one_to_one :parent, :class => @c2
956
971
  @c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
@@ -983,12 +998,12 @@ describe Sequel::Model, "one_to_one" do
983
998
  h = []
984
999
  @c2.one_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
985
1000
  @c2.class_eval do
986
- class_variable_set(:@@blah, h)
1001
+ self::Foo = h
987
1002
  def blah(x)
988
- self.class.send(:class_variable_get, :@@blah) << (x ? x.pk : :x)
1003
+ model::Foo << (x ? x.pk : :x)
989
1004
  end
990
1005
  def blahr(x)
991
- self.class.send(:class_variable_get, :@@blah) << 6
1006
+ model::Foo << 6
992
1007
  end
993
1008
  end
994
1009
  p = @c2.load(:id=>10)
@@ -1004,13 +1019,11 @@ describe Sequel::Model, "one_to_one" do
1004
1019
  h = []
1005
1020
  @c2.one_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
1006
1021
  @c2.class_eval do
1007
- class_variable_set(:@@blah, h)
1022
+ self::Foo = h
1008
1023
  def al(v)
1009
- self.class.send(:class_variable_get, :@@blah) << v.pk
1010
- end
1011
- def @dataset.fetch_rows(sql)
1012
- yield({:id=>20})
1024
+ model::Foo << v.pk
1013
1025
  end
1026
+ @dataset._fetch = {:id=>20}
1014
1027
  end
1015
1028
  p = @c2.load(:id=>10)
1016
1029
  parent = p.parent
@@ -1048,15 +1061,15 @@ describe Sequel::Model, "one_to_one" do
1048
1061
  h = []
1049
1062
  @c2.one_to_one :parent, :class => @c2, :before_set=>:bs, :after_set=>:as
1050
1063
  @c2.class_eval do
1051
- class_variable_set(:@@blah, h)
1064
+ self::Foo = h
1052
1065
  def []=(a, v)
1053
- a == :node_id ? (self.class.send(:class_variable_get, :@@blah) << 5) : super
1066
+ a == :node_id ? (model::Foo << 5) : super
1054
1067
  end
1055
1068
  def bs(x)
1056
- self.class.send(:class_variable_get, :@@blah) << x.pk
1069
+ model::Foo << x.pk
1057
1070
  end
1058
1071
  def as(x)
1059
- self.class.send(:class_variable_get, :@@blah) << x.pk * 2
1072
+ model::Foo << x.pk * 2
1060
1073
  end
1061
1074
  end
1062
1075
  p.parent = c
@@ -1072,16 +1085,9 @@ end
1072
1085
 
1073
1086
  describe Sequel::Model, "one_to_many" do
1074
1087
  before do
1075
- MODEL_DB.reset
1076
-
1077
1088
  @c1 = Class.new(Sequel::Model(:attributes)) do
1078
- def _refresh(ds); end
1079
1089
  unrestrict_primary_key
1080
1090
  columns :id, :node_id, :y, :z
1081
-
1082
- def self.[](id)
1083
- load(id.is_a?(Array) ? {:id => id[0], :z => id[1]} : {:id => id})
1084
- end
1085
1091
  end
1086
1092
 
1087
1093
  @c2 = Class.new(Sequel::Model(:nodes)) do
@@ -1094,68 +1100,58 @@ describe Sequel::Model, "one_to_many" do
1094
1100
  columns :id, :x
1095
1101
  end
1096
1102
  @dataset = @c2.dataset
1097
-
1098
- @c2.dataset.extend(Module.new {
1099
- def empty?; false; end
1100
- def fetch_rows(sql)
1101
- @db << sql
1102
- yield Hash.new
1103
- end
1104
- })
1105
-
1106
- @c1.dataset.extend(Module.new {
1107
- def empty?; opts.has_key?(:empty) ? (super; true) : false; end
1108
- def fetch_rows(sql)
1109
- @db << sql
1110
- yield Hash.new
1111
- end
1112
- })
1103
+ @dataset._fetch = {}
1104
+ @c1.dataset._fetch = proc{|sql| sql =~ /SELECT 1/ ? {:a=>1} : {}}
1105
+ MODEL_DB.reset
1113
1106
  end
1114
1107
 
1115
1108
  it "should use implicit key if omitted" do
1116
1109
  @c2.one_to_many :attributes, :class => @c1
1117
-
1118
- n = @c2.new(:id => 1234)
1119
- a = n.attributes_dataset
1120
- a.should be_a_kind_of(Sequel::Dataset)
1121
- a.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1110
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1122
1111
  end
1123
1112
 
1124
1113
  it "should use implicit class if omitted" do
1125
- class ::HistoricalValue < Sequel::Model
1114
+ begin
1115
+ class ::HistoricalValue < Sequel::Model; end
1116
+ @c2.one_to_many :historical_values
1117
+
1118
+ v = @c2.new(:id => 1234).historical_values_dataset
1119
+ v.should be_a_kind_of(Sequel::Dataset)
1120
+ v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1121
+ v.model.should == HistoricalValue
1122
+ ensure
1123
+ Object.send(:remove_const, :HistoricalValue)
1126
1124
  end
1127
-
1128
- @c2.one_to_many :historical_values
1129
-
1130
- n = @c2.new(:id => 1234)
1131
- v = n.historical_values_dataset
1132
- v.should be_a_kind_of(Sequel::Dataset)
1133
- v.sql.should == 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1134
- v.model.should == HistoricalValue
1135
1125
  end
1136
1126
 
1137
1127
  it "should use class inside a module if given as a string" do
1138
- module ::Historical
1139
- class Value < Sequel::Model
1128
+ begin
1129
+ module ::Historical
1130
+ class Value < Sequel::Model; end
1140
1131
  end
1132
+ @c2.one_to_many :historical_values, :class=>'Historical::Value'
1133
+
1134
+ v = @c2.new(:id => 1234).historical_values_dataset
1135
+ v.should be_a_kind_of(Sequel::Dataset)
1136
+ v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
1137
+ v.model.should == Historical::Value
1138
+ ensure
1139
+ Object.send(:remove_const, :Historical)
1141
1140
  end
1142
-
1143
- @c2.one_to_many :historical_values, :class=>'Historical::Value'
1144
-
1145
- n = @c2.new(:id => 1234)
1146
- v = n.historical_values_dataset
1147
- v.should be_a_kind_of(Sequel::Dataset)
1148
- v.sql.should == 'SELECT * FROM values WHERE (values.node_id = 1234)'
1149
- v.model.should == Historical::Value
1150
1141
  end
1151
1142
 
1152
- it "should use explicit key if given" do
1143
+ it "should use a callback if given one as the argument" do
1153
1144
  @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1154
1145
 
1155
- n = @c2.new(:id => 1234)
1156
- a = n.attributes_dataset
1157
- a.should be_a_kind_of(Sequel::Dataset)
1158
- a.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1146
+ d = @c2.load(:id => 1234)
1147
+ d.associations[:attributes] = []
1148
+ d.attributes(proc{|ds| ds.filter{name > 'M'}}).should_not == []
1149
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE ((attributes.nodeid = 1234) AND (name > 'M'))"]
1150
+ end
1151
+
1152
+ it "should use explicit key if given" do
1153
+ @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1154
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1159
1155
  end
1160
1156
 
1161
1157
  it "should support_composite keys" do
@@ -1180,10 +1176,8 @@ describe Sequel::Model, "one_to_many" do
1180
1176
  it "should define an add_ method that works on existing records" do
1181
1177
  @c2.one_to_many :attributes, :class => @c1
1182
1178
 
1183
- n = @c2.new(:id => 1234)
1184
- a = @c1.new(:id => 2345)
1185
- a.save
1186
- MODEL_DB.reset
1179
+ n = @c2.load(:id => 1234)
1180
+ a = @c1.load(:id => 2345)
1187
1181
  a.should == n.add_attribute(a)
1188
1182
  a.values.should == {:node_id => 1234, :id => 2345}
1189
1183
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
@@ -1192,52 +1186,53 @@ describe Sequel::Model, "one_to_many" do
1192
1186
  it "should define an add_ method that works on new records" do
1193
1187
  @c2.one_to_many :attributes, :class => @c1
1194
1188
 
1195
- n = @c2.new(:id => 1234)
1189
+ n = @c2.load(:id => 1234)
1196
1190
  a = @c1.new(:id => 234)
1197
- # do not save
1198
- MODEL_DB.reset
1191
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:node_id => 1234, :id => 234}
1199
1192
  a.should == n.add_attribute(a)
1200
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1193
+ sqls = MODEL_DB.sqls
1194
+ sqls.shift.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1195
+ sqls.should == ["SELECT * FROM attributes WHERE (id = 234) LIMIT 1"]
1201
1196
  a.values.should == {:node_id => 1234, :id => 234}
1202
1197
  end
1203
1198
 
1204
1199
  it "should define a remove_ method that works on existing records" do
1205
1200
  @c2.one_to_many :attributes, :class => @c1
1206
1201
 
1207
- n = @c2.new(:id => 1234)
1208
- a = @c1.new(:id => 2345, :node_id => 1234)
1209
- a.save
1210
- MODEL_DB.reset
1202
+ n = @c2.load(:id => 1234)
1203
+ a = @c1.load(:id => 2345, :node_id => 1234)
1211
1204
  a.should == n.remove_attribute(a)
1212
1205
  a.values.should == {:node_id => nil, :id => 2345}
1213
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1206
+ MODEL_DB.sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1", 'UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1214
1207
  end
1215
1208
 
1216
1209
  it "should have the remove_ method raise an error if the passed object is not already associated" do
1217
1210
  @c2.one_to_many :attributes, :class => @c1
1218
- @c1.dataset.opts[:empty] = true
1219
1211
 
1220
1212
  n = @c2.new(:id => 1234)
1221
1213
  a = @c1.load(:id => 2345, :node_id => 1234)
1222
- MODEL_DB.reset
1214
+ @c1.dataset._fetch = []
1223
1215
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1224
- MODEL_DB.sqls.should == ["SELECT 1 FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1216
+ MODEL_DB.sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1225
1217
  end
1226
1218
 
1227
1219
  it "should accept a hash for the add_ method and create a new record" do
1228
1220
  @c2.one_to_many :attributes, :class => @c1
1229
1221
  n = @c2.new(:id => 1234)
1230
1222
  MODEL_DB.reset
1231
- @c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(:id => 234)
1232
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1223
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:node_id => 1234, :id => 234}
1224
+ n.add_attribute(:id => 234).should == @c1.load(:node_id => 1234, :id => 234)
1225
+ sqls = MODEL_DB.sqls
1226
+ sqls.shift.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
1227
+ sqls.should == ["SELECT * FROM attributes WHERE (id = 234) LIMIT 1"]
1233
1228
  end
1234
1229
 
1235
1230
  it "should accept a primary key for the add_ method" do
1236
1231
  @c2.one_to_many :attributes, :class => @c1
1237
1232
  n = @c2.new(:id => 1234)
1238
- MODEL_DB.reset
1239
- @c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(234)
1240
- MODEL_DB.sqls.first.should == "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"
1233
+ @c1.dataset._fetch = {:id=>234, :node_id=>nil}
1234
+ n.add_attribute(234).should == @c1.load(:node_id => 1234, :id => 234)
1235
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE id = 234", "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"]
1241
1236
  end
1242
1237
 
1243
1238
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
@@ -1248,13 +1243,8 @@ describe Sequel::Model, "one_to_many" do
1248
1243
  it "should accept a primary key for the remove_ method and remove an existing record" do
1249
1244
  @c2.one_to_many :attributes, :class => @c1
1250
1245
  n = @c2.new(:id => 1234)
1251
- ds = @c1.dataset
1252
- def ds.fetch_rows(sql)
1253
- db << sql
1254
- yield({:id=>234, :node_id=>1234})
1255
- end
1256
- MODEL_DB.reset
1257
- @c1.load(:node_id => nil, :id => 234).should == n.remove_attribute(234)
1246
+ @c1.dataset._fetch = {:id=>234, :node_id=>1234}
1247
+ n.remove_attribute(234).should == @c1.load(:node_id => nil, :id => 234)
1258
1248
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.id = 234)) LIMIT 1',
1259
1249
  'UPDATE attributes SET node_id = NULL WHERE (id = 234)']
1260
1250
  end
@@ -1268,10 +1258,8 @@ describe Sequel::Model, "one_to_many" do
1268
1258
  @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1269
1259
 
1270
1260
  n = @c2.new(:id => 1234, :xxx=>5)
1271
- a = @c1.new(:id => 2345)
1272
- a.save
1273
- MODEL_DB.reset
1274
- a.should == n.add_attribute(a)
1261
+ a = @c1.load(:id => 2345)
1262
+ n.add_attribute(a).should == a
1275
1263
  MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
1276
1264
  end
1277
1265
 
@@ -1279,15 +1267,13 @@ describe Sequel::Model, "one_to_many" do
1279
1267
  @c2.one_to_many :attributes, :class => @c1
1280
1268
 
1281
1269
  n = @c2.new(:id => 1234)
1282
- a = @c1.new(:id => 2345)
1283
- a.save
1284
- MODEL_DB.reset
1270
+ a = @c1.load(:id => 2345)
1285
1271
  n.associations[:attributes] = []
1286
1272
  a.should == n.add_attribute(a)
1287
1273
  a.should == n.add_attribute(a)
1288
1274
  a.values.should == {:node_id => 1234, :id => 2345}
1289
- MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1290
1275
  n.attributes.should == [a]
1276
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1291
1277
  end
1292
1278
 
1293
1279
  it "should have add_ method respect composite keys" do
@@ -1295,18 +1281,24 @@ describe Sequel::Model, "one_to_many" do
1295
1281
 
1296
1282
  n = @c2.load(:id => 1234, :x=>5)
1297
1283
  a = @c1.load(:id => 2345)
1298
- a.should == n.add_attribute(a)
1299
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
1284
+ n.add_attribute(a).should == a
1285
+ sqls = MODEL_DB.sqls
1286
+ sqls.shift.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
1287
+ sqls.should == []
1300
1288
  end
1301
1289
 
1302
1290
  it "should have add_ method accept a composite key" do
1303
1291
  @c1.set_primary_key :id, :z
1304
1292
  @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
1293
+ @c1.dataset._fetch = {:id => 2345, :z => 8, :node_id => 1234, :y=>5}
1305
1294
 
1306
1295
  n = @c2.load(:id => 1234, :x=>5)
1307
1296
  a = @c1.load(:id => 2345, :z => 8, :node_id => 1234, :y=>5)
1308
- a.should == n.add_attribute([2345, 8])
1309
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
1297
+ n.add_attribute([2345, 8]).should == a
1298
+ sqls = MODEL_DB.sqls
1299
+ sqls.shift.should =~ /SELECT \* FROM attributes WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\) LIMIT 1/
1300
+ sqls.shift.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
1301
+ sqls.should == []
1310
1302
  end
1311
1303
 
1312
1304
  it "should have remove_ method respect composite keys" do
@@ -1314,31 +1306,29 @@ describe Sequel::Model, "one_to_many" do
1314
1306
 
1315
1307
  n = @c2.load(:id => 1234, :x=>5)
1316
1308
  a = @c1.load(:id => 2345, :node_id=>1234, :y=>5)
1317
- a.should == n.remove_attribute(a)
1318
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1309
+ n.remove_attribute(a).should == a
1310
+ sqls = MODEL_DB.sqls
1311
+ sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(id = 2345\)/
1312
+ sqls.should == ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.y = 5) AND (id = 2345)) LIMIT 1"]
1319
1313
  end
1320
1314
 
1321
1315
  it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
1322
1316
  @c1.set_primary_key :id, :y
1323
1317
  @c2.one_to_many :attributes, :class => @c1, :key=>:node_id, :primary_key=>:id
1324
1318
  n = @c2.new(:id => 123)
1325
- ds = @c1.dataset
1326
- def ds.fetch_rows(sql)
1327
- db << sql
1328
- yield({:id=>234, :node_id=>123, :y=>5})
1329
- end
1330
- MODEL_DB.reset
1331
- @c1.load(:node_id => nil, :y => 5, :id => 234).should == n.remove_attribute([234, 5])
1332
- MODEL_DB.sqls.length.should == 2
1333
- MODEL_DB.sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(attributes\.(id|y) = (234|5)\) AND \(attributes\.(id|y) = (234|5)\)\) LIMIT 1/
1334
- MODEL_DB.sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
1319
+ @c1.dataset._fetch = {:id=>234, :node_id=>123, :y=>5}
1320
+ n.remove_attribute([234, 5]).should == @c1.load(:node_id => nil, :y => 5, :id => 234)
1321
+ sqls = MODEL_DB.sqls
1322
+ sqls.length.should == 2
1323
+ sqls.first.should =~ /SELECT \* FROM attributes WHERE \(\(attributes.node_id = 123\) AND \(attributes\.(id|y) = (234|5)\) AND \(attributes\.(id|y) = (234|5)\)\) LIMIT 1/
1324
+ sqls.last.should =~ /UPDATE attributes SET node_id = NULL WHERE \(\((id|y) = (234|5)\) AND \((id|y) = (234|5)\)\)/
1335
1325
  end
1336
1326
 
1337
1327
  it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
1338
1328
  @c2.one_to_many :attributes, :class => @c1
1339
1329
  n = @c2.new(:id => 1234)
1340
1330
  a = @c1.new(:id => 2345)
1341
- def a.valid?(opts); false; end
1331
+ def a.validate() errors.add(:id, 'foo') end
1342
1332
  proc{n.add_attribute(a)}.should raise_error(Sequel::Error)
1343
1333
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1344
1334
  end
@@ -1347,7 +1337,7 @@ describe Sequel::Model, "one_to_many" do
1347
1337
  @c2.one_to_many :attributes, :class => @c1, :validate=>false
1348
1338
  n = @c2.new(:id => 1234)
1349
1339
  a = @c1.new(:id => 2345)
1350
- def a.valid?(opts); false; end
1340
+ def a.validate() errors.add(:id, 'foo') end
1351
1341
  n.add_attribute(a).should == a
1352
1342
  n.remove_attribute(a).should == a
1353
1343
  end
@@ -1364,81 +1354,48 @@ describe Sequel::Model, "one_to_many" do
1364
1354
 
1365
1355
  it "should use :primary_key option if given" do
1366
1356
  @c1.one_to_many :nodes, :class => @c2, :primary_key => :node_id, :key=>:id
1367
- n = @c1.load(:id => 1234, :node_id=>4321)
1368
- n.nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1357
+ @c1.load(:id => 1234, :node_id=>4321).nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1369
1358
  end
1370
1359
 
1371
1360
  it "should support a select option" do
1372
1361
  @c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
1373
-
1374
- n = @c2.new(:id => 1234)
1375
- n.attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1362
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1376
1363
  end
1377
1364
 
1378
1365
  it "should support a conditions option" do
1379
1366
  @c2.one_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1380
- n = @c2.new(:id => 1234)
1381
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
1382
- @c2.one_to_many :attributes, :class => @c1, :conditions => ~:a
1383
- n = @c2.new(:id => 1234)
1384
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
1367
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (a = 32))"
1368
+ @c2.one_to_many :attributes, :class => @c1, :conditions => Sequel.~(:a)
1369
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND NOT a)"
1385
1370
  end
1386
1371
 
1387
1372
  it "should support an order option" do
1388
1373
  @c2.one_to_many :attributes, :class => @c1, :order => :kind
1389
-
1390
- n = @c2.new(:id => 1234)
1391
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1374
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1392
1375
  end
1393
1376
 
1394
1377
  it "should support an array for the order option" do
1395
1378
  @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
1396
-
1397
- n = @c2.new(:id => 1234)
1398
- n.attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1379
+ @c2.new(:id => 1234).attributes_dataset.sql.should == "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1399
1380
  end
1400
1381
 
1401
1382
  it "should return array with all members of the association" do
1402
1383
  @c2.one_to_many :attributes, :class => @c1
1403
-
1404
- n = @c2.new(:id => 1234)
1405
- atts = n.attributes
1406
- atts.should be_a_kind_of(Array)
1407
- atts.size.should == 1
1408
- atts.first.should be_a_kind_of(@c1)
1409
- atts.first.values.should == {}
1410
-
1411
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1384
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1412
1385
  end
1413
1386
 
1414
1387
  it "should accept a block" do
1415
1388
  @c2.one_to_many :attributes, :class => @c1 do |ds|
1416
1389
  ds.filter(:xxx => @xxx)
1417
1390
  end
1418
-
1419
- n = @c2.new(:id => 1234)
1420
- atts = n.attributes
1421
- atts.should be_a_kind_of(Array)
1422
- atts.size.should == 1
1423
- atts.first.should be_a_kind_of(@c1)
1424
- atts.first.values.should == {}
1425
-
1426
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))']
1391
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))'
1427
1392
  end
1428
1393
 
1429
1394
  it "should support :order option with block" do
1430
1395
  @c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
1431
1396
  ds.filter(:xxx => @xxx)
1432
1397
  end
1433
-
1434
- n = @c2.new(:id => 1234)
1435
- atts = n.attributes
1436
- atts.should be_a_kind_of(Array)
1437
- atts.size.should == 1
1438
- atts.first.should be_a_kind_of(@c1)
1439
- atts.first.values.should == {}
1440
-
1441
- MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind']
1398
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind'
1442
1399
  end
1443
1400
 
1444
1401
  it "should have the block argument affect the _dataset method" do
@@ -1450,18 +1407,11 @@ describe Sequel::Model, "one_to_many" do
1450
1407
 
1451
1408
  it "should support a :dataset option that is used instead of the default" do
1452
1409
  c1 = @c1
1453
- @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.filter(:nodeid=>pk).invert}, :order=>:a, :limit=>10 do |ds|
1410
+ @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.exclude(:nodeid=>pk)}, :order=>:a, :limit=>10 do |ds|
1454
1411
  ds.filter(:xxx => 5)
1455
1412
  end
1456
-
1457
1413
  @c2.new(:id => 1234).all_other_attributes_dataset.sql.should == 'SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10'
1458
- n = @c2.new(:id => 1234)
1459
- atts = n.all_other_attributes
1460
- atts.should be_a_kind_of(Array)
1461
- atts.size.should == 1
1462
- atts.first.should be_a_kind_of(@c1)
1463
- atts.first.values.should == {}
1464
-
1414
+ @c2.new(:id => 1234).all_other_attributes.should == [@c1.load({})]
1465
1415
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10']
1466
1416
  end
1467
1417
 
@@ -1479,9 +1429,7 @@ describe Sequel::Model, "one_to_many" do
1479
1429
 
1480
1430
  it "should set cached instance variable when accessed" do
1481
1431
  @c2.one_to_many :attributes, :class => @c1
1482
-
1483
1432
  n = @c2.new(:id => 1234)
1484
- MODEL_DB.reset
1485
1433
  n.associations.include?(:attributes).should == false
1486
1434
  atts = n.attributes
1487
1435
  atts.should == n.associations[:attributes]
@@ -1490,9 +1438,7 @@ describe Sequel::Model, "one_to_many" do
1490
1438
 
1491
1439
  it "should use cached instance variable if available" do
1492
1440
  @c2.one_to_many :attributes, :class => @c1
1493
-
1494
1441
  n = @c2.new(:id => 1234)
1495
- MODEL_DB.reset
1496
1442
  n.associations[:attributes] = 42
1497
1443
  n.attributes.should == 42
1498
1444
  MODEL_DB.sqls.should == []
@@ -1500,9 +1446,7 @@ describe Sequel::Model, "one_to_many" do
1500
1446
 
1501
1447
  it "should not use cached instance variable if asked to reload" do
1502
1448
  @c2.one_to_many :attributes, :class => @c1
1503
-
1504
1449
  n = @c2.new(:id => 1234)
1505
- MODEL_DB.reset
1506
1450
  n.associations[:attributes] = 42
1507
1451
  n.attributes(true).should_not == 42
1508
1452
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
@@ -1510,10 +1454,8 @@ describe Sequel::Model, "one_to_many" do
1510
1454
 
1511
1455
  it "should add item to cached instance variable if it exists when calling add_" do
1512
1456
  @c2.one_to_many :attributes, :class => @c1
1513
-
1514
1457
  n = @c2.new(:id => 1234)
1515
- att = @c1.new(:id => 345)
1516
- MODEL_DB.reset
1458
+ att = @c1.load(:id => 345)
1517
1459
  a = []
1518
1460
  n.associations[:attributes] = a
1519
1461
  n.add_attribute(att)
@@ -1535,7 +1477,6 @@ describe Sequel::Model, "one_to_many" do
1535
1477
 
1536
1478
  n = @c2.load(:id => 1234)
1537
1479
  att = @c1.load(:id => 345)
1538
- MODEL_DB.reset
1539
1480
  a = [att]
1540
1481
  n.associations[:attributes] = a
1541
1482
  n.remove_attribute(att)
@@ -1587,13 +1528,9 @@ describe Sequel::Model, "one_to_many" do
1587
1528
  n = @c2.new(:id => 1234)
1588
1529
  atts = n.attributes
1589
1530
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1590
- atts.should be_a_kind_of(Array)
1591
- atts.size.should == 1
1592
- atts.first.should be_a_kind_of(@c1)
1593
- atts.first.values.should == {}
1594
- atts.first.node.should == n
1595
-
1596
- MODEL_DB.sqls.length.should == 1
1531
+ atts.should == [@c1.load({})]
1532
+ atts.map{|a| a.node}.should == [n]
1533
+ MODEL_DB.sqls.should == []
1597
1534
  end
1598
1535
 
1599
1536
  it "should use an explicit reciprocal instance variable if given" do
@@ -1602,19 +1539,15 @@ describe Sequel::Model, "one_to_many" do
1602
1539
  n = @c2.new(:id => 1234)
1603
1540
  atts = n.attributes
1604
1541
  MODEL_DB.sqls.should == ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1605
- atts.should be_a_kind_of(Array)
1606
- atts.size.should == 1
1607
- atts.first.should be_a_kind_of(@c1)
1608
- atts.first.values.should == {}
1609
- atts.first.associations[:wxyz].should == n
1610
-
1611
- MODEL_DB.sqls.length.should == 1
1542
+ atts.should == [@c1.load({})]
1543
+ atts.map{|a| a.associations[:wxyz]}.should == [n]
1544
+ MODEL_DB.sqls.should == []
1612
1545
  end
1613
1546
 
1614
1547
  it "should have an remove_all_ method that removes all associations" do
1615
1548
  @c2.one_to_many :attributes, :class => @c1
1616
1549
  @c2.new(:id => 1234).remove_all_attributes
1617
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
1550
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)']
1618
1551
  end
1619
1552
 
1620
1553
  it "should have remove_all method respect association filters" do
@@ -1628,13 +1561,15 @@ describe Sequel::Model, "one_to_many" do
1628
1561
  it "should have the remove_all_ method respect the :primary_key option" do
1629
1562
  @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1630
1563
  @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
1631
- MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 5)'
1564
+ MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (node_id = 5)']
1632
1565
  end
1633
1566
 
1634
1567
  it "should have the remove_all_ method respect composite keys" do
1635
1568
  @c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
1636
1569
  @c2.new(:id => 1234, :x=>5).remove_all_attributes
1637
- MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\)\)/
1570
+ sqls = MODEL_DB.sqls
1571
+ sqls.pop.should =~ /UPDATE attributes SET (node_id|y) = NULL, (node_id|y) = NULL WHERE \(\(node_id = 1234\) AND \(y = 5\)\)/
1572
+ sqls.should == []
1638
1573
  end
1639
1574
 
1640
1575
  it "remove_all should set the cached instance variable to []" do
@@ -1648,10 +1583,8 @@ describe Sequel::Model, "one_to_many" do
1648
1583
  @c2.one_to_many :attributes, :class => @c1
1649
1584
  attrib = @c1.new(:id=>3)
1650
1585
  node = @c2.new(:id => 1234)
1651
- d = @c1.dataset
1652
- def d.fetch_rows(s); end
1586
+ @c1.dataset._fetch = [[], [{:id=>3, :node_id=>1234}]]
1653
1587
  node.attributes.should == []
1654
- def attrib.save(*); self end
1655
1588
  node.add_attribute(attrib)
1656
1589
  node.associations[:attributes].should == [attrib]
1657
1590
  node.remove_all_attributes.should == [attrib]
@@ -1665,15 +1598,12 @@ describe Sequel::Model, "one_to_many" do
1665
1598
  it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
1666
1599
  @c2.one_to_many :attributes, :class => @c1
1667
1600
  @c1.many_to_one :node, :class => @c2
1668
- d = @c1.dataset
1669
- def d.fetch_rows(s); end
1670
- d = @c2.dataset
1671
- def d.fetch_rows(s); end
1601
+ @c2.dataset._fetch = []
1602
+ @c1.dataset._fetch = [[], [{:id=>3, :node_id=>1234}]]
1672
1603
  attrib = @c1.new(:id=>3)
1673
- node = @c2.new(:id => 1234)
1604
+ node = @c2.load(:id => 1234)
1674
1605
  node.attributes.should == []
1675
1606
  attrib.node.should == nil
1676
- def attrib.save(*); self end
1677
1607
  node.add_attribute(attrib)
1678
1608
  attrib.associations[:node].should == node
1679
1609
  node.remove_all_attributes
@@ -1761,18 +1691,18 @@ describe Sequel::Model, "one_to_many" do
1761
1691
  h = []
1762
1692
  @c2.one_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
1763
1693
  @c2.class_eval do
1764
- class_variable_set(:@@blah, h)
1694
+ self::Foo = h
1765
1695
  def _add_attribute(v)
1766
- self.class.send(:class_variable_get, :@@blah) << 4
1696
+ model::Foo << 4
1767
1697
  end
1768
1698
  def _remove_attribute(v)
1769
- self.class.send(:class_variable_get, :@@blah) << 5
1699
+ model::Foo << 5
1770
1700
  end
1771
1701
  def blah(x)
1772
- self.class.send(:class_variable_get, :@@blah) << x.pk
1702
+ model::Foo << x.pk
1773
1703
  end
1774
1704
  def blahr(x)
1775
- self.class.send(:class_variable_get, :@@blah) << 6
1705
+ model::Foo << 6
1776
1706
  end
1777
1707
  end
1778
1708
  p = @c2.load(:id=>10)
@@ -1788,17 +1718,12 @@ describe Sequel::Model, "one_to_many" do
1788
1718
  h = []
1789
1719
  @c2.one_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
1790
1720
  @c2.class_eval do
1791
- class_variable_set(:@@blah, h)
1721
+ self::Foo = h
1792
1722
  def al(v)
1793
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk}
1794
- end
1795
- end
1796
- @c1.class_eval do
1797
- def @dataset.fetch_rows(sql)
1798
- yield({:id=>20})
1799
- yield({:id=>30})
1723
+ v.each{|x| model::Foo << x.pk}
1800
1724
  end
1801
1725
  end
1726
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}]
1802
1727
  p = @c2.load(:id=>10, :parent_id=>20)
1803
1728
  attributes = p.attributes
1804
1729
  h.should == [[10, [20, 30]], 20, 30]
@@ -1845,23 +1770,13 @@ describe Sequel::Model, "one_to_many" do
1845
1770
  end
1846
1771
 
1847
1772
  describe Sequel::Model, "many_to_many" do
1848
-
1849
1773
  before do
1850
- MODEL_DB.reset
1851
-
1852
1774
  @c1 = Class.new(Sequel::Model(:attributes)) do
1853
1775
  unrestrict_primary_key
1854
1776
  attr_accessor :yyy
1855
1777
  def self.name; 'Attribute'; end
1856
1778
  def self.to_s; 'Attribute'; end
1857
- columns :id, :y
1858
- def _refresh(ds)
1859
- self
1860
- end
1861
-
1862
- def self.[](id)
1863
- load(id.is_a?(Array) ? {:id => id[0], :y => id[1]} : {:id => id})
1864
- end
1779
+ columns :id, :y, :z
1865
1780
  end
1866
1781
 
1867
1782
  @c2 = Class.new(Sequel::Model(:nodes)) do
@@ -1873,98 +1788,66 @@ describe Sequel::Model, "many_to_many" do
1873
1788
  columns :id, :x
1874
1789
  end
1875
1790
  @dataset = @c2.dataset
1791
+ @c1.dataset.autoid = 1
1876
1792
 
1877
- [@c1, @c2].each do |c|
1878
- c.dataset.extend(Module.new {
1879
- def fetch_rows(sql)
1880
- @db << sql
1881
- yield Hash.new
1882
- end
1883
- })
1884
- end
1793
+ [@c1, @c2].each{|c| c.dataset._fetch = {}}
1794
+ MODEL_DB.reset
1885
1795
  end
1886
1796
 
1887
1797
  it "should use implicit key values and join table if omitted" do
1888
1798
  @c2.many_to_many :attributes, :class => @c1
1889
-
1890
- n = @c2.new(:id => 1234)
1891
- a = n.attributes_dataset
1892
- a.should be_a_kind_of(Sequel::Dataset)
1893
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1799
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1894
1800
  end
1895
1801
 
1896
1802
  it "should use implicit class if omitted" do
1897
- class ::Tag < Sequel::Model
1803
+ begin
1804
+ class ::Tag < Sequel::Model; end
1805
+ @c2.many_to_many :tags
1806
+ @c2.new(:id => 1234).tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1807
+ ensure
1808
+ Object.send(:remove_const, :Tag)
1898
1809
  end
1899
-
1900
- @c2.many_to_many :tags
1901
-
1902
- n = @c2.new(:id => 1234)
1903
- a = n.tags_dataset
1904
- a.should be_a_kind_of(Sequel::Dataset)
1905
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1906
1810
  end
1907
1811
 
1908
1812
  it "should use class inside module if given as a string" do
1909
- module ::Historical
1910
- class Tag < Sequel::Model
1813
+ begin
1814
+ module ::Historical
1815
+ class Tag < Sequel::Model; end
1911
1816
  end
1817
+ @c2.many_to_many :tags, :class=>'::Historical::Tag'
1818
+ @c2.new(:id => 1234).tags_dataset.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1819
+ ensure
1820
+ Object.send(:remove_const, :Historical)
1912
1821
  end
1913
-
1914
- @c2.many_to_many :tags, :class=>'::Historical::Tag'
1915
-
1916
- n = @c2.new(:id => 1234)
1917
- a = n.tags_dataset
1918
- a.should be_a_kind_of(Sequel::Dataset)
1919
- a.sql.should == 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON ((nodes_tags.tag_id = tags.id) AND (nodes_tags.node_id = 1234))'
1920
1822
  end
1921
1823
 
1922
- it "should use explicit key values and join table if given" do
1923
- @c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
1924
-
1925
- n = @c2.new(:id => 1234)
1926
- a = n.attributes_dataset
1927
- a.should be_a_kind_of(Sequel::Dataset)
1928
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
1824
+ it "should respect :eager_loader_predicate_key when lazily loading" do
1825
+ @c2.many_to_many :attributes, :class => @c1, :eager_loading_predicate_key=>Sequel.subscript(:attributes_nodes__node_id, 0)
1826
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id[0] = 1234))'
1929
1827
  end
1930
1828
 
1931
- it "should handle an aliased join table when eager loading" do
1932
- r = @c2.many_to_many(:attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes)
1933
-
1934
- a = @c2.eager_loading_dataset(r, @c2.dataset, [:id], nil)
1935
- a.sql.should == 'SELECT id, attributes_nodes.node_id AS x_foreign_key_x FROM nodes'
1829
+ it "should use explicit key values and join table if given" do
1830
+ @c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
1831
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON ((attribute2node.attributeid = attributes.id) AND (attribute2node.nodeid = 1234))'
1936
1832
  end
1937
1833
 
1938
1834
  it "should support a conditions option" do
1939
1835
  @c2.many_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1940
- n = @c2.new(:id => 1234)
1941
- a = n.attributes_dataset
1942
- a.should be_a_kind_of(Sequel::Dataset)
1943
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1836
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1837
+
1944
1838
  @c2.many_to_many :attributes, :class => @c1, :conditions => ['a = ?', 32]
1945
- n = @c2.new(:id => 1234)
1946
- a = n.attributes_dataset
1947
- a.should be_a_kind_of(Sequel::Dataset)
1948
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1949
- n.attributes.should == [@c1.load({})]
1839
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (a = 32)'
1840
+ @c2.new(:id => 1234).attributes.should == [@c1.load({})]
1950
1841
  end
1951
1842
 
1952
1843
  it "should support an order option" do
1953
1844
  @c2.many_to_many :attributes, :class => @c1, :order => :blah
1954
-
1955
- n = @c2.new(:id => 1234)
1956
- a = n.attributes_dataset
1957
- a.should be_a_kind_of(Sequel::Dataset)
1958
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah'
1845
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah'
1959
1846
  end
1960
1847
 
1961
1848
  it "should support an array for the order option" do
1962
1849
  @c2.many_to_many :attributes, :class => @c1, :order => [:blah1, :blah2]
1963
-
1964
- n = @c2.new(:id => 1234)
1965
- a = n.attributes_dataset
1966
- a.should be_a_kind_of(Sequel::Dataset)
1967
- a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah1, blah2'
1850
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah1, blah2'
1968
1851
  end
1969
1852
 
1970
1853
  it "should support :left_primary_key and :right_primary_key options" do
@@ -1999,19 +1882,13 @@ describe Sequel::Model, "many_to_many" do
1999
1882
  it "should support a select option" do
2000
1883
  @c2.many_to_many :attributes, :class => @c1, :select => :blah
2001
1884
 
2002
- n = @c2.new(:id => 1234)
2003
- a = n.attributes_dataset
2004
- a.should be_a_kind_of(Sequel::Dataset)
2005
- a.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1885
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2006
1886
  end
2007
1887
 
2008
1888
  it "should support an array for the select option" do
2009
- @c2.many_to_many :attributes, :class => @c1, :select => [:attributes.*, :attribute_nodes__blah2]
1889
+ @c2.many_to_many :attributes, :class => @c1, :select => [Sequel::SQL::ColumnAll.new(:attributes), :attribute_nodes__blah2]
2010
1890
 
2011
- n = @c2.new(:id => 1234)
2012
- a = n.attributes_dataset
2013
- a.should be_a_kind_of(Sequel::Dataset)
2014
- a.sql.should == 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
1891
+ @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2015
1892
  end
2016
1893
 
2017
1894
  it "should accept a block" do
@@ -2021,11 +1898,7 @@ describe Sequel::Model, "many_to_many" do
2021
1898
 
2022
1899
  n = @c2.new(:id => 1234)
2023
1900
  n.xxx = 555
2024
- a = n.attributes
2025
- a.should be_a_kind_of(Array)
2026
- a.size.should == 1
2027
- a.first.should be_a_kind_of(@c1)
2028
- MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555)'
1901
+ n.attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555)'
2029
1902
  end
2030
1903
 
2031
1904
  it "should allow the :order option while accepting a block" do
@@ -2035,20 +1908,9 @@ describe Sequel::Model, "many_to_many" do
2035
1908
 
2036
1909
  n = @c2.new(:id => 1234)
2037
1910
  n.xxx = 555
2038
- a = n.attributes
2039
- a.should be_a_kind_of(Array)
2040
- a.size.should == 1
2041
- a.first.should be_a_kind_of(@c1)
2042
- MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555) ORDER BY blah1, blah2'
1911
+ n.attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 555) ORDER BY blah1, blah2'
2043
1912
  end
2044
1913
 
2045
- it "should have the block argument affect the _dataset method" do
2046
- @c2.many_to_many :attributes, :class => @c1 do |ds|
2047
- ds.filter(:xxx => 456)
2048
- end
2049
- @c2.new(:id => 1234).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (xxx = 456)'
2050
- end
2051
-
2052
1914
  it "should support a :dataset option that is used instead of the default" do
2053
1915
  c1 = @c1
2054
1916
  @c2.many_to_many :attributes, :class => @c1, :dataset=>proc{c1.join_table(:natural, :an).filter(:an__nodeid=>pk)}, :order=> :a, :limit=>10, :select=>nil do |ds|
@@ -2058,11 +1920,8 @@ describe Sequel::Model, "many_to_many" do
2058
1920
  n = @c2.new(:id => 1234)
2059
1921
  n.xxx = 555
2060
1922
  n.attributes_dataset.sql.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
2061
- a = n.attributes
2062
- a.should be_a_kind_of(Array)
2063
- a.size.should == 1
2064
- a.first.should be_a_kind_of(@c1)
2065
- MODEL_DB.sqls.first.should == 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
1923
+ n.attributes.should == [@c1.load({})]
1924
+ MODEL_DB.sqls.should == ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10']
2066
1925
  end
2067
1926
 
2068
1927
  it "should support a :limit option" do
@@ -2077,15 +1936,32 @@ describe Sequel::Model, "many_to_many" do
2077
1936
  @c2.new(:id => 1234).attributes_dataset.opts[:eager].should == {:attributes=>nil}
2078
1937
  end
2079
1938
 
1939
+ it "should handle an aliased join table" do
1940
+ @c2.many_to_many :attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes
1941
+ n = @c2.load(:id => 1234)
1942
+ a = @c1.load(:id => 2345)
1943
+ n.attributes_dataset.sql.should == "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))"
1944
+ a.should == n.add_attribute(a)
1945
+ a.should == n.remove_attribute(a)
1946
+ n.remove_all_attributes
1947
+ sqls = MODEL_DB.sqls
1948
+ ['INSERT INTO attribute2node (node_id, attribute_id) VALUES (1234, 2345)',
1949
+ 'INSERT INTO attribute2node (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.shift))
1950
+ ["DELETE FROM attribute2node WHERE ((node_id = 1234) AND (attribute_id = 2345))",
1951
+ "DELETE FROM attribute2node WHERE ((attribute_id = 2345) AND (node_id = 1234))"].should(include(sqls.shift))
1952
+ sqls.should == ["DELETE FROM attribute2node WHERE (node_id = 1234)"]
1953
+ end
1954
+
2080
1955
  it "should define an add_ method that works on existing records" do
2081
1956
  @c2.many_to_many :attributes, :class => @c1
2082
1957
 
2083
1958
  n = @c2.load(:id => 1234)
2084
1959
  a = @c1.load(:id => 2345)
2085
- a.should == n.add_attribute(a)
1960
+ n.add_attribute(a).should == a
1961
+ sqls = MODEL_DB.sqls
2086
1962
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
2087
- 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
2088
- ].should(include(MODEL_DB.sqls.first))
1963
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.shift))
1964
+ sqls.should == []
2089
1965
  end
2090
1966
 
2091
1967
  it "should define an add_ method that works with a primary key" do
@@ -2093,21 +1969,25 @@ describe Sequel::Model, "many_to_many" do
2093
1969
 
2094
1970
  n = @c2.load(:id => 1234)
2095
1971
  a = @c1.load(:id => 2345)
2096
- a.should == n.add_attribute(2345)
1972
+ @c1.dataset._fetch = {:id=>2345}
1973
+ n.add_attribute(2345).should == a
1974
+ sqls = MODEL_DB.sqls
2097
1975
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
2098
- 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
2099
- ].should(include(MODEL_DB.sqls.first))
1976
+ 'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'].should(include(sqls.pop))
1977
+ sqls.should == ["SELECT * FROM attributes WHERE id = 2345"]
2100
1978
  end
2101
1979
 
2102
1980
  it "should allow passing a hash to the add_ method which creates a new record" do
2103
1981
  @c2.many_to_many :attributes, :class => @c1
2104
1982
 
2105
1983
  n = @c2.load(:id => 1234)
2106
- @c1.load(:id => 1).should == n.add_attribute(:id => 1)
2107
- MODEL_DB.sqls.first.should == 'INSERT INTO attributes (id) VALUES (1)'
1984
+ @c1.dataset._fetch = @c1.instance_dataset._fetch = {:id=>1}
1985
+ n.add_attribute(:id => 1).should == @c1.load(:id => 1)
1986
+ sqls = MODEL_DB.sqls
2108
1987
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 1)',
2109
1988
  'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (1, 1234)'
2110
- ].should(include(MODEL_DB.sqls.last))
1989
+ ].should(include(sqls.pop))
1990
+ sqls.should == ['INSERT INTO attributes (id) VALUES (1)', "SELECT * FROM attributes WHERE (id = 1) LIMIT 1"]
2111
1991
  end
2112
1992
 
2113
1993
  it "should define a remove_ method that works on existing records" do
@@ -2115,8 +1995,8 @@ describe Sequel::Model, "many_to_many" do
2115
1995
 
2116
1996
  n = @c2.new(:id => 1234)
2117
1997
  a = @c1.new(:id => 2345)
2118
- a.should == n.remove_attribute(a)
2119
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
1998
+ n.remove_attribute(a).should == a
1999
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))']
2120
2000
  end
2121
2001
 
2122
2002
  it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
@@ -2127,13 +2007,8 @@ describe Sequel::Model, "many_to_many" do
2127
2007
  it "should accept a primary key for the remove_ method and remove an existing record" do
2128
2008
  @c2.many_to_many :attributes, :class => @c1
2129
2009
  n = @c2.new(:id => 1234)
2130
- ds = @c1.dataset
2131
- def ds.fetch_rows(sql)
2132
- db << sql
2133
- yield({:id=>234})
2134
- end
2135
- MODEL_DB.reset
2136
- @c1.load(:id => 234).should == n.remove_attribute(234)
2010
+ @c1.dataset._fetch = {:id=>234}
2011
+ n.remove_attribute(234).should == @c1.load(:id => 234)
2137
2012
  MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE (attributes.id = 234) LIMIT 1",
2138
2013
  "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2139
2014
  end
@@ -2148,10 +2023,12 @@ describe Sequel::Model, "many_to_many" do
2148
2023
 
2149
2024
  n = @c2.load(:id => 1234).set(:xxx=>5)
2150
2025
  a = @c1.load(:id => 2345).set(:yyy=>8)
2151
- a.should == n.add_attribute(a)
2026
+ n.add_attribute(a).should == a
2027
+ sqls = MODEL_DB.sqls
2152
2028
  ['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 8)',
2153
2029
  'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (8, 5)'
2154
- ].should(include(MODEL_DB.sqls.first))
2030
+ ].should(include(sqls.pop))
2031
+ sqls.should == []
2155
2032
  end
2156
2033
 
2157
2034
  it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
@@ -2166,11 +2043,13 @@ describe Sequel::Model, "many_to_many" do
2166
2043
  end
2167
2044
 
2168
2045
  it "should have the add_ method respect composite keys" do
2169
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2046
+ @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :z]
2170
2047
  n = @c2.load(:id => 1234, :x=>5)
2171
- a = @c1.load(:id => 2345, :y=>8)
2048
+ a = @c1.load(:id => 2345, :z=>8)
2172
2049
  a.should == n.add_attribute(a)
2173
- m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(MODEL_DB.sqls.first)
2050
+ sqls = MODEL_DB.sqls
2051
+ m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(sqls.pop)
2052
+ sqls.should == []
2174
2053
  m.should_not == nil
2175
2054
  map = {'l1'=>1234, 'l2'=>5, 'r1'=>2345, 'r2'=>8}
2176
2055
  %w[l1 l2 r1 r2].each do |x|
@@ -2186,11 +2065,16 @@ describe Sequel::Model, "many_to_many" do
2186
2065
  end
2187
2066
 
2188
2067
  it "should have the add_ method respect composite keys" do
2189
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2068
+ @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :z]
2069
+ @c1.set_primary_key [:id, :z]
2190
2070
  n = @c2.load(:id => 1234, :x=>5)
2191
- a = @c1.load(:id => 2345, :y=>8)
2192
- a.should == n.add_attribute([2345, 8])
2193
- MODEL_DB.sqls.first.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
2071
+ a = @c1.load(:id => 2345, :z=>8)
2072
+ @c1.dataset._fetch = {:id => 2345, :z=>8}
2073
+ n.add_attribute([2345, 8]).should == a
2074
+ sqls = MODEL_DB.sqls
2075
+ sqls.shift.should =~ /SELECT \* FROM attributes WHERE \(\((id|z) = (8|2345)\) AND \((id|z) = (8|2345)\)\) LIMIT 1/
2076
+ sqls.pop.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
2077
+ sqls.should == []
2194
2078
  end
2195
2079
 
2196
2080
  it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
@@ -2198,14 +2082,14 @@ describe Sequel::Model, "many_to_many" do
2198
2082
 
2199
2083
  n = @c2.new(:id => 1234, :xxx=>5)
2200
2084
  a = @c1.new(:id => 2345, :yyy=>8)
2201
- a.should == n.remove_attribute(a)
2202
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))'
2085
+ n.remove_attribute(a).should == a
2086
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))']
2203
2087
  end
2204
2088
 
2205
2089
  it "should have the remove_ method respect composite keys" do
2206
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2090
+ @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :z]
2207
2091
  n = @c2.load(:id => 1234, :x=>5)
2208
- a = @c1.load(:id => 2345, :y=>8)
2092
+ a = @c1.load(:id => 2345, :z=>8)
2209
2093
  a.should == n.remove_attribute(a)
2210
2094
  MODEL_DB.sqls.should == ["DELETE FROM attributes_nodes WHERE ((l1 = 1234) AND (l2 = 5) AND (r1 = 2345) AND (r2 = 8))"]
2211
2095
  end
@@ -2214,17 +2098,12 @@ describe Sequel::Model, "many_to_many" do
2214
2098
  @c1.set_primary_key [:id, :y]
2215
2099
  @c2.many_to_many :attributes, :class => @c1
2216
2100
  n = @c2.new(:id => 1234)
2217
- ds = @c1.dataset
2218
- def ds.fetch_rows(sql)
2219
- db << sql
2220
- yield({:id=>234, :y=>8})
2221
- end
2222
- MODEL_DB.reset
2101
+ @c1.dataset._fetch = {:id=>234, :y=>8}
2223
2102
  @c1.load(:id => 234, :y=>8).should == n.remove_attribute([234, 8])
2103
+ sqls = MODEL_DB.sqls
2224
2104
  ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.id = 234) AND (attributes.y = 8)) LIMIT 1",
2225
- "SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.y = 8) AND (attributes.id = 234)) LIMIT 1"].should include(MODEL_DB.sqls.first)
2226
- MODEL_DB.sqls.last.should == "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"
2227
- MODEL_DB.sqls.length.should == 2
2105
+ "SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) WHERE ((attributes.y = 8) AND (attributes.id = 234)) LIMIT 1"].should include(sqls.shift)
2106
+ sqls.should == ["DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2228
2107
  end
2229
2108
 
2230
2109
  it "should raise an error if the model object doesn't have a valid primary key" do
@@ -2242,6 +2121,7 @@ describe Sequel::Model, "many_to_many" do
2242
2121
  n = @c1.new
2243
2122
  a = @c2.load(:id=>123)
2244
2123
  n.new?.should == true
2124
+ @c1.dataset._fetch = {:id=>1}
2245
2125
  a.add_attribute(n)
2246
2126
  n.new?.should == false
2247
2127
  end
@@ -2250,7 +2130,7 @@ describe Sequel::Model, "many_to_many" do
2250
2130
  @c2.many_to_many :attributes, :class => @c1
2251
2131
  n = @c1.new
2252
2132
  a = @c2.load(:id=>123)
2253
- def n.valid?(opts); false; end
2133
+ def n.validate() errors.add(:id, 'foo') end
2254
2134
  proc{a.add_attribute(n)}.should raise_error(Sequel::ValidationFailed)
2255
2135
  end
2256
2136
 
@@ -2259,7 +2139,7 @@ describe Sequel::Model, "many_to_many" do
2259
2139
  n = @c1.new
2260
2140
  n.raise_on_save_failure = false
2261
2141
  a = @c2.load(:id=>123)
2262
- def n.valid?(opts); false; end
2142
+ def n.validate() errors.add(:id, 'foo') end
2263
2143
  proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
2264
2144
  end
2265
2145
 
@@ -2267,7 +2147,8 @@ describe Sequel::Model, "many_to_many" do
2267
2147
  @c2.many_to_many :attributes, :class => @c1, :validate=>false
2268
2148
  n = @c1.new
2269
2149
  a = @c2.load(:id=>123)
2270
- def n.valid?(opts); false; end
2150
+ def n.validate() errors.add(:id, 'foo') end
2151
+ @c1.dataset._fetch = {:id=>1}
2271
2152
  a.add_attribute(n)
2272
2153
  n.new?.should == false
2273
2154
  end
@@ -2282,31 +2163,23 @@ describe Sequel::Model, "many_to_many" do
2282
2163
  it "should provide an array with all members of the association" do
2283
2164
  @c2.many_to_many :attributes, :class => @c1
2284
2165
 
2285
- n = @c2.new(:id => 1234)
2286
- atts = n.attributes
2287
- atts.should be_a_kind_of(Array)
2288
- atts.size.should == 1
2289
- atts.first.should be_a_kind_of(@c1)
2290
-
2291
- MODEL_DB.sqls.first.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))'
2166
+ @c2.new(:id => 1234).attributes.should == [@c1.load({})]
2167
+ MODEL_DB.sqls.should == ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))']
2292
2168
  end
2293
2169
 
2294
2170
  it "should set cached instance variable when accessed" do
2295
2171
  @c2.many_to_many :attributes, :class => @c1
2296
2172
 
2297
2173
  n = @c2.new(:id => 1234)
2298
- MODEL_DB.reset
2299
2174
  n.associations.include?(:attributes).should == false
2300
2175
  atts = n.attributes
2301
2176
  atts.should == n.associations[:attributes]
2302
- MODEL_DB.sqls.length.should == 1
2303
2177
  end
2304
2178
 
2305
2179
  it "should use cached instance variable if available" do
2306
2180
  @c2.many_to_many :attributes, :class => @c1
2307
2181
 
2308
2182
  n = @c2.new(:id => 1234)
2309
- MODEL_DB.reset
2310
2183
  n.associations[:attributes] = 42
2311
2184
  n.attributes.should == 42
2312
2185
  MODEL_DB.sqls.should == []
@@ -2316,18 +2189,16 @@ describe Sequel::Model, "many_to_many" do
2316
2189
  @c2.many_to_many :attributes, :class => @c1
2317
2190
 
2318
2191
  n = @c2.new(:id => 1234)
2319
- MODEL_DB.reset
2320
2192
  n.associations[:attributes] = 42
2321
2193
  n.attributes(true).should_not == 42
2322
- MODEL_DB.sqls.length.should == 1
2194
+ MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234))"]
2323
2195
  end
2324
2196
 
2325
2197
  it "should add item to cached instance variable if it exists when calling add_" do
2326
2198
  @c2.many_to_many :attributes, :class => @c1
2327
2199
 
2328
2200
  n = @c2.new(:id => 1234)
2329
- att = @c1.new(:id => 345)
2330
- MODEL_DB.reset
2201
+ att = @c1.load(:id => 345)
2331
2202
  a = []
2332
2203
  n.associations[:attributes] = a
2333
2204
  n.add_attribute(att)
@@ -2339,7 +2210,7 @@ describe Sequel::Model, "many_to_many" do
2339
2210
  @c1.many_to_many :nodes, :class => @c2
2340
2211
 
2341
2212
  n = @c2.new(:id => 1234)
2342
- att = @c1.new(:id => 345)
2213
+ att = @c1.load(:id => 345)
2343
2214
  att.associations[:nodes] = []
2344
2215
  n.add_attribute(att)
2345
2216
  att.nodes.should == [n]
@@ -2349,8 +2220,7 @@ describe Sequel::Model, "many_to_many" do
2349
2220
  @c2.many_to_many :attributes, :class => @c1
2350
2221
 
2351
2222
  n = @c2.new(:id => 1234)
2352
- att = @c1.new(:id => 345)
2353
- MODEL_DB.reset
2223
+ att = @c1.load(:id => 345)
2354
2224
  a = [att]
2355
2225
  n.associations[:attributes] = a
2356
2226
  n.remove_attribute(att)
@@ -2397,13 +2267,13 @@ describe Sequel::Model, "many_to_many" do
2397
2267
  it "should have an remove_all_ method that removes all associations" do
2398
2268
  @c2.many_to_many :attributes, :class => @c1
2399
2269
  @c2.new(:id => 1234).remove_all_attributes
2400
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
2270
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE (node_id = 1234)']
2401
2271
  end
2402
2272
 
2403
2273
  it "should have the remove_all_ method respect the :left_primary_key option" do
2404
2274
  @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
2405
2275
  @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
2406
- MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 5)'
2276
+ MODEL_DB.sqls.should == ['DELETE FROM attributes_nodes WHERE (node_id = 5)']
2407
2277
  end
2408
2278
 
2409
2279
  it "should have the remove_all_ method respect composite keys" do
@@ -2421,10 +2291,9 @@ describe Sequel::Model, "many_to_many" do
2421
2291
 
2422
2292
  it "remove_all should return the array of previously associated items if the cached instance variable exists" do
2423
2293
  @c2.many_to_many :attributes, :class => @c1
2424
- attrib = @c1.new(:id=>3)
2425
- node = @c2.new(:id => 1234)
2426
- d = @c1.dataset
2427
- def d.fetch_rows(s); end
2294
+ attrib = @c1.load(:id=>3)
2295
+ node = @c2.load(:id => 1234)
2296
+ @c1.dataset._fetch = []
2428
2297
  node.attributes.should == []
2429
2298
  node.add_attribute(attrib)
2430
2299
  node.associations[:attributes].should == [attrib]
@@ -2439,11 +2308,9 @@ describe Sequel::Model, "many_to_many" do
2439
2308
  it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
2440
2309
  @c2.many_to_many :attributes, :class => @c1
2441
2310
  @c1.many_to_many :nodes, :class => @c2
2442
- d = @c1.dataset
2443
- def d.fetch_rows(s); end
2444
- d = @c2.dataset
2445
- def d.fetch_rows(s); end
2446
- attrib = @c1.new(:id=>3)
2311
+ @c1.dataset._fetch = []
2312
+ @c2.dataset._fetch = []
2313
+ attrib = @c1.load(:id=>3)
2447
2314
  node = @c2.new(:id => 1234)
2448
2315
  node.attributes.should == []
2449
2316
  attrib.nodes.should == []
@@ -2459,8 +2326,9 @@ describe Sequel::Model, "many_to_many" do
2459
2326
  o.add_attribute(@c1.load(:id=>44))
2460
2327
  o.remove_attribute(@c1.load(:id=>45))
2461
2328
  o.remove_all_attributes
2462
- MODEL_DB.sqls.first =~ /INSERT INTO attributes_nodes \((node_id|attribute_id|x), (node_id|attribute_id|x), (node_id|attribute_id|x)\) VALUES \((1234|123|44), (1234|123|44), (1234|123|44)\)/
2463
- MODEL_DB.sqls[1..-1].should == ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2329
+ sqls = MODEL_DB.sqls
2330
+ sqls.shift =~ /INSERT INTO attributes_nodes \((node_id|attribute_id|x), (node_id|attribute_id|x), (node_id|attribute_id|x)\) VALUES \((1234|123|44), (1234|123|44), (1234|123|44)\)/
2331
+ sqls.should == ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2464
2332
  "DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234))"]
2465
2333
  end
2466
2334
 
@@ -2543,18 +2411,18 @@ describe Sequel::Model, "many_to_many" do
2543
2411
  h = []
2544
2412
  @c2.many_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
2545
2413
  @c2.class_eval do
2546
- class_variable_set(:@@blah, h)
2414
+ self::Foo = h
2547
2415
  def _add_attribute(v)
2548
- self.class.send(:class_variable_get, :@@blah) << 4
2416
+ model::Foo << 4
2549
2417
  end
2550
2418
  def _remove_attribute(v)
2551
- self.class.send(:class_variable_get, :@@blah) << 5
2419
+ model::Foo << 5
2552
2420
  end
2553
2421
  def blah(x)
2554
- self.class.send(:class_variable_get, :@@blah) << x.pk
2422
+ model::Foo << x.pk
2555
2423
  end
2556
2424
  def blahr(x)
2557
- self.class.send(:class_variable_get, :@@blah) << 6
2425
+ model::Foo << 6
2558
2426
  end
2559
2427
  end
2560
2428
  p = @c2.load(:id=>10)
@@ -2570,17 +2438,12 @@ describe Sequel::Model, "many_to_many" do
2570
2438
  h = []
2571
2439
  @c2.many_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
2572
2440
  @c2.class_eval do
2573
- class_variable_set(:@@blah, h)
2441
+ self::Foo = h
2574
2442
  def al(v)
2575
- v.each{|x| self.class.send(:class_variable_get, :@@blah) << x.pk}
2576
- end
2577
- end
2578
- @c1.class_eval do
2579
- def @dataset.fetch_rows(sql)
2580
- yield({:id=>20})
2581
- yield({:id=>30})
2443
+ v.each{|x| model::Foo << x.pk}
2582
2444
  end
2583
2445
  end
2446
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}]
2584
2447
  p = @c2.load(:id=>10, :parent_id=>20)
2585
2448
  attributes = p.attributes
2586
2449
  h.should == [[10, [20, 30]], 20, 30]
@@ -2623,14 +2486,7 @@ describe Sequel::Model, "many_to_many" do
2623
2486
 
2624
2487
  it "should support a :uniq option that removes duplicates from the association" do
2625
2488
  @c2.many_to_many :attributes, :class => @c1, :uniq=>true
2626
- @c1.class_eval do
2627
- def @dataset.fetch_rows(sql)
2628
- yield({:id=>20})
2629
- yield({:id=>30})
2630
- yield({:id=>20})
2631
- yield({:id=>30})
2632
- end
2633
- end
2489
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
2634
2490
  @c2.load(:id=>10, :parent_id=>20).attributes.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
2635
2491
  end
2636
2492
 
@@ -2651,76 +2507,733 @@ describe Sequel::Model, "many_to_many" do
2651
2507
  @c2.many_to_many :attributes, :class => @c1 do |ds|
2652
2508
  ds.filter(:join_table_att=>3)
2653
2509
  end
2654
- @c1.dataset.instance_eval do
2655
- def fetch_rows(sql, &block)
2656
- db.sqls << sql
2657
- yield(:id=>2)
2658
- end
2659
- end
2510
+ @c1.dataset._fetch = {:id=>2}
2660
2511
  @c2.load(:id=>1).remove_attribute(2)
2661
2512
  MODEL_DB.sqls.should == ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1)) WHERE ((join_table_att = 3) AND (attributes.id = 2)) LIMIT 1",
2662
2513
  "DELETE FROM attributes_nodes WHERE ((node_id = 1) AND (attribute_id = 2))"]
2663
2514
  end
2664
2515
  end
2665
2516
 
2666
- describe Sequel::Model, " association reflection methods" do
2517
+ describe "Filtering by associations" do
2667
2518
  before do
2668
- MODEL_DB.reset
2669
- @c1 = Class.new(Sequel::Model(:nodes)) do
2670
- def self.name; 'Node'; end
2671
- def self.to_s; 'Node'; end
2519
+ @Album = Class.new(Sequel::Model(:albums))
2520
+ artist = @Artist = Class.new(Sequel::Model(:artists))
2521
+ tag = @Tag = Class.new(Sequel::Model(:tags))
2522
+ track = @Track = Class.new(Sequel::Model(:tracks))
2523
+ album_info = @AlbumInfo = Class.new(Sequel::Model(:album_infos))
2524
+ @Artist.columns :id, :id1, :id2
2525
+ @Tag.columns :id, :tid1, :tid2
2526
+ @Track.columns :id, :album_id, :album_id1, :album_id2
2527
+ @AlbumInfo.columns :id, :album_id, :album_id1, :album_id2
2528
+ @Album.class_eval do
2529
+ columns :id, :id1, :id2, :artist_id, :artist_id1, :artist_id2
2530
+ many_to_one :artist, :class=>artist
2531
+ one_to_many :tracks, :class=>track, :key=>:album_id
2532
+ one_to_one :album_info, :class=>album_info, :key=>:album_id
2533
+ many_to_many :tags, :class=>tag, :left_key=>:album_id, :join_table=>:albums_tags
2534
+
2535
+ many_to_one :cartist, :class=>artist, :key=>[:artist_id1, :artist_id2], :primary_key=>[:id1, :id2]
2536
+ one_to_many :ctracks, :class=>track, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2537
+ one_to_one :calbum_info, :class=>album_info, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2538
+ many_to_many :ctags, :class=>tag, :left_key=>[:album_id1, :album_id2], :left_primary_key=>[:id1, :id2], :right_key=>[:tag_id1, :tag_id2], :right_primary_key=>[:tid1, :tid2], :join_table=>:albums_tags
2672
2539
  end
2673
2540
  end
2541
+
2542
+ it "should be able to filter on many_to_one associations" do
2543
+ @Album.filter(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id = 3)'
2544
+ end
2545
+
2546
+ it "should be able to filter on one_to_many associations" do
2547
+ @Album.filter(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.id = 3)'
2548
+ end
2549
+
2550
+ it "should be able to filter on one_to_one associations" do
2551
+ @Album.filter(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.id = 3)'
2552
+ end
2553
+
2554
+ it "should be able to filter on many_to_many associations" do
2555
+ @Album.filter(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id = 3) AND (albums_tags.album_id IS NOT NULL))))'
2556
+ end
2557
+
2558
+ it "should be able to filter on many_to_one associations with composite keys" do
2559
+ @Album.filter(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1 = 3) AND (albums.artist_id2 = 4))'
2560
+ end
2561
+
2562
+ it "should be able to filter on one_to_many associations with composite keys" do
2563
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 = 3) AND (albums.id2 = 4))'
2564
+ end
2565
+
2566
+ it "should be able to filter on one_to_one associations with composite keys" do
2567
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 = 3) AND (albums.id2 = 4))'
2568
+ end
2569
+
2570
+ it "should be able to filter on many_to_many associations with composite keys" do
2571
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE ((albums_tags.tag_id1 = 3) AND (albums_tags.tag_id2 = 4) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2572
+ end
2573
+
2574
+ it "should work inside a complex filter" do
2575
+ artist = @Artist.load(:id=>3)
2576
+ @Album.filter{foo & {:artist=>artist}}.sql.should == 'SELECT * FROM albums WHERE (foo AND (albums.artist_id = 3))'
2577
+ track = @Track.load(:album_id=>4)
2578
+ @Album.filter{foo & [[:artist, artist], [:tracks, track]]}.sql.should == 'SELECT * FROM albums WHERE (foo AND (albums.artist_id = 3) AND (albums.id = 4))'
2579
+ end
2580
+
2581
+ it "should raise for an invalid association name" do
2582
+ proc{@Album.filter(:foo=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2583
+ end
2584
+
2585
+ it "should raise for an invalid association type" do
2586
+ @Album.many_to_many :iatags, :clone=>:tags
2587
+ @Album.association_reflection(:iatags)[:type] = :foo
2588
+ proc{@Album.filter(:iatags=>@Tag.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2589
+ end
2590
+
2591
+ it "should raise for an invalid associated object class " do
2592
+ proc{@Album.filter(:tags=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2593
+ end
2594
+
2595
+ it "should raise for an invalid associated object class when multiple objects are used" do
2596
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2597
+ end
2598
+
2599
+ it "should correctly handle case when a multiple value association is used" do
2600
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2601
+ end
2602
+
2603
+ it "should not affect non-association IN/NOT IN filtering with an empty array" do
2604
+ @Album.filter(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (tag_id != tag_id)'
2605
+ @Album.exclude(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (tag_id = tag_id)'
2606
+ end
2607
+
2608
+ it "should work correctly in subclasses" do
2609
+ c = Class.new(@Album)
2610
+ c.many_to_one :sartist, :class=>@Artist
2611
+ c.filter(:sartist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (albums.sartist_id = 3)'
2612
+ end
2613
+
2614
+ it "should be able to exclude on many_to_one associations" do
2615
+ @Album.exclude(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id != 3) OR (albums.artist_id IS NULL))'
2616
+ end
2617
+
2618
+ it "should be able to exclude on one_to_many associations" do
2619
+ @Album.exclude(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.id != 3) OR (albums.id IS NULL))'
2620
+ end
2621
+
2622
+ it "should be able to exclude on one_to_one associations" do
2623
+ @Album.exclude(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.id != 3) OR (albums.id IS NULL))'
2624
+ end
2625
+
2626
+ it "should be able to exclude on many_to_many associations" do
2627
+ @Album.exclude(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id = 3) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2628
+ end
2629
+
2630
+ it "should be able to exclude on many_to_one associations with composite keys" do
2631
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1 != 3) OR (albums.artist_id2 != 4) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2632
+ end
2633
+
2634
+ it "should be able to exclude on one_to_many associations with composite keys" do
2635
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 != 3) OR (albums.id2 != 4) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2636
+ end
2637
+
2638
+ it "should be able to exclude on one_to_one associations with composite keys" do
2639
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1 != 3) OR (albums.id2 != 4) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2640
+ end
2641
+
2642
+ it "should be able to exclude on many_to_many associations with composite keys" do
2643
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE ((albums_tags.tag_id1 = 3) AND (albums_tags.tag_id2 = 4) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2644
+ end
2645
+
2646
+ it "should be able to filter on multiple many_to_one associations" do
2647
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id IN (3, 4))'
2648
+ end
2649
+
2650
+ it "should be able to filter on multiple one_to_many associations" do
2651
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3, 4))'
2652
+ end
2653
+
2654
+ it "should be able to filter on multiple one_to_one associations" do
2655
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3, 4))'
2656
+ end
2657
+
2658
+ it "should be able to filter on multiple many_to_many associations" do
2659
+ @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3, 4)) AND (albums_tags.album_id IS NOT NULL))))'
2660
+ end
2661
+
2662
+ it "should be able to filter on multiple many_to_one associations with composite keys" do
2663
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4), (5, 6)))'
2664
+ end
2665
+
2666
+ it "should be able to filter on multiple one_to_many associations with composite keys" do
2667
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4), (5, 6)))'
2668
+ end
2669
+
2670
+ it "should be able to filter on multiple one_to_one associations with composite keys" do
2671
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4), (5, 6)))'
2672
+ end
2673
+
2674
+ it "should be able to filter on multiple many_to_many associations with composite keys" do
2675
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4), (5, 6))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2676
+ end
2677
+
2678
+ it "should be able to exclude on multiple many_to_one associations" do
2679
+ @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (3, 4)) OR (albums.artist_id IS NULL))'
2680
+ end
2681
+
2682
+ it "should be able to exclude on multiple one_to_many associations" do
2683
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3, 4)) OR (albums.id IS NULL))'
2684
+ end
2685
+
2686
+ it "should be able to exclude on multiple one_to_one associations" do
2687
+ @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3, 4)) OR (albums.id IS NULL))'
2688
+ end
2689
+
2690
+ it "should be able to exclude on multiple many_to_many associations" do
2691
+ @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3, 4)) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2692
+ end
2693
+
2694
+ it "should be able to exclude on multiple many_to_one associations with composite keys" do
2695
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4), (5, 6))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2696
+ end
2697
+
2698
+ it "should be able to exclude on multiple one_to_many associations with composite keys" do
2699
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4), (5, 6))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2700
+ end
2701
+
2702
+ it "should be able to exclude on multiple one_to_one associations with composite keys" do
2703
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4), (5, 6))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2704
+ end
2705
+
2706
+ it "should be able to exclude on multiple many_to_many associations with composite keys" do
2707
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4), (5, 6))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2708
+ end
2709
+
2710
+ it "should be able to handle NULL values when filtering many_to_one associations" do
2711
+ @Album.filter(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2712
+ end
2713
+
2714
+ it "should be able to handle NULL values when filtering one_to_many associations" do
2715
+ @Album.filter(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2716
+ end
2717
+
2718
+ it "should be able to handle NULL values when filtering one_to_one associations" do
2719
+ @Album.filter(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2720
+ end
2721
+
2722
+ it "should be able to handle NULL values when filtering many_to_many associations" do
2723
+ @Album.filter(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2724
+ end
2725
+
2726
+ it "should be able to handle filtering with NULL values for many_to_one associations with composite keys" do
2727
+ @Album.filter(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2728
+ @Album.filter(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2729
+ @Album.filter(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2730
+ end
2731
+
2732
+ it "should be able to filter with NULL values for one_to_many associations with composite keys" do
2733
+ @Album.filter(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2734
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2735
+ @Album.filter(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2736
+ end
2737
+
2738
+ it "should be able to filter with NULL values for one_to_one associations with composite keys" do
2739
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2740
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2741
+ @Album.filter(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2742
+ end
2743
+
2744
+ it "should be able to filter with NULL values for many_to_many associations with composite keys" do
2745
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2746
+ @Album.filter(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2747
+ @Album.filter(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2748
+ end
2749
+
2750
+ it "should be able to handle NULL values when excluding many_to_one associations" do
2751
+ @Album.exclude(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2752
+ end
2753
+
2754
+ it "should be able to handle NULL values when excluding one_to_many associations" do
2755
+ @Album.exclude(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2756
+ end
2757
+
2758
+ it "should be able to handle NULL values when excluding one_to_one associations" do
2759
+ @Album.exclude(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2760
+ end
2761
+
2762
+ it "should be able to handle NULL values when excluding many_to_many associations" do
2763
+ @Album.exclude(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2764
+ end
2765
+
2766
+ it "should be able to handle excluding with NULL values for many_to_one associations with composite keys" do
2767
+ @Album.exclude(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2768
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2769
+ @Album.exclude(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2770
+ end
2771
+
2772
+ it "should be able to excluding with NULL values for one_to_many associations with composite keys" do
2773
+ @Album.exclude(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2774
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2775
+ @Album.exclude(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2776
+ end
2777
+
2778
+ it "should be able to excluding with NULL values for one_to_one associations with composite keys" do
2779
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2780
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2781
+ @Album.exclude(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2782
+ end
2783
+
2784
+ it "should be able to excluding with NULL values for many_to_many associations with composite keys" do
2785
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2786
+ @Album.exclude(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
2787
+ @Album.exclude(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
2788
+ end
2789
+
2790
+ it "should be able to handle NULL values when filtering multiple many_to_one associations" do
2791
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id IN (3))'
2792
+ @Album.filter(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2793
+ end
2794
+
2795
+ it "should be able to handle NULL values when filtering multiple one_to_many associations" do
2796
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3))'
2797
+ @Album.filter(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2798
+ end
2799
+
2800
+ it "should be able to handle NULL values when filtering multiple one_to_one associations" do
2801
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (3))'
2802
+ @Album.filter(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2803
+ end
2804
+
2805
+ it "should be able to handle NULL values when filtering multiple many_to_many associations" do
2806
+ @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3)) AND (albums_tags.album_id IS NOT NULL))))'
2807
+ @Album.filter(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
2808
+ end
2809
+
2810
+ it "should be able to handle NULL values when filtering multiple many_to_one associations with composite keys" do
2811
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4)))'
2812
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4)))'
2813
+ end
2814
+
2815
+ it "should be able handle NULL values when filtering multiple one_to_many associations with composite keys" do
2816
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2817
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2818
+ end
2819
+
2820
+ it "should be able to handle NULL values when filtering multiple one_to_one associations with composite keys" do
2821
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2822
+ @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
2823
+ end
2824
+
2825
+ it "should be able to handle NULL values when filtering multiple many_to_many associations with composite keys" do
2826
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2827
+ @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2828
+ end
2829
+
2830
+ it "should be able to handle NULL values when excluding multiple many_to_one associations" do
2831
+ @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (3)) OR (albums.artist_id IS NULL))'
2832
+ @Album.exclude(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2833
+ end
2834
+
2835
+ it "should be able to handle NULL values when excluding multiple one_to_many associations" do
2836
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3)) OR (albums.id IS NULL))'
2837
+ @Album.exclude(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2838
+ end
2839
+
2840
+ it "should be able to handle NULL values when excluding multiple one_to_one associations" do
2841
+ @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (3)) OR (albums.id IS NULL))'
2842
+ @Album.exclude(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2843
+ end
2844
+
2845
+ it "should be able to handle NULL values when excluding multiple many_to_many associations" do
2846
+ @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3)) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2847
+ @Album.exclude(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
2848
+ end
2849
+
2850
+ it "should be able to handle NULL values when excluding multiple many_to_one associations with composite keys" do
2851
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2852
+ @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2853
+ end
2854
+
2855
+ it "should be able handle NULL values when excluding multiple one_to_many associations with composite keys" do
2856
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2857
+ @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2858
+ end
2859
+
2860
+ it "should be able to handle NULL values when excluding multiple one_to_one associations with composite keys" do
2861
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2862
+ @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2863
+ end
2864
+
2865
+ it "should be able to handle NULL values when excluding multiple many_to_many associations with composite keys" do
2866
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2867
+ @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2868
+ end
2869
+
2870
+ it "should be able to filter on many_to_one association datasets" do
2871
+ @Album.filter(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((x = 1) AND (artists.id IS NOT NULL))))'
2872
+ end
2873
+
2874
+ it "should be able to filter on one_to_many association datasets" do
2875
+ @Album.filter(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((x = 1) AND (tracks.album_id IS NOT NULL))))'
2876
+ end
2877
+
2878
+ it "should be able to filter on one_to_one association datasets" do
2879
+ @Album.filter(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((x = 1) AND (album_infos.album_id IS NOT NULL))))'
2880
+ end
2881
+
2882
+ it "should be able to filter on many_to_many association datasets" do
2883
+ @Album.filter(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_tags.album_id IS NOT NULL))))'
2884
+ end
2885
+
2886
+ it "should be able to filter on many_to_one association datasets with composite keys" do
2887
+ @Album.filter(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((x = 1) AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL))))'
2888
+ end
2889
+
2890
+ it "should be able to filter on one_to_many association datasets with composite keys" do
2891
+ @Album.filter(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((x = 1) AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL))))'
2892
+ end
2893
+
2894
+ it "should be able to filter on one_to_one association datasets with composite keys" do
2895
+ @Album.filter(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((x = 1) AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL))))'
2896
+ end
2897
+
2898
+ it "should be able to filter on many_to_many association datasets with composite keys" do
2899
+ @Album.filter(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN (SELECT tags.tid1, tags.tid2 FROM tags WHERE ((x = 1) AND (tags.tid1 IS NOT NULL) AND (tags.tid2 IS NOT NULL)))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
2900
+ end
2901
+
2902
+ it "should be able to exclude on many_to_one association datasets" do
2903
+ @Album.exclude(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((x = 1) AND (artists.id IS NOT NULL)))) OR (albums.artist_id IS NULL))'
2904
+ end
2905
+
2906
+ it "should be able to exclude on one_to_many association datasets" do
2907
+ @Album.exclude(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((x = 1) AND (tracks.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2908
+ end
2909
+
2910
+ it "should be able to exclude on one_to_one association datasets" do
2911
+ @Album.exclude(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((x = 1) AND (album_infos.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2912
+ end
2913
+
2914
+ it "should be able to exclude on many_to_many association datasets" do
2915
+ @Album.exclude(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
2916
+ end
2917
+
2918
+ it "should be able to exclude on many_to_one association datasets with composite keys" do
2919
+ @Album.exclude(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((x = 1) AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL)))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
2920
+ end
2921
+
2922
+ it "should be able to exclude on one_to_many association datasets with composite keys" do
2923
+ @Album.exclude(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((x = 1) AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2924
+ end
2925
+
2926
+ it "should be able to exclude on one_to_one association datasets with composite keys" do
2927
+ @Album.exclude(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((x = 1) AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2928
+ end
2929
+
2930
+ it "should be able to exclude on many_to_many association datasets with composite keys" do
2931
+ @Album.exclude(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN (SELECT tags.tid1, tags.tid2 FROM tags WHERE ((x = 1) AND (tags.tid1 IS NOT NULL) AND (tags.tid2 IS NOT NULL)))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
2932
+ end
2933
+
2934
+ it "should do a regular IN query if the dataset for a different model is used" do
2935
+ @Album.filter(:artist=>@Album.select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
2936
+ end
2937
+
2938
+ it "should do a regular IN query if a non-model dataset is used" do
2939
+ @Album.filter(:artist=>@Album.db.from(:albums).select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
2940
+ end
2941
+ end
2942
+
2943
+ describe "Sequel::Model Associations with clashing column names" do
2944
+ before do
2945
+ @db = Sequel.mock(:fetch=>{:id=>1, :object_id=>2})
2946
+ @Foo = Class.new(Sequel::Model(@db[:foos]))
2947
+ @Bar = Class.new(Sequel::Model(@db[:bars]))
2948
+ @Foo.columns :id, :object_id
2949
+ @Bar.columns :id, :object_id
2950
+ @Foo.def_column_alias(:obj_id, :object_id)
2951
+ @Bar.def_column_alias(:obj_id, :object_id)
2952
+ @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
2953
+ @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
2954
+ @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>:object_id, :primary_key=>:object_id, :primary_key_method=>:obj_id, :class=>@Foo
2955
+ @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:foo_id, :right_key=>:object_id, :class=>@Bar
2956
+ @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:object_id, :right_key=>:foo_id, :class=>@Foo
2957
+ @foo = @Foo.load(:id=>1, :object_id=>2)
2958
+ @bar = @Bar.load(:id=>1, :object_id=>2)
2959
+ @db.sqls
2960
+ end
2961
+
2962
+ it "should have working regular association methods" do
2963
+ @Bar.first.foo.should == @foo
2964
+ @db.sqls.should == ["SELECT * FROM bars LIMIT 1", "SELECT * FROM foos WHERE (foos.object_id = 2) LIMIT 1"]
2965
+ @Foo.first.bars.should == [@bar]
2966
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_id = 2)"]
2967
+ @Foo.first.bar.should == @bar
2968
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_id = 2) LIMIT 1"]
2969
+ @Foo.first.mtmbars.should == [@bar]
2970
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT bars.* FROM bars INNER JOIN bars_foos ON ((bars_foos.object_id = bars.object_id) AND (bars_foos.foo_id = 2))"]
2971
+ @Bar.first.mtmfoos.should == [@foo]
2972
+ @db.sqls.should == ["SELECT * FROM bars LIMIT 1", "SELECT foos.* FROM foos INNER JOIN bars_foos ON ((bars_foos.foo_id = foos.object_id) AND (bars_foos.object_id = 2))"]
2973
+ end
2974
+
2975
+ it "should have working eager loading methods" do
2976
+ @Bar.eager(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
2977
+ @db.sqls.should == ["SELECT * FROM bars", "SELECT * FROM foos WHERE (foos.object_id IN (2))"]
2978
+ @Foo.eager(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
2979
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_id IN (2))"]
2980
+ @Foo.eager(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
2981
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_id IN (2))"]
2982
+ @db.fetch = [[{:id=>1, :object_id=>2}], [{:id=>1, :object_id=>2, :x_foreign_key_x=>2}]]
2983
+ @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
2984
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT bars.*, bars_foos.foo_id AS x_foreign_key_x FROM bars INNER JOIN bars_foos ON ((bars_foos.object_id = bars.object_id) AND (bars_foos.foo_id IN (2)))"]
2985
+ @db.fetch = [[{:id=>1, :object_id=>2}], [{:id=>1, :object_id=>2, :x_foreign_key_x=>2}]]
2986
+ @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
2987
+ @db.sqls.should == ["SELECT * FROM bars", "SELECT foos.*, bars_foos.object_id AS x_foreign_key_x FROM foos INNER JOIN bars_foos ON ((bars_foos.foo_id = foos.object_id) AND (bars_foos.object_id IN (2)))"]
2988
+ end
2989
+
2990
+ it "should have working eager graphing methods" do
2991
+ @db.fetch = {:id=>1, :object_id=>2, :foo_id=>1, :foo_object_id=>2}
2992
+ @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
2993
+ @db.sqls.should == ["SELECT bars.id, bars.object_id, foo.id AS foo_id, foo.object_id AS foo_object_id FROM bars LEFT OUTER JOIN foos AS foo ON (foo.object_id = bars.object_id)"]
2994
+ @db.fetch = {:id=>1, :object_id=>2, :bars_id=>1, :bars_object_id=>2}
2995
+ @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
2996
+ @db.sqls.should == ["SELECT foos.id, foos.object_id, bars.id AS bars_id, bars.object_id AS bars_object_id FROM foos LEFT OUTER JOIN bars ON (bars.object_id = foos.object_id)"]
2997
+ @db.fetch = {:id=>1, :object_id=>2, :bar_id=>1, :bar_object_id=>2}
2998
+ @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
2999
+ @db.sqls.should == ["SELECT foos.id, foos.object_id, bar.id AS bar_id, bar.object_id AS bar_object_id FROM foos LEFT OUTER JOIN bars AS bar ON (bar.object_id = foos.object_id)"]
3000
+ @db.fetch = {:id=>1, :object_id=>2, :mtmfoos_id=>1, :mtmfoos_object_id=>2}
3001
+ @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
3002
+ @db.sqls.should == ["SELECT bars.id, bars.object_id, mtmfoos.id AS mtmfoos_id, mtmfoos.object_id AS mtmfoos_object_id FROM bars LEFT OUTER JOIN bars_foos ON (bars_foos.object_id = bars.object_id) LEFT OUTER JOIN foos AS mtmfoos ON (mtmfoos.object_id = bars_foos.foo_id)"]
3003
+ @db.fetch = {:id=>1, :object_id=>2, :mtmbars_id=>1, :mtmbars_object_id=>2}
3004
+ @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
3005
+ @db.sqls.should == ["SELECT foos.id, foos.object_id, mtmbars.id AS mtmbars_id, mtmbars.object_id AS mtmbars_object_id FROM foos LEFT OUTER JOIN bars_foos ON (bars_foos.foo_id = foos.object_id) LEFT OUTER JOIN bars AS mtmbars ON (mtmbars.object_id = bars_foos.object_id)"]
3006
+ end
3007
+
3008
+ it "should have working filter by associations with model instances" do
3009
+ @Bar.first(:foo=>@foo).should == @bar
3010
+ @db.sqls.should == ["SELECT * FROM bars WHERE (bars.object_id = 2) LIMIT 1"]
3011
+ @Foo.first(:bars=>@bar).should == @foo
3012
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_id = 2) LIMIT 1"]
3013
+ @Foo.first(:bar=>@bar).should == @foo
3014
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_id = 2) LIMIT 1"]
3015
+ @Foo.first(:mtmbars=>@bar).should == @foo
3016
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars_foos.foo_id FROM bars_foos WHERE ((bars_foos.object_id = 2) AND (bars_foos.foo_id IS NOT NULL)))) LIMIT 1"]
3017
+ @Bar.first(:mtmfoos=>@foo).should == @bar
3018
+ @db.sqls.should == ["SELECT * FROM bars WHERE (bars.object_id IN (SELECT bars_foos.object_id FROM bars_foos WHERE ((bars_foos.foo_id = 2) AND (bars_foos.object_id IS NOT NULL)))) LIMIT 1"]
3019
+ end
3020
+
3021
+ it "should have working modification methods" do
3022
+ b = @Bar.load(:id=>2, :object_id=>3)
3023
+ f = @Foo.load(:id=>2, :object_id=>3)
3024
+ @db.numrows = 1
3025
+
3026
+ @bar.foo = f
3027
+ @bar.obj_id.should == 3
3028
+ @foo.bar = @bar
3029
+ @bar.obj_id.should == 2
3030
+
3031
+ @foo.add_bar(b)
3032
+ @db.fetch = [[{:id=>1, :object_id=>2}, {:id=>2, :object_id=>2}], [{:id=>1, :object_id=>2}]]
3033
+ @foo.bars.should == [@bar, b]
3034
+ @foo.remove_bar(b)
3035
+ @foo.bars.should == [@bar]
3036
+ @foo.remove_all_bars
3037
+ @foo.bars.should == []
3038
+
3039
+ @db.fetch = [[{:id=>1, :object_id=>2}], [], [{:id=>2, :object_id=>2}]]
3040
+ @bar = @Bar.load(:id=>1, :object_id=>2)
3041
+ @foo.mtmbars.should == [@bar]
3042
+ @foo.remove_all_mtmbars
3043
+ @foo.mtmbars.should == []
3044
+ @foo.add_mtmbar(b)
3045
+ @foo.mtmbars.should == [b]
3046
+ @foo.remove_mtmbar(b)
3047
+ @foo.mtmbars.should == []
3048
+
3049
+ @db.fetch = [[{:id=>2, :object_id=>3}], [], [{:id=>2, :object_id=>3}]]
3050
+ @bar.add_mtmfoo(f)
3051
+ @bar.mtmfoos.should == [f]
3052
+ @bar.remove_all_mtmfoos
3053
+ @bar.mtmfoos.should == []
3054
+ @bar.add_mtmfoo(f)
3055
+ @bar.mtmfoos.should == [f]
3056
+ @bar.remove_mtmfoo(f)
3057
+ @bar.mtmfoos.should == []
3058
+ end
3059
+ end
3060
+
3061
+ describe "Sequel::Model Associations with non-column expression keys" do
3062
+ before do
3063
+ @db = Sequel.mock(:fetch=>{:id=>1, :object_ids=>[2]})
3064
+ @Foo = Class.new(Sequel::Model(@db[:foos]))
3065
+ @Bar = Class.new(Sequel::Model(@db[:bars]))
3066
+ @Foo.columns :id, :object_ids
3067
+ @Bar.columns :id, :object_ids
3068
+ m = Module.new{def obj_id; object_ids[0]; end}
3069
+ @Foo.include m
3070
+ @Bar.include m
3071
+
3072
+ @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>Sequel.subscript(:object_ids, 0), :key=>Sequel.subscript(:object_ids, 0), :key_method=>:obj_id, :class=>@Bar
3073
+ @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>Sequel.subscript(:object_ids, 0), :key=>Sequel.subscript(:object_ids, 0), :key_method=>:obj_id, :class=>@Bar
3074
+ @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>Sequel.subscript(:object_ids, 0), :primary_key=>Sequel.subscript(:object_ids, 0), :primary_key_method=>:obj_id, :class=>@Foo
3075
+ @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>Sequel.subscript(:object_ids, 0), :right_primary_key=>Sequel.subscript(:object_ids, 0), :right_primary_key_method=>:obj_id, :left_key=>Sequel.subscript(:foo_ids, 0), :right_key=>Sequel.subscript(:bar_ids, 0), :class=>@Bar
3076
+ @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>Sequel.subscript(:object_ids, 0), :right_primary_key=>Sequel.subscript(:object_ids, 0), :right_primary_key_method=>:obj_id, :left_key=>Sequel.subscript(:bar_ids, 0), :right_key=>Sequel.subscript(:foo_ids, 0), :class=>@Foo
3077
+ @foo = @Foo.load(:id=>1, :object_ids=>[2])
3078
+ @bar = @Bar.load(:id=>1, :object_ids=>[2])
3079
+ @db.sqls
3080
+ end
3081
+
3082
+ it "should have working regular association methods" do
3083
+ @Bar.first.foo.should == @foo
3084
+ @db.sqls.should == ["SELECT * FROM bars LIMIT 1", "SELECT * FROM foos WHERE (foos.object_ids[0] = 2) LIMIT 1"]
3085
+ @Foo.first.bars.should == [@bar]
3086
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_ids[0] = 2)"]
3087
+ @Foo.first.bar.should == @bar
3088
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_ids[0] = 2) LIMIT 1"]
3089
+ @Foo.first.mtmbars.should == [@bar]
3090
+ @db.sqls.should == ["SELECT * FROM foos LIMIT 1", "SELECT bars.* FROM bars INNER JOIN bars_foos ON ((bars_foos.bar_ids[0] = bars.object_ids[0]) AND (bars_foos.foo_ids[0] = 2))"]
3091
+ @Bar.first.mtmfoos.should == [@foo]
3092
+ @db.sqls.should == ["SELECT * FROM bars LIMIT 1", "SELECT foos.* FROM foos INNER JOIN bars_foos ON ((bars_foos.foo_ids[0] = foos.object_ids[0]) AND (bars_foos.bar_ids[0] = 2))"]
3093
+ end
3094
+
3095
+ it "should have working eager loading methods" do
3096
+ @Bar.eager(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
3097
+ @db.sqls.should == ["SELECT * FROM bars", "SELECT * FROM foos WHERE (foos.object_ids[0] IN (2))"]
3098
+ @Foo.eager(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
3099
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_ids[0] IN (2))"]
3100
+ @Foo.eager(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
3101
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_ids[0] IN (2))"]
3102
+ @db.fetch = [[{:id=>1, :object_ids=>[2]}], [{:id=>1, :object_ids=>[2], :x_foreign_key_x=>2}]]
3103
+ @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
3104
+ @db.sqls.should == ["SELECT * FROM foos", "SELECT bars.*, bars_foos.foo_ids[0] AS x_foreign_key_x FROM bars INNER JOIN bars_foos ON ((bars_foos.bar_ids[0] = bars.object_ids[0]) AND (bars_foos.foo_ids[0] IN (2)))"]
3105
+ @db.fetch = [[{:id=>1, :object_ids=>[2]}], [{:id=>1, :object_ids=>[2], :x_foreign_key_x=>2}]]
3106
+ @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
3107
+ @db.sqls.should == ["SELECT * FROM bars", "SELECT foos.*, bars_foos.bar_ids[0] AS x_foreign_key_x FROM foos INNER JOIN bars_foos ON ((bars_foos.foo_ids[0] = foos.object_ids[0]) AND (bars_foos.bar_ids[0] IN (2)))"]
3108
+ end
3109
+
3110
+ it "should have working eager graphing methods" do
3111
+ @db.fetch = {:id=>1, :object_ids=>[2], :foo_id=>1, :foo_object_ids=>[2]}
3112
+ @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
3113
+ @db.sqls.should == ["SELECT bars.id, bars.object_ids, foo.id AS foo_id, foo.object_ids AS foo_object_ids FROM bars LEFT OUTER JOIN foos AS foo ON (foo.object_ids[0] = bars.object_ids[0])"]
3114
+ @db.fetch = {:id=>1, :object_ids=>[2], :bars_id=>1, :bars_object_ids=>[2]}
3115
+ @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
3116
+ @db.sqls.should == ["SELECT foos.id, foos.object_ids, bars.id AS bars_id, bars.object_ids AS bars_object_ids FROM foos LEFT OUTER JOIN bars ON (bars.object_ids[0] = foos.object_ids[0])"]
3117
+ @db.fetch = {:id=>1, :object_ids=>[2], :bar_id=>1, :bar_object_ids=>[2]}
3118
+ @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
3119
+ @db.sqls.should == ["SELECT foos.id, foos.object_ids, bar.id AS bar_id, bar.object_ids AS bar_object_ids FROM foos LEFT OUTER JOIN bars AS bar ON (bar.object_ids[0] = foos.object_ids[0])"]
3120
+ @db.fetch = {:id=>1, :object_ids=>[2], :mtmfoos_id=>1, :mtmfoos_object_ids=>[2]}
3121
+ @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
3122
+ @db.sqls.should == ["SELECT bars.id, bars.object_ids, mtmfoos.id AS mtmfoos_id, mtmfoos.object_ids AS mtmfoos_object_ids FROM bars LEFT OUTER JOIN bars_foos ON (bars_foos.bar_ids[0] = bars.object_ids[0]) LEFT OUTER JOIN foos AS mtmfoos ON (mtmfoos.object_ids[0] = bars_foos.foo_ids[0])"]
3123
+ @db.fetch = {:id=>1, :object_ids=>[2], :mtmbars_id=>1, :mtmbars_object_ids=>[2]}
3124
+ @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
3125
+ @db.sqls.should == ["SELECT foos.id, foos.object_ids, mtmbars.id AS mtmbars_id, mtmbars.object_ids AS mtmbars_object_ids FROM foos LEFT OUTER JOIN bars_foos ON (bars_foos.foo_ids[0] = foos.object_ids[0]) LEFT OUTER JOIN bars AS mtmbars ON (mtmbars.object_ids[0] = bars_foos.bar_ids[0])"]
3126
+ end
3127
+
3128
+ it "should have working filter by associations with model instances" do
3129
+ @Bar.first(:foo=>@foo).should == @bar
3130
+ @db.sqls.should == ["SELECT * FROM bars WHERE (bars.object_ids[0] = 2) LIMIT 1"]
3131
+ @Foo.first(:bars=>@bar).should == @foo
3132
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] = 2) LIMIT 1"]
3133
+ @Foo.first(:bar=>@bar).should == @foo
3134
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] = 2) LIMIT 1"]
3135
+ @Foo.first(:mtmbars=>@bar).should == @foo
3136
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars_foos.foo_ids[0] FROM bars_foos WHERE ((bars_foos.bar_ids[0] = 2) AND (bars_foos.foo_ids[0] IS NOT NULL)))) LIMIT 1"]
3137
+ @Bar.first(:mtmfoos=>@foo).should == @bar
3138
+ @db.sqls.should == ["SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT bars_foos.bar_ids[0] FROM bars_foos WHERE ((bars_foos.foo_ids[0] = 2) AND (bars_foos.bar_ids[0] IS NOT NULL)))) LIMIT 1"]
3139
+ end
3140
+
3141
+ it "should have working filter by associations with model datasets" do
3142
+ @Bar.first(:foo=>@Foo.where(:id=>@foo.id)).should == @bar
3143
+ @db.sqls.should == ["SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT foos.object_ids[0] FROM foos WHERE ((id = 1) AND (foos.object_ids[0] IS NOT NULL)))) LIMIT 1"]
3144
+ @Foo.first(:bars=>@Bar.where(:id=>@bar.id)).should == @foo
3145
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((id = 1) AND (bars.object_ids[0] IS NOT NULL)))) LIMIT 1"]
3146
+ @Foo.first(:bar=>@Bar.where(:id=>@bar.id)).should == @foo
3147
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((id = 1) AND (bars.object_ids[0] IS NOT NULL)))) LIMIT 1"]
3148
+ @Foo.first(:mtmbars=>@Bar.where(:id=>@bar.id)).should == @foo
3149
+ @db.sqls.should == ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars_foos.foo_ids[0] FROM bars_foos WHERE ((bars_foos.bar_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((id = 1) AND (bars.object_ids[0] IS NOT NULL)))) AND (bars_foos.foo_ids[0] IS NOT NULL)))) LIMIT 1"]
3150
+ @Bar.first(:mtmfoos=>@Foo.where(:id=>@foo.id)).should == @bar
3151
+ @db.sqls.should == ["SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT bars_foos.bar_ids[0] FROM bars_foos WHERE ((bars_foos.foo_ids[0] IN (SELECT foos.object_ids[0] FROM foos WHERE ((id = 1) AND (foos.object_ids[0] IS NOT NULL)))) AND (bars_foos.bar_ids[0] IS NOT NULL)))) LIMIT 1"]
3152
+ end
3153
+ end
3154
+
3155
+ describe "Model#pk_or_nil" do
3156
+ before do
3157
+ @m = Class.new(Sequel::Model)
3158
+ @m.columns :id, :x, :y
3159
+ end
2674
3160
 
2675
- it "#all_association_reflections should include all association reflection hashes" do
2676
- @c1.all_association_reflections.should == []
3161
+ it "should be default return the value of the :id column" do
3162
+ m = @m.load(:id => 111, :x => 2, :y => 3)
3163
+ m.pk_or_nil.should == 111
3164
+ end
2677
3165
 
2678
- @c1.associate :many_to_one, :parent, :class => @c1
2679
- @c1.all_association_reflections.collect{|v| v[:name]}.should == [:parent]
2680
- @c1.all_association_reflections.collect{|v| v[:type]}.should == [:many_to_one]
2681
- @c1.all_association_reflections.collect{|v| v[:class]}.should == [@c1]
3166
+ it "should be return the primary key value for custom primary key" do
3167
+ @m.set_primary_key :x
3168
+ m = @m.load(:id => 111, :x => 2, :y => 3)
3169
+ m.pk_or_nil.should == 2
3170
+ end
2682
3171
 
2683
- @c1.associate :one_to_many, :children, :class => @c1
2684
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}
2685
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:name]}.should == [:children, :parent]
2686
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:type]}.should == [:one_to_many, :many_to_one]
2687
- @c1.all_association_reflections.sort_by{|x|x[:name].to_s}.collect{|v| v[:class]}.should == [@c1, @c1]
3172
+ it "should be return the primary key value for composite primary key" do
3173
+ @m.set_primary_key [:y, :x]
3174
+ m = @m.load(:id => 111, :x => 2, :y => 3)
3175
+ m.pk_or_nil.should == [3, 2]
2688
3176
  end
2689
3177
 
2690
- it "#association_reflection should return nil for nonexistent association" do
2691
- @c1.association_reflection(:blah).should == nil
3178
+ it "should raise if no primary key" do
3179
+ @m.set_primary_key nil
3180
+ m = @m.new(:id => 111, :x => 2, :y => 3)
3181
+ m.pk_or_nil.should be_nil
3182
+
3183
+ @m.no_primary_key
3184
+ m = @m.new(:id => 111, :x => 2, :y => 3)
3185
+ m.pk_or_nil.should be_nil
3186
+ end
3187
+ end
3188
+
3189
+ describe Sequel::Model, "#refresh" do
3190
+ before do
3191
+ @c = Class.new(Sequel::Model(:items)) do
3192
+ unrestrict_primary_key
3193
+ columns :id, :x
3194
+ end
3195
+ MODEL_DB.reset
2692
3196
  end
2693
3197
 
2694
- it "#association_reflection should return association reflection hash if association exists" do
2695
- @c1.associate :many_to_one, :parent, :class => @c1
2696
- @c1.association_reflection(:parent).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
2697
- @c1.association_reflection(:parent)[:name].should == :parent
2698
- @c1.association_reflection(:parent)[:type].should == :many_to_one
2699
- @c1.association_reflection(:parent)[:class].should == @c1
3198
+ specify "should remove cached associations" do
3199
+ @c.many_to_one :node, :class=>@c
3200
+ @m = @c.new(:id => 555)
3201
+ @m.associations[:node] = 15
3202
+ @m.reload
3203
+ @m.associations.should == {}
3204
+ end
3205
+ end
3206
+
3207
+ describe "Model#freeze" do
3208
+ before do
3209
+ class ::Album < Sequel::Model
3210
+ columns :id
3211
+ class B < Sequel::Model
3212
+ columns :id, :album_id
3213
+ many_to_one :album, :class=>Album
3214
+ end
3215
+ one_to_one :b, :key=>:album_id, :class=>B
3216
+ end
3217
+ @o = Album.load(:id=>1).freeze
3218
+ MODEL_DB.sqls
3219
+ end
3220
+ after do
3221
+ Object.send(:remove_const, :Album)
3222
+ end
2700
3223
 
2701
- @c1.associate :one_to_many, :children, :class => @c1
2702
- @c1.association_reflection(:children).should be_a_kind_of(Sequel::Model::Associations::AssociationReflection)
2703
- @c1.association_reflection(:children)[:name].should == :children
2704
- @c1.association_reflection(:children)[:type].should == :one_to_many
2705
- @c1.association_reflection(:children)[:class].should == @c1
3224
+ it "should freeze the object's associations" do
3225
+ @o.associations.frozen?.should be_true
2706
3226
  end
2707
3227
 
2708
- it "#associations should include all association names" do
2709
- @c1.associations.should == []
2710
- @c1.associate :many_to_one, :parent, :class => @c1
2711
- @c1.associations.should == [:parent]
2712
- @c1.associate :one_to_many, :children, :class => @c1
2713
- @c1.associations.sort_by{|x|x.to_s}.should == [:children, :parent]
3228
+ it "should not break associations getters" do
3229
+ Album::B.dataset._fetch = {:album_id=>1, :id=>2}
3230
+ @o.b.should == Album::B.load(:id=>2, :album_id=>1)
3231
+ @o.associations[:b].should be_nil
2714
3232
  end
2715
3233
 
2716
- it "association reflections should be copied upon subclasing" do
2717
- @c1.associate :many_to_one, :parent, :class => @c1
2718
- c = Class.new(@c1)
2719
- @c1.associations.should == [:parent]
2720
- c.associations.should == [:parent]
2721
- c.associate :many_to_one, :parent2, :class => @c1
2722
- @c1.associations.should == [:parent]
2723
- c.associations.sort_by{|x| x.to_s}.should == [:parent, :parent2]
2724
- c.instance_methods.map{|x| x.to_s}.should include('parent')
3234
+ it "should not break reciprocal associations" do
3235
+ b = Album::B.load(:id=>2, :album_id=>nil)
3236
+ b.album = @o
3237
+ @o.associations[:b].should be_nil
2725
3238
  end
2726
3239
  end