sequel 3.21.0 → 3.41.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 (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -1,85 +1,83 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "NestedAttributes plugin" do
4
- before do
5
- mods = @mods = []
6
- i = 0
7
- gi = lambda{i}
8
- ii = lambda{i+=1}
9
- ds_mod = Module.new do
10
- def empty?; false; end
11
- define_method(:insert) do |h|
12
- x = ii.call
13
- mods << [:i, first_source, h, x]
14
- x
15
- end
16
- define_method(:insert_select) do |h|
17
- x = ii.call
18
- mods << [:is, first_source, h, x]
19
- h.merge(:id=>x)
20
- end
21
- define_method(:update) do |h|
22
- mods << [:u, first_source, h, literal(opts[:where])]
23
- 1
24
- end
25
- define_method(:delete) do
26
- mods << [:d, first_source, literal(opts[:where])]
27
- 1
28
- end
4
+ def check_sqls(should, is)
5
+ if should.is_a?(Array)
6
+ should.should include(is)
7
+ else
8
+ should.should == is
29
9
  end
30
- db = Sequel::Database.new({})
31
- db.meta_def(:dataset) do |*a|
32
- x = super(*a)
33
- x.extend(ds_mod)
34
- x
35
- end
36
- @c = Class.new(Sequel::Model(db))
10
+ end
11
+
12
+ def check_sql_array(*shoulds)
13
+ sqls = @db.sqls
14
+ sqls.length.should == shoulds.length
15
+ shoulds.zip(sqls){|s, i| check_sqls(s, i)}
16
+ end
17
+
18
+ before do
19
+ @db = Sequel.mock(:autoid=>1, :numrows=>1)
20
+ @c = Class.new(Sequel::Model(@db))
37
21
  @c.plugin :nested_attributes
38
22
  @Artist = Class.new(@c).set_dataset(:artists)
39
23
  @Album = Class.new(@c).set_dataset(:albums)
40
24
  @Tag = Class.new(@c).set_dataset(:tags)
41
- [@Artist, @Album, @Tag].each do |m|
42
- m.dataset.extend(ds_mod)
43
- end
25
+ @Concert = Class.new(@c).set_dataset(:concerts)
26
+ @Artist.plugin :skip_create_refresh
27
+ @Album.plugin :skip_create_refresh
28
+ @Tag.plugin :skip_create_refresh
29
+ @Concert.plugin :skip_create_refresh
44
30
  @Artist.columns :id, :name
45
31
  @Album.columns :id, :name, :artist_id
46
32
  @Tag.columns :id, :name
33
+ @Concert.columns :tour, :date, :artist_id, :playlist
34
+ @Concert.set_primary_key([:tour, :date])
35
+ @Concert.unrestrict_primary_key
47
36
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
37
+ @Artist.one_to_many :concerts, :class=>@Concert, :key=>:artist_id
48
38
  @Artist.one_to_one :first_album, :class=>@Album, :key=>:artist_id
49
39
  @Album.many_to_one :artist, :class=>@Artist
50
40
  @Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
51
41
  @Artist.nested_attributes :albums, :first_album, :destroy=>true, :remove=>true
42
+ @Artist.nested_attributes :concerts, :destroy=>true, :remove=>true
52
43
  @Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
44
+ @db.sqls
53
45
  end
54
46
 
55
47
  it "should support creating new many_to_one objects" do
56
48
  a = @Album.new({:name=>'Al', :artist_attributes=>{:name=>'Ar'}})
57
- @mods.should == []
49
+ @db.sqls.should == []
58
50
  a.save
59
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
51
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
52
+ ["INSERT INTO albums (name, artist_id) VALUES ('Al', 1)", "INSERT INTO albums (artist_id, name) VALUES (1, 'Al')"])
60
53
  end
61
54
 
62
55
  it "should support creating new one_to_one objects" do
63
56
  a = @Artist.new(:name=>'Ar')
64
57
  a.id = 1
65
58
  a.first_album_attributes = {:name=>'Al'}
66
- @mods.should == []
59
+ @db.sqls.should == []
67
60
  a.save
68
- @mods.should == [[:is, :artists, {:name=>"Ar", :id=>1}, 1], [:is, :albums, {:name=>"Al"}, 2], [:u, :albums, {:artist_id=>nil}, "((artist_id = 1) AND (id != 2))"], [:u, :albums, {:name=>"Al", :artist_id=>1}, "(id = 2)"]]
61
+ check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
62
+ "UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
63
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
69
64
  end
70
65
 
71
66
  it "should support creating new one_to_many objects" do
72
67
  a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
73
- @mods.should == []
68
+ @db.sqls.should == []
74
69
  a.save
75
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
70
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
71
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
76
72
  end
77
73
 
78
74
  it "should support creating new many_to_many objects" do
79
75
  a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}]})
80
- @mods.should == []
76
+ @db.sqls.should == []
81
77
  a.save
82
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3]]
78
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
79
+ "INSERT INTO tags (name) VALUES ('T')",
80
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"])
83
81
  end
84
82
 
85
83
  it "should add new objects to the cached association array as soon as the *_attributes= method is called" do
@@ -88,14 +86,64 @@ describe "NestedAttributes plugin" do
88
86
  a.albums.first.tags.should == [@Tag.new(:name=>'T')]
89
87
  end
90
88
 
89
+ it "should support creating new objects with composite primary keys" do
90
+ insert = nil
91
+ @Concert.class_eval do
92
+ define_method :_insert do
93
+ insert = values
94
+ end
95
+ def before_create # Have to define the CPK somehow.
96
+ self.tour = 'To'
97
+ self.date = '2004-04-05'
98
+ super
99
+ end
100
+ end
101
+ a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:playlist=>'Pl'}]})
102
+ @db.sqls.should == []
103
+ a.save
104
+ @db.sqls.should == ["INSERT INTO artists (name) VALUES ('Ar')"]
105
+ insert.should == {:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl'}
106
+ end
107
+
108
+ it "should support creating new objects with specific primary keys if :unmatched_pk => :create is set" do
109
+ @Artist.nested_attributes :albums, :unmatched_pk=>:create
110
+ insert = nil
111
+ @Album.class_eval do
112
+ unrestrict_primary_key
113
+ define_method :_insert do
114
+ insert = values
115
+ end
116
+ end
117
+ a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:id=>7, :name=>'Al'}]})
118
+ @db.sqls.should == []
119
+ a.save
120
+ @db.sqls.should == ["INSERT INTO artists (name) VALUES ('Ar')"]
121
+ insert.should == {:artist_id=>1, :name=>'Al', :id=>7}
122
+ end
123
+
124
+ it "should support creating new objects with specific composite primary keys if :unmatched_pk => :create is set" do
125
+ insert = nil
126
+ @Artist.nested_attributes :concerts, :unmatched_pk=>:create
127
+ @Concert.class_eval do
128
+ define_method :_insert do
129
+ insert = values
130
+ end
131
+ end
132
+ a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl'}]})
133
+ @db.sqls.should == []
134
+ a.save
135
+ @db.sqls.should == ["INSERT INTO artists (name) VALUES ('Ar')"]
136
+ insert.should == {:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl'}
137
+ end
138
+
91
139
  it "should support updating many_to_one objects" do
92
140
  al = @Album.load(:id=>10, :name=>'Al')
93
141
  ar = @Artist.load(:id=>20, :name=>'Ar')
94
142
  al.associations[:artist] = ar
95
143
  al.set(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
96
- @mods.should == []
144
+ @db.sqls.should == []
97
145
  al.save
98
- @mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
146
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
99
147
  end
100
148
 
101
149
  it "should support updating one_to_one objects" do
@@ -103,9 +151,9 @@ describe "NestedAttributes plugin" do
103
151
  ar = @Artist.load(:id=>20, :name=>'Ar')
104
152
  ar.associations[:first_album] = al
105
153
  ar.set(:first_album_attributes=>{:id=>10, :name=>'Al2'})
106
- @mods.should == []
154
+ @db.sqls.should == []
107
155
  ar.save
108
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
156
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
109
157
  end
110
158
 
111
159
  it "should support updating one_to_many objects" do
@@ -113,9 +161,19 @@ describe "NestedAttributes plugin" do
113
161
  ar = @Artist.load(:id=>20, :name=>'Ar')
114
162
  ar.associations[:albums] = [al]
115
163
  ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2'}])
116
- @mods.should == []
164
+ @db.sqls.should == []
117
165
  ar.save
118
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:u, :albums, {:name=>"Al2"}, '(id = 10)']]
166
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
167
+ end
168
+
169
+ it "should support updating one_to_many objects with _delete/_remove flags set to false" do
170
+ al = @Album.load(:id=>10, :name=>'Al')
171
+ ar = @Artist.load(:id=>20, :name=>'Ar')
172
+ ar.associations[:albums] = [al]
173
+ ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :_delete => 'f', :_remove => '0'}])
174
+ @db.sqls.should == []
175
+ ar.save
176
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
119
177
  end
120
178
 
121
179
  it "should support updating many_to_many objects" do
@@ -123,19 +181,39 @@ describe "NestedAttributes plugin" do
123
181
  t = @Tag.load(:id=>20, :name=>'T')
124
182
  a.associations[:tags] = [t]
125
183
  a.set(:tags_attributes=>[{:id=>20, :name=>'T2'}])
126
- @mods.should == []
184
+ @db.sqls.should == []
127
185
  a.save
128
- @mods.should == [[:u, :albums, {:name=>"Al"}, '(id = 10)'], [:u, :tags, {:name=>"T2"}, '(id = 20)']]
186
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
129
187
  end
130
188
 
189
+ it "should support updating many_to_many objects with _delete/_remove flags set to false" do
190
+ a = @Album.load(:id=>10, :name=>'Al')
191
+ t = @Tag.load(:id=>20, :name=>'T')
192
+ a.associations[:tags] = [t]
193
+ a.set(:tags_attributes=>[{:id=>20, :name=>'T2', '_delete' => false, '_remove' => 'F'}])
194
+ @db.sqls.should == []
195
+ a.save
196
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
197
+ end
198
+
199
+ it "should support updating objects with composite primary keys" do
200
+ ar = @Artist.load(:id=>10, :name=>'Ar')
201
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
202
+ ar.associations[:concerts] = [co]
203
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl2'}])
204
+ @db.sqls.should == []
205
+ ar.save
206
+ check_sql_array("UPDATE artists SET name = 'Ar' WHERE (id = 10)", ["UPDATE concerts SET playlist = 'Pl2' WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET playlist = 'Pl2' WHERE ((date = '2004-04-05') AND (tour = 'To'))"])
207
+ end
208
+
131
209
  it "should support removing many_to_one objects" do
132
210
  al = @Album.load(:id=>10, :name=>'Al')
133
211
  ar = @Artist.load(:id=>20, :name=>'Ar')
134
212
  al.associations[:artist] = ar
135
213
  al.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})
136
- @mods.should == []
214
+ @db.sqls.should == []
137
215
  al.save
138
- @mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)']]
216
+ check_sql_array(["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"])
139
217
  end
140
218
 
141
219
  it "should support removing one_to_one objects" do
@@ -143,10 +221,9 @@ describe "NestedAttributes plugin" do
143
221
  ar = @Artist.load(:id=>20, :name=>'Ar')
144
222
  ar.associations[:first_album] = al
145
223
  ar.set(:first_album_attributes=>{:id=>10, :_remove=>'t'})
146
- @mods.should == []
224
+ @db.sqls.should == []
147
225
  ar.save
148
- @mods.should == [[:u, :albums, {:artist_id=>nil}, "(artist_id = 20)"], [:u, :artists, {:name=>"Ar"}, "(id = 20)"]]
149
-
226
+ @db.sqls.should == ["UPDATE albums SET artist_id = NULL WHERE (artist_id = 20)", "UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
150
227
  end
151
228
 
152
229
  it "should support removing one_to_many objects" do
@@ -154,9 +231,12 @@ describe "NestedAttributes plugin" do
154
231
  ar = @Artist.load(:id=>20, :name=>'Ar')
155
232
  ar.associations[:albums] = [al]
156
233
  ar.set(:albums_attributes=>[{:id=>10, :_remove=>'t'}])
157
- @mods.should == []
234
+ @db.sqls.should == []
235
+ @Album.dataset._fetch = {:id=>1}
158
236
  ar.save
159
- @mods.should == [[:u, :albums, {:name=>"Al", :artist_id=>nil}, '(id = 10)'], [:u, :artists, {:name=>"Ar"}, '(id = 20)']]
237
+ check_sql_array("SELECT 1 AS one FROM albums WHERE ((albums.artist_id = 20) AND (id = 10)) LIMIT 1",
238
+ ["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
239
+ "UPDATE artists SET name = 'Ar' WHERE (id = 20)")
160
240
  end
161
241
 
162
242
  it "should support removing many_to_many objects" do
@@ -164,19 +244,33 @@ describe "NestedAttributes plugin" do
164
244
  t = @Tag.load(:id=>20, :name=>'T')
165
245
  a.associations[:tags] = [t]
166
246
  a.set(:tags_attributes=>[{:id=>20, :_remove=>true}])
167
- @mods.should == []
247
+ @db.sqls.should == []
168
248
  a.save
169
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)']]
249
+ @db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)"]
170
250
  end
171
251
 
252
+ it "should support removing objects with composite primary keys" do
253
+ ar = @Artist.load(:id=>10, :name=>'Ar')
254
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
255
+ ar.associations[:concerts] = [co]
256
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_remove=>'t'}])
257
+ @db.sqls.should == []
258
+ @Concert.dataset._fetch = {:id=>1}
259
+ ar.save
260
+ check_sql_array(["SELECT 1 AS one FROM concerts WHERE ((concerts.artist_id = 10) AND (tour = 'To') AND (date = '2004-04-05')) LIMIT 1", "SELECT 1 AS one FROM concerts WHERE ((concerts.artist_id = 10) AND (date = '2004-04-05') AND (tour = 'To')) LIMIT 1"],
261
+ ["UPDATE concerts SET artist_id = NULL, playlist = 'Pl' WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET playlist = 'Pl', artist_id = NULL WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET artist_id = NULL, playlist = 'Pl' WHERE ((date = '2004-04-05') AND (tour = 'To'))", "UPDATE concerts SET playlist = 'Pl', artist_id = NULL WHERE ((date = '2004-04-05') AND (tour = 'To'))"],
262
+ "UPDATE artists SET name = 'Ar' WHERE (id = 10)")
263
+ end
264
+
172
265
  it "should support destroying many_to_one objects" do
173
266
  al = @Album.load(:id=>10, :name=>'Al')
174
267
  ar = @Artist.load(:id=>20, :name=>'Ar')
175
268
  al.associations[:artist] = ar
176
269
  al.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})
177
- @mods.should == []
270
+ @db.sqls.should == []
178
271
  al.save
179
- @mods.should == [[:u, :albums, {:artist_id=>nil, :name=>'Al'}, '(id = 10)'], [:d, :artists, '(id = 20)']]
272
+ check_sql_array(["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
273
+ "DELETE FROM artists WHERE (id = 20)")
180
274
  end
181
275
 
182
276
  it "should support destroying one_to_one objects" do
@@ -184,9 +278,9 @@ describe "NestedAttributes plugin" do
184
278
  ar = @Artist.load(:id=>20, :name=>'Ar')
185
279
  ar.associations[:first_album] = al
186
280
  ar.set(:first_album_attributes=>{:id=>10, :_delete=>'t'})
187
- @mods.should == []
281
+ @db.sqls.should == []
188
282
  ar.save
189
- @mods.should == [[:u, :artists, {:name=>"Ar"}, "(id = 20)"], [:d, :albums, "(id = 10)"]]
283
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
190
284
  end
191
285
 
192
286
  it "should support destroying one_to_many objects" do
@@ -194,9 +288,9 @@ describe "NestedAttributes plugin" do
194
288
  ar = @Artist.load(:id=>20, :name=>'Ar')
195
289
  ar.associations[:albums] = [al]
196
290
  ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
197
- @mods.should == []
291
+ @db.sqls.should == []
198
292
  ar.save
199
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)'], [:d, :albums, '(id = 10)']]
293
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
200
294
  end
201
295
 
202
296
  it "should support destroying many_to_many objects" do
@@ -204,19 +298,29 @@ describe "NestedAttributes plugin" do
204
298
  t = @Tag.load(:id=>20, :name=>'T')
205
299
  a.associations[:tags] = [t]
206
300
  a.set(:tags_attributes=>[{:id=>20, :_delete=>true}])
207
- @mods.should == []
301
+ @db.sqls.should == []
208
302
  a.save
209
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
303
+ @db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
210
304
  end
211
305
 
306
+ it "should support destroying objects with composite primary keys" do
307
+ ar = @Artist.load(:id=>10, :name=>'Ar')
308
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
309
+ ar.associations[:concerts] = [co]
310
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])
311
+ @db.sqls.should == []
312
+ ar.save
313
+ check_sql_array("UPDATE artists SET name = 'Ar' WHERE (id = 10)", ["DELETE FROM concerts WHERE ((tour = 'To') AND (date = '2004-04-05'))", "DELETE FROM concerts WHERE ((date = '2004-04-05') AND (tour = 'To'))"])
314
+ end
315
+
212
316
  it "should support both string and symbol keys in nested attribute hashes" do
213
317
  a = @Album.load(:id=>10, :name=>'Al')
214
318
  t = @Tag.load(:id=>20, :name=>'T')
215
319
  a.associations[:tags] = [t]
216
320
  a.set('tags_attributes'=>[{'id'=>20, '_delete'=>true}])
217
- @mods.should == []
321
+ @db.sqls.should == []
218
322
  a.save
219
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
323
+ @db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
220
324
  end
221
325
 
222
326
  it "should support using a hash instead of an array for to_many nested attributes" do
@@ -224,9 +328,9 @@ describe "NestedAttributes plugin" do
224
328
  t = @Tag.load(:id=>20, :name=>'T')
225
329
  a.associations[:tags] = [t]
226
330
  a.set('tags_attributes'=>{'1'=>{'id'=>20, '_delete'=>true}})
227
- @mods.should == []
331
+ @db.sqls.should == []
228
332
  a.save
229
- @mods.should == [[:d, :at, '((album_id = 10) AND (tag_id = 20))'], [:u, :albums, {:name=>"Al"}, '(id = 10)'], [:d, :tags, '(id = 20)']]
333
+ @db.sqls.should == ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
230
334
  end
231
335
 
232
336
  it "should only allow destroying associated objects if :destroy option is used in the nested_attributes call" do
@@ -263,11 +367,41 @@ describe "NestedAttributes plugin" do
263
367
  ar = @Artist.load(:id=>20, :name=>'Ar')
264
368
  ar.associations[:albums] = [al]
265
369
  ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
266
- @mods.should == []
370
+ @db.sqls.should == []
267
371
  ar.save
268
- @mods.should == [[:u, :artists, {:name=>"Ar"}, '(id = 20)']]
372
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
269
373
  end
270
374
 
375
+ it "should not raise an Error if an unmatched primary key is given, if the :unmatched_pk=>:ignore option is used" do
376
+ @Artist.nested_attributes :albums, :unmatched_pk=>:ignore
377
+ al = @Album.load(:id=>10, :name=>'Al')
378
+ ar = @Artist.load(:id=>20, :name=>'Ar')
379
+ ar.associations[:albums] = [al]
380
+ ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
381
+ @db.sqls.should == []
382
+ ar.save
383
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
384
+ end
385
+
386
+ it "should raise an Error if a composite primary key is given in a nested attribute hash, but no matching associated object exists" do
387
+ ar = @Artist.load(:id=>10, :name=>'Ar')
388
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
389
+ ar.associations[:concerts] = [co]
390
+ proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-04', :_delete=>'t'}])}.should raise_error(Sequel::Error)
391
+ proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])}.should_not raise_error(Sequel::Error)
392
+ end
393
+
394
+ it "should not raise an Error if an unmatched composite primary key is given, if the :strict=>false option is used" do
395
+ @Artist.nested_attributes :concerts, :strict=>false
396
+ ar = @Artist.load(:id=>10, :name=>'Ar')
397
+ co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
398
+ ar.associations[:concerts] = [co]
399
+ ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-06', :_delete=>'t'}])
400
+ @db.sqls.should == []
401
+ ar.save
402
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 10)"]
403
+ end
404
+
271
405
  it "should not save if nested attribute is not valid and should include nested attribute validation errors in the main object's validation errors" do
272
406
  @Artist.class_eval do
273
407
  def validate
@@ -276,10 +410,10 @@ describe "NestedAttributes plugin" do
276
410
  end
277
411
  end
278
412
  a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
279
- @mods.should == []
413
+ @db.sqls.should == []
280
414
  proc{a.save}.should raise_error(Sequel::ValidationFailed)
281
415
  a.errors.full_messages.should == ['artist name cannot be Ar']
282
- @mods.should == []
416
+ @db.sqls.should == []
283
417
  # Should preserve attributes
284
418
  a.artist.name.should == 'Ar'
285
419
  end
@@ -294,9 +428,10 @@ describe "NestedAttributes plugin" do
294
428
  end
295
429
  end
296
430
  a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
297
- @mods.should == []
431
+ @db.sqls.should == []
298
432
  a.save
299
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
433
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
434
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
300
435
  end
301
436
 
302
437
  it "should not attempt to validate nested attributes if the :validate=>false option is passed to save" do
@@ -307,23 +442,25 @@ describe "NestedAttributes plugin" do
307
442
  end
308
443
  end
309
444
  a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
310
- @mods.should == []
445
+ @db.sqls.should == []
311
446
  a.save(:validate=>false)
312
- @mods.should == [[:is, :artists, {:name=>"Ar"}, 1], [:is, :albums, {:name=>"Al", :artist_id=>1}, 2]]
447
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
448
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
313
449
  end
314
450
 
315
451
  it "should not accept nested attributes unless explicitly specified" do
316
452
  @Artist.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
317
453
  proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.should raise_error(Sequel::Error)
318
- @mods.should == []
454
+ @db.sqls.should == []
319
455
  end
320
456
 
321
457
  it "should save when save_changes or update is called if nested attribute associated objects changed but there are no changes to the main object" do
322
458
  al = @Album.load(:id=>10, :name=>'Al')
323
459
  ar = @Artist.load(:id=>20, :name=>'Ar')
324
460
  al.associations[:artist] = ar
461
+ @db.sqls.should == []
325
462
  al.update(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
326
- @mods.should == [[:u, :artists, {:name=>"Ar2"}, '(id = 20)']]
463
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
327
464
  end
328
465
 
329
466
  it "should have a :limit option limiting the amount of entries" do
@@ -331,19 +468,40 @@ describe "NestedAttributes plugin" do
331
468
  arr = [{:name=>'T'}]
332
469
  proc{@Album.new({:name=>'Al', :tags_attributes=>arr*3})}.should raise_error(Sequel::Error)
333
470
  a = @Album.new({:name=>'Al', :tags_attributes=>arr*2})
334
- @mods.should == []
471
+ @db.sqls.should == []
335
472
  a.save
336
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3], [:is, :tags, {:name=>"T"}, 4], [:i, :at, {:album_id=>1, :tag_id=>4}, 5]]
473
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
474
+ "INSERT INTO tags (name) VALUES ('T')",
475
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
476
+ "INSERT INTO tags (name) VALUES ('T')",
477
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
337
478
  end
338
479
 
339
480
  it "should accept a block that each hash gets passed to determine if it should be processed" do
340
481
  @Album.nested_attributes(:tags){|h| h[:name].empty?}
341
482
  a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}, {:name=>''}, {:name=>'T2'}]})
342
- @mods.should == []
483
+ @db.sqls.should == []
343
484
  a.save
344
- @mods.should == [[:is, :albums, {:name=>"Al"}, 1], [:is, :tags, {:name=>"T"}, 2], [:i, :at, {:album_id=>1, :tag_id=>2}, 3], [:is, :tags, {:name=>"T2"}, 4], [:i, :at, {:album_id=>1, :tag_id=>4}, 5]]
485
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
486
+ "INSERT INTO tags (name) VALUES ('T')",
487
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
488
+ "INSERT INTO tags (name) VALUES ('T2')",
489
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
345
490
  end
346
491
 
492
+ it "should accept a :transform block that returns a changed attributes hash" do
493
+ @Album.nested_attributes :tags, :transform=>proc{|parent, hash| hash[:name] << parent.name; hash }
494
+ a = @Album.new(:name => 'Al')
495
+ a.set(:tags_attributes=>[{:name=>'T'}, {:name=>'T2'}])
496
+ @db.sqls.should == []
497
+ a.save
498
+ check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
499
+ "INSERT INTO tags (name) VALUES ('TAl')",
500
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
501
+ "INSERT INTO tags (name) VALUES ('T2Al')",
502
+ ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
503
+ end
504
+
347
505
  it "should return objects created/modified in the internal methods" do
348
506
  @Album.nested_attributes :tags, :remove=>true, :strict=>false
349
507
  objs = []
@@ -355,7 +513,7 @@ describe "NestedAttributes plugin" do
355
513
  a = @Album.new(:name=>'Al')
356
514
  a.associations[:tags] = [@Tag.load(:id=>6, :name=>'A'), @Tag.load(:id=>7, :name=>'A2')]
357
515
  a.tags_attributes = [{:id=>6, :name=>'T'}, {:id=>7, :name=>'T2', :_remove=>true}, {:name=>'T3'}, {:id=>8, :name=>'T4'}, {:id=>9, :name=>'T5', :_remove=>true}]
358
- objs.should == [[@Tag.load(:id=>6, :name=>'T'), :update], [@Tag.load(:id=>7, :name=>'A2'), :remove], [@Tag.new(:name=>'T3'), :create], [nil, :update], [nil, :remove]]
516
+ objs.should == [[@Tag.load(:id=>6, :name=>'T'), :update], [@Tag.load(:id=>7, :name=>'A2'), :remove], [@Tag.new(:name=>'T3'), :create]]
359
517
  end
360
518
 
361
519
  it "should raise an error if updating modifies the associated objects keys" do
@@ -387,9 +545,30 @@ describe "NestedAttributes plugin" do
387
545
  t = @Tag.load(:id=>30, :name=>'T', :number=>10)
388
546
  al.associations[:tags] = [t]
389
547
  al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
390
- @mods.should == []
548
+ @db.sqls.should == []
549
+ al.save
550
+ check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
551
+ "UPDATE tags SET name = 'T2' WHERE (id = 30)",
552
+ "INSERT INTO tags (name) VALUES ('T3')",
553
+ ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
554
+ proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
555
+ proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
556
+ end
557
+
558
+ it "should accept a proc for the :fields option that accepts the associated object and returns an array of fields" do
559
+ @Tag.columns :id, :name, :number
560
+ @Album.nested_attributes :tags, :destroy=>true, :remove=>true, :fields=>proc{|object| object.is_a?(@Tag) ? [:name] : []}
561
+
562
+ al = @Album.load(:id=>10, :name=>'Al')
563
+ t = @Tag.load(:id=>30, :name=>'T', :number=>10)
564
+ al.associations[:tags] = [t]
565
+ al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
566
+ @db.sqls.should == []
391
567
  al.save
392
- @mods.should == [[:u, :albums, {:name=>'Al'}, '(id = 10)'], [:u, :tags, {:name=>'T2'}, '(id = 30)'], [:is, :tags, {:name=>"T3"}, 1], [:i, :at, {:album_id=>10, :tag_id=>1}, 2]]
568
+ check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
569
+ "UPDATE tags SET name = 'T2' WHERE (id = 30)",
570
+ "INSERT INTO tags (name) VALUES ('T3')",
571
+ ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
393
572
  proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
394
573
  proc{al.set(:tags_attributes=>[{:name=>'T2', :number=>3}])}.should raise_error(Sequel::Error)
395
574
  end