viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,74 @@
1
+ module Sequel
2
+ module Plugins
3
+ # Sequel's built in Single Table Inheritance plugin makes subclasses
4
+ # of this model only load rows where the given key field matches the
5
+ # subclass's name. If the key given has a NULL value or there are
6
+ # any problems looking up the class, uses the current class.
7
+ #
8
+ # You should only use this in the parent class, not in the subclasses.
9
+ #
10
+ # You shouldn't call set_dataset in the model after applying this
11
+ # plugin, otherwise subclasses might use the wrong dataset.
12
+ #
13
+ # The filters and row_proc that sti_key sets up in subclasses may not work correctly if
14
+ # those subclasses have further subclasses. For those middle subclasses,
15
+ # you may need to call set_dataset manually with the correct filter and
16
+ # row_proc.
17
+ module SingleTableInheritance
18
+ # Set the sti_key and sti_dataset for the model, and change the
19
+ # dataset's row_proc so that the dataset yields objects of varying classes,
20
+ # where the class used has the same name as the key field.
21
+ def self.configure(model, key)
22
+ model.instance_eval do
23
+ @sti_key = key
24
+ @sti_dataset = dataset
25
+ dataset.row_proc = lambda{|r| model.sti_load(r)}
26
+ end
27
+ end
28
+
29
+ module ClassMethods
30
+ # The base dataset for STI, to which filters are added to get
31
+ # only the models for the specific STI subclass.
32
+ attr_reader :sti_dataset
33
+
34
+ # The column name holding the STI key for this model
35
+ attr_reader :sti_key
36
+
37
+ # Copy the sti_key and sti_dataset to the subclasses, and filter the
38
+ # subclass's dataset so it is restricted to rows where the key column
39
+ # matches the subclass's name.
40
+ def inherited(subclass)
41
+ super
42
+ sk = sti_key
43
+ sd = sti_dataset
44
+ subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>subclass.name.to_s), :inherited=>true)
45
+ subclass.instance_eval do
46
+ @sti_key = sk
47
+ @sti_dataset = sd
48
+ @simple_table = nil
49
+ end
50
+ end
51
+
52
+ # Return an instance of the class specified by sti_key,
53
+ # used by the row_proc.
54
+ def sti_load(r)
55
+ v = r[sti_key]
56
+ model = if (v && v != '')
57
+ constantize(v) rescue self
58
+ else
59
+ self
60
+ end
61
+ model.load(r)
62
+ end
63
+ end
64
+
65
+ module InstanceMethods
66
+ # Set the sti_key column to the name of the model.
67
+ def before_create
68
+ return false if super == false
69
+ send("#{model.sti_key}=", model.name.to_s) unless send(model.sti_key)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,45 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The Subclasses plugin keeps track of all subclasses of the
4
+ # current model class. Direct subclasses are available via the
5
+ # subclasses method, and all descendent classes are available via the
6
+ # descendents method.
7
+ #
8
+ # c = Class.new(Sequel::Model)
9
+ # c.plugin :subclasses
10
+ # sc1 = Class.new(c)
11
+ # sc2 = Class.new(c)
12
+ # ssc1 = Class.new(sc1)
13
+ # c.subclasses # [sc1, sc2]
14
+ # sc1.subclasses # [ssc1]
15
+ # sc2.subclasses # []
16
+ # ssc1.subclasses # []
17
+ # c.descendents # [sc1, ssc1, sc2]
18
+ module Subclasses
19
+ # Initialize the subclasses instance variable for the model.
20
+ def self.apply(model, &block)
21
+ model.instance_variable_set(:@subclasses, [])
22
+ end
23
+
24
+ module ClassMethods
25
+ # All subclasses for the current model. Does not
26
+ # include the model itself.
27
+ attr_reader :subclasses
28
+
29
+ # All descendent classes of this model.
30
+ def descendents
31
+ subclasses.map{|x| [x] + x.descendents}.flatten
32
+ end
33
+
34
+ # Add the subclass to this model's current subclasses,
35
+ # and initialize a new subclasses instance variable
36
+ # in the subclass.
37
+ def inherited(subclass)
38
+ super
39
+ subclasses << subclass
40
+ subclass.instance_variable_set(:@subclasses, [])
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The tactical_eager_loading plugin allows you to eagerly load
4
+ # an association for all objects retrieved from the same dataset
5
+ # without calling eager on the dataset. If you attempt to load
6
+ # associated objects for a record and the association for that
7
+ # object is currently not cached, it assumes you want to get
8
+ # the associated objects for all objects retrieved with the dataset that
9
+ # retrieved the current object.
10
+ #
11
+ # Tactical eager loading only takes affect if you retrieved the
12
+ # current object with Dataset#all, it doesn't work if you
13
+ # retrieved the current object with Dataset#each.
14
+ #
15
+ # Basically, this allows the following code to issue only two queries:
16
+ #
17
+ # Album.filter{id<100}.all do |a|
18
+ # a.artists
19
+ # end
20
+ module TacticalEagerLoading
21
+ module InstanceMethods
22
+ # The dataset that retrieved this object, set if the object was
23
+ # reteived via Dataset#all with an active identity map.
24
+ attr_accessor :retrieved_by
25
+
26
+ # All model objects retrieved with this object, set if the object was
27
+ # reteived via Dataset#all with an active identity map.
28
+ attr_accessor :retrieved_with
29
+
30
+ private
31
+
32
+ # If there is an active identity map and the association is not in the
33
+ # associations cache and the object was reteived via Dataset#all,
34
+ # eagerly load the association for all model objects retrieved with the
35
+ # current object.
36
+ def load_associated_objects(opts, reload=false)
37
+ name = opts[:name]
38
+ if !associations.include?(name) && retrieved_by
39
+ retrieved_by.send(:eager_load, retrieved_with, name=>{})
40
+ end
41
+ super
42
+ end
43
+ end
44
+
45
+ module DatasetMethods
46
+ private
47
+
48
+ # If there is an active identity map, set the reteived_with attribute for the object
49
+ # with the current dataset and array of all objects.
50
+ def post_load(objects)
51
+ super
52
+ objects.each do |o|
53
+ next unless o.is_a?(Sequel::Model)
54
+ o.retrieved_by = self
55
+ o.retrieved_with = objects
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,87 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The timestamps plugin creates hooks that automatically set create and
4
+ # update timestamp fields. Both field names used are configurable, and you
5
+ # can also set whether to overwrite existing create timestamps (false
6
+ # by default), or whether to set the update timestamp when creating (also
7
+ # false by default).
8
+ module Timestamps
9
+ # Configure the plugin by setting the avialable options. Note that
10
+ # if this method is run more than once, previous settings are ignored,
11
+ # and it will just use the settings given or the default settings. Options:
12
+ # * :create - The field to hold the create timestamp (default: :created_at)
13
+ # * :force - Whether to overwrite an existing create timestamp (default: false)
14
+ # * :update - The field to hold the update timestamp (default: :updated_at)
15
+ # * :update_on_create - Whether to set the update timestamp to the create timestamp when creating (default: false)
16
+ def self.configure(model, opts={})
17
+ model.instance_eval do
18
+ @create_timestamp_field = opts[:create]||:created_at
19
+ @update_timestamp_field = opts[:update]||:updated_at
20
+ @create_timestamp_overwrite = opts[:force]||false
21
+ @set_update_timestamp_on_create = opts[:update_on_create]||false
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ # The field to store the create timestamp
27
+ attr_reader :create_timestamp_field
28
+
29
+ # The field to store the update timestamp
30
+ attr_reader :update_timestamp_field
31
+
32
+ # Whether to overwrite the create timestamp if it already exists
33
+ def create_timestamp_overwrite?
34
+ @create_timestamp_overwrite
35
+ end
36
+
37
+ # Copy the class instance variables used from the superclass to the subclass
38
+ def inherited(subclass)
39
+ super
40
+ [:@create_timestamp_field, :@update_timestamp_field, :@create_timestamp_overwrite, :@set_update_timestamp_on_create].each do |iv|
41
+ subclass.instance_variable_set(iv, instance_variable_get(iv))
42
+ end
43
+ end
44
+
45
+ # Whether to set the update timestamp to the create timestamp when creating
46
+ def set_update_timestamp_on_create?
47
+ @set_update_timestamp_on_create
48
+ end
49
+ end
50
+
51
+ module InstanceMethods
52
+ # Set the create timestamp when creating
53
+ def before_create
54
+ super
55
+ set_create_timestamp
56
+ end
57
+
58
+ # Set the update timestamp when updating
59
+ def before_update
60
+ super
61
+ set_update_timestamp
62
+ end
63
+
64
+ private
65
+
66
+ # If the object has accessor methods for the create timestamp field, and
67
+ # the create timestamp value is nil or overwriting it is allowed, set the
68
+ # create timestamp field to the time given or the current time. If setting
69
+ # the update timestamp on creation is configured, set the update timestamp
70
+ # as well.
71
+ def set_create_timestamp(time=nil)
72
+ field = model.create_timestamp_field
73
+ meth = :"#{field}="
74
+ self.send(meth, time||=Sequel.datetime_class.now) if respond_to?(field) && respond_to?(meth) && (model.create_timestamp_overwrite? || send(field).nil?)
75
+ set_update_timestamp(time) if model.set_update_timestamp_on_create?
76
+ end
77
+
78
+ # Set the update timestamp to the time given or the current time if the
79
+ # object has a setter method for the update timestamp field.
80
+ def set_update_timestamp(time=nil)
81
+ meth = :"#{model.update_timestamp_field}="
82
+ self.send(meth, time||Sequel.datetime_class.now) if respond_to?(meth)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,118 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The touch plugin adds a touch method to model instances, which saves
4
+ # the object with a modified timestamp. By default, it uses the
5
+ # :updated_at column, but you can set which column to use.
6
+ # It also supports touching of associations, so that when the current
7
+ # model object is updated or destroyed, the associated rows in the
8
+ # database can have their modified timestamp updated to the current
9
+ # timestamp.
10
+ #
11
+ # Since the instance touch method works on model instances,
12
+ # it uses Time.now for the timestamp. The association touching works
13
+ # on datasets, so it updates all related rows in a single query, using
14
+ # the SQL standard CURRENT_TIMESTAMP. Both of these can be overridden
15
+ # easily if necessary.
16
+ module Touch
17
+ # The default column to update when touching
18
+ TOUCH_COLUMN_DEFAULT = :updated_at
19
+
20
+ # Set the touch_column and touched_associations variables for the model.
21
+ # Options:
22
+ # * :associations - The associations to touch when a model instance is
23
+ # updated or destroyed. Can be a symbol for a single association,
24
+ # a hash with association keys and column values, or an array of
25
+ # symbols and/or hashes. If a symbol is used, the column used
26
+ # when updating the associated objects is the model's touch_column.
27
+ # If a hash is used, the value is used as the column to update.
28
+ # * :column - The column to modify when touching a model instance.
29
+ def self.configure(model, opts={})
30
+ model.touch_column = opts[:column] || TOUCH_COLUMN_DEFAULT if opts[:column] || !model.touch_column
31
+ model.instance_variable_set(:@touched_associations, {})
32
+ model.touch_associations(opts[:associations]) if opts[:associations]
33
+ end
34
+
35
+ module ClassMethods
36
+ # The column to modify when touching a model instance, as a symbol. Also used
37
+ # as the default column when touching associations, if
38
+ # the associations don't specify a column.
39
+ attr_accessor :touch_column
40
+
41
+ # A hash specifying the associations to touch when instances are
42
+ # updated or destroyed. Keys are association dataset method name symbols and values
43
+ # are column name symbols.
44
+ attr_reader :touched_associations
45
+
46
+ # Set the touch_column for the subclass to be the same as the current class.
47
+ # Also, create a copy of the touched_associations in the subclass.
48
+ def inherited(subclass)
49
+ super
50
+ subclass.touch_column = touch_column
51
+ subclass.instance_variable_set(:@touched_associations, touched_associations.dup)
52
+ end
53
+
54
+ # Add additional associations to be touched. See the :association option
55
+ # of the Sequel::Plugin::Touch.configure method for the format of the associations
56
+ # arguments.
57
+ def touch_associations(*associations)
58
+ associations.flatten.each do |a|
59
+ a = {a=>touch_column} if a.is_a?(Symbol)
60
+ a.each do |k,v|
61
+ raise(Error, "invalid association: #{k}") unless r = association_reflection(k)
62
+ touched_associations[r.dataset_method] = v
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ module InstanceMethods
69
+ # Touch all of the model's touched_associations when destroying the object.
70
+ def after_destroy
71
+ super
72
+ touch_associations
73
+ end
74
+
75
+ # Touch all of the model's touched_associations when updating the object.
76
+ def after_update
77
+ super
78
+ touch_associations
79
+ end
80
+
81
+ # Touch the model object. If a column is not given, use the model's touch_column
82
+ # as the column. If the column to use is not one of the model's columns, just
83
+ # save the changes to the object instead of attempting to a value that doesn't
84
+ # exist.
85
+ def touch(column=nil)
86
+ if column
87
+ set(column=>touch_instance_value)
88
+ else
89
+ column = model.touch_column
90
+ set(column=>touch_instance_value) if columns.include?(column)
91
+ end
92
+ save_changes
93
+ end
94
+
95
+ private
96
+
97
+ # The value to use when modifying the touch column for the association datasets. Uses
98
+ # the SQL standard CURRENT_TIMESTAMP.
99
+ def touch_association_value
100
+ Sequel::CURRENT_TIMESTAMP
101
+ end
102
+
103
+ # Directly update the database using the association dataset for each association.
104
+ def touch_associations
105
+ model.touched_associations.each do |meth, column|
106
+ send(meth).update(column=>touch_association_value)
107
+ end
108
+ end
109
+
110
+ # The value to use when modifying the touch column for the model instance.
111
+ # Uses Time.now to work well with typecasting.
112
+ def touch_instance_value
113
+ Time.now
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,72 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The TypecastOnLoad plugin exists because most of Sequel's database adapters don't
4
+ # have complete control over typecasting, and may return columns that aren't
5
+ # typecast correctly (with correct being defined as how the model object
6
+ # would typecast the same column values).
7
+ #
8
+ # This plugin modifies Model.load to call the setter methods (which typecast
9
+ # by default) for all columns given. You can either specify the columns to
10
+ # typecast on load in the plugin call itself, or afterwards using
11
+ # add_typecast_on_load_columns:
12
+ #
13
+ # Album.plugin :typecast_on_load, :release_date, :record_date
14
+ # # or:
15
+ # Album.plugin :typecast_on_load
16
+ # Album.add_typecast_on_load_columns :release_date, :record_date
17
+ #
18
+ # If the database returns release_date and record_date columns as strings
19
+ # instead of dates, this will ensure that if you access those columns through
20
+ # the model object, you'll get Date objects instead of strings.
21
+ module TypecastOnLoad
22
+ # Call add_typecast_on_load_columns on the passed column arguments.
23
+ def self.configure(model, *columns, &block)
24
+ model.instance_eval do
25
+ @typecast_on_load_columns ||= []
26
+ add_typecast_on_load_columns(*columns)
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+ # The columns to typecast on load for this model.
32
+ attr_reader :typecast_on_load_columns
33
+
34
+ # Add additional columns to typecast on load for this model.
35
+ def add_typecast_on_load_columns(*columns)
36
+ @typecast_on_load_columns.concat(columns)
37
+ end
38
+
39
+ # Give the subclass a copy of the typecast on load columns.
40
+ def inherited(subclass)
41
+ super
42
+ subclass.instance_variable_set(:@typecast_on_load_columns, typecast_on_load_columns.dup)
43
+ end
44
+
45
+ # Call the setter method for each of the typecast on load columns,
46
+ # ensuring the model object will have the correct typecasting even
47
+ # if the database doesn't typecast the columns correctly.
48
+ def load(values)
49
+ super.load_typecast
50
+ end
51
+ end
52
+
53
+ module InstanceMethods
54
+ def load_typecast
55
+ model.typecast_on_load_columns.each do |c|
56
+ if v = values[c]
57
+ send("#{c}=", v)
58
+ end
59
+ end
60
+ changed_columns.clear
61
+ self
62
+ end
63
+
64
+ private
65
+
66
+ def _refresh(dataset)
67
+ super.load_typecast
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end