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
@@ -0,0 +1,87 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The prepared_statements_associations plugin modifies the regular association
4
+ # load method to use a cached prepared statement to load the associations.
5
+ # It will not work on all associations, but it should skip the use of prepared
6
+ # statements for associations where it will not work, assuming you load the
7
+ # plugin before defining the associations.
8
+ #
9
+ # Usage:
10
+ #
11
+ # # Make all model subclasses more safe when using prepared statements (called before loading subclasses)
12
+ # Sequel::Model.plugin :prepared_statements_associations
13
+ #
14
+ # # Make the Album class more safe when using prepared statements
15
+ # Album.plugin :prepared_statements_associations
16
+ module PreparedStatementsAssociations
17
+ # Synchronize access to the integer sequence so that no two calls get the same integer.
18
+ MUTEX = Mutex.new
19
+
20
+ i = 0
21
+ # This plugin names prepared statements uniquely using an integer sequence, this
22
+ # lambda returns the next integer to use.
23
+ NEXT = lambda{MUTEX.synchronize{i += 1}}
24
+
25
+ module ClassMethods
26
+ # Disable prepared statement use if a block is given, or the :dataset or :conditions
27
+ # options are used, or you are cloning an association.
28
+ def associate(type, name, opts = {}, &block)
29
+ if block || opts[:dataset] || opts[:conditions] || (opts[:clone] && association_reflection(opts[:clone])[:prepared_statement] == false)
30
+ opts = opts.merge(:prepared_statement=>false)
31
+ end
32
+ super(type, name, opts, &block)
33
+ end
34
+ end
35
+
36
+ module InstanceMethods
37
+ private
38
+
39
+ # Return a bound variable hash that maps the keys in +ks+ (qualified by the +table+)
40
+ # to the values of the results of sending the methods in +vs+.
41
+ def association_bound_variable_hash(table, ks, vs)
42
+ Hash[*ks.zip(vs).map{|k, v| [:"#{table}.#{k}", send(v)]}.flatten]
43
+ end
44
+
45
+ # Given an association reflection, return a bound variable hash for the given
46
+ # association for this instance's values.
47
+ def association_bound_variables(opts)
48
+ case opts[:type]
49
+ when :many_to_one
50
+ association_bound_variable_hash(opts.associated_class.table_name, opts.primary_keys, opts[:keys])
51
+ when :one_to_many
52
+ association_bound_variable_hash(opts.associated_class.table_name, opts[:keys], opts[:primary_keys])
53
+ when :many_to_many
54
+ association_bound_variable_hash(opts.join_table_alias, opts[:left_keys], opts[:left_primary_keys])
55
+ when :many_through_many
56
+ association_bound_variable_hash(opts.final_reverse_edge[:alias], Array(opts[:left_key]), opts[:left_primary_keys])
57
+ end
58
+ end
59
+
60
+ # Given an association reflection, return and cache a prepared statement for this association such
61
+ # that, given appropriate bound variables, the prepared statement will work correctly for any
62
+ # instance.
63
+ def association_prepared_statement(opts)
64
+ opts.send(:cached_fetch, :prepared_statement) do
65
+ ps = _associated_dataset(opts, {}).unbind.first.prepare(opts.returns_array? ? :select : :first, :"smpsap_#{NEXT.call}")
66
+ ps.log_sql = true
67
+ ps
68
+ end
69
+ end
70
+
71
+ # If a prepared statement can be used to load the associated objects, execute it to retrieve them. Otherwise,
72
+ # fall back to the default implementation.
73
+ def _load_associated_objects(opts, dynamic_opts={})
74
+ if !opts.can_have_associated_objects?(self) || dynamic_opts[:callback] || (ps = opts[:prepared_statement]) == false
75
+ super
76
+ else
77
+ if bv = association_bound_variables(opts)
78
+ (ps || association_prepared_statement(opts)).call(bv)
79
+ else
80
+ super
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,82 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The prepared_statements_safe plugin modifies the model to reduce the number of
4
+ # prepared statements that can be created, by setting as many columns as possible
5
+ # before creating, and by changing +save_changes+ to save all columns instead of
6
+ # just the changed ones.
7
+ #
8
+ # This plugin depends on the +prepared_statements+ plugin.
9
+ #
10
+ # Usage:
11
+ #
12
+ # # Make all model subclasses more safe when using prepared statements (called before loading subclasses)
13
+ # Sequel::Model.plugin :prepared_statements_safe
14
+ #
15
+ # # Make the Album class more safe when using prepared statements
16
+ # Album.plugin :prepared_statements_safe
17
+ module PreparedStatementsSafe
18
+ # Depend on the prepared_statements plugin
19
+ def self.apply(model)
20
+ model.plugin(:prepared_statements)
21
+ end
22
+
23
+ # Set the column defaults to use when creating on the model.
24
+ def self.configure(model)
25
+ model.send(:set_prepared_statements_column_defaults)
26
+ end
27
+
28
+ module ClassMethods
29
+ # A hash with column symbol keys and default values. Instance's
30
+ # values are merged into this hash before creating to reduce the
31
+ # number of free columns (columns that may or may not be present
32
+ # in the INSERT statement), as the number of prepared statements
33
+ # that can be created is 2^N (where N is the number of free columns).
34
+ attr_reader :prepared_statements_column_defaults
35
+
36
+ def inherited(subclass)
37
+ super
38
+ subclass.instance_variable_set(:@prepared_statements_column_defaults, @prepared_statements_column_defaults) if @prepared_statements_column_defaults && !subclass.prepared_statements_column_defaults
39
+ end
40
+
41
+ # Set the column defaults to use when creating on the subclass.
42
+ def set_dataset(*)
43
+ x = super
44
+ set_prepared_statements_column_defaults
45
+ x
46
+ end
47
+
48
+ private
49
+
50
+ # Set the column defaults based on the database schema. All columns
51
+ # are set to a default value unless they are a primary key column or
52
+ # they don't have a parseable default.
53
+ def set_prepared_statements_column_defaults
54
+ if db_schema
55
+ h = {}
56
+ db_schema.each do |k, v|
57
+ h[k] = v[:ruby_default] if (v[:ruby_default] || !v[:default]) && !v[:primary_key]
58
+ end
59
+ @prepared_statements_column_defaults = h
60
+ end
61
+ end
62
+ end
63
+
64
+ module InstanceMethods
65
+ # Merge the current values into the default values to reduce the number
66
+ # of free columns.
67
+ def before_create
68
+ if v = model.prepared_statements_column_defaults
69
+ set_values(v.merge(values))
70
+ end
71
+ super
72
+ end
73
+
74
+ # Always do a full save of all columns to reduce the number of prepared
75
+ # statements that can be used.
76
+ def save_changes(opts={})
77
+ save(opts) || false if modified?
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,59 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The prepared_statements_with_pk plugin allows Dataset#with_pk for model datasets
4
+ # to use prepared statements by extract the values of previously bound variables
5
+ # using <tt>Dataset#unbind</tt>, and attempting to use a prepared statement if the
6
+ # variables can be unbound correctly. See +Unbinder+ for details about what types of
7
+ # dataset filters can be unbound correctly.
8
+ #
9
+ # This plugin depends on the +prepared_statements+ plugin and should be considered unsafe.
10
+ # Unbinding dataset values cannot be done correctly in all cases, and use of this plugin
11
+ # in cases where not there are variables that are not unbound can lead to an denial of
12
+ # service attack by allocating an arbitrary number of prepared statements. You have been
13
+ # warned.
14
+ #
15
+ # Usage:
16
+ #
17
+ # # Make all model subclasses use prepared statements for Dataset#with_pk (called before loading subclasses)
18
+ # Sequel::Model.plugin :prepared_statements_with_pk
19
+ #
20
+ # # Make the Album class use prepared statements for Dataset#with_pk
21
+ # Album.plugin :prepared_statements_with_pk
22
+ module PreparedStatementsWithPk
23
+ # Depend on the prepared_statements plugin
24
+ def self.apply(model)
25
+ model.plugin(:prepared_statements)
26
+ end
27
+
28
+ module ClassMethods
29
+ private
30
+
31
+ # Return a prepared statement that can be used to lookup a row given a dataset for the row matching
32
+ # the primary key.
33
+ def prepared_lookup_dataset(ds)
34
+ cached_prepared_statement(:lookup_sql, ds.sql){prepare_statement(ds.filter(prepared_statement_key_array(primary_key).map{|k, v| [SQL::QualifiedIdentifier.new(ds.model.table_name, k), v]}), :first)}
35
+ end
36
+ end
37
+
38
+ module DatasetMethods
39
+ # Use a prepared statement to find a row with the matching primary key
40
+ # inside this dataset.
41
+ def with_pk(pk)
42
+ begin
43
+ ds, bv = unbind
44
+ rescue UnbindDuplicate
45
+ super
46
+ else
47
+ begin
48
+ bv = bv.merge!(model.primary_key_hash(pk)){|k, v1, v2| ((v1 == v2) ? v1 : raise(UnbindDuplicate))}
49
+ rescue UnbindDuplicate
50
+ super
51
+ else
52
+ model.send(:prepared_lookup_dataset, ds).call(bv)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -109,7 +109,15 @@ module Sequel
109
109
  ka = opts[:key_alias] ||= :x_root_x
110
110
  t = opts[:cte_name] ||= :t
111
111
  opts[:reciprocal] = nil
112
- c_all = SQL::ColumnAll.new(model.table_name)
112
+ c_all = if model.dataset.recursive_cte_requires_column_aliases?
113
+ # Work around Oracle/ruby-oci8 bug that returns integers as BigDecimals in recursive queries.
114
+ conv_bd = model.db.database_type == :oracle
115
+ col_aliases = model.dataset.columns
116
+ model_table = model.table_name
117
+ col_aliases.map{|c| SQL::QualifiedIdentifier.new(model_table, c)}
118
+ else
119
+ [SQL::ColumnAll.new(model.table_name)]
120
+ end
113
121
 
114
122
  a = opts.merge(opts.fetch(:ancestors, {}))
115
123
  ancestors = a.fetch(:name, :ancestors)
@@ -125,9 +133,9 @@ module Sequel
125
133
  end
126
134
  table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
127
135
  model.from(t => table_alias).
128
- with_recursive(t, base_ds.select_all,
129
- recursive_ds.
130
- select(c_all))
136
+ with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
137
+ recursive_ds.select(*c_all),
138
+ :args=>col_aliases)
131
139
  end
132
140
  aal = Array(a[:after_load])
133
141
  aal << proc do |m, ancs|
@@ -152,7 +160,7 @@ module Sequel
152
160
  end
153
161
  a[:after_load] ||= aal
154
162
  a[:eager_loader] ||= proc do |eo|
155
- id_map = eo[:key_hash][key]
163
+ id_map = eo[:id_map]
156
164
  parent_map = {}
157
165
  children_map = {}
158
166
  eo[:rows].each do |obj|
@@ -163,9 +171,9 @@ module Sequel
163
171
  end
164
172
  r = model.association_reflection(ancestors)
165
173
  base_case = model.filter(prkey=>id_map.keys).
166
- select(SQL::AliasedExpression.new(prkey, ka), c_all)
174
+ select(SQL::AliasedExpression.new(prkey, ka), *c_all)
167
175
  recursive_case = model.join(t, key=>prkey).
168
- select(SQL::QualifiedIdentifier.new(t, ka), c_all)
176
+ select(SQL::QualifiedIdentifier.new(t, ka), *c_all)
169
177
  if c = r[:conditions]
170
178
  (base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
171
179
  (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
@@ -175,7 +183,8 @@ module Sequel
175
183
  elds = model.eager_loading_dataset(r,
176
184
  model.from(t => table_alias).
177
185
  with_recursive(t, base_case,
178
- recursive_case),
186
+ recursive_case,
187
+ :args=>(([ka] + col_aliases) if col_aliases)),
179
188
  r.select,
180
189
  eo[:associations], eo)
181
190
  elds = elds.select_append(ka) unless elds.opts[:select] == nil
@@ -192,7 +201,9 @@ module Sequel
192
201
  (children_map[obj[key]] ||= []) << obj
193
202
  end
194
203
 
195
- if roots = id_map[obj.values.delete(ka)]
204
+ kv = obj.values.delete(ka)
205
+ kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
206
+ if roots = id_map[kv]
196
207
  roots.each do |root|
197
208
  root.associations[ancestors] << obj
198
209
  end
@@ -222,9 +233,9 @@ module Sequel
222
233
  end
223
234
  table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
224
235
  model.from(t => table_alias).
225
- with_recursive(t, base_ds.select_all,
226
- recursive_ds.
227
- select(SQL::ColumnAll.new(model.table_name)))
236
+ with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
237
+ recursive_ds.select(*c_all),
238
+ :args=>col_aliases)
228
239
  end
229
240
  dal = Array(d[:after_load])
230
241
  dal << proc do |m, descs|
@@ -248,7 +259,7 @@ module Sequel
248
259
  end
249
260
  d[:after_load] = dal
250
261
  d[:eager_loader] ||= proc do |eo|
251
- id_map = eo[:key_hash][prkey]
262
+ id_map = eo[:id_map]
252
263
  associations = eo[:associations]
253
264
  parent_map = {}
254
265
  children_map = {}
@@ -259,9 +270,9 @@ module Sequel
259
270
  end
260
271
  r = model.association_reflection(descendants)
261
272
  base_case = model.filter(key=>id_map.keys).
262
- select(SQL::AliasedExpression.new(key, ka), c_all)
273
+ select(SQL::AliasedExpression.new(key, ka), *c_all)
263
274
  recursive_case = model.join(t, prkey=>key).
264
- select(SQL::QualifiedIdentifier.new(t, ka), c_all)
275
+ select(SQL::QualifiedIdentifier.new(t, ka), *c_all)
265
276
  if c = r[:conditions]
266
277
  (base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
267
278
  (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
@@ -276,7 +287,8 @@ module Sequel
276
287
  end
277
288
  table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
278
289
  elds = model.eager_loading_dataset(r,
279
- model.from(t => table_alias).with_recursive(t, base_case, recursive_case),
290
+ model.from(t => table_alias).with_recursive(t, base_case, recursive_case,
291
+ :args=>(([ka] + col_aliases + (level ? [la] : [])) if col_aliases)),
280
292
  r.select,
281
293
  associations, eo)
282
294
  elds = elds.select_append(ka) unless elds.opts[:select] == nil
@@ -296,7 +308,9 @@ module Sequel
296
308
  parent_map[opk] = obj
297
309
  end
298
310
 
299
- if root = id_map[obj.values.delete(ka)].first
311
+ kv = obj.values.delete(ka)
312
+ kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
313
+ if root = id_map[kv].first
300
314
  root.associations[descendants] << obj
301
315
  end
302
316
 
@@ -29,7 +29,7 @@ module Sequel
29
29
  # Drops the table if it exists and then runs create_table. Should probably
30
30
  # not be used except in testing.
31
31
  def create_table!(*args, &block)
32
- drop_table rescue nil
32
+ drop_table?
33
33
  create_table(*args, &block)
34
34
  end
35
35
 
@@ -38,11 +38,16 @@ module Sequel
38
38
  create_table(*args, &block) unless table_exists?
39
39
  end
40
40
 
41
- # Drops table.
41
+ # Drops table. If the table doesn't exist, this will probably raise an error.
42
42
  def drop_table
43
43
  db.drop_table(table_name)
44
44
  end
45
45
 
46
+ # Drops table if it already exists, do nothing if it doesn't exist.
47
+ def drop_table?
48
+ db.drop_table?(table_name)
49
+ end
50
+
46
51
  # Returns table schema created with set_schema for direct descendant of Model.
47
52
  # Does not retreive schema information from the database, see db_schema if you
48
53
  # want that.
@@ -61,7 +66,7 @@ module Sequel
61
66
  # test code or simple examples.
62
67
  def set_schema(name = nil, &block)
63
68
  set_dataset(db[name]) if name
64
- @schema = Sequel::Schema::Generator.new(db, &block)
69
+ @schema = db.create_table_generator(&block)
65
70
  set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
66
71
  end
67
72
 
@@ -11,8 +11,17 @@ module Sequel
11
11
  # set the @deserialized_values entry. This plugin adds a before_save hook
12
12
  # that serializes all @deserialized_values to @values.
13
13
  #
14
- # You can use either marshal, yaml, or json as the serialization format.
15
- # If you use yaml or json, you should require them by yourself.
14
+ # You can specify the serialization format as a pair of serializer/deserializer
15
+ # callable objects. You can also specify the serialization format as a single
16
+ # symbol, if such a symbol has a registered serializer/deserializer pair in the
17
+ # plugin. By default, the plugin registers the :marshal, :yaml, and :json
18
+ # serialization formats. To register your own serialization formats, use
19
+ # Sequel::Plugins::Serialization.register_format.
20
+ # If you use yaml or json format, you need to require the libraries, Sequel
21
+ # does not do the requiring for you.
22
+ #
23
+ # You can specify the columns to serialize when loading the plugin, or later
24
+ # using the serialize_attributes class method.
16
25
  #
17
26
  # Because of how this plugin works, it must be used inside each model class
18
27
  # that needs serialization, after any set_dataset method calls in that class.
@@ -22,56 +31,96 @@ module Sequel
22
31
  # == Example
23
32
  #
24
33
  # require 'sequel'
34
+ # # Require json, as the plugin doesn't require it for you.
25
35
  # require 'json'
36
+ #
37
+ # # Register custom serializer/deserializer pair
38
+ # Sequel::Plugins::Serialization.register_format(:reverse,
39
+ # lambda{|v| v.reverse},
40
+ # lambda{|v| v.reverse})
41
+ #
26
42
  # class User < Sequel::Model
43
+ # # Built-in format support when loading the plugin
27
44
  # plugin :serialization, :json, :permissions
28
- # # or
45
+ #
46
+ # # Built-in format support after loading the plugin using serialize_attributes
29
47
  # plugin :serialization
30
- # serialize_attributes :marshal, :permissions, :attributes
48
+ # serialize_attributes :marshal, :permissions
49
+ #
50
+ # # Use custom registered serialization format just like built-in format
51
+ # serialize_attributes :reverse, :password
52
+ #
53
+ # # Use a custom serializer/deserializer pair without registering
54
+ # serialize_attributes [lambda{|v| v.reverse}, lambda{|v| v.reverse}], :password
31
55
  # end
32
56
  # user = User.create
33
57
  # user.permissions = { :global => 'read-only' }
34
58
  # user.save
35
59
  module Serialization
60
+ # The default serializers supported by the serialization module.
61
+ # Use register_format to add serializers to this hash.
62
+ REGISTERED_FORMATS = {}
63
+
36
64
  # Set up the column readers to do deserialization and the column writers
37
65
  # to save the value in deserialized_values.
38
66
  def self.apply(model, *args)
39
- model.instance_eval{@serialization_map = {}}
67
+ model.instance_eval do
68
+ @deserialization_map = {}
69
+ @serialization_map = {}
70
+ end
40
71
  end
41
72
 
73
+ # Automatically call serialize_attributes with the format and columns unless
74
+ # no columns were provided.
42
75
  def self.configure(model, format=nil, *columns)
43
76
  model.serialize_attributes(format, *columns) unless columns.empty?
44
77
  end
45
78
 
79
+ # Register a serializer/deserializer pair with a format symbol, to allow
80
+ # models to pick this format by name. Both serializer and deserializer
81
+ # should be callable objects.
82
+ def self.register_format(format, serializer, deserializer)
83
+ REGISTERED_FORMATS[format] = [serializer, deserializer]
84
+ end
85
+ register_format(:marshal, lambda{|v| [Marshal.dump(v)].pack('m')}, lambda{|v| Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)})
86
+ register_format(:yaml, lambda{|v| v.to_yaml}, lambda{|v| YAML.load(v)})
87
+ register_format(:json, lambda{|v| v.to_json}, lambda{|v| JSON.parse(v)})
88
+
46
89
  module ClassMethods
47
- # A map of the serialized columns for this model. Keys are column
48
- # symbols, values are serialization formats (:marshal, :yaml, or :json).
90
+ # A hash with column name symbols and callable values, with the value
91
+ # called to deserialize the column.
92
+ attr_reader :deserialization_map
93
+
94
+ # A hash with column name symbols and callable values, with the value
95
+ # called to serialize the column.
49
96
  attr_reader :serialization_map
50
97
 
51
98
  # Module to store the serialized column accessor methods, so they can
52
99
  # call be overridden and call super to get the serialization behavior
53
100
  attr_accessor :serialization_module
54
101
 
55
- # Copy the serialization format and columns to serialize into the subclass.
102
+ # Copy the serialization_map and deserialization map into the subclass.
56
103
  def inherited(subclass)
57
104
  super
58
105
  sm = serialization_map.dup
59
- subclass.instance_eval{@serialization_map = sm}
60
- end
61
-
62
- # The first value in the serialization map. This is only for
63
- # backwards compatibility, use serialization_map in new code.
64
- def serialization_format
65
- serialization_map.values.first
106
+ dsm = deserialization_map.dup
107
+ subclass.instance_eval do
108
+ @deserialization_map = dsm
109
+ @serialization_map = sm
110
+ end
66
111
  end
67
112
 
68
113
  # Create instance level reader that deserializes column values on request,
69
- # and instance level writer that stores new deserialized value in deserialized
70
- # columns
114
+ # and instance level writer that stores new deserialized values.
71
115
  def serialize_attributes(format, *columns)
72
- raise(Error, "Unsupported serialization format (#{format}), should be :marshal, :yaml, or :json") unless [:marshal, :yaml, :json].include?(format)
116
+ if format.is_a?(Symbol)
117
+ unless format = REGISTERED_FORMATS[format]
118
+ raise(Error, "Unsupported serialization format: #{format} (valid formats: #{REGISTERED_FORMATS.keys.map{|k| k.inspect}.join})")
119
+ end
120
+ end
121
+ serializer, deserializer = format
73
122
  raise(Error, "No columns given. The serialization plugin requires you specify which columns to serialize") if columns.empty?
74
- define_serialized_attribute_accessor(format, *columns)
123
+ define_serialized_attribute_accessor(serializer, deserializer, *columns)
75
124
  end
76
125
 
77
126
  # The columns that will be serialized. This is only for
@@ -83,12 +132,13 @@ module Sequel
83
132
  private
84
133
 
85
134
  # Add serializated attribute acessor methods to the serialization_module
86
- def define_serialized_attribute_accessor(format, *columns)
135
+ def define_serialized_attribute_accessor(serializer, deserializer, *columns)
87
136
  m = self
88
137
  include(self.serialization_module ||= Module.new) unless serialization_module
89
138
  serialization_module.class_eval do
90
139
  columns.each do |column|
91
- m.serialization_map[column] = format
140
+ m.serialization_map[column] = serializer
141
+ m.deserialization_map[column] = deserializer
92
142
  define_method(column) do
93
143
  if deserialized_values.has_key?(column)
94
144
  deserialized_values[column]
@@ -110,52 +160,51 @@ module Sequel
110
160
  attr_reader :deserialized_values
111
161
 
112
162
  # Set @deserialized_values to the empty hash
113
- def initialize(*args, &block)
163
+ def initialize_set(values)
114
164
  @deserialized_values = {}
115
165
  super
116
166
  end
117
167
 
118
- # Serialize all deserialized values
168
+ # Serialize deserialized values before saving
119
169
  def before_save
120
- deserialized_values.each{|k,v| @values[k] = serialize_value(k, v)}
170
+ serialize_deserialized_values
121
171
  super
122
172
  end
123
173
 
124
- # Empty the deserialized values when refreshing.
125
- def refresh
126
- @deserialized_values = {}
174
+ # Initialization the deserialized values for objects retrieved from the database.
175
+ def set_values(*)
176
+ @deserialized_values ||= {}
127
177
  super
128
178
  end
129
179
 
130
180
  private
131
181
 
132
- # Deserialize the column from either marshal or yaml format
182
+ # Empty the deserialized values when refreshing.
183
+ def _refresh(*)
184
+ @deserialized_values = {}
185
+ super
186
+ end
187
+
188
+ # Deserialize the column value. Called when the model column accessor is called to
189
+ # return a deserialized value.
133
190
  def deserialize_value(column, v)
134
- return v if v.nil?
135
- case model.serialization_map[column]
136
- when :marshal
137
- Marshal.load(v.unpack('m')[0]) rescue Marshal.load(v)
138
- when :yaml
139
- YAML.load v if v
140
- when :json
141
- JSON.parse v if v
142
- else
143
- raise Error, "Bad serialization format (#{model.serialization_map[column].inspect}) for column #{column.inspect}"
191
+ unless v.nil?
192
+ raise Sequel::Error, "no entry in deserialization_map for #{column.inspect}" unless callable = model.deserialization_map[column]
193
+ callable.call(v)
144
194
  end
145
195
  end
146
196
 
147
- # Serialize the column to either marshal or yaml format
197
+ # Serialize all deserialized values
198
+ def serialize_deserialized_values
199
+ deserialized_values.each{|k,v| @values[k] = serialize_value(k, v)}
200
+ end
201
+
202
+ # Serialize the column value. Called before saving to ensure the serialized value
203
+ # is saved in the database.
148
204
  def serialize_value(column, v)
149
- return v if v.nil?
150
- case model.serialization_map[column]
151
- when :marshal
152
- [Marshal.dump(v)].pack('m')
153
- when :yaml
154
- v.to_yaml
155
- when :json
156
- v.to_json
157
- else
158
- raise Error, "Bad serialization format (#{model.serialization_map[column].inspect}) for column #{column.inspect}"
205
+ unless v.nil?
206
+ raise Sequel::Error, "no entry in serialization_map for #{column.inspect}" unless callable = model.serialization_map[column]
207
+ callable.call(v)
159
208
  end
160
209
  end
161
210
  end