sequel 3.11.0 → 3.12.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 +70 -0
- data/Rakefile +1 -1
- data/doc/active_record.rdoc +896 -0
- data/doc/advanced_associations.rdoc +46 -31
- data/doc/association_basics.rdoc +14 -9
- data/doc/dataset_basics.rdoc +3 -3
- data/doc/migration.rdoc +1011 -0
- data/doc/model_hooks.rdoc +198 -0
- data/doc/querying.rdoc +811 -86
- data/doc/release_notes/3.12.0.txt +304 -0
- data/doc/sharding.rdoc +17 -0
- data/doc/sql.rdoc +537 -0
- data/doc/validations.rdoc +501 -0
- data/lib/sequel/adapters/jdbc.rb +19 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
- data/lib/sequel/adapters/mysql.rb +5 -4
- data/lib/sequel/adapters/odbc.rb +3 -2
- data/lib/sequel/adapters/shared/mssql.rb +7 -6
- data/lib/sequel/adapters/shared/mysql.rb +2 -7
- data/lib/sequel/adapters/shared/postgres.rb +2 -8
- data/lib/sequel/adapters/shared/sqlite.rb +2 -5
- data/lib/sequel/adapters/sqlite.rb +4 -4
- data/lib/sequel/core.rb +0 -1
- data/lib/sequel/database.rb +2 -1060
- data/lib/sequel/database/connecting.rb +227 -0
- data/lib/sequel/database/dataset.rb +58 -0
- data/lib/sequel/database/dataset_defaults.rb +127 -0
- data/lib/sequel/database/logging.rb +62 -0
- data/lib/sequel/database/misc.rb +246 -0
- data/lib/sequel/database/query.rb +390 -0
- data/lib/sequel/database/schema_generator.rb +7 -3
- data/lib/sequel/database/schema_methods.rb +351 -7
- data/lib/sequel/dataset/actions.rb +9 -2
- data/lib/sequel/dataset/misc.rb +6 -2
- data/lib/sequel/dataset/mutation.rb +3 -11
- data/lib/sequel/dataset/query.rb +49 -6
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/migration.rb +395 -113
- data/lib/sequel/extensions/schema_dumper.rb +21 -13
- data/lib/sequel/model.rb +27 -25
- data/lib/sequel/model/associations.rb +72 -34
- data/lib/sequel/model/base.rb +74 -18
- data/lib/sequel/model/errors.rb +8 -1
- data/lib/sequel/plugins/active_model.rb +8 -0
- data/lib/sequel/plugins/association_pks.rb +87 -0
- data/lib/sequel/plugins/association_proxies.rb +8 -0
- data/lib/sequel/plugins/boolean_readers.rb +12 -6
- data/lib/sequel/plugins/caching.rb +14 -7
- data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
- data/lib/sequel/plugins/composition.rb +2 -1
- data/lib/sequel/plugins/force_encoding.rb +10 -7
- data/lib/sequel/plugins/hook_class_methods.rb +12 -11
- data/lib/sequel/plugins/identity_map.rb +9 -0
- data/lib/sequel/plugins/instance_hooks.rb +23 -13
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +18 -4
- data/lib/sequel/plugins/nested_attributes.rb +1 -0
- data/lib/sequel/plugins/optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +9 -8
- data/lib/sequel/plugins/schema.rb +8 -0
- data/lib/sequel/plugins/serialization.rb +1 -3
- data/lib/sequel/plugins/sharding.rb +135 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
- data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
- data/lib/sequel/plugins/string_stripper.rb +26 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
- data/lib/sequel/plugins/timestamps.rb +15 -2
- data/lib/sequel/plugins/touch.rb +13 -0
- data/lib/sequel/plugins/update_primary_key.rb +48 -0
- data/lib/sequel/plugins/validation_class_methods.rb +8 -0
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/sql.rb +17 -20
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +5 -5
- data/spec/core/core_sql_spec.rb +17 -1
- data/spec/core/database_spec.rb +17 -5
- data/spec/core/dataset_spec.rb +31 -8
- data/spec/core/schema_generator_spec.rb +8 -1
- data/spec/core/schema_spec.rb +13 -0
- data/spec/extensions/association_pks_spec.rb +85 -0
- data/spec/extensions/hook_class_methods_spec.rb +9 -9
- data/spec/extensions/migration_spec.rb +339 -219
- data/spec/extensions/schema_dumper_spec.rb +28 -17
- data/spec/extensions/sharding_spec.rb +272 -0
- data/spec/extensions/single_table_inheritance_spec.rb +92 -4
- data/spec/extensions/skip_create_refresh_spec.rb +17 -0
- data/spec/extensions/string_stripper_spec.rb +23 -0
- data/spec/extensions/update_primary_key_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +5 -5
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
- data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
- data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
- data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
- data/spec/integration/eager_loader_test.rb +20 -20
- data/spec/integration/migrator_test.rb +187 -0
- data/spec/integration/plugin_test.rb +150 -0
- data/spec/integration/schema_test.rb +13 -2
- data/spec/model/associations_spec.rb +41 -14
- data/spec/model/base_spec.rb +69 -0
- data/spec/model/eager_loading_spec.rb +7 -3
- data/spec/model/record_spec.rb +79 -4
- data/spec/model/validations_spec.rb +21 -9
- metadata +66 -5
- data/doc/schema.rdoc +0 -36
- data/lib/sequel/database/schema_sql.rb +0 -320
|
@@ -7,6 +7,7 @@ module Sequel
|
|
|
7
7
|
# Nested attributes are created using the nested_attributes method:
|
|
8
8
|
#
|
|
9
9
|
# Artist.one_to_many :albums
|
|
10
|
+
# Artist.plugin :nested_attributes
|
|
10
11
|
# Artist.nested_attributes :albums
|
|
11
12
|
# a = Artist.new(:name=>'YJM',
|
|
12
13
|
# :albums_attributes=>[{:name=>'RF'}, {:name=>'MO'}])
|
|
@@ -146,11 +146,11 @@ module Sequel
|
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
148
|
a[:after_load] ||= aal
|
|
149
|
-
a[:eager_loader] ||= proc do |
|
|
150
|
-
id_map = key_hash[key]
|
|
149
|
+
a[:eager_loader] ||= proc do |eo|
|
|
150
|
+
id_map = eo[:key_hash][key]
|
|
151
151
|
parent_map = {}
|
|
152
152
|
children_map = {}
|
|
153
|
-
|
|
153
|
+
eo[:rows].each do |obj|
|
|
154
154
|
parent_map[obj[prkey]] = obj
|
|
155
155
|
(children_map[obj[key]] ||= []) << obj
|
|
156
156
|
obj.associations[ancestors] = []
|
|
@@ -164,7 +164,7 @@ module Sequel
|
|
|
164
164
|
model.join(t, key=>prkey).
|
|
165
165
|
select(SQL::QualifiedIdentifier.new(t, ka), c_all)),
|
|
166
166
|
r.select,
|
|
167
|
-
associations).all do |obj|
|
|
167
|
+
eo[:associations], eo).all do |obj|
|
|
168
168
|
opk = obj[prkey]
|
|
169
169
|
if in_pm = parent_map.has_key?(opk)
|
|
170
170
|
if idm_obj = parent_map[opk]
|
|
@@ -224,11 +224,12 @@ module Sequel
|
|
|
224
224
|
end
|
|
225
225
|
end
|
|
226
226
|
d[:after_load] = dal
|
|
227
|
-
d[:eager_loader] ||= proc do |
|
|
228
|
-
id_map = key_hash[prkey]
|
|
227
|
+
d[:eager_loader] ||= proc do |eo|
|
|
228
|
+
id_map = eo[:key_hash][prkey]
|
|
229
|
+
associations = eo[:associations]
|
|
229
230
|
parent_map = {}
|
|
230
231
|
children_map = {}
|
|
231
|
-
|
|
232
|
+
eo[:rows].each do |obj|
|
|
232
233
|
parent_map[obj[prkey]] = obj
|
|
233
234
|
obj.associations[descendants] = []
|
|
234
235
|
obj.associations[childrena] = []
|
|
@@ -248,7 +249,7 @@ module Sequel
|
|
|
248
249
|
model.eager_loading_dataset(r,
|
|
249
250
|
model.from(t).with_recursive(t, base_case, recursive_case),
|
|
250
251
|
r.select,
|
|
251
|
-
associations).all do |obj|
|
|
252
|
+
associations, eo).all do |obj|
|
|
252
253
|
if level
|
|
253
254
|
no_cache = no_cache_level == obj.values.delete(la)
|
|
254
255
|
end
|
|
@@ -8,6 +8,14 @@ module Sequel
|
|
|
8
8
|
# This plugin is mostly suited to test code. If there is any
|
|
9
9
|
# chance that your application's schema could change, you should
|
|
10
10
|
# be using the migration extension instead.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
#
|
|
14
|
+
# # Add the schema methods to all model subclasses (called before loading subclasses)
|
|
15
|
+
# Sequel::Model.plugin :schema
|
|
16
|
+
#
|
|
17
|
+
# # Add the schema methods to the Album class
|
|
18
|
+
# Album.plugin :schema
|
|
11
19
|
module Schema
|
|
12
20
|
module ClassMethods
|
|
13
21
|
# Creates table, using the column information from set_schema.
|
|
@@ -117,10 +117,8 @@ module Sequel
|
|
|
117
117
|
|
|
118
118
|
# Serialize all deserialized values
|
|
119
119
|
def before_save
|
|
120
|
+
deserialized_values.each{|k,v| @values[k] = serialize_value(k, v)}
|
|
120
121
|
super
|
|
121
|
-
deserialized_values.each do |k,v|
|
|
122
|
-
@values[k] = serialize_value(k, v)
|
|
123
|
-
end
|
|
124
122
|
end
|
|
125
123
|
|
|
126
124
|
# Empty the deserialized values when refreshing.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The sharding plugin makes it easy to use Sequel's sharding features
|
|
4
|
+
# with models. It lets you create model objects on specific shards,
|
|
5
|
+
# and any models retrieved from specific shards are automatically
|
|
6
|
+
# saved back to those shards. It also works with associations,
|
|
7
|
+
# so that model objects retrieved from specific shards will only
|
|
8
|
+
# return associated objects from that shard, and using the
|
|
9
|
+
# add/remove/remove_all association methods will only affect
|
|
10
|
+
# that shard.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
#
|
|
14
|
+
# # Add the sharding support to all model subclasses (called before loading subclasses)
|
|
15
|
+
# Sequel::Model.plugin :sharding
|
|
16
|
+
#
|
|
17
|
+
# # Add the sharding support to the Album class
|
|
18
|
+
# Album.plugin :sharding
|
|
19
|
+
module Sharding
|
|
20
|
+
module ClassMethods
|
|
21
|
+
# Create a new object on the given shard s.
|
|
22
|
+
def create_using_server(s, values={}, &block)
|
|
23
|
+
new_using_server(s, values, &block).save
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# When eagerly loading, if the current dataset has a defined shard and the
|
|
27
|
+
# dataset that you will be using to get the associated records does not,
|
|
28
|
+
# use the current dataset's shard for the associated dataset.
|
|
29
|
+
def eager_loading_dataset(opts, ds, select, associations, eager_options={})
|
|
30
|
+
ds = super(opts, ds, select, associations, eager_options)
|
|
31
|
+
if !ds.opts[:server] and s = eager_options[:self] and server = s.opts[:server]
|
|
32
|
+
ds = ds.server(server)
|
|
33
|
+
end
|
|
34
|
+
ds
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Return a newly instantiated object that is tied to the given
|
|
38
|
+
# shard s. When the object is saved, a record will be inserted
|
|
39
|
+
# on shard s.
|
|
40
|
+
def new_using_server(s, values={}, &block)
|
|
41
|
+
new(values, &block).set_server(s)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module InstanceMethods
|
|
46
|
+
# Set the shard that this object is tied to. Returns self.
|
|
47
|
+
def set_server(s)
|
|
48
|
+
@server = s
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Set the server that this object is tied to, unless it has
|
|
53
|
+
# already been set. Returns self.
|
|
54
|
+
def set_server?(s)
|
|
55
|
+
@server ||= s
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Ensure that the instance dataset is tied to the correct shard.
|
|
60
|
+
def this
|
|
61
|
+
use_server(super)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# Ensure that association datasets are tied to the correct shard.
|
|
67
|
+
def _apply_association_options(*args)
|
|
68
|
+
use_server(super)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Ensure that the object is inserted into the correct shard.
|
|
72
|
+
def _insert_dataset
|
|
73
|
+
use_server(super)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Ensure that the join table for many_to_many associations uses the correct shard.
|
|
77
|
+
def _join_table_dataset(opts)
|
|
78
|
+
use_server(super)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Make sure to use the correct shard when using a transaction
|
|
82
|
+
def checked_transaction(opts={}, &block)
|
|
83
|
+
super(@server ? {:server=>@server}.merge(opts) : opts, &block)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# If creating the object by doing <tt>add_association</tt> for a
|
|
87
|
+
# +many_to_many+ association, make sure the associated object is created on the
|
|
88
|
+
# current object's shard, unless the passed object already has an assigned shard.
|
|
89
|
+
def ensure_associated_primary_key(opts, o, *args)
|
|
90
|
+
o.set_server?(@server) if o.respond_to?(:set_server?)
|
|
91
|
+
super
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Set the given dataset to use the current object's shard.
|
|
95
|
+
def use_server(ds)
|
|
96
|
+
@server ? ds.server(@server) : ds
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
module DatasetMethods
|
|
101
|
+
# If a row proc exists on the dataset, replace it with one that calls the
|
|
102
|
+
# previous row_proc, but calls set_server on the output of that row_proc,
|
|
103
|
+
# ensuring that objects retrieved by a specific shard know which shard they
|
|
104
|
+
# are tied to.
|
|
105
|
+
def server(s)
|
|
106
|
+
ds = super
|
|
107
|
+
if rp = row_proc
|
|
108
|
+
ds.row_proc = proc{|r| rp.call(r).set_server(s)}
|
|
109
|
+
end
|
|
110
|
+
ds
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
# Set the shard of all retrieved objects to the shard of
|
|
116
|
+
# the table's dataset, or the current shard if the table's
|
|
117
|
+
# dataset does not have a shard.
|
|
118
|
+
def graph_each
|
|
119
|
+
ta = @opts[:graph][:table_aliases]
|
|
120
|
+
s = @opts[:server]
|
|
121
|
+
super do |r|
|
|
122
|
+
r.each do |k, v|
|
|
123
|
+
if ds = ta[k]
|
|
124
|
+
dss = ds.opts[:server]
|
|
125
|
+
end
|
|
126
|
+
vs = dss || s
|
|
127
|
+
v.set_server(vs) if vs && v.respond_to?(:set_server)
|
|
128
|
+
end
|
|
129
|
+
yield r
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -1,27 +1,76 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
3
|
+
# The single_table_inheritance plugin allows storing all objects
|
|
4
|
+
# in the same class hierarchy in the same table. It makes it so
|
|
5
|
+
# subclasses of this model only load rows related to the subclass,
|
|
6
|
+
# and when you retrieve rows from the main class, you get instances
|
|
7
|
+
# of the subclasses (if the rows should use the subclasses's class).
|
|
8
|
+
#
|
|
9
|
+
# By default, the plugin assumes that the +sti_key+ column (the first
|
|
10
|
+
# argument to the plugin) holds the class name as a string. However,
|
|
11
|
+
# you can override this by using the <tt>:model_map</tt> option and/or
|
|
12
|
+
# the <tt>:key_map</tt> option.
|
|
7
13
|
#
|
|
8
|
-
# You should only
|
|
14
|
+
# You should only load this plugin in the parent class, not in the subclasses.
|
|
9
15
|
#
|
|
10
16
|
# You shouldn't call set_dataset in the model after applying this
|
|
11
|
-
# plugin, otherwise subclasses might use the wrong dataset.
|
|
17
|
+
# plugin, otherwise subclasses might use the wrong dataset. You should
|
|
18
|
+
# make sure this plugin is loaded before the subclasses. Note that since you
|
|
19
|
+
# need to load the plugin before the subclasses are created, you can't use
|
|
20
|
+
# direct class references in the plugin class. You should specify subclasses
|
|
21
|
+
# in the plugin call using class name strings or symbols, see usage below.
|
|
12
22
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
23
|
+
# Usage:
|
|
24
|
+
#
|
|
25
|
+
# # Use the default of storing the class name in the sti_key
|
|
26
|
+
# # column (:kind in this case)
|
|
27
|
+
# Employee.plugin :single_table_inheritance, :kind
|
|
28
|
+
#
|
|
29
|
+
# # Using integers to store the class type, with a :model_map hash
|
|
30
|
+
# # and an sti_key of :type
|
|
31
|
+
# Employee.plugin :single_table_inheritance, :type,
|
|
32
|
+
# :model_map=>{1=>:Staff, 2=>:Manager}
|
|
33
|
+
#
|
|
34
|
+
# # Using non-class name strings
|
|
35
|
+
# Employee.plugin :single_table_inheritance, :type,
|
|
36
|
+
# :model_map=>{'line staff'=>:Staff, 'supervisor'=>:Manager}
|
|
37
|
+
#
|
|
38
|
+
# # Using custom procs, with :model_map taking column values
|
|
39
|
+
# # and yielding either a class, string, symbol, or nil,
|
|
40
|
+
# # and :key_map taking a class object and returning the column
|
|
41
|
+
# # value to use
|
|
42
|
+
# Employee.plugin :single_table_inheritance, :type,
|
|
43
|
+
# :model_map=>proc{|v| v.reverse},
|
|
44
|
+
# :key_map=>proc{|klass| klass.name.reverse}
|
|
45
|
+
#
|
|
46
|
+
# One minor issue to note is that if you specify the <tt>:key_map</tt>
|
|
47
|
+
# option as a hash, instead of having it inferred from the <tt>:model_map</tt>,
|
|
48
|
+
# you should only use class name strings as keys, you should not use symbols
|
|
49
|
+
# as keys.
|
|
17
50
|
module SingleTableInheritance
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
# where the class used has the same name as the key field.
|
|
21
|
-
def self.configure(model, key)
|
|
51
|
+
# Setup the necessary STI variables, see the module RDoc for SingleTableInheritance
|
|
52
|
+
def self.configure(model, key, opts={})
|
|
22
53
|
model.instance_eval do
|
|
54
|
+
@sti_key_array = nil
|
|
23
55
|
@sti_key = key
|
|
24
56
|
@sti_dataset = dataset
|
|
57
|
+
@sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
|
|
58
|
+
@sti_key_map = if km = opts[:key_map]
|
|
59
|
+
if km.is_a?(Hash)
|
|
60
|
+
h = Hash.new{|h,k| h[k.to_s] unless k.is_a?(String)}
|
|
61
|
+
h.merge!(km)
|
|
62
|
+
else
|
|
63
|
+
km
|
|
64
|
+
end
|
|
65
|
+
elsif sti_model_map.is_a?(Hash)
|
|
66
|
+
h = Hash.new{|h,k| h[k.to_s] unless k.is_a?(String)}
|
|
67
|
+
sti_model_map.each do |k,v|
|
|
68
|
+
h[v.to_s] = k
|
|
69
|
+
end
|
|
70
|
+
h
|
|
71
|
+
else
|
|
72
|
+
lambda{|klass| klass.name.to_s}
|
|
73
|
+
end
|
|
25
74
|
dataset.row_proc = lambda{|r| model.sti_load(r)}
|
|
26
75
|
end
|
|
27
76
|
end
|
|
@@ -34,17 +83,38 @@ module Sequel
|
|
|
34
83
|
# The column name holding the STI key for this model
|
|
35
84
|
attr_reader :sti_key
|
|
36
85
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
86
|
+
# Array holding keys for all subclasses of this class, used for the
|
|
87
|
+
# dataset filter in subclasses. Nil in the main class.
|
|
88
|
+
attr_reader :sti_key_array
|
|
89
|
+
|
|
90
|
+
# A hash/proc with class keys and column value values, mapping
|
|
91
|
+
# the the class to a particular value given to the sti_key column.
|
|
92
|
+
# Used to set the column value when creating objects, and for the
|
|
93
|
+
# filter when retrieving objects in subclasses.
|
|
94
|
+
attr_reader :sti_key_map
|
|
95
|
+
|
|
96
|
+
# A hash/proc with column value keys and class values, mapping
|
|
97
|
+
# the value of the sti_key column to the appropriate class to use.
|
|
98
|
+
attr_reader :sti_model_map
|
|
99
|
+
|
|
100
|
+
# Copy the necessary attributes to the subclasses, and filter the
|
|
101
|
+
# subclass's dataset based on the sti_kep_map entry for the class.
|
|
40
102
|
def inherited(subclass)
|
|
41
103
|
super
|
|
42
104
|
sk = sti_key
|
|
43
105
|
sd = sti_dataset
|
|
44
|
-
|
|
106
|
+
skm = sti_key_map
|
|
107
|
+
smm = sti_model_map
|
|
108
|
+
key = skm[subclass]
|
|
109
|
+
sti_subclass_added(key)
|
|
110
|
+
ska = [key]
|
|
111
|
+
subclass.set_dataset(sd.filter(SQL::QualifiedIdentifier.new(table_name, sk)=>ska), :inherited=>true)
|
|
45
112
|
subclass.instance_eval do
|
|
46
113
|
@sti_key = sk
|
|
114
|
+
@sti_key_array = ska
|
|
47
115
|
@sti_dataset = sd
|
|
116
|
+
@sti_key_map = skm
|
|
117
|
+
@sti_model_map = smm
|
|
48
118
|
@simple_table = nil
|
|
49
119
|
end
|
|
50
120
|
end
|
|
@@ -52,21 +122,43 @@ module Sequel
|
|
|
52
122
|
# Return an instance of the class specified by sti_key,
|
|
53
123
|
# used by the row_proc.
|
|
54
124
|
def sti_load(r)
|
|
55
|
-
|
|
56
|
-
|
|
125
|
+
sti_class(sti_model_map[r[sti_key]]).load(r)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Make sure that all subclasses of the parent class correctly include
|
|
129
|
+
# keys for all of their descendant classes.
|
|
130
|
+
def sti_subclass_added(key)
|
|
131
|
+
if sti_key_array
|
|
132
|
+
sti_key_array << key
|
|
133
|
+
superclass.sti_subclass_added(key)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
# Return a class object. If a class is given, return it directly.
|
|
140
|
+
# Treat strings and symbols as class names. If nil is given or
|
|
141
|
+
# an invalid class name string or symbol is used, return self.
|
|
142
|
+
# Raise an error for other types.
|
|
143
|
+
def sti_class(v)
|
|
144
|
+
case v
|
|
145
|
+
when String, Symbol
|
|
57
146
|
constantize(v) rescue self
|
|
58
|
-
|
|
147
|
+
when nil
|
|
59
148
|
self
|
|
149
|
+
when Class
|
|
150
|
+
v
|
|
151
|
+
else
|
|
152
|
+
raise(Error, "Invalid class type used: #{v.inspect}")
|
|
60
153
|
end
|
|
61
|
-
model.load(r)
|
|
62
154
|
end
|
|
63
155
|
end
|
|
64
156
|
|
|
65
157
|
module InstanceMethods
|
|
66
|
-
# Set the sti_key column
|
|
158
|
+
# Set the sti_key column based on the sti_key_map.
|
|
67
159
|
def before_create
|
|
68
|
-
|
|
69
|
-
|
|
160
|
+
send("#{model.sti_key}=", model.sti_key_map[model]) unless send(model.sti_key)
|
|
161
|
+
super
|
|
70
162
|
end
|
|
71
163
|
end
|
|
72
164
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# SkipCreateRefresh is a simple plugin that make Sequel not
|
|
4
|
+
# refresh after saving a new model object. Sequel does the
|
|
5
|
+
# refresh to make sure all columns are populated, which is
|
|
6
|
+
# necessary so that database defaults work correctly.
|
|
7
|
+
#
|
|
8
|
+
# This plugin is mostly for performance reasons where you
|
|
9
|
+
# want to save the cost of select statement after the insert,
|
|
10
|
+
# but it could also help cases where records are not
|
|
11
|
+
# immediately available for selection after insertion.
|
|
12
|
+
#
|
|
13
|
+
# Note that Sequel does not attempt to refresh records when
|
|
14
|
+
# updating existing model objects, only when inserting new
|
|
15
|
+
# model objects.
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
#
|
|
19
|
+
# # Make all model subclass instances skip refreshes when saving
|
|
20
|
+
# # (called before loading subclasses)
|
|
21
|
+
# Sequel::Model.plugin :skip_create_refresh
|
|
22
|
+
#
|
|
23
|
+
# # Make the Album class skip refreshes when saving
|
|
24
|
+
# Album.plugin :skip_create_refresh
|
|
25
|
+
module SkipCreateRefresh
|
|
26
|
+
module InstanceMethods
|
|
27
|
+
private
|
|
28
|
+
# Do nothing instead of refreshing the record inside of save.
|
|
29
|
+
def _save_refresh
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|