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
@@ -40,7 +40,7 @@ module Sequel
40
40
  def full_messages
41
41
  inject([]) do |m, kv|
42
42
  att, errors = *kv
43
- errors.each {|e| m << "#{Array(att).join(ATTRIBUTE_JOINER)} #{e}"}
43
+ errors.each {|e| m << (e.is_a?(LiteralString) ? e : "#{Array(att).join(ATTRIBUTE_JOINER)} #{e}")}
44
44
  m
45
45
  end
46
46
  end
@@ -1,6 +1,18 @@
1
1
  module Sequel
2
2
  # Exception class raised when +raise_on_save_failure+ is set and a before hook returns false
3
- class BeforeHookFailed < Error; end
3
+ # or an around hook doesn't call super or yield.
4
+ class HookFailed < Error
5
+ # The Sequel::Model instance related to this error.
6
+ attr_reader :model
7
+
8
+ def initialize(message, model=nil)
9
+ @model = model
10
+ super(message)
11
+ end
12
+ end
13
+
14
+ # Deprecated alias for HookFailed, kept for backwards compatibility
15
+ BeforeHookFailed = HookFailed
4
16
 
5
17
  # Exception class raised when +require_modification+ is set and an UPDATE or DELETE statement to modify the dataset doesn't
6
18
  # modify a single row.
@@ -11,7 +23,18 @@ module Sequel
11
23
 
12
24
  # Exception class raised when +raise_on_save_failure+ is set and validation fails
13
25
  class ValidationFailed < Error
26
+ # The Sequel::Model object related to this exception.
27
+ attr_reader :model
28
+
29
+ # The Sequel::Model::Errors object related to this exception.
30
+ attr_reader :errors
31
+
14
32
  def initialize(errors)
33
+ if errors.is_a?(Sequel::Model)
34
+ @model = errors
35
+ errors = @model.errors
36
+ end
37
+
15
38
  if errors.respond_to?(:full_messages)
16
39
  @errors = errors
17
40
  super(errors.full_messages.join(', '))
@@ -19,6 +42,5 @@ module Sequel
19
42
  super
20
43
  end
21
44
  end
22
- attr_reader :errors
23
45
  end
24
46
  end
@@ -121,7 +121,7 @@ module Sequel
121
121
  def constantize(s)
122
122
  s = s.to_s
123
123
  return s.constantize if s.respond_to?(:constantize)
124
- raise(NameError, "#{inspect} is not a valid constant name!") unless m = VALID_CONSTANT_NAME_REGEXP.match(s.to_s)
124
+ raise(NameError, "#{s.inspect} is not a valid constant name!") unless m = VALID_CONSTANT_NAME_REGEXP.match(s)
125
125
  Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
126
126
  end
127
127
 
data/lib/sequel/model.rb CHANGED
@@ -11,9 +11,9 @@ module Sequel
11
11
  # with the +Database+ in +source+ to create the
12
12
  # dataset to use)
13
13
  # Dataset :: Sets the dataset for this model to +source+.
14
- # Symbol :: Sets the table name for this model to +source+. The
15
- # class will use the default database for model
16
- # classes in order to create the dataset.
14
+ # other :: Sets the table name for this model to +source+. The
15
+ # class will use the default database for model
16
+ # classes in order to create the dataset.
17
17
  #
18
18
  # The purpose of this method is to set the dataset/database automatically
19
19
  # for a model class, if the table name doesn't match the implicit
@@ -35,13 +35,18 @@ module Sequel
35
35
  # dataset # => DB1[:comments]
36
36
  # end
37
37
  def self.Model(source)
38
- Model::ANONYMOUS_MODEL_CLASSES[source] ||= if source.is_a?(Database)
38
+ if Sequel::Model.cache_anonymous_models && (klass = Sequel.synchronize{Model::ANONYMOUS_MODEL_CLASSES[source]})
39
+ return klass
40
+ end
41
+ klass = if source.is_a?(Database)
39
42
  c = Class.new(Model)
40
43
  c.db = source
41
44
  c
42
45
  else
43
46
  Class.new(Model).set_dataset(source)
44
47
  end
48
+ Sequel.synchronize{Model::ANONYMOUS_MODEL_CLASSES[source] = klass} if Sequel::Model.cache_anonymous_models
49
+ klass
45
50
  end
46
51
 
47
52
  # <tt>Sequel::Model</tt> is an object relational mapper built on top of Sequel core. Each
@@ -49,14 +54,18 @@ module Sequel
49
54
  # called directly on the class. Model datasets return rows as model instances,
50
55
  # which have fairly standard ORM instance behavior.
51
56
  #
52
- # <tt>Sequel::Model</tt> is built completely out of plugins, the only method not part of a
53
- # plugin is the plugin method itself. Plugins can override any class, instance, or
54
- # dataset method defined by a previous plugin and call super to get the default
55
- # behavior.
57
+ # <tt>Sequel::Model</tt> is built completely out of plugins. Plugins can override any class,
58
+ # instance, or dataset method defined by a previous plugin and call super to get the default
59
+ # behavior. By default, <tt>Sequel::Model</tt> loads two plugins, <tt>Sequel::Model</tt>
60
+ # (which is itself a plugin) for the base support, and <tt>Sequel::Model::Associations</tt>
61
+ # for the associations support.
56
62
  #
57
63
  # You can set the +SEQUEL_NO_ASSOCIATIONS+ constant or environment variable to
58
64
  # make Sequel not load the associations plugin by default.
59
65
  class Model
66
+ # Cache anonymous models created by Sequel::Model()
67
+ @cache_anonymous_models = true
68
+
60
69
  # Map that stores model classes created with <tt>Sequel::Model()</tt>, to allow the reopening
61
70
  # of classes when dealing with code reloading.
62
71
  ANONYMOUS_MODEL_CLASSES = {}
@@ -69,7 +78,8 @@ module Sequel
69
78
  EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@db]
70
79
 
71
80
  # Boolean settings that can be modified at the global, class, or instance level.
72
- BOOLEAN_SETTINGS = [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting, :raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_transactions]
81
+ BOOLEAN_SETTINGS = [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting, \
82
+ :raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_after_commit_rollback, :use_transactions]
73
83
 
74
84
  # Hooks that are called before an action. Can return false to not do the action. When
75
85
  # overriding these, it is recommended to call +super+ as the last line of your method,
@@ -78,7 +88,13 @@ module Sequel
78
88
 
79
89
  # Hooks that are called after an action. When overriding these, it is recommended to call
80
90
  # +super+ on the first line of your method, so later hooks are called after earlier hooks.
81
- AFTER_HOOKS = [:after_initialize, :after_create, :after_update, :after_save, :after_destroy, :after_validation]
91
+ AFTER_HOOKS = [:after_initialize, :after_create, :after_update, :after_save, :after_destroy,
92
+ :after_validation, :after_commit, :after_rollback, :after_destroy_commit, :after_destroy_rollback]
93
+
94
+ # Hooks that are called around an action. If overridden, these methods must call super
95
+ # exactly once if the behavior they wrap is desired. The can be used to rescue exceptions
96
+ # raised by the code they wrap or ensure that some behavior is executed no matter what.
97
+ AROUND_HOOKS = [:around_create, :around_update, :around_save, :around_destroy, :around_validation]
82
98
 
83
99
  # Empty instance methods to create that the user can override to get hook/callback behavior.
84
100
  # Just like any other method defined by Sequel, if you override one of these, you should
@@ -96,7 +112,9 @@ module Sequel
96
112
  :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
97
113
  :@simple_pk=>nil, :@simple_table=>nil, :@strict_param_setting=>nil,
98
114
  :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
99
- :@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil}
115
+ :@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil,
116
+ :@use_after_commit_rollback=>nil, :@fast_pk_lookup_sql=>nil,
117
+ :@fast_instance_delete_sql=>nil}
100
118
 
101
119
  # Regular expression that determines if a method name is normal in the sense that
102
120
  # it could be used literally in ruby code without using send. Used to
@@ -126,6 +144,7 @@ module Sequel
126
144
  @strict_param_setting = true
127
145
  @typecast_empty_string_to_nil = true
128
146
  @typecast_on_assignment = true
147
+ @use_after_commit_rollback = true
129
148
  @use_transactions = true
130
149
 
131
150
  Sequel.require %w"default_inflections inflections plugins base exceptions errors", "model"
@@ -0,0 +1,2 @@
1
+ ::SEQUEL_NO_CORE_EXTENSIONS = true unless defined?(::SEQUEL_NO_CORE_EXTENSIONS)
2
+ require 'sequel'
@@ -16,7 +16,14 @@ module Sequel
16
16
  # # Make the Album class active_model compliant
17
17
  # Album.plugin :active_model
18
18
  module ActiveModel
19
- ClassMethods = ::ActiveModel::Naming
19
+ module ClassMethods
20
+ include ::ActiveModel::Naming
21
+
22
+ # Class level cache for to_partial_path.
23
+ def _to_partial_path
24
+ @_to_partial_path ||= "#{underscore(pluralize(to_s))}/#{underscore(demodulize(to_s))}".freeze
25
+ end
26
+ end
20
27
 
21
28
  module InstanceMethods
22
29
  # The default string to join composite primary keys with in to_param.
@@ -56,6 +63,11 @@ module Sequel
56
63
  k.join(to_param_joiner)
57
64
  end
58
65
  end
66
+
67
+ # Returns a string identifying the path associated with the object.
68
+ def to_partial_path
69
+ model._to_partial_path
70
+ end
59
71
 
60
72
  private
61
73
 
@@ -14,7 +14,7 @@ module Sequel
14
14
  #
15
15
  # Note that it uses the singular form of the association name. Also note
16
16
  # that the setter both associates to new primary keys not in the assocation
17
- # and disassociated from primary keys not provided to the method.
17
+ # and disassociates from primary keys not provided to the method.
18
18
  #
19
19
  # This plugin makes modifications directly to the underlying tables,
20
20
  # it does not create or return any model objects, and therefore does
@@ -56,6 +56,7 @@ module Sequel
56
56
  _join_table_dataset(opts).filter(opts[:left_key]=>send(opts[:left_primary_key])).select_map(opts[:right_key])
57
57
  end
58
58
  def_association_pks_setter(opts) do |pks|
59
+ pks = convert_pk_array(opts, pks)
59
60
  checked_transaction do
60
61
  ds = _join_table_dataset(opts).filter(opts[:left_key]=>send(opts[:left_primary_key]))
61
62
  ds.exclude(opts[:right_key]=>pks).delete
@@ -71,17 +72,34 @@ module Sequel
71
72
  super
72
73
  return if opts[:type] == :one_to_one
73
74
  def_association_pks_getter(opts) do
74
- send(opts.dataset_method).select_map(opts[:primary_key])
75
+ send(opts.dataset_method).select_map(opts.associated_class.primary_key)
75
76
  end
76
77
  def_association_pks_setter(opts) do |pks|
78
+ pks = convert_pk_array(opts, pks)
77
79
  checked_transaction do
78
80
  ds = send(opts.dataset_method)
79
- ds.unfiltered.filter(opts[:primary_key]=>pks).update(opts[:key]=>pk)
80
- ds.exclude(opts[:primary_key]=>pks).update(opts[:key]=>nil)
81
+ primary_key = opts.associated_class.primary_key
82
+ key = opts[:key]
83
+ ds.unfiltered.filter(primary_key=>pks).update(key=>pk)
84
+ ds.exclude(primary_key=>pks).update(key=>nil)
81
85
  end
82
86
  end
83
87
  end
84
88
  end
89
+
90
+ module InstanceMethods
91
+ private
92
+
93
+ # If the associated class's primary key column type is integer,
94
+ # typecast all provided values to integer before using them.
95
+ def convert_pk_array(opts, pks)
96
+ if klass = opts.associated_class and sch = klass.db_schema and col = sch[klass.primary_key] and col[:type] == :integer
97
+ pks.map{|pk| model.db.typecast_value(:integer, pk)}
98
+ else
99
+ pks
100
+ end
101
+ end
102
+ end
85
103
  end
86
104
  end
87
105
  end
@@ -53,9 +53,21 @@ module Sequel
53
53
  # The time to live for the cache store, in seconds.
54
54
  attr_reader :cache_ttl
55
55
 
56
- # Set the time to live for the cache store, in seconds (default is 3600, # so 1 hour).
57
- def set_cache_ttl(ttl)
58
- @cache_ttl = ttl
56
+ # Delete the cached object with the given primary key.
57
+ def cache_delete_pk(pk)
58
+ cache_delete(cache_key(pk))
59
+ end
60
+
61
+ # Return the cached object with the given primary key,
62
+ # or nil if no such object is in the cache.
63
+ def cache_get_pk(pk)
64
+ cache_get(cache_key(pk))
65
+ end
66
+
67
+ # Return a key string for the given primary key.
68
+ def cache_key(pk)
69
+ raise(Error, 'no primary key for this record') unless pk.is_a?(Array) ? pk.all? : pk
70
+ "#{self}:#{Array(pk).join(',')}"
59
71
  end
60
72
 
61
73
  # Copy the necessary class instance variables to the subclass.
@@ -69,8 +81,13 @@ module Sequel
69
81
  @cache_ttl = ttl
70
82
  @cache_ignore_exceptions = cache_ignore_exceptions
71
83
  end
72
- end
84
+ end
73
85
 
86
+ # Set the time to live for the cache store, in seconds (default is 3600, # so 1 hour).
87
+ def set_cache_ttl(ttl)
88
+ @cache_ttl = ttl
89
+ end
90
+
74
91
  private
75
92
 
76
93
  # Delete the entry with the matching key from the cache
@@ -79,6 +96,8 @@ module Sequel
79
96
  nil
80
97
  end
81
98
 
99
+ # Returned the cached object, or nil if the object was not
100
+ # in the cached
82
101
  def cache_get(ck)
83
102
  if @cache_ignore_exceptions
84
103
  @cache_store.get(ck) rescue nil
@@ -87,11 +106,6 @@ module Sequel
87
106
  end
88
107
  end
89
108
 
90
- # Return a key string for the pk
91
- def cache_key(pk)
92
- "#{self}:#{Array(pk).join(',')}"
93
- end
94
-
95
109
  # Set the object in the cache_store with the given key for cache_ttl seconds.
96
110
  def cache_set(ck, obj)
97
111
  @cache_store.set(ck, obj, @cache_ttl)
@@ -120,14 +134,7 @@ module Sequel
120
134
  # primary key value(s) for the object. If the model does not have a primary
121
135
  # key, raise an Error.
122
136
  def cache_key
123
- raise(Error, "No primary key is associated with this model") unless key = primary_key
124
- pk = case key
125
- when Array
126
- key.collect{|k| @values[k]}
127
- else
128
- @values[key] || (raise Error, 'no primary key for this record')
129
- end
130
- model.send(:cache_key, pk)
137
+ model.cache_key(pk)
131
138
  end
132
139
 
133
140
  # Remove the object from the cache when deleting
@@ -140,7 +147,7 @@ module Sequel
140
147
 
141
148
  # Delete this object from the cache
142
149
  def cache_delete
143
- model.send(:cache_delete, cache_key)
150
+ model.cache_delete_pk(pk)
144
151
  end
145
152
  end
146
153
  end
@@ -90,9 +90,9 @@ module Sequel
90
90
  @cti_columns = {table_name=>columns}
91
91
  @cti_table_map = opts[:table_map] || {}
92
92
  dataset.row_proc = if key
93
- lambda{|r| (m.call(r[key]) rescue model).load(r)}
93
+ lambda{|r| (m.call(r[key]) rescue model).call(r)}
94
94
  else
95
- lambda{|r| model.load(r)}
95
+ model
96
96
  end
97
97
  end
98
98
  end
@@ -152,9 +152,9 @@ module Sequel
152
152
  subclass.instance_eval do
153
153
  m = method(:constantize)
154
154
  dataset.row_proc = if cti_key
155
- lambda{|r| (m.call(r[ck]) rescue subclass).load(r)}
155
+ lambda{|r| (m.call(r[ck]) rescue subclass).call(r)}
156
156
  else
157
- lambda{|r| subclass.load(r)}
157
+ subclass
158
158
  end
159
159
  (columns - [cbm.primary_key]).each{|a| define_lazy_attribute_getter(a)}
160
160
  cti_tables.reverse.each do |table|
@@ -1,20 +1,53 @@
1
1
  module Sequel
2
2
  module Plugins
3
- # The composition plugin allows you to easily define getter and
4
- # setter instance methods for a class where the backing data
5
- # is composed of other getters and decomposed to other setters.
3
+ # The composition plugin allows you to easily define a virtual
4
+ # attribute where the backing data is composed of other columns.
6
5
  #
7
- # A simple example of this is when you have a database table with
8
- # separate columns for year, month, and day, but where you want
9
- # to deal with Date objects in your ruby code. This can be handled
10
- # with:
6
+ # There are two ways to use the plugin. One way is with the
7
+ # :mapping option. A simple example of this is when you have a
8
+ # database table with separate columns for year, month, and day,
9
+ # but where you want to deal with Date objects in your ruby code.
10
+ # This can be handled with:
11
11
  #
12
12
  # Album.plugin :composition
13
13
  # Album.composition :date, :mapping=>[:year, :month, :day]
14
14
  #
15
- # The :mapping option is optional, but you can define custom
16
- # composition and decomposition procs via the :composer and
17
- # :decomposer options.
15
+ # With the :mapping option, you can provide a :class option
16
+ # that gives the class to use, but if that is not provided, it
17
+ # is inferred from the name of the composition (e.g. :date -> Date).
18
+ # When the <tt>date</tt> method is called, it will return a
19
+ # Date object by calling:
20
+ #
21
+ # Date.new(year, month, day)
22
+ #
23
+ # When saving the object, if the date composition has been used
24
+ # (by calling either the getter or setter method), it will
25
+ # populate the related columns of the object before saving:
26
+ #
27
+ # self.year = date.year
28
+ # self.month = date.month
29
+ # self.day = date.day
30
+ #
31
+ # The :mapping option is just a shortcut that works in particular
32
+ # cases. To handle any case, you can define a custom :composer
33
+ # and :decomposer procs. The :composer proc will be instance_evaled
34
+ # the first time the getter is called, and the :decomposer proc
35
+ # will be instance_evaled before saving. The above example could
36
+ # also be implemented as:
37
+ #
38
+ # Album.composition :date,
39
+ # :composer=>proc{Date.new(year, month, day) if year || month || day},
40
+ # :decomposer=>(proc do
41
+ # if d = compositions[:date]
42
+ # self.year = d.year
43
+ # self.month = d.month
44
+ # self.day = d.day
45
+ # else
46
+ # self.year = nil
47
+ # self.month = nil
48
+ # self.day = nil
49
+ # end
50
+ # end)
18
51
  #
19
52
  # Note that when using the composition object, you should not
20
53
  # modify the underlying columns if you are also instantiating
@@ -117,9 +150,8 @@ module Sequel
117
150
  module InstanceMethods
118
151
  # Clear the cached compositions when refreshing.
119
152
  def _refresh(ds)
120
- v = super
153
+ super
121
154
  compositions.clear
122
- v
123
155
  end
124
156
 
125
157
  # For each composition, set the columns in the model class based
@@ -0,0 +1,198 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The constraint_validations plugin is designed to be used with databases
4
+ # that used the constraint_validations extension when creating their
5
+ # tables. The extension adds validation metadata for constraints created,
6
+ # and this plugin reads that metadata and automatically creates validations
7
+ # for all of the constraints. For example, if you used the extension
8
+ # and created your albums table like this:
9
+ #
10
+ # DB.create_table(:albums) do
11
+ # primary_key :id
12
+ # String :name
13
+ # validate do
14
+ # min_length 5, :name
15
+ # end
16
+ # end
17
+ #
18
+ # Then when you went to save an album that uses this plugin:
19
+ #
20
+ # Album.create(:name=>'abc')
21
+ # # raises Sequel::ValidationFailed: name is shorter than 5 characters
22
+ #
23
+ # Usage:
24
+ #
25
+ # # Make all model subclasses use constraint validations (called before loading subclasses)
26
+ # Sequel::Model.plugin :constraint_validations
27
+ #
28
+ # # Make the Album class use constraint validations
29
+ # Album.plugin :constraint_validations
30
+ module ConstraintValidations
31
+ # The default constraint validation metadata table name.
32
+ DEFAULT_CONSTRAINT_VALIDATIONS_TABLE = :sequel_constraint_validations
33
+
34
+ # Automatically load the validation_helpers plugin to run the actual validations.
35
+ def self.apply(model, opts={})
36
+ model.plugin :validation_helpers
37
+ end
38
+
39
+ # Parse the constraint validations metadata from the database. Options:
40
+ # :constraint_validations_table :: Override the name of the constraint validations
41
+ # metadata table. Should only be used if the table
42
+ # name was overridden when creating the constraint
43
+ # validations.
44
+ def self.configure(model, opts={})
45
+ model.instance_variable_set(:@constraint_validations_table, opts[:constraint_validations_table] || DEFAULT_CONSTRAINT_VALIDATIONS_TABLE)
46
+ model.send(:parse_constraint_validations)
47
+ end
48
+
49
+ module DatabaseMethods
50
+ # A hash of validation method call metadata for all tables in the database.
51
+ # The hash is keyed by table name string and contains arrays of validation
52
+ # method call arrays.
53
+ attr_accessor :constraint_validations
54
+ end
55
+
56
+ module ClassMethods
57
+ # An array of validation method call arrays. Each array is an array that
58
+ # is splatted to send to perform a validation via validation_helpers.
59
+ attr_reader :constraint_validations
60
+
61
+ # The name of the table containing the constraint validations metadata.
62
+ attr_reader :constraint_validations_table
63
+
64
+ # Copy the name of the constraint validations metadata table into the subclass.
65
+ def inherited(subclass)
66
+ super
67
+ subclass.instance_variable_set(:@constraint_validations_table, @constraint_validations_table)
68
+ end
69
+
70
+ # Parse the constraint validations from the database whenever the dataset
71
+ # changes.
72
+ def set_dataset(*)
73
+ r = super
74
+ parse_constraint_validations
75
+ r
76
+ end
77
+
78
+ private
79
+
80
+ # If the database has not already parsed constraint validation
81
+ # metadata, then run a query to get the metadata data and transform it
82
+ # into arrays of validation method calls.
83
+ #
84
+ # If this model has associated dataset, use the model's table name
85
+ # to get the validations for just this model.
86
+ def parse_constraint_validations
87
+ db.extend(DatabaseMethods)
88
+
89
+ unless hash = Sequel.synchronize{db.constraint_validations}
90
+ hash = {}
91
+ db.from(constraint_validations_table).each do |r|
92
+ (hash[r[:table]] ||= []) << constraint_validation_array(r)
93
+ end
94
+ Sequel.synchronize{db.constraint_validations = hash}
95
+ end
96
+
97
+ if @dataset
98
+ ds = @dataset.clone
99
+ ds.quote_identifiers = false
100
+ table_name = ds.literal(model.table_name)
101
+ @constraint_validations = Sequel.synchronize{hash[table_name]} || []
102
+ end
103
+ end
104
+
105
+ # Given a specific database constraint validation metadata row hash, transform
106
+ # it in an validation method call array suitable for splatting to send.
107
+ def constraint_validation_array(r)
108
+ opts = {}
109
+ opts[:message] = r[:message] if r[:message]
110
+ opts[:allow_nil] = true if db.typecast_value(:boolean, r[:allow_nil])
111
+ type = r[:validation_type].to_sym
112
+ arg = r[:argument]
113
+ column = r[:column]
114
+
115
+ case type
116
+ when :like, :ilike
117
+ arg = constraint_validation_like_to_regexp(arg, type == :ilike)
118
+ type = :format
119
+ when :exact_length, :min_length, :max_length
120
+ arg = arg.to_i
121
+ when :length_range
122
+ arg = constraint_validation_int_range(arg)
123
+ when :format
124
+ arg = Regexp.new(arg)
125
+ when :iformat
126
+ arg = Regexp.new(arg, Regexp::IGNORECASE)
127
+ type = :format
128
+ when :includes_str_array
129
+ arg = arg.split(',')
130
+ type = :includes
131
+ when :includes_int_array
132
+ arg = arg.split(',').map{|x| x.to_i}
133
+ type = :includes
134
+ when :includes_int_range
135
+ arg = constraint_validation_int_range(arg)
136
+ type = :includes
137
+ end
138
+
139
+ column = if type == :unique
140
+ column.split(',').map{|c| c.to_sym}
141
+ else
142
+ column.to_sym
143
+ end
144
+
145
+ a = [:"validates_#{type}"]
146
+ if arg
147
+ a << arg
148
+ end
149
+ a << column
150
+ unless opts.empty?
151
+ a << opts
152
+ end
153
+ a
154
+ end
155
+
156
+ # Return a range of integers assuming the argument is in
157
+ # 1..2 or 1...2 format.
158
+ def constraint_validation_int_range(arg)
159
+ arg =~ /(\d+)\.\.(\.)?(\d+)/
160
+ Range.new($1.to_i, $3.to_i, $2 == '.')
161
+ end
162
+
163
+ # Transform the LIKE pattern string argument into a
164
+ # Regexp argument suitable for use with validates_format.
165
+ def constraint_validation_like_to_regexp(arg, case_insensitive)
166
+ arg = Regexp.escape(arg).gsub(/%%|%|_/) do |s|
167
+ case s
168
+ when '%%'
169
+ '%'
170
+ when '%'
171
+ '.*'
172
+ when '_'
173
+ '.'
174
+ end
175
+ end
176
+ arg = "\\A#{arg}\\z"
177
+
178
+ if case_insensitive
179
+ Regexp.new(arg, Regexp::IGNORECASE)
180
+ else
181
+ Regexp.new(arg)
182
+ end
183
+ end
184
+ end
185
+
186
+ module InstanceMethods
187
+ # Run all of the constraint validations parsed from the database
188
+ # when validating the instance.
189
+ def validate
190
+ super
191
+ model.constraint_validations.each do |v|
192
+ send(*v)
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end