sequel 5.30.0 → 5.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +86 -0
- data/README.rdoc +1 -1
- data/doc/advanced_associations.rdoc +4 -4
- data/doc/association_basics.rdoc +10 -5
- data/doc/code_order.rdoc +12 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/model_dataset_method_design.rdoc +1 -1
- data/doc/postgresql.rdoc +71 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +5 -5
- data/lib/sequel/adapters/shared/mysql.rb +9 -9
- data/lib/sequel/adapters/shared/oracle.rb +16 -16
- data/lib/sequel/adapters/shared/postgres.rb +169 -14
- data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +33 -6
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/connection_pool/sharded_single.rb +4 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
- data/lib/sequel/connection_pool/single.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +2 -2
- data/lib/sequel/core.rb +318 -314
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +16 -10
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +0 -1
- data/lib/sequel/database/schema_methods.rb +15 -16
- data/lib/sequel/database/transactions.rb +7 -4
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/query.rb +5 -4
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/connection_expiration.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +2 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/index_caching.rb +9 -7
- data/lib/sequel/extensions/integer64.rb +2 -0
- data/lib/sequel/extensions/migration.rb +1 -2
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +7 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +6 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +5 -7
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +0 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +10 -4
- data/lib/sequel/extensions/server_block.rb +3 -3
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model.rb +2 -0
- data/lib/sequel/model/associations.rb +54 -25
- data/lib/sequel/model/base.rb +70 -57
- data/lib/sequel/model/plugins.rb +3 -3
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/boolean_subsets.rb +4 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +28 -28
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +13 -13
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +3 -7
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_array_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements.rb +5 -11
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +10 -16
- data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +2 -0
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/timezones.rb +6 -4
- data/lib/sequel/version.rb +1 -1
- metadata +18 -2
@@ -109,6 +109,8 @@ module Sequel
|
|
109
109
|
|
110
110
|
Plugins.inherited_instance_variables(self, :@association_proxy_to_dataset=>nil)
|
111
111
|
|
112
|
+
private
|
113
|
+
|
112
114
|
# Changes the association method to return a proxy instead of the associated objects
|
113
115
|
# directly.
|
114
116
|
def def_association_method(opts)
|
@@ -58,8 +58,7 @@ module Sequel
|
|
58
58
|
# restricted_columns.
|
59
59
|
def get_setter_methods
|
60
60
|
meths = super
|
61
|
-
|
62
|
-
if (!defined?(::Sequel::Plugins::WhitelistSecurity) || !plugins.include?(::Sequel::Plugins::WhitelistSecurity) || !allowed_columns) && restricted_columns
|
61
|
+
if (!defined?(::Sequel::Plugins::WhitelistSecurity::ClassMethods) || !is_a?(::Sequel::Plugins::WhitelistSecurity::ClassMethods) || !allowed_columns) && restricted_columns
|
63
62
|
meths -= restricted_columns.map{|x| "#{x}="}
|
64
63
|
end
|
65
64
|
meths
|
@@ -31,7 +31,10 @@ module Sequel
|
|
31
31
|
# Create boolean subset methods for each boolean column.
|
32
32
|
def self.configure(model, &block)
|
33
33
|
model.instance_exec do
|
34
|
-
|
34
|
+
if block
|
35
|
+
define_singleton_method(:boolean_subset_args, &block)
|
36
|
+
singleton_class.send(:private, :boolean_subset_args)
|
37
|
+
end
|
35
38
|
create_boolean_subsets if @dataset
|
36
39
|
end
|
37
40
|
end
|
@@ -278,6 +278,27 @@ module Sequel
|
|
278
278
|
|
279
279
|
Plugins.inherited_instance_variables(self, :@cti_models=>nil, :@cti_tables=>nil, :@cti_table_columns=>nil, :@cti_instance_dataset=>nil, :@cti_table_map=>nil, :@cti_alias=>nil, :@cti_ignore_subclass_columns=>nil, :@cti_qualify_tables=>nil)
|
280
280
|
|
281
|
+
# The table name for the current model class's main table.
|
282
|
+
def table_name
|
283
|
+
if cti_tables && cti_tables.length > 1
|
284
|
+
@cti_alias
|
285
|
+
else
|
286
|
+
super
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# The name of the most recently joined table.
|
291
|
+
def cti_table_name
|
292
|
+
cti_tables.last
|
293
|
+
end
|
294
|
+
|
295
|
+
# The model class for the given key value.
|
296
|
+
def sti_class_from_key(key)
|
297
|
+
sti_class(sti_model_map[key])
|
298
|
+
end
|
299
|
+
|
300
|
+
private
|
301
|
+
|
281
302
|
def inherited(subclass)
|
282
303
|
ds = sti_dataset
|
283
304
|
|
@@ -289,7 +310,7 @@ module Sequel
|
|
289
310
|
# Set table if this is a class table inheritance
|
290
311
|
table = nil
|
291
312
|
columns = nil
|
292
|
-
if
|
313
|
+
if n = subclass.name
|
293
314
|
if table = cti_table_map[n.to_sym]
|
294
315
|
columns = db.schema(table).map(&:first)
|
295
316
|
else
|
@@ -340,27 +361,6 @@ module Sequel
|
|
340
361
|
end
|
341
362
|
end
|
342
363
|
|
343
|
-
# The table name for the current model class's main table.
|
344
|
-
def table_name
|
345
|
-
if cti_tables && cti_tables.length > 1
|
346
|
-
@cti_alias
|
347
|
-
else
|
348
|
-
super
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
# The name of the most recently joined table.
|
353
|
-
def cti_table_name
|
354
|
-
cti_tables ? cti_tables.last : dataset.first_source_alias
|
355
|
-
end
|
356
|
-
|
357
|
-
# The model class for the given key value.
|
358
|
-
def sti_class_from_key(key)
|
359
|
-
sti_class(sti_model_map[key])
|
360
|
-
end
|
361
|
-
|
362
|
-
private
|
363
|
-
|
364
364
|
# If using a subquery for class table inheritance, also use a subquery
|
365
365
|
# when setting subclass dataset.
|
366
366
|
def sti_subclass_dataset(key)
|
@@ -383,11 +383,6 @@ module Sequel
|
|
383
383
|
self
|
384
384
|
end
|
385
385
|
|
386
|
-
# Don't allow use of prepared statements.
|
387
|
-
def use_prepared_statements_for?(type)
|
388
|
-
false
|
389
|
-
end
|
390
|
-
|
391
386
|
# Set the sti_key column based on the sti_key_map.
|
392
387
|
def before_validation
|
393
388
|
if new? && (set = self[model.sti_key])
|
@@ -422,7 +417,7 @@ module Sequel
|
|
422
417
|
@values[primary_key] ||= nid
|
423
418
|
end
|
424
419
|
end
|
425
|
-
|
420
|
+
@values[primary_key]
|
426
421
|
end
|
427
422
|
|
428
423
|
# Update rows in all backing tables, using the columns in each table.
|
@@ -438,6 +433,11 @@ module Sequel
|
|
438
433
|
end
|
439
434
|
end
|
440
435
|
end
|
436
|
+
|
437
|
+
# Don't allow use of prepared statements.
|
438
|
+
def use_prepared_statements_for?(type)
|
439
|
+
false
|
440
|
+
end
|
441
441
|
end
|
442
442
|
end
|
443
443
|
end
|
data/lib/sequel/plugins/dirty.rb
CHANGED
@@ -61,6 +61,19 @@ module Sequel
|
|
61
61
|
# that were used in the update statement.
|
62
62
|
attr_reader :previous_changes
|
63
63
|
|
64
|
+
# Reset the initial values after saving.
|
65
|
+
def after_save
|
66
|
+
super
|
67
|
+
reset_initial_values
|
68
|
+
end
|
69
|
+
|
70
|
+
# Save the current changes so they are available after updating. This happens
|
71
|
+
# before after_save resets them.
|
72
|
+
def after_update
|
73
|
+
super
|
74
|
+
@previous_changes = column_changes
|
75
|
+
end
|
76
|
+
|
64
77
|
# An array with the initial value and the current value
|
65
78
|
# of the column, if the column has been changed. If the
|
66
79
|
# column has not been changed, returns nil.
|
@@ -165,19 +178,6 @@ module Sequel
|
|
165
178
|
super
|
166
179
|
end
|
167
180
|
|
168
|
-
# Reset the initial values after saving.
|
169
|
-
def after_save
|
170
|
-
super
|
171
|
-
reset_initial_values
|
172
|
-
end
|
173
|
-
|
174
|
-
# Save the current changes so they are available after updating. This happens
|
175
|
-
# before after_save resets them.
|
176
|
-
def after_update
|
177
|
-
super
|
178
|
-
@previous_changes = column_changes
|
179
|
-
end
|
180
|
-
|
181
181
|
# When changing the column value, save the initial column value. If the column
|
182
182
|
# value is changed back to the initial value, update changed columns to remove
|
183
183
|
# the column.
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The forbid_lazy_load plugin forbids lazy loading of associations
|
6
|
+
# for objects in cases where the object wasn't loaded with a
|
7
|
+
# method that only returns a single object.
|
8
|
+
#
|
9
|
+
# The main reason for doing this is it makes it easier to detect
|
10
|
+
# N+1 query issues. Note that Sequel also offers a
|
11
|
+
# tactical_eager_loading plugin which will automatically eagerly
|
12
|
+
# load associations for all objects retrived in the same query
|
13
|
+
# if any object would attempt to lazily load an association. That
|
14
|
+
# approach may be simpler if you are trying to prevent N+1 issues,
|
15
|
+
# though it does retain more objects in memory.
|
16
|
+
#
|
17
|
+
# This plugin offers multiple different ways to forbid lazy
|
18
|
+
# loading. You can forbid lazy loading associations for individual
|
19
|
+
# model instances:
|
20
|
+
#
|
21
|
+
# obj = Album[1]
|
22
|
+
# obj.forbid_lazy_load
|
23
|
+
# obj.artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
24
|
+
#
|
25
|
+
# +forbid_lazy_load+ is automatically called on instances if the
|
26
|
+
# instances are loaded via a method such as Dataset#all,
|
27
|
+
# Dataset#each, and other methods that load multiple instances
|
28
|
+
# at once. These are the cases where lazily loading associations
|
29
|
+
# for such instances can cause N+1 issues.
|
30
|
+
#
|
31
|
+
# Album.all.first.artist
|
32
|
+
# objs.first.artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
33
|
+
#
|
34
|
+
# Album.each do |obj|
|
35
|
+
# obj.artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Album[1].artist # no error
|
39
|
+
#
|
40
|
+
# Album.first.artist # no error
|
41
|
+
#
|
42
|
+
# You can allow lazy loading associations for an instance that it
|
43
|
+
# was previously forbidden for:
|
44
|
+
#
|
45
|
+
# obj = Album.all.first
|
46
|
+
# obj.allow_lazy_load
|
47
|
+
# obj.artist # no error
|
48
|
+
#
|
49
|
+
# You can forbid lazy loading associations on a per-call basis,
|
50
|
+
# even if lazy loading of associations is allowed for the instance:
|
51
|
+
#
|
52
|
+
# obj = Album[1]
|
53
|
+
# obj.artist(forbid_lazy_load: true)
|
54
|
+
# # raises Sequel::Plugins::ForbidLazyLoad::Error
|
55
|
+
#
|
56
|
+
# This also works for allowing lazy loading associations for a
|
57
|
+
# specific association load even if it is forbidden for the instance:
|
58
|
+
#
|
59
|
+
# obj = Album.all.first
|
60
|
+
# obj.artist(forbid_lazy_load: false)
|
61
|
+
# # nothing raised
|
62
|
+
#
|
63
|
+
# You can also forbid lazy loading on a per-association basis using the
|
64
|
+
# +:forbid_lazy_load+ association option with a +true+ value:
|
65
|
+
#
|
66
|
+
# Album.many_to_one :artist, forbid_lazy_load: true
|
67
|
+
# Album[1].artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
68
|
+
#
|
69
|
+
# However, you probably don't want to do this as it will forbid any
|
70
|
+
# lazy loading of the association, even if the loading could not
|
71
|
+
# result in an N+1 issue.
|
72
|
+
#
|
73
|
+
# On the flip side, you can allow lazy loading using the
|
74
|
+
# +:forbid_lazy_load+ association option with a +false+ value:
|
75
|
+
#
|
76
|
+
# Album.many_to_one :artist, forbid_lazy_load: false
|
77
|
+
# Album.all.first.artist # no error
|
78
|
+
#
|
79
|
+
# One reason to do this is when using a plugin like static_cache
|
80
|
+
# on the associated model, where a query is not actually issued
|
81
|
+
# when doing a lazy association load. To make that particular
|
82
|
+
# case easier, this plugin makes Model.finalize_associations
|
83
|
+
# automatically set the association option if the associated
|
84
|
+
# class uses the static_cache plugin.
|
85
|
+
#
|
86
|
+
# Note that even with this plugin, there can still be N+1 issues,
|
87
|
+
# such as:
|
88
|
+
#
|
89
|
+
# Album.each do |obj| # 1 query for all albums
|
90
|
+
# Artist[obj.artist_id] # 1 query per album for each artist
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# Usage:
|
94
|
+
#
|
95
|
+
# # Make all model subclasses support forbidding lazy load
|
96
|
+
# # (called before loading subclasses)
|
97
|
+
# Sequel::Model.plugin :forbid_lazy_load
|
98
|
+
#
|
99
|
+
# # Make the Album class support forbidding lazy load
|
100
|
+
# Album.plugin :forbid_lazy_load
|
101
|
+
module ForbidLazyLoad
|
102
|
+
# Error raised when attempting to lazy load an association when
|
103
|
+
# lazy loading has been forbidden.
|
104
|
+
class Error < StandardError
|
105
|
+
end
|
106
|
+
|
107
|
+
module ClassMethods
|
108
|
+
Plugins.def_dataset_methods(self, :forbid_lazy_load)
|
109
|
+
|
110
|
+
# If the static_cache plugin is used by the associated class for
|
111
|
+
# an association, allow lazy loading that association, since the
|
112
|
+
# lazy association load will use a hash table lookup and not a query.
|
113
|
+
def allow_lazy_load_for_static_cache_associations
|
114
|
+
# :nocov:
|
115
|
+
if defined?(::Sequel::Plugins::StaticCache::ClassMethods)
|
116
|
+
# :nocov:
|
117
|
+
@association_reflections.each_value do |ref|
|
118
|
+
if ref.associated_class.is_a?(::Sequel::Plugins::StaticCache::ClassMethods)
|
119
|
+
ref[:forbid_lazy_load] = false
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Allow lazy loading for static cache associations before finalizing.
|
126
|
+
def finalize_associations
|
127
|
+
allow_lazy_load_for_static_cache_associations
|
128
|
+
super
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module InstanceMethods
|
133
|
+
# Set this model instance to allow lazy loading of associations.
|
134
|
+
def allow_lazy_load
|
135
|
+
@forbid_lazy_load = false
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
# Set this model instance to not allow lazy loading of associations.
|
140
|
+
def forbid_lazy_load
|
141
|
+
@forbid_lazy_load = true
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
# Allow lazy loading for objects returned by singular associations.
|
148
|
+
def _load_associated_object(opts, dynamic_opts)
|
149
|
+
# The implementation that loads these associations does
|
150
|
+
# .all.first, which would result in the object returned being
|
151
|
+
# marked as forbidding lazy load.
|
152
|
+
obj = super
|
153
|
+
obj.allow_lazy_load if obj.is_a?(InstanceMethods)
|
154
|
+
obj
|
155
|
+
end
|
156
|
+
|
157
|
+
# Raise an Error if lazy loading has been forbidden for
|
158
|
+
# the instance, association, or call.
|
159
|
+
def _load_associated_objects(opts, dynamic_opts=OPTS)
|
160
|
+
case dynamic_opts[:forbid_lazy_load]
|
161
|
+
when false
|
162
|
+
# nothing
|
163
|
+
when nil
|
164
|
+
unless dynamic_opts[:reload]
|
165
|
+
case opts[:forbid_lazy_load]
|
166
|
+
when nil
|
167
|
+
raise Error, "lazy loading forbidden for this object (association: #{opts.inspect}, object: #{inspect})" if @forbid_lazy_load
|
168
|
+
when false
|
169
|
+
# nothing
|
170
|
+
else
|
171
|
+
raise Error, "lazy loading forbidden for this association (#{opts.inspect})"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
else
|
175
|
+
raise Error, "lazy loading forbidden for this association method call (association: #{opts.inspect})"
|
176
|
+
end
|
177
|
+
|
178
|
+
super
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
module DatasetMethods
|
183
|
+
# Mark model instances retrieved in this call as forbidding lazy loading.
|
184
|
+
def each
|
185
|
+
if row_proc
|
186
|
+
super do |obj|
|
187
|
+
obj.forbid_lazy_load if obj.is_a?(InstanceMethods)
|
188
|
+
yield obj
|
189
|
+
end
|
190
|
+
else
|
191
|
+
super
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Mark model instances retrieved in this call as forbidding lazy loading.
|
196
|
+
def with_sql_each(sql)
|
197
|
+
if row_proc
|
198
|
+
super(sql) do |obj|
|
199
|
+
obj.forbid_lazy_load if obj.is_a?(InstanceMethods)
|
200
|
+
yield obj
|
201
|
+
end
|
202
|
+
else
|
203
|
+
super
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Mark model instances retrieved in this call as allowing lazy loading.
|
208
|
+
def with_sql_first(sql)
|
209
|
+
obj = super
|
210
|
+
obj.allow_lazy_load if obj.is_a?(InstanceMethods)
|
211
|
+
obj
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The instance_specific_default plugin exists to make it easier to use a
|
6
|
+
# global :instance_specific association option, or to warn or raise when Sequel
|
7
|
+
# has to guess which value to use :instance_specific option (Sequel defaults to
|
8
|
+
# guessing true as that is the conservative setting). It is helpful to
|
9
|
+
# use this plugin, particularly with the :warn or :raise settings, to determine
|
10
|
+
# which associations should have :instance_specific set. Setting the
|
11
|
+
# :instance_specific to false for associations that are not instance specific
|
12
|
+
# can improve performance.
|
13
|
+
#
|
14
|
+
# Associations are instance-specific if their block calls
|
15
|
+
# a model instance method, or where the value of the block varies
|
16
|
+
# based on runtime state, and the variance is outside of a delayed evaluation.
|
17
|
+
# For example, with the following three associations:
|
18
|
+
#
|
19
|
+
# Album.one_to_one :first_track, class: :Track do |ds|
|
20
|
+
# ds.where(number: 1)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Album.one_to_one :last_track, class: :Track do |ds|
|
24
|
+
# ds.where(number: num_tracks)
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Album.one_to_many :recent_tracks, class: :Track do |ds|
|
28
|
+
# ds.where{date_updated > Date.today - 10}
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# +first_track+ is not instance specific, but +last_track+ and +recent_tracks+ are.
|
32
|
+
# +last_trac+ is because the +num_tracks+ call in the block is calling
|
33
|
+
# <tt>Album#num_tracks</tt>. +recent_tracks+ is because the value will change over
|
34
|
+
# time. This plugin allows you to find these cases, and set the :instance_specific
|
35
|
+
# option appropriately for them:
|
36
|
+
#
|
37
|
+
# Album.one_to_one :first_track, class: :Track, instance_specific: false do |ds|
|
38
|
+
# ds.where(number: 1)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# Album.one_to_one :last_track, class: :Track, instance_specific: true do |ds|
|
42
|
+
# ds.where(number: num_tracks)
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# Album.one_to_many :recent_tracks, class: :Track, instance_specific: true do |ds|
|
46
|
+
# ds.where{date_updated > Date.today - 10}
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# For the +recent_tracks+ association, instead of marking it instance_specific, you
|
50
|
+
# could also use a delayed evaluation, since it doesn't actually contain
|
51
|
+
# instance-specific code:
|
52
|
+
#
|
53
|
+
# Album.one_to_many :recent_tracks, class: :Track, instance_specific: false do |ds|
|
54
|
+
# ds.where{date_updated > Sequel.delay{Date.today - 10}}
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Possible arguments to provide when loading the plugin:
|
58
|
+
#
|
59
|
+
# true :: Set the :instance_specific option to true
|
60
|
+
# false :: Set the :instance_specific option to false
|
61
|
+
# :default :: Call super to set the :instance_specific option
|
62
|
+
# :warn :: Emit a warning before calling super to set the :instance_specific option
|
63
|
+
# :raise :: Raise a Sequel::Error if an :instance_specific option is not provided and
|
64
|
+
# an association could be instance-specific.
|
65
|
+
#
|
66
|
+
# Note that this plugin only affects associations which could be instance
|
67
|
+
# specific (those with blocks), where the :instance_specific option was not
|
68
|
+
# specified when the association was created.
|
69
|
+
#
|
70
|
+
# Usage:
|
71
|
+
#
|
72
|
+
# # Set how to handle associations that could be instance specific
|
73
|
+
# # but did not specify an :instance_specific option, for all subclasses
|
74
|
+
# # (set before creating subclasses).
|
75
|
+
# Sequel::Model.plugin :instance_specific_default, :warn
|
76
|
+
#
|
77
|
+
# # Set how to handle associations that could be instance specific
|
78
|
+
# # but did not specify an :instance_specific option, for the Album class
|
79
|
+
# Album.plugin :instance_specific_default, :warn
|
80
|
+
module InstanceSpecificDefault
|
81
|
+
# Set how to handle associations that could be instance specific but did
|
82
|
+
# not specify an :instance_specific value.
|
83
|
+
def self.configure(model, default)
|
84
|
+
model.instance_variable_set(:@instance_specific_default, default)
|
85
|
+
end
|
86
|
+
|
87
|
+
module ClassMethods
|
88
|
+
Plugins.inherited_instance_variables(self, :@instance_specific_default=>nil)
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Return the appropriate :instance_specific value, or warn or raise if
|
93
|
+
# configured.
|
94
|
+
def _association_instance_specific_default(name)
|
95
|
+
case @instance_specific_default
|
96
|
+
when true, false
|
97
|
+
return @instance_specific_default
|
98
|
+
when :default
|
99
|
+
# nothing
|
100
|
+
when :warn
|
101
|
+
warn("possibly instance-specific association without :instance_specific option (class: #{self}, association: #{name})", :uplevel => 3)
|
102
|
+
when :raise
|
103
|
+
raise Sequel::Error, "possibly instance-specific association without :instance_specific option (class: #{self}, association: #{name})"
|
104
|
+
else
|
105
|
+
raise Sequel::Error, "invalid value passed to instance_specific_default plugin: #{@instance_specific_default.inspect}"
|
106
|
+
end
|
107
|
+
|
108
|
+
super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|