sequel 5.107.0 → 5.109.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +5 -5
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/amalgalite.rb +34 -8
  5. data/lib/sequel/adapters/ibmdb.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  7. data/lib/sequel/adapters/jdbc/mssql.rb +28 -5
  8. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +14 -3
  10. data/lib/sequel/adapters/jdbc.rb +4 -3
  11. data/lib/sequel/adapters/mock.rb +1 -1
  12. data/lib/sequel/adapters/mysql.rb +7 -2
  13. data/lib/sequel/adapters/mysql2.rb +5 -4
  14. data/lib/sequel/adapters/postgres.rb +42 -39
  15. data/lib/sequel/adapters/shared/db2.rb +5 -2
  16. data/lib/sequel/adapters/shared/mssql.rb +11 -5
  17. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  18. data/lib/sequel/adapters/shared/postgres.rb +80 -1028
  19. data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -32
  20. data/lib/sequel/adapters/shared/sqlite.rb +3 -3
  21. data/lib/sequel/adapters/sqlite.rb +13 -9
  22. data/lib/sequel/adapters/trilogy.rb +2 -1
  23. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +11 -1
  25. data/lib/sequel/connection_pool/sharded_single.rb +1 -1
  26. data/lib/sequel/connection_pool/sharded_threaded.rb +7 -5
  27. data/lib/sequel/connection_pool/sharded_timed_queue.rb +3 -3
  28. data/lib/sequel/connection_pool/threaded.rb +9 -7
  29. data/lib/sequel/connection_pool/timed_queue.rb +2 -2
  30. data/lib/sequel/connection_pool.rb +3 -3
  31. data/lib/sequel/core.rb +44 -24
  32. data/lib/sequel/database/connecting.rb +1 -1
  33. data/lib/sequel/database/features.rb +1 -1
  34. data/lib/sequel/database/misc.rb +6 -11
  35. data/lib/sequel/database/query.rb +2 -2
  36. data/lib/sequel/database/schema_generator.rb +4 -4
  37. data/lib/sequel/database/schema_methods.rb +2 -2
  38. data/lib/sequel/database/transactions.rb +5 -7
  39. data/lib/sequel/dataset/actions.rb +2 -2
  40. data/lib/sequel/dataset/features.rb +4 -4
  41. data/lib/sequel/dataset/graph.rb +1 -1
  42. data/lib/sequel/dataset/misc.rb +3 -3
  43. data/lib/sequel/dataset/prepared_statements.rb +2 -2
  44. data/lib/sequel/dataset/query.rb +8 -8
  45. data/lib/sequel/dataset/sql.rb +32 -13
  46. data/lib/sequel/dataset.rb +2 -2
  47. data/lib/sequel/deprecated.rb +2 -2
  48. data/lib/sequel/exceptions.rb +2 -2
  49. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  50. data/lib/sequel/extensions/async_thread_pool.rb +3 -3
  51. data/lib/sequel/extensions/auto_cast_date_and_time.rb +2 -2
  52. data/lib/sequel/extensions/blank.rb +2 -2
  53. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  54. data/lib/sequel/extensions/core_refinements.rb +7 -10
  55. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  56. data/lib/sequel/extensions/datetime_parse_to_time.rb +2 -2
  57. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -3
  58. data/lib/sequel/extensions/graph_each.rb +1 -1
  59. data/lib/sequel/extensions/index_caching.rb +1 -1
  60. data/lib/sequel/extensions/inflector.rb +5 -5
  61. data/lib/sequel/extensions/is_distinct_from.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +20 -4
  63. data/lib/sequel/extensions/migration.rb +21 -7
  64. data/lib/sequel/extensions/named_timezones.rb +9 -9
  65. data/lib/sequel/extensions/pg_array.rb +10 -8
  66. data/lib/sequel/extensions/pg_array_ops.rb +69 -14
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +2 -2
  68. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +3 -3
  69. data/lib/sequel/extensions/pg_enum.rb +2 -2
  70. data/lib/sequel/extensions/pg_extended_date_support.rb +5 -5
  71. data/lib/sequel/extensions/pg_extended_integer_support.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  73. data/lib/sequel/extensions/pg_hstore_ops.rb +5 -5
  74. data/lib/sequel/extensions/pg_inet.rb +2 -2
  75. data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
  76. data/lib/sequel/extensions/pg_interval.rb +6 -6
  77. data/lib/sequel/extensions/pg_json.rb +4 -4
  78. data/lib/sequel/extensions/pg_json_ops.rb +15 -13
  79. data/lib/sequel/extensions/pg_range.rb +6 -6
  80. data/lib/sequel/extensions/pg_range_ops.rb +7 -7
  81. data/lib/sequel/extensions/pg_row.rb +4 -4
  82. data/lib/sequel/extensions/pg_row_ops.rb +6 -6
  83. data/lib/sequel/extensions/provenance.rb +2 -2
  84. data/lib/sequel/extensions/query.rb +2 -2
  85. data/lib/sequel/extensions/s.rb +6 -10
  86. data/lib/sequel/extensions/schema_dumper.rb +10 -6
  87. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +2 -0
  88. data/lib/sequel/extensions/sql_comments.rb +1 -1
  89. data/lib/sequel/extensions/sql_log_normalizer.rb +1 -1
  90. data/lib/sequel/extensions/sqlite_json_ops.rb +2 -2
  91. data/lib/sequel/extensions/string_agg.rb +5 -5
  92. data/lib/sequel/extensions/symbol_aref.rb +12 -32
  93. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -5
  94. data/lib/sequel/extensions/symbol_as.rb +1 -2
  95. data/lib/sequel/extensions/symbol_as_refinement.rb +1 -5
  96. data/lib/sequel/extensions/transaction_connection_validator.rb +1 -1
  97. data/lib/sequel/model/associations.rb +17 -17
  98. data/lib/sequel/model/base.rb +18 -15
  99. data/lib/sequel/model/inflections.rb +1 -1
  100. data/lib/sequel/model/plugins.rb +2 -2
  101. data/lib/sequel/model.rb +2 -2
  102. data/lib/sequel/plugins/association_pks.rb +1 -1
  103. data/lib/sequel/plugins/association_proxies.rb +4 -4
  104. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +2 -0
  105. data/lib/sequel/plugins/caching.rb +28 -3
  106. data/lib/sequel/plugins/class_table_inheritance.rb +1 -1
  107. data/lib/sequel/plugins/column_encryption.rb +10 -10
  108. data/lib/sequel/plugins/concurrent_eager_loading.rb +2 -2
  109. data/lib/sequel/plugins/csv_serializer.rb +9 -20
  110. data/lib/sequel/plugins/deprecated_associations.rb +2 -2
  111. data/lib/sequel/plugins/eager_each.rb +1 -1
  112. data/lib/sequel/plugins/finder.rb +1 -1
  113. data/lib/sequel/plugins/forbid_lazy_load.rb +3 -3
  114. data/lib/sequel/plugins/insert_conflict.rb +1 -1
  115. data/lib/sequel/plugins/insert_returning_select.rb +1 -1
  116. data/lib/sequel/plugins/instance_filters.rb +8 -0
  117. data/lib/sequel/plugins/instance_hooks.rb +19 -0
  118. data/lib/sequel/plugins/json_serializer.rb +4 -4
  119. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  120. data/lib/sequel/plugins/many_through_many.rb +1 -1
  121. data/lib/sequel/plugins/mssql_optimistic_locking.rb +9 -0
  122. data/lib/sequel/plugins/optimistic_locking.rb +12 -2
  123. data/lib/sequel/plugins/optimistic_locking_base.rb +14 -0
  124. data/lib/sequel/plugins/pg_array_associations.rb +4 -4
  125. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -3
  126. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +2 -2
  127. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +11 -2
  128. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  129. data/lib/sequel/plugins/select_on_skipped_update.rb +63 -0
  130. data/lib/sequel/plugins/serialization.rb +1 -1
  131. data/lib/sequel/plugins/sharding.rb +1 -1
  132. data/lib/sequel/plugins/single_table_inheritance.rb +21 -3
  133. data/lib/sequel/plugins/skip_saving_columns.rb +2 -2
  134. data/lib/sequel/plugins/split_values.rb +1 -1
  135. data/lib/sequel/plugins/sql_comments.rb +8 -8
  136. data/lib/sequel/plugins/subclasses.rb +6 -6
  137. data/lib/sequel/plugins/subset_conditions.rb +5 -16
  138. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  139. data/lib/sequel/plugins/throw_failures.rb +3 -3
  140. data/lib/sequel/plugins/unused_associations.rb +5 -5
  141. data/lib/sequel/plugins/validate_associated.rb +7 -1
  142. data/lib/sequel/plugins/validate_associated_context.rb +52 -0
  143. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  144. data/lib/sequel/plugins/xml_serializer.rb +2 -0
  145. data/lib/sequel/sql.rb +120 -116
  146. data/lib/sequel/version.rb +1 -1
  147. metadata +5 -3
@@ -5,7 +5,7 @@ module Sequel
5
5
  # The lazy_attributes plugin allows users to easily set that some attributes
6
6
  # should not be loaded by default when loading model objects. If the attribute
7
7
  # is needed after the instance has been retrieved, a database query is made to
8
- # retreive the value of the attribute.
8
+ # retrieve the value of the attribute.
9
9
  #
10
10
  # This plugin depends on the tactical_eager_loading plugin, and allows you to
11
11
  # eagerly load lazy attributes for all objects retrieved with the current object.
@@ -243,7 +243,7 @@ module Sequel
243
243
  # :conditions :: Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
244
244
  # :join_type :: The join type to use for the join, defaults to :left_outer.
245
245
  # :only_conditions :: Conditions to use for the join instead of the ones specified by the keys.
246
- # opts :: The options for the associaion. Takes the same options as many_to_many.
246
+ # opts :: The options for the association. Takes the same options as many_to_many.
247
247
  def many_through_many(name, through, opts=OPTS, &block)
248
248
  associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block)
249
249
  end
@@ -22,6 +22,13 @@ module Sequel
22
22
  #
23
23
  # plugin :mssql_optimistic_locking, lock_column: :column_name
24
24
  #
25
+ # By default, the plugin allows using the lock column setter method to
26
+ # either increase or decrease the lock column value. To prevent increases,
27
+ # which could potentially be used to skip an update when it update should
28
+ # be made, you can use the +:prevent_increase+ plugin option:
29
+ #
30
+ # plugin :mssql_optimistic_locking, prevent_increase: true
31
+ #
25
32
  # This plugin relies on the instance_filters plugin.
26
33
  module MssqlOptimisticLocking
27
34
  # Load the instance_filters plugin into the model.
@@ -32,6 +39,8 @@ module Sequel
32
39
  # Set the lock column
33
40
  def self.configure(model, opts=OPTS)
34
41
  model.lock_column = opts[:lock_column] || model.lock_column || :timestamp
42
+ # SEQUEL6: Make prevent_increase true by default
43
+ model.send(:prevent_lock_column_increase!) if opts[:prevent_increase]
35
44
  end
36
45
 
37
46
  module InstanceMethods
@@ -20,6 +20,13 @@ module Sequel
20
20
  #
21
21
  # plugin :optimistic_locking, lock_column: :version
22
22
  #
23
+ # By default, the plugin allows using the lock column setter method to
24
+ # either increase or decrease the lock column value. To prevent increases,
25
+ # which could potentially be used to skip an update when it update should
26
+ # be made, you can use the +:prevent_increase+ plugin option:
27
+ #
28
+ # plugin :optimistic_locking, prevent_increase: true
29
+ #
23
30
  # This plugin relies on the instance_filters plugin.
24
31
  module OptimisticLocking
25
32
  # Exception class raised when trying to update or destroy a stale object.
@@ -32,6 +39,8 @@ module Sequel
32
39
  # Set the lock column
33
40
  def self.configure(model, opts=OPTS)
34
41
  model.lock_column = opts[:lock_column] || model.lock_column || :lock_version
42
+ # SEQUEL6: Make prevent_increase true by default
43
+ model.send(:prevent_lock_column_increase!) if opts[:prevent_increase]
35
44
  end
36
45
 
37
46
  module InstanceMethods
@@ -42,9 +51,10 @@ module Sequel
42
51
  def _update_columns(columns)
43
52
  lc = model.lock_column
44
53
  lcv = get_column_value(lc)
45
- columns[lc] = lcv + 1
54
+ new_lcv = columns[lc] = lcv + 1
46
55
  super
47
- set_column_value("#{lc}=", lcv + 1)
56
+ set_column_value("#{lc}=", nil)
57
+ set_column_value("#{lc}=", new_lcv)
48
58
  changed_columns.delete(lc)
49
59
  nil
50
60
  end
@@ -12,6 +12,20 @@ module Sequel
12
12
  module ClassMethods
13
13
  # The column holding the version of the lock
14
14
  attr_accessor :lock_column
15
+
16
+ private
17
+
18
+ # Prevent increasing the value of the lock column. If the lock column
19
+ # has an existing value, do not allow setting a value that is higher
20
+ # than it.
21
+ def prevent_lock_column_increase!
22
+ lc = lock_column
23
+ define_method(:"#{lc}=") do |v|
24
+ unless v && (lv = send(lc)) && v > lv
25
+ super(v)
26
+ end
27
+ end
28
+ end
15
29
 
16
30
  Plugins.inherited_instance_variables(self, :@lock_column=>nil)
17
31
  end
@@ -534,9 +534,9 @@ module Sequel
534
534
  def many_to_pg_array_association_filter_expression(op, ref, obj)
535
535
  pk = ref.qualify(model.table_name, ref.primary_key)
536
536
  key = ref[:key]
537
- # :nocov:
537
+ # simplecov:disable
538
538
  expr = case obj
539
- # :nocov:
539
+ # simplecov:enable
540
540
  when Sequel::Model
541
541
  if (assoc_pks = obj.get_column_value(key)) && !assoc_pks.empty?
542
542
  Sequel[pk=>assoc_pks.to_a]
@@ -556,9 +556,9 @@ module Sequel
556
556
  # Support filtering by pg_array_to_many associations using a subquery.
557
557
  def pg_array_to_many_association_filter_expression(op, ref, obj)
558
558
  key = ref.qualify(model.table_name, ref[:key_column])
559
- # :nocov:
559
+ # simplecov:disable
560
560
  expr = case obj
561
- # :nocov:
561
+ # simplecov:enable
562
562
  when Sequel::Model
563
563
  if pkv = obj.get_column_value(ref.primary_key_method)
564
564
  Sequel.pg_array_op(key).contains(Sequel.pg_array([pkv], ref.array_type))
@@ -261,9 +261,9 @@ module Sequel
261
261
  messages = model.pg_auto_constraint_validations_messages
262
262
 
263
263
  unless override
264
- # :nocov:
264
+ # simplecov:disable
265
265
  case e
266
- # :nocov:
266
+ # simplecov:enable
267
267
  when Sequel::NotNullConstraintViolation
268
268
  if column = info[:column]
269
269
  add_pg_constraint_validation_error([m.call(column)], messages[:not_null])
@@ -297,7 +297,7 @@ module Sequel
297
297
  end
298
298
  end
299
299
  rescue
300
- # If there is an error trying to conver the constraint violation
300
+ # If there is an error trying to convert the constraint violation
301
301
  # into a validation failure, it's best to just raise the constraint
302
302
  # violation. This can make debugging the above block of code more
303
303
  # difficult.
@@ -45,7 +45,7 @@ module Sequel
45
45
  key = key.column
46
46
  end
47
47
 
48
- # :nocov:
48
+ # simplecov:disable
49
49
  # many_to_pg_array association type does not need changes, as it
50
50
  # already converts the values to a typed postgres array, it does
51
51
  # not call the code that uses :eager_loading_predicate_transform.
@@ -54,7 +54,7 @@ module Sequel
54
54
  # unless it is one of these types, but external association types
55
55
  # could potentially reach it.
56
56
  sch = case ref[:type]
57
- # :nocov:
57
+ # simplecov:enable
58
58
  when :many_to_one, :one_to_one, :one_to_many, :pg_array_to_many
59
59
  ref.associated_class.db_schema
60
60
  when :many_to_many, :one_through_one
@@ -21,6 +21,13 @@ module Sequel
21
21
  # optimistic locking. The disadvantage is that testing can be
22
22
  # more difficult if you are modifying the underlying row between
23
23
  # when a model is retrieved and when it is saved.
24
+
25
+ # By default, the plugin allows using the +xmin=+ setter method to
26
+ # either increase or decrease the lock value. To prevent increases,
27
+ # which could potentially be used to skip an update when it update should
28
+ # be made, you can use the +:prevent_increase+ plugin argument:
29
+ #
30
+ # plugin :pg_xmin_optimistic_locking, prevent_increase: true
24
31
  #
25
32
  # This plugin may not work with the class_table_inheritance plugin.
26
33
  #
@@ -29,7 +36,7 @@ module Sequel
29
36
  WILDCARD = LiteralString.new('*').freeze
30
37
 
31
38
  # Define the xmin column accessor
32
- def self.apply(model)
39
+ def self.apply(model, prevent_increase: false)
33
40
  model.instance_exec do
34
41
  plugin(:optimistic_locking_base)
35
42
  @lock_column = :xmin
@@ -39,9 +46,11 @@ module Sequel
39
46
 
40
47
  # Update the dataset to append the xmin column if it is usable
41
48
  # and there is a dataset for the model.
42
- def self.configure(model)
49
+ def self.configure(model, prevent_increase: false)
43
50
  model.instance_exec do
44
51
  set_dataset(@dataset) if @dataset
52
+ # SEQUEL6: Make prevent_increase true by default
53
+ prevent_lock_column_increase! if prevent_increase
45
54
  end
46
55
  end
47
56
 
@@ -181,11 +181,11 @@ module Sequel
181
181
  # Disabling usage of prepared statements for insert_select on SQLite seems to be the
182
182
  # simplest way to workaround the problem.
183
183
  db.database_type != :sqlite
184
- # :nocov:
184
+ # simplecov:disable
185
185
  when :delete, :refresh
186
186
  Sequel::Deprecation.deprecate("The :delete and :refresh prepared statement types", "There should be no need to check if these types are supported")
187
187
  false
188
- # :nocov:
188
+ # simplecov:enable
189
189
  else
190
190
  raise Error, "unsupported type used: #{type.inspect}"
191
191
  end
@@ -0,0 +1,63 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # This plugin changes the default model behavior so that for existing
6
+ # objects, if Model#save_changes skips the save as the object is not
7
+ # modified, or if the UPDATE query is skipped as there were no columns
8
+ # to update, Sequel will issue a SELECT query to make sure the object
9
+ # still exists. This is only done if require_modification is set for
10
+ # the object, so that these cases will raise the same error that
11
+ # Sequel would raise if the UPDATE require was run but did not modify
12
+ # a row.
13
+ #
14
+ # class Item < Sequel::Model
15
+ # plugin :select_on_skipped_update
16
+ # end
17
+ #
18
+ # item = Item[1]
19
+ #
20
+ # # Delete the item from the database
21
+ # Item.where(id: 1).delete
22
+ #
23
+ # # By default, this would return nil, but with the plugin,
24
+ # # this raises Sequel::NoExistingRow.
25
+ # item.save_changes
26
+ #
27
+ # # By default, this would return the object without issuing the
28
+ # # UPDATE query (as the columns to update is empty. With the plugin,
29
+ # # this raises Sequel::NoExistingRow.
30
+ # item.save(columns: [])
31
+ module SelectOnSkippedUpdate
32
+ COUNT = Sequel.function(:count).*.as(:count)
33
+ private_constant :COUNT
34
+
35
+ module InstanceMethods
36
+ # If an UPDATE statement was skipped as the object wasn't modified, run a SELECT query.
37
+ def save_changes(opts=OPTS)
38
+ ret = super
39
+ run_select_on_skipped_update if ret.nil? && require_modification
40
+ ret
41
+ end
42
+
43
+ private
44
+
45
+ # If an UPDATE statement was skipped as there were no columns to update, run a SELECT query.
46
+ def _update_columns(columns)
47
+ ret = super
48
+ run_select_on_skipped_update if ret.nil? && columns.empty? && require_modification
49
+ ret
50
+ end
51
+
52
+ # Run a SELECT query on the dataset to determine whether the object still exists in the database.
53
+ # This will result in an exception if a single row is not modified.
54
+ def run_select_on_skipped_update
55
+ ds = _update_dataset
56
+ unless ds.select(COUNT).single_value! == 1
57
+ raise(NoMatchingRow, "Attempt to select object did not result in a single row modification")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -154,7 +154,7 @@ module Sequel
154
154
 
155
155
  private
156
156
 
157
- # Add serializated attribute acessor methods to the serialization_module
157
+ # Add serializated attribute accessor methods to the serialization_module
158
158
  def define_serialized_attribute_accessor(serializer, deserializer, *columns)
159
159
  m = self
160
160
  include(@serialization_module ||= Sequel.set_temp_name(Module.new){"#{name}::@serialization_module"}) unless @serialization_module
@@ -6,7 +6,7 @@ module Sequel
6
6
  # in the following ways:
7
7
  #
8
8
  # * It automatically saves model instances back to the
9
- # shard they were retreived from.
9
+ # shard they were retrieved from.
10
10
  # * It makes model associations use the same shard as the model object.
11
11
  # * It adds a slightly nicer API for creating model instances on
12
12
  # specific shards.
@@ -12,6 +12,11 @@ module Sequel
12
12
  # argument to the plugin) holds the class name as a string. However,
13
13
  # you can override this by using the <tt>:model_map</tt> option and/or
14
14
  # the <tt>:key_map</tt> option.
15
+ #
16
+ # The +sti_key+ column can be set via mass assignment, just like any
17
+ # non-primary key column. Make sure to follow the recommendations in the
18
+ # {mass assignment guide}[rdoc-ref:doc/mass_assignment.rdoc]
19
+ # to ensure this column is not set with untrusted data.
15
20
  #
16
21
  # You should only load this plugin in the parent class, not in the subclasses.
17
22
  #
@@ -84,6 +89,7 @@ module Sequel
84
89
  @sti_key_array = nil
85
90
  @sti_key = key
86
91
  @sti_dataset = dataset
92
+ @sti_root_class = self
87
93
  @sti_model_map = opts[:model_map] || lambda{|v| v if v && v != ''}
88
94
  @sti_key_map = if km = opts[:key_map]
89
95
  if km.is_a?(Hash)
@@ -150,7 +156,7 @@ module Sequel
150
156
  # This defaults to a lookup in the key map.
151
157
  attr_reader :sti_key_chooser
152
158
 
153
- Plugins.inherited_instance_variables(self, :@sti_dataset=>nil, :@sti_key=>nil, :@sti_key_map=>nil, :@sti_model_map=>nil, :@sti_key_chooser=>nil)
159
+ Plugins.inherited_instance_variables(self, :@sti_dataset=>nil, :@sti_key=>nil, :@sti_key_map=>nil, :@sti_model_map=>nil, :@sti_key_chooser=>nil, :@sti_root_class=>nil)
154
160
 
155
161
  # Freeze STI information when freezing model class. Note that
156
162
  # because of how STI works, you should not freeze an STI subclass
@@ -194,7 +200,7 @@ module Sequel
194
200
  end
195
201
 
196
202
  # Copy the necessary attributes to the subclasses, and filter the
197
- # subclass's dataset based on the sti_kep_map entry for the class.
203
+ # subclass's dataset based on the sti_key_map entry for the class.
198
204
  def inherited(subclass)
199
205
  super
200
206
  key = Array(sti_key_map[subclass]).dup
@@ -221,11 +227,23 @@ module Sequel
221
227
  # Return a class object. If a class is given, return it directly.
222
228
  # Treat strings and symbols as class names. If nil is given or
223
229
  # an invalid class name string or symbol is used, return self.
230
+ # If the class is valid but is not a subclass of the class that
231
+ # loaded the single_table_inheritance plugin, use the class that
232
+ # loaded the plugin.
224
233
  # Raise an error for other types.
225
234
  def sti_class(v)
226
235
  case v
227
236
  when String, Symbol
228
- constantize(v) rescue self
237
+ begin
238
+ klass = constantize(v)
239
+ if Class === klass && @sti_root_class >= klass
240
+ klass
241
+ else
242
+ @sti_root_class
243
+ end
244
+ rescue
245
+ self
246
+ end
229
247
  when nil
230
248
  self
231
249
  when Class
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Sequel
4
4
  module Plugins
5
- # The skip_saving_columms plugin allows skipping specific columns when
5
+ # The skip_saving_columns plugin allows skipping specific columns when
6
6
  # saving. By default, it skips columns that the database schema
7
7
  # indicates are generated columns:
8
8
  #
@@ -90,7 +90,7 @@ module Sequel
90
90
 
91
91
  # Skip the columns the model has marked to skip when updating
92
92
  # only changed columns.
93
- def _save_update_changed_colums_hash
93
+ def _save_update_changed_columns_hash
94
94
  _save_removed_skipped_columns(super)
95
95
  end
96
96
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Sequel
4
4
  module Plugins
5
- # The split_values plugin splits the values hash retreived from the
5
+ # The split_values plugin splits the values hash retrieved from the
6
6
  # database, and moves keys from the values hash that are not columns
7
7
  # in the model's dataset to a separate hash. This makes it so the
8
8
  # values hash only stores columns from the model's dataset.
@@ -63,9 +63,9 @@ module Sequel
63
63
  super(*a, &block)
64
64
  end
65
65
  end
66
- # :nocov:
66
+ # simplecov:disable
67
67
  mod.send(:ruby2_keywords, meth) if mod.respond_to?(:ruby2_keywords, true)
68
- # :nocov:
68
+ # simplecov:enable
69
69
  end
70
70
 
71
71
  def self.apply(model)
@@ -101,9 +101,9 @@ module Sequel
101
101
  super(*a, &block)
102
102
  end
103
103
  end
104
- # :nocov:
104
+ # simplecov:disable
105
105
  ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
106
- # :nocov:
106
+ # simplecov:enable
107
107
  end
108
108
 
109
109
  private
@@ -133,9 +133,9 @@ module Sequel
133
133
  super(*a, &block)
134
134
  end
135
135
  end
136
- # :nocov:
136
+ # simplecov:disable
137
137
  ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
138
- # :nocov:
138
+ # simplecov:enable
139
139
  end
140
140
 
141
141
  private
@@ -163,9 +163,9 @@ module Sequel
163
163
  super(*a, &block)
164
164
  end
165
165
  end
166
- # :nocov:
166
+ # simplecov:disable
167
167
  ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
168
- # :nocov:
168
+ # simplecov:enable
169
169
  end
170
170
 
171
171
  private
@@ -40,9 +40,9 @@ module Sequel
40
40
 
41
41
  # Initialize the subclasses instance variable for the model.
42
42
  def self.apply(model, &block)
43
- # :nocov:
43
+ # simplecov:disable
44
44
  model.instance_variable_set(:@subclasses, []) if NEED_SUBCLASSES
45
- # :nocov:
45
+ # simplecov:enable
46
46
  model.instance_variable_set(:@on_subclass, block)
47
47
  end
48
48
 
@@ -51,13 +51,13 @@ module Sequel
51
51
  # class created.
52
52
  attr_reader :on_subclass
53
53
 
54
- # :nocov:
54
+ # simplecov:disable
55
55
  if NEED_SUBCLASSES
56
56
  # All subclasses for the current model. Does not
57
57
  # include the model itself.
58
58
  attr_reader :subclasses
59
59
  end
60
- # :nocov:
60
+ # simplecov:enable
61
61
 
62
62
  # All descendent classes of this model.
63
63
  def descendants
@@ -85,9 +85,9 @@ module Sequel
85
85
  # in the subclass.
86
86
  def inherited(subclass)
87
87
  super
88
- # :nocov:
88
+ # simplecov:disable
89
89
  Sequel.synchronize{subclasses << subclass} if NEED_SUBCLASSES
90
- # :nocov:
90
+ # simplecov:enable
91
91
  on_subclass.call(subclass) if on_subclass
92
92
  end
93
93
  end
@@ -105,22 +105,11 @@ module Sequel
105
105
 
106
106
  private
107
107
 
108
- if RUBY_VERSION >= '2'
109
- # Backbone of #where_any and #where_all
110
- def _where_any_all(meth, name, args)
111
- ds = model.dataset
112
- # #bind used here because the dataset module may not yet be included in the model's dataset
113
- where(name, Sequel.send(meth, *args.map{|a| self.instance_method(:"#{a}_conditions").bind(ds).call}))
114
- end
115
- else
116
- # Cannot bind module method to arbitrary objects in Ruby 1.9.
117
- # :nocov:
118
- def _where_any_all(meth, name, args)
119
- ds = model.dataset.clone
120
- ds.extend(self)
121
- where(name, Sequel.send(meth, *args.map{|a| ds.send(:"#{a}_conditions")}))
122
- end
123
- # :nocov:
108
+ # Backbone of #where_any and #where_all
109
+ def _where_any_all(meth, name, args)
110
+ ds = model.dataset
111
+ # #bind used here because the dataset module may not yet be included in the model's dataset
112
+ where(name, Sequel.send(meth, *args.map{|a| self.instance_method(:"#{a}_conditions").bind(ds).call}))
124
113
  end
125
114
  end
126
115
  end
@@ -162,7 +162,7 @@ module Sequel
162
162
  end
163
163
 
164
164
  # Filter the objects used when tactical eager loading.
165
- # By default, this removes frozen objects and objects that alreayd have the association loaded,
165
+ # By default, this removes frozen objects and objects that already have the association loaded,
166
166
  # as well as objects where the reflection for the association is not the same as the receiver's
167
167
  # reflection for the association.
168
168
  def _filter_tactical_eager_load_objects(opts)
@@ -64,7 +64,7 @@ module Sequel
64
64
  end
65
65
  end
66
66
 
67
- if RUBY_VERSION >= '2.2' && (!defined?(JRUBY_VERSION) || JRUBY_VERSION > '9.1')
67
+ if RUBY_VERSION >= '2.2' && (!defined?(JRUBY_VERSION) || JRUBY_VERSION.to_r > Rational('9.1'))
68
68
  # Throw HookFailed with the generated error. If the throw is not
69
69
  # caught, just return the originally generated error.
70
70
  def hook_failed_error(msg)
@@ -86,7 +86,7 @@ module Sequel
86
86
  # UncaughtThrowError was added in Ruby 2.2. Older Ruby versions
87
87
  # used ArgumentError with "uncaught throw" at the start of the message
88
88
 
89
- # :nocov:
89
+ # simplecov:disable
90
90
  def hook_failed_error(msg)
91
91
  e = super
92
92
  throw HookFailed, e
@@ -102,7 +102,7 @@ module Sequel
102
102
  raise e2 unless e2.message.start_with?('uncaught throw')
103
103
  e
104
104
  end
105
- # :nocov:
105
+ # simplecov:enable
106
106
  end
107
107
  end
108
108
  end
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- # :nocov:
3
+ # simplecov:disable
4
4
 
5
5
  # This entire file is excluded from coverage testing. This is because it
6
6
  # requires coverage testing to work, and if you've already loaded Sequel
@@ -67,7 +67,7 @@ module Sequel
67
67
  #
68
68
  # You can use the output of the +unused_associations+ method to determine
69
69
  # which associations are not used at all in your application, and can
70
- # be eliminiated.
70
+ # be eliminated.
71
71
  #
72
72
  # You can also take that unused association data and pass it to the
73
73
  # +unused_association_options+ method, which will return an array of
@@ -131,7 +131,7 @@ module Sequel
131
131
  # = Automatic Usage of Unused Association Data
132
132
  #
133
133
  # Since it can be a pain to manually update all of your code
134
- # to remove unused assocations or add options to prevent the
134
+ # to remove unused associations or add options to prevent the
135
135
  # definition of unused associations, the unused_associations
136
136
  # plugin comes with support to take previously saved unused
137
137
  # association data, and use it to not create unused associations,
@@ -265,7 +265,7 @@ module Sequel
265
265
  module ClassMethods
266
266
  # Only the data is copied to subclasses, to allow the :modify_associations
267
267
  # plugin option to affect them. The :file and :coverage_file are not copied
268
- # to subclasses, as users are expected ot call methods such as
268
+ # to subclasses, as users are expected to call methods such as
269
269
  # unused_associations only on the class that is loading the plugin.
270
270
  Plugins.inherited_instance_variables(self, :@unused_associations_data=>nil)
271
271
 
@@ -526,4 +526,4 @@ module Sequel
526
526
  end
527
527
  end
528
528
  end
529
- # :nocov:
529
+ # simplecov:enable
@@ -43,6 +43,12 @@ module Sequel
43
43
  module InstanceMethods
44
44
  private
45
45
 
46
+ # Whether the associated object is valid. By default, the same as calling
47
+ # +valid?+ on the object, but exists so it can be overridden.
48
+ def associated_object_valid?(reflection, obj)
49
+ obj.valid?
50
+ end
51
+
46
52
  # Delay validating the associated object until validating the current object.
47
53
  def delay_validate_associated_object(reflection, obj)
48
54
  after_validation_hook{validate_associated_object(reflection, obj)}
@@ -62,7 +68,7 @@ module Sequel
62
68
  end
63
69
  end
64
70
 
65
- unless obj.valid?
71
+ unless associated_object_valid?(reflection, obj)
66
72
  if ignore_key_errors
67
73
  # Ignore errors on the key column in the associated object. This column
68
74
  # will be set when saving to a presumably valid value using a column
@@ -0,0 +1,52 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The validate_associated_context plugin extends the validate_associated
6
+ # plugin to work with the validation_contexts plugin. It ensures associated
7
+ # objects are validated using the validation context of the current object.
8
+ #
9
+ # This plugin loads the validate_associated and validation_contexts plugins.
10
+ # The associated model must also use the validation_contexts plugin in order
11
+ # to access the validation context.
12
+ #
13
+ # class Album < Sequel::Model
14
+ # plugin :validate_associated_context
15
+ # many_to_one :artist
16
+ #
17
+ # def validate
18
+ # super
19
+ # validate_associated_object(model.association_reflection(:artist), artist) if artist
20
+ # end
21
+ # end
22
+ #
23
+ # class Artist < Sequel::Model
24
+ # plugin :validation_contexts
25
+ # def validate
26
+ # super
27
+ # errors.add(:name, 'not present') if name.nil? && validation_context == :publish
28
+ # end
29
+ # end
30
+ #
31
+ # album = Album.new(artist: Artist.new)
32
+ # album.valid? # => true
33
+ # album.valid?(validation_context: :publish) # => false
34
+ module ValidateAssociatedContext
35
+ # Depend on the validate_associated and validation_contexts plugins.
36
+ def self.apply(model)
37
+ model.plugin :validate_associated
38
+ model.plugin :validation_contexts
39
+ end
40
+
41
+ module InstanceMethods
42
+ private
43
+
44
+ # Whether the associated object is valid, using the validation context
45
+ # of the current object.
46
+ def associated_object_valid?(reflection, obj)
47
+ obj.valid?(:validation_context=>validation_context)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end