sequel 5.30.0 → 5.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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