sequel 5.30.0 → 5.35.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +86 -0
  3. data/README.rdoc +1 -1
  4. data/doc/advanced_associations.rdoc +4 -4
  5. data/doc/association_basics.rdoc +10 -5
  6. data/doc/code_order.rdoc +12 -2
  7. data/doc/dataset_filtering.rdoc +2 -2
  8. data/doc/model_dataset_method_design.rdoc +1 -1
  9. data/doc/postgresql.rdoc +71 -0
  10. data/doc/release_notes/5.31.0.txt +148 -0
  11. data/doc/release_notes/5.32.0.txt +46 -0
  12. data/doc/release_notes/5.33.0.txt +24 -0
  13. data/doc/release_notes/5.34.0.txt +40 -0
  14. data/doc/release_notes/5.35.0.txt +56 -0
  15. data/doc/testing.rdoc +1 -1
  16. data/lib/sequel/adapters/oracle.rb +2 -1
  17. data/lib/sequel/adapters/shared/access.rb +6 -6
  18. data/lib/sequel/adapters/shared/mssql.rb +5 -5
  19. data/lib/sequel/adapters/shared/mysql.rb +9 -9
  20. data/lib/sequel/adapters/shared/oracle.rb +16 -16
  21. data/lib/sequel/adapters/shared/postgres.rb +169 -14
  22. data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
  23. data/lib/sequel/adapters/shared/sqlite.rb +33 -6
  24. data/lib/sequel/adapters/tinytds.rb +1 -0
  25. data/lib/sequel/connection_pool/sharded_single.rb +4 -1
  26. data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
  27. data/lib/sequel/connection_pool/single.rb +1 -1
  28. data/lib/sequel/connection_pool/threaded.rb +2 -2
  29. data/lib/sequel/core.rb +318 -314
  30. data/lib/sequel/database/connecting.rb +1 -1
  31. data/lib/sequel/database/misc.rb +16 -10
  32. data/lib/sequel/database/query.rb +3 -1
  33. data/lib/sequel/database/schema_generator.rb +0 -1
  34. data/lib/sequel/database/schema_methods.rb +15 -16
  35. data/lib/sequel/database/transactions.rb +7 -4
  36. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
  37. data/lib/sequel/dataset/query.rb +5 -4
  38. data/lib/sequel/deprecated.rb +3 -1
  39. data/lib/sequel/exceptions.rb +2 -0
  40. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  41. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  42. data/lib/sequel/extensions/connection_expiration.rb +2 -2
  43. data/lib/sequel/extensions/connection_validator.rb +2 -2
  44. data/lib/sequel/extensions/core_refinements.rb +2 -0
  45. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
  46. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  47. data/lib/sequel/extensions/index_caching.rb +9 -7
  48. data/lib/sequel/extensions/integer64.rb +2 -0
  49. data/lib/sequel/extensions/migration.rb +1 -2
  50. data/lib/sequel/extensions/pg_array_ops.rb +4 -0
  51. data/lib/sequel/extensions/pg_enum.rb +7 -2
  52. data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
  53. data/lib/sequel/extensions/pg_hstore.rb +6 -0
  54. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  55. data/lib/sequel/extensions/pg_inet.rb +15 -5
  56. data/lib/sequel/extensions/pg_interval.rb +2 -0
  57. data/lib/sequel/extensions/pg_json_ops.rb +2 -0
  58. data/lib/sequel/extensions/pg_range.rb +5 -7
  59. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  60. data/lib/sequel/extensions/pg_row.rb +0 -1
  61. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  62. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  63. data/lib/sequel/extensions/s.rb +2 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +10 -4
  65. data/lib/sequel/extensions/server_block.rb +3 -3
  66. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  67. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  68. data/lib/sequel/extensions/to_dot.rb +9 -3
  69. data/lib/sequel/model.rb +2 -0
  70. data/lib/sequel/model/associations.rb +54 -25
  71. data/lib/sequel/model/base.rb +70 -57
  72. data/lib/sequel/model/plugins.rb +3 -3
  73. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  74. data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
  75. data/lib/sequel/plugins/association_pks.rb +60 -18
  76. data/lib/sequel/plugins/association_proxies.rb +2 -0
  77. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  78. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  79. data/lib/sequel/plugins/class_table_inheritance.rb +28 -28
  80. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  81. data/lib/sequel/plugins/dirty.rb +13 -13
  82. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  83. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  84. data/lib/sequel/plugins/json_serializer.rb +3 -7
  85. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  86. data/lib/sequel/plugins/pg_array_associations.rb +2 -3
  87. data/lib/sequel/plugins/prepared_statements.rb +5 -11
  88. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  89. data/lib/sequel/plugins/rcte_tree.rb +10 -16
  90. data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
  91. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  92. data/lib/sequel/plugins/string_stripper.rb +1 -1
  93. data/lib/sequel/plugins/subclasses.rb +2 -0
  94. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  95. data/lib/sequel/timezones.rb +6 -4
  96. data/lib/sequel/version.rb +1 -1
  97. 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
- #if !(respond_to?(:allowed_columns) && allowed_columns) && restricted_columns
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
- define_singleton_method(:boolean_subset_args, &block) if block
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 (n = subclass.name) && !n.empty?
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
- db.dataset.supports_insert_select? ? nil : @values[primary_key]
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
@@ -82,11 +82,13 @@ module Sequel
82
82
  end
83
83
  END
84
84
  else
85
+ # :nocov:
85
86
  # :nodoc:
86
87
  def self.csv_call(*args, opts, &block)
87
88
  CSV.send(*args, opts, &block)
88
89
  end
89
90
  # :nodoc:
91
+ # :nocov:
90
92
  end
91
93
 
92
94
  module ClassMethods
@@ -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