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.
- data/CHANGELOG +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- 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
|