sequel 5.108.0 → 5.110.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.
- checksums.yaml +4 -4
- data/lib/sequel/adapters/ado.rb +1 -0
- data/lib/sequel/adapters/amalgalite.rb +34 -8
- data/lib/sequel/adapters/jdbc/mssql.rb +28 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +14 -3
- data/lib/sequel/adapters/jdbc.rb +2 -1
- data/lib/sequel/adapters/postgres.rb +5 -2
- data/lib/sequel/adapters/shared/access.rb +5 -1
- data/lib/sequel/adapters/shared/db2.rb +3 -0
- data/lib/sequel/adapters/shared/mssql.rb +7 -1
- data/lib/sequel/adapters/shared/oracle.rb +7 -0
- data/lib/sequel/adapters/shared/postgres.rb +97 -1001
- data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -32
- data/lib/sequel/adapters/sqlite.rb +13 -9
- data/lib/sequel/adapters/trilogy.rb +2 -1
- data/lib/sequel/core.rb +71 -23
- data/lib/sequel/database/misc.rb +1 -6
- data/lib/sequel/database/schema_methods.rb +3 -3
- data/lib/sequel/database/transactions.rb +1 -3
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/sql.rb +14 -3
- data/lib/sequel/extensions/core_refinements.rb +1 -4
- data/lib/sequel/extensions/current_datetime_timestamp.rb +2 -2
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -3
- data/lib/sequel/extensions/lit_require_frozen.rb +17 -1
- data/lib/sequel/extensions/named_timezones.rb +9 -7
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_json_ops.rb +4 -2
- data/lib/sequel/extensions/pg_loose_count.rb +37 -2
- data/lib/sequel/extensions/s.rb +4 -8
- data/lib/sequel/extensions/schema_dumper.rb +6 -2
- data/lib/sequel/extensions/sql_comments.rb +0 -4
- data/lib/sequel/extensions/string_date_time.rb +7 -4
- data/lib/sequel/extensions/symbol_aref.rb +11 -31
- data/lib/sequel/extensions/symbol_aref_refinement.rb +1 -4
- data/lib/sequel/extensions/symbol_as.rb +1 -2
- data/lib/sequel/extensions/symbol_as_refinement.rb +1 -5
- data/lib/sequel/model/base.rb +4 -9
- data/lib/sequel/plugins/caching.rb +27 -2
- data/lib/sequel/plugins/csv_serializer.rb +9 -20
- data/lib/sequel/plugins/instance_filters.rb +8 -0
- data/lib/sequel/plugins/instance_hooks.rb +19 -0
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +9 -0
- data/lib/sequel/plugins/optimistic_locking.rb +12 -2
- data/lib/sequel/plugins/optimistic_locking_base.rb +14 -0
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +11 -2
- data/lib/sequel/plugins/select_on_skipped_update.rb +63 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +20 -2
- data/lib/sequel/plugins/subset_conditions.rb +5 -16
- data/lib/sequel/plugins/throw_failures.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +7 -1
- data/lib/sequel/plugins/validate_associated_context.rb +52 -0
- data/lib/sequel/sql.rb +109 -105
- data/lib/sequel/version.rb +1 -1
- metadata +5 -3
|
@@ -22,14 +22,17 @@ class String
|
|
|
22
22
|
|
|
23
23
|
# Converts a string into a DateTime object.
|
|
24
24
|
def to_datetime
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Sequel::Deprecation.deprecate("String#to_datetime is deprecated and will be removed in Sequel 6.")
|
|
26
|
+
begin
|
|
27
|
+
DateTime.parse(self, Sequel.convert_two_digit_years)
|
|
28
|
+
rescue => e
|
|
29
|
+
raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
|
|
30
|
+
end
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
# Converts a string into a Time or DateTime object, depending on the
|
|
31
34
|
# value of Sequel.datetime_class
|
|
32
|
-
def to_sequel_time
|
|
35
|
+
def to_sequel_time # SEQUEL6: Simplify making an alias to to_time
|
|
33
36
|
if Sequel.datetime_class == DateTime
|
|
34
37
|
DateTime.parse(self, Sequel.convert_two_digit_years)
|
|
35
38
|
else
|
|
@@ -15,41 +15,21 @@
|
|
|
15
15
|
#
|
|
16
16
|
# Sequel.extension :symbol_aref
|
|
17
17
|
#
|
|
18
|
-
#
|
|
19
|
-
# is a refinement version of this in the symbol_aref_refinement extension.
|
|
18
|
+
# There is a refinement version of this in the symbol_aref_refinement extension.
|
|
20
19
|
#
|
|
21
20
|
# Related module: Sequel::SymbolAref
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
super
|
|
31
|
-
end
|
|
22
|
+
module Sequel::SymbolAref
|
|
23
|
+
def [](v)
|
|
24
|
+
case v
|
|
25
|
+
when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier
|
|
26
|
+
Sequel::SQL::QualifiedIdentifier.new(self, v)
|
|
27
|
+
else
|
|
28
|
+
super
|
|
32
29
|
end
|
|
33
30
|
end
|
|
31
|
+
end
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
end
|
|
38
|
-
# simplecov:disable
|
|
39
|
-
else
|
|
40
|
-
class Symbol
|
|
41
|
-
if method_defined?(:[])
|
|
42
|
-
alias_method :aref_before_sequel, :[]
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def [](v)
|
|
46
|
-
case v
|
|
47
|
-
when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier
|
|
48
|
-
Sequel::SQL::QualifiedIdentifier.new(self, v)
|
|
49
|
-
else
|
|
50
|
-
aref_before_sequel(v)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
33
|
+
class Symbol
|
|
34
|
+
prepend Sequel::SymbolAref
|
|
54
35
|
end
|
|
55
|
-
# simplecov:enable
|
|
@@ -25,10 +25,7 @@
|
|
|
25
25
|
#
|
|
26
26
|
# Related module: Sequel::SymbolAref
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
|
|
30
|
-
# simplecov:enable
|
|
31
|
-
|
|
28
|
+
#
|
|
32
29
|
module Sequel::SymbolAref
|
|
33
30
|
refine Symbol do
|
|
34
31
|
def [](v)
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
# Sequel.extension :symbol_as
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
# is a refinement version of this in the symbol_as_refinement extension.
|
|
17
|
+
# There is a refinement version of this in the symbol_as_refinement extension.
|
|
19
18
|
|
|
20
19
|
#
|
|
21
20
|
class Symbol
|
|
@@ -23,10 +23,7 @@
|
|
|
23
23
|
#
|
|
24
24
|
# Related module: Sequel::SymbolAs
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
|
|
28
|
-
# simplecov:enable
|
|
29
|
-
|
|
26
|
+
#
|
|
30
27
|
module Sequel::SymbolAs
|
|
31
28
|
refine Symbol do
|
|
32
29
|
def as(aliaz, columns=nil)
|
|
@@ -34,4 +31,3 @@ module Sequel::SymbolAs
|
|
|
34
31
|
end
|
|
35
32
|
end
|
|
36
33
|
end
|
|
37
|
-
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -1188,7 +1188,7 @@ END
|
|
|
1188
1188
|
# By default it calls send with the argument to get the value. This can
|
|
1189
1189
|
# be overridden if you have columns that conflict with existing
|
|
1190
1190
|
# method names.
|
|
1191
|
-
|
|
1191
|
+
define_method(:get_column_value, Kernel.instance_method(:send))
|
|
1192
1192
|
|
|
1193
1193
|
# Set the value of the column. Takes two arguments. The first is a
|
|
1194
1194
|
# symbol or string argument for the column name, suffixed with =. The
|
|
@@ -1196,7 +1196,7 @@ END
|
|
|
1196
1196
|
# with the argument to set the value. This can be overridden if you have
|
|
1197
1197
|
# columns that conflict with existing method names (unlikely for setter
|
|
1198
1198
|
# methods, but possible).
|
|
1199
|
-
|
|
1199
|
+
define_method(:set_column_value, Kernel.instance_method(:send))
|
|
1200
1200
|
|
|
1201
1201
|
# Creates new instance and passes the given values to set.
|
|
1202
1202
|
# If a block is given, yield the instance to the block.
|
|
@@ -1293,7 +1293,7 @@ END
|
|
|
1293
1293
|
# self.class.
|
|
1294
1294
|
#
|
|
1295
1295
|
# Artist.new.model # => Artist
|
|
1296
|
-
|
|
1296
|
+
define_method(:model, Kernel.instance_method(:class))
|
|
1297
1297
|
|
|
1298
1298
|
# The autoincrementing primary key for this model object. Should be
|
|
1299
1299
|
# overridden if you have a composite primary key with one part of it
|
|
@@ -2433,12 +2433,7 @@ END
|
|
|
2433
2433
|
plugin self
|
|
2434
2434
|
def_initialize_nil_instance_variables
|
|
2435
2435
|
|
|
2436
|
-
singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
|
|
2437
|
-
# simplecov:disable
|
|
2438
|
-
if RUBY_VERSION >= '1.9.3'
|
|
2439
|
-
# simplecov:enable
|
|
2440
|
-
singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
|
|
2441
|
-
end
|
|
2436
|
+
singleton_class.send(:undef_method, :dup, :clone, :initialize_copy, :initialize_clone, :initialize_dup)
|
|
2442
2437
|
|
|
2443
2438
|
# simplecov:disable
|
|
2444
2439
|
if defined?(Sequel::Postgres::SEQUEL_PG_VERSION_INTEGER) && Sequel::Postgres::SEQUEL_PG_VERSION_INTEGER >= 11800
|
|
@@ -29,6 +29,13 @@ module Sequel
|
|
|
29
29
|
#
|
|
30
30
|
# You should not use this plugin if you are using sharding and there are different
|
|
31
31
|
# rows for the same primary key on different shards.
|
|
32
|
+
#
|
|
33
|
+
# Note that if you are using a composite key, and multiple columns in the composite
|
|
34
|
+
# key could potentially contain the character ",", you should override the
|
|
35
|
+
# +composite_cache_key+ class method to return an an unambiguous string identifying
|
|
36
|
+
# the specific primary key argument given. For consistency, if you would like to
|
|
37
|
+
# modify the cache key string used for scalar primary keys, you can override the
|
|
38
|
+
# +scalar_cache_key+ class method.
|
|
32
39
|
#
|
|
33
40
|
# Usage:
|
|
34
41
|
#
|
|
@@ -81,8 +88,26 @@ module Sequel
|
|
|
81
88
|
|
|
82
89
|
# Return a key string for the given primary key.
|
|
83
90
|
def cache_key(pk)
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
case pk
|
|
92
|
+
when Array
|
|
93
|
+
raise(Error, 'no primary key for this record') unless pk.all?
|
|
94
|
+
"#{cache_key_prefix}:#{composite_cache_key(pk)}"
|
|
95
|
+
else
|
|
96
|
+
raise(Error, 'no primary key for this record') unless pk
|
|
97
|
+
"#{cache_key_prefix}:#{scalar_cache_key(pk)}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Return an appropriate cache key string for a valid scalar primary key.
|
|
102
|
+
# By default, converts the primary key to a string.
|
|
103
|
+
def scalar_cache_key(pk)
|
|
104
|
+
pk.to_s
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Return an appropriate cache key string for a valid composite primary key.
|
|
108
|
+
# By default, joins with the parts of the compsite key with ",".
|
|
109
|
+
def composite_cache_key(pk)
|
|
110
|
+
pk.join(',')
|
|
86
111
|
end
|
|
87
112
|
|
|
88
113
|
Plugins.inherited_instance_variables(self, :@cache_store=>nil, :@cache_ttl=>nil, :@cache_ignore_exceptions=>nil)
|
|
@@ -73,23 +73,12 @@ module Sequel
|
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def self.csv_call(*args, opts, &block)
|
|
81
|
-
CSV.send(*args, **opts, &block)
|
|
82
|
-
end
|
|
83
|
-
END
|
|
84
|
-
else
|
|
85
|
-
# simplecov:disable
|
|
86
|
-
# :nodoc:
|
|
87
|
-
def self.csv_call(*args, opts, &block)
|
|
88
|
-
CSV.send(*args, opts, &block)
|
|
89
|
-
end
|
|
90
|
-
# :nodoc:
|
|
91
|
-
# simplecov:enable
|
|
76
|
+
# simplecov:disable
|
|
77
|
+
def self.csv_call(*args, opts, &block)
|
|
78
|
+
Sequel::Deprecation.deprecate("Sequel::Plugins::CsvSerializer.csv_call will be removed in Sequel 6.")
|
|
79
|
+
CSV.send(*args, **opts, &block)
|
|
92
80
|
end
|
|
81
|
+
# simplecov:enable
|
|
93
82
|
|
|
94
83
|
module ClassMethods
|
|
95
84
|
# The default opts to use when serializing model objects to CSV
|
|
@@ -97,7 +86,7 @@ module Sequel
|
|
|
97
86
|
|
|
98
87
|
# Attempt to parse an array of instances from the given CSV string
|
|
99
88
|
def array_from_csv(csv, opts = OPTS)
|
|
100
|
-
|
|
89
|
+
CSV.parse(csv, **process_csv_serializer_opts(opts)).map do |row|
|
|
101
90
|
row = row.to_hash
|
|
102
91
|
row.delete(nil)
|
|
103
92
|
new(row)
|
|
@@ -147,7 +136,7 @@ module Sequel
|
|
|
147
136
|
# :headers :: The headers to use for the CSV line. Use nil for a header
|
|
148
137
|
# to specify the column should be ignored.
|
|
149
138
|
def from_csv(csv, opts = OPTS)
|
|
150
|
-
row =
|
|
139
|
+
row = CSV.parse_line(csv, **model.process_csv_serializer_opts(opts)).to_hash
|
|
151
140
|
row.delete(nil)
|
|
152
141
|
set(row)
|
|
153
142
|
end
|
|
@@ -165,7 +154,7 @@ module Sequel
|
|
|
165
154
|
opts = model.process_csv_serializer_opts(opts)
|
|
166
155
|
headers = opts[:headers]
|
|
167
156
|
|
|
168
|
-
|
|
157
|
+
CSV.generate(**model.process_csv_serializer_opts(opts)) do |csv|
|
|
169
158
|
csv << headers.map{|k| public_send(k)}
|
|
170
159
|
end
|
|
171
160
|
end
|
|
@@ -184,7 +173,7 @@ module Sequel
|
|
|
184
173
|
items = opts.delete(:array) || self
|
|
185
174
|
headers = opts[:headers]
|
|
186
175
|
|
|
187
|
-
|
|
176
|
+
CSV.generate(**opts) do |csv|
|
|
188
177
|
items.each do |object|
|
|
189
178
|
csv << headers.map{|header| object.public_send(header)}
|
|
190
179
|
end
|
|
@@ -80,6 +80,14 @@ module Sequel
|
|
|
80
80
|
instance_filters << [args, block]
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Clear instance filters when saving changes even if the
|
|
84
|
+
# object wasn't modified.
|
|
85
|
+
def save_changes(opts=OPTS)
|
|
86
|
+
ret = super
|
|
87
|
+
clear_instance_filters if ret.nil?
|
|
88
|
+
ret
|
|
89
|
+
end
|
|
90
|
+
|
|
83
91
|
private
|
|
84
92
|
|
|
85
93
|
# If there are any instance filters, make sure not to use the
|
|
@@ -93,6 +93,25 @@ module Sequel
|
|
|
93
93
|
super
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
# Run and clear save/update/validation hooks when saving changes even if the
|
|
97
|
+
# object wasn't modified.
|
|
98
|
+
def save_changes(opts=OPTS)
|
|
99
|
+
ret = super
|
|
100
|
+
if ret.nil? && @instance_hooks
|
|
101
|
+
run_before_instance_hooks(:before_validation)
|
|
102
|
+
run_before_instance_hooks(:after_validation)
|
|
103
|
+
run_before_instance_hooks(:before_save)
|
|
104
|
+
run_before_instance_hooks(:before_update)
|
|
105
|
+
ret = super
|
|
106
|
+
run_before_instance_hooks(:after_update)
|
|
107
|
+
run_before_instance_hooks(:after_save)
|
|
108
|
+
[:before_validation, :after_validation, :before_save, :before_update, :after_update, :after_save].each do |type|
|
|
109
|
+
@instance_hooks.delete(type)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
ret
|
|
113
|
+
end
|
|
114
|
+
|
|
96
115
|
private
|
|
97
116
|
|
|
98
117
|
# Add the block as an instance level hook. For before hooks, add it to
|
|
@@ -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}=",
|
|
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
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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
|
|
@@ -105,22 +105,11 @@ module Sequel
|
|
|
105
105
|
|
|
106
106
|
private
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
# simplecov:disable
|
|
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
|
-
# simplecov:enable
|
|
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
|
|
@@ -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)
|
|
@@ -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
|
|
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
|