sequel 5.38.0 → 5.43.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +54 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/doc/cheat_sheet.rdoc +5 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/fork_safety.rdoc +84 -0
- data/doc/postgresql.rdoc +1 -1
- data/doc/querying.rdoc +3 -3
- data/doc/release_notes/5.39.0.txt +19 -0
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/sql.rdoc +1 -1
- data/doc/testing.rdoc +2 -0
- data/lib/sequel/adapters/ado.rb +16 -16
- data/lib/sequel/adapters/jdbc.rb +2 -2
- data/lib/sequel/adapters/shared/mssql.rb +21 -1
- data/lib/sequel/adapters/shared/postgres.rb +5 -3
- data/lib/sequel/adapters/shared/sqlite.rb +35 -1
- data/lib/sequel/database/misc.rb +1 -2
- data/lib/sequel/database/schema_generator.rb +16 -1
- data/lib/sequel/database/schema_methods.rb +19 -5
- data/lib/sequel/database/transactions.rb +1 -1
- data/lib/sequel/dataset/features.rb +10 -0
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/sql.rb +32 -10
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/date_arithmetic.rb +7 -9
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +8 -0
- data/lib/sequel/extensions/migration.rb +2 -0
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_interval.rb +34 -8
- data/lib/sequel/extensions/pg_row.rb +1 -0
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/model/associations.rb +28 -4
- data/lib/sequel/model/base.rb +23 -6
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_validations.rb +15 -1
- data/lib/sequel/plugins/column_encryption.rb +711 -0
- data/lib/sequel/plugins/composition.rb +7 -2
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/json_serializer.rb +37 -22
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +4 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/serialization.rb +8 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/validation_helpers.rb +6 -2
- data/lib/sequel/timezones.rb +8 -3
- data/lib/sequel/version.rb +1 -1
- metadata +36 -21
@@ -6,6 +6,14 @@
|
|
6
6
|
#
|
7
7
|
# Sequel.extension :blank
|
8
8
|
|
9
|
+
[FalseClass, Object, NilClass, Numeric, String, TrueClass].each do |klass|
|
10
|
+
# :nocov:
|
11
|
+
if klass.method_defined?(:blank?)
|
12
|
+
klass.send(:alias_method, :blank?, :blank?)
|
13
|
+
end
|
14
|
+
# :nocov:
|
15
|
+
end
|
16
|
+
|
9
17
|
class FalseClass
|
10
18
|
# false is always blank
|
11
19
|
def blank?
|
@@ -49,14 +49,12 @@ module Sequel
|
|
49
49
|
# Options:
|
50
50
|
# :cast :: Cast to the specified type instead of the default if casting
|
51
51
|
def date_sub(expr, interval, opts=OPTS)
|
52
|
-
|
53
|
-
|
54
|
-
interval.each{|k,v| h[k] = -v unless v.nil?}
|
55
|
-
h
|
56
|
-
else
|
57
|
-
-interval
|
52
|
+
if defined?(ActiveSupport::Duration) && interval.is_a?(ActiveSupport::Duration)
|
53
|
+
interval = interval.parts
|
58
54
|
end
|
59
|
-
|
55
|
+
parts = {}
|
56
|
+
interval.each{|k,v| parts[k] = -v unless v.nil?}
|
57
|
+
DateAdd.new(expr, parts, opts)
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
@@ -113,12 +111,12 @@ module Sequel
|
|
113
111
|
end
|
114
112
|
when :mssql, :h2, :access, :sqlanywhere
|
115
113
|
units = case db_type
|
116
|
-
when :mssql, :sqlanywhere
|
117
|
-
MSSQL_DURATION_UNITS
|
118
114
|
when :h2
|
119
115
|
H2_DURATION_UNITS
|
120
116
|
when :access
|
121
117
|
ACCESS_DURATION_UNITS
|
118
|
+
else
|
119
|
+
MSSQL_DURATION_UNITS
|
122
120
|
end
|
123
121
|
each_valid_interval_unit(h, units) do |value, sql_unit|
|
124
122
|
expr = Sequel.function(:DATEADD, sql_unit, value, expr)
|
@@ -105,6 +105,14 @@ class String
|
|
105
105
|
yield Inflections if block_given?
|
106
106
|
Inflections
|
107
107
|
end
|
108
|
+
|
109
|
+
%w'classify constantize dasherize demodulize foreign_key humanize pluralize singularize tableize underscore'.each do |m|
|
110
|
+
# :nocov:
|
111
|
+
if method_defined?(m)
|
112
|
+
alias_method(m, m)
|
113
|
+
end
|
114
|
+
# :nocov:
|
115
|
+
end
|
108
116
|
|
109
117
|
# By default, camelize converts the string to UpperCamelCase. If the argument to camelize
|
110
118
|
# is set to :lower then camelize produces lowerCamelCase.
|
@@ -68,7 +68,9 @@ module Sequel
|
|
68
68
|
# Allow calling private methods for backwards compatibility
|
69
69
|
@db.send(method_sym, *args, &block)
|
70
70
|
end
|
71
|
+
# :nocov:
|
71
72
|
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
73
|
+
# :nocov:
|
72
74
|
|
73
75
|
# This object responds to all methods the database responds to.
|
74
76
|
def respond_to_missing?(meth, include_private)
|
@@ -84,9 +84,9 @@ module Sequel
|
|
84
84
|
def convert_output_time_other(v, output_timezone)
|
85
85
|
Time.at(v.to_i, :in => output_timezone)
|
86
86
|
end
|
87
|
-
else
|
88
87
|
# :nodoc:
|
89
88
|
# :nocov:
|
89
|
+
else
|
90
90
|
def convert_input_time_other(v, input_timezone)
|
91
91
|
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
92
92
|
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
@@ -105,6 +105,8 @@ module Sequel
|
|
105
105
|
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
106
106
|
end
|
107
107
|
end
|
108
|
+
# :nodoc:
|
109
|
+
# :nocov:
|
108
110
|
end
|
109
111
|
|
110
112
|
# Handle both TZInfo 1 and TZInfo 2
|
@@ -142,6 +144,8 @@ module Sequel
|
|
142
144
|
# Convert timezone offset from UTC to the offset for the output_timezone
|
143
145
|
(v - local_offset).new_offset(local_offset)
|
144
146
|
end
|
147
|
+
# :nodoc:
|
148
|
+
# :nocov:
|
145
149
|
end
|
146
150
|
|
147
151
|
# Returns TZInfo::Timezone instance if given a String.
|
@@ -213,6 +213,7 @@ module Sequel
|
|
213
213
|
scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, type)}"
|
214
214
|
define_method(meth){|v| typecast_value_pg_array(v, creator, scalar_typecast_method)}
|
215
215
|
private meth
|
216
|
+
alias_method(meth, meth)
|
216
217
|
end
|
217
218
|
|
218
219
|
@schema_type_classes[:"#{type}_array"] = PGArray
|
@@ -34,6 +34,13 @@
|
|
34
34
|
|
35
35
|
require 'active_support/duration'
|
36
36
|
|
37
|
+
# :nocov:
|
38
|
+
begin
|
39
|
+
require 'active_support/version'
|
40
|
+
rescue LoadError
|
41
|
+
end
|
42
|
+
# :nocov:
|
43
|
+
|
37
44
|
module Sequel
|
38
45
|
module Postgres
|
39
46
|
module IntervalDatabaseMethods
|
@@ -61,34 +68,47 @@ module Sequel
|
|
61
68
|
|
62
69
|
# Creates callable objects that convert strings into ActiveSupport::Duration instances.
|
63
70
|
class Parser
|
71
|
+
# Whether ActiveSupport::Duration.new takes parts as array instead of hash
|
72
|
+
USE_PARTS_ARRAY = !defined?(ActiveSupport::VERSION::STRING) || ActiveSupport::VERSION::STRING < '5.1'
|
73
|
+
|
74
|
+
if defined?(ActiveSupport::Duration::SECONDS_PER_MONTH)
|
75
|
+
SECONDS_PER_MONTH = ActiveSupport::Duration::SECONDS_PER_MONTH
|
76
|
+
SECONDS_PER_YEAR = ActiveSupport::Duration::SECONDS_PER_YEAR
|
77
|
+
# :nocov:
|
78
|
+
else
|
79
|
+
SECONDS_PER_MONTH = 2592000
|
80
|
+
SECONDS_PER_YEAR = 31557600
|
81
|
+
# :nocov:
|
82
|
+
end
|
83
|
+
|
64
84
|
# Parse the interval input string into an ActiveSupport::Duration instance.
|
65
85
|
def call(string)
|
66
86
|
raise(InvalidValue, "invalid or unhandled interval format: #{string.inspect}") unless matches = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:(?:([+-])?(\d{2,10}):(\d\d):(\d\d(\.\d+)?))|([+-]?\d+ hours?\s?)?([+-]?\d+ mins?\s?)?([+-]?\d+(\.\d+)? secs?\s?)?)?\z/.match(string)
|
67
87
|
|
68
88
|
value = 0
|
69
|
-
parts =
|
89
|
+
parts = {}
|
70
90
|
|
71
91
|
if v = matches[1]
|
72
92
|
v = v.to_i
|
73
|
-
value +=
|
74
|
-
parts
|
93
|
+
value += SECONDS_PER_YEAR * v
|
94
|
+
parts[:years] = v
|
75
95
|
end
|
76
96
|
if v = matches[2]
|
77
97
|
v = v.to_i
|
78
|
-
value +=
|
79
|
-
parts
|
98
|
+
value += SECONDS_PER_MONTH * v
|
99
|
+
parts[:months] = v
|
80
100
|
end
|
81
101
|
if v = matches[3]
|
82
102
|
v = v.to_i
|
83
103
|
value += 86400 * v
|
84
|
-
parts
|
104
|
+
parts[:days] = v
|
85
105
|
end
|
86
106
|
if matches[5]
|
87
107
|
seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
|
88
108
|
seconds += matches[8] ? matches[7].to_f : matches[7].to_i
|
89
109
|
seconds *= -1 if matches[4] == '-'
|
90
110
|
value += seconds
|
91
|
-
parts
|
111
|
+
parts[:seconds] = seconds
|
92
112
|
elsif matches[9] || matches[10] || matches[11]
|
93
113
|
seconds = 0
|
94
114
|
if v = matches[9]
|
@@ -101,8 +121,14 @@ module Sequel
|
|
101
121
|
seconds += matches[12] ? v.to_f : v.to_i
|
102
122
|
end
|
103
123
|
value += seconds
|
104
|
-
parts
|
124
|
+
parts[:seconds] = seconds
|
125
|
+
end
|
126
|
+
|
127
|
+
# :nocov:
|
128
|
+
if USE_PARTS_ARRAY
|
129
|
+
parts = parts.to_a
|
105
130
|
end
|
131
|
+
# :nocov:
|
106
132
|
|
107
133
|
ActiveSupport::Duration.new(value, parts)
|
108
134
|
end
|
@@ -1929,7 +1929,22 @@ module Sequel
|
|
1929
1929
|
# can be easily overridden in the class itself while allowing for
|
1930
1930
|
# super to be called.
|
1931
1931
|
def association_module_def(name, opts=OPTS, &block)
|
1932
|
-
association_module(opts)
|
1932
|
+
mod = association_module(opts)
|
1933
|
+
mod.send(:define_method, name, &block)
|
1934
|
+
mod.send(:alias_method, name, name)
|
1935
|
+
end
|
1936
|
+
|
1937
|
+
# Add a method to the module included in the class, so the method
|
1938
|
+
# can be easily overridden in the class itself while allowing for
|
1939
|
+
# super to be called. This method allows passing keywords through
|
1940
|
+
# the defined methods.
|
1941
|
+
def association_module_delegate_def(name, opts, &block)
|
1942
|
+
mod = association_module(opts)
|
1943
|
+
mod.send(:define_method, name, &block)
|
1944
|
+
# :nocov:
|
1945
|
+
mod.send(:ruby2_keywords, name) if mod.respond_to?(:ruby2_keywords, true)
|
1946
|
+
# :nocov:
|
1947
|
+
mod.send(:alias_method, name, name)
|
1933
1948
|
end
|
1934
1949
|
|
1935
1950
|
# Add a private method to the module included in the class.
|
@@ -1981,17 +1996,17 @@ module Sequel
|
|
1981
1996
|
|
1982
1997
|
if adder = opts[:adder]
|
1983
1998
|
association_module_private_def(opts[:_add_method], opts, &adder)
|
1984
|
-
|
1999
|
+
association_module_delegate_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
|
1985
2000
|
end
|
1986
2001
|
|
1987
2002
|
if remover = opts[:remover]
|
1988
2003
|
association_module_private_def(opts[:_remove_method], opts, &remover)
|
1989
|
-
|
2004
|
+
association_module_delegate_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
|
1990
2005
|
end
|
1991
2006
|
|
1992
2007
|
if clearer = opts[:clearer]
|
1993
2008
|
association_module_private_def(opts[:_remove_all_method], opts, &clearer)
|
1994
|
-
|
2009
|
+
association_module_delegate_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
|
1995
2010
|
end
|
1996
2011
|
end
|
1997
2012
|
|
@@ -2423,6 +2438,9 @@ module Sequel
|
|
2423
2438
|
run_association_callbacks(opts, :after_add, o)
|
2424
2439
|
o
|
2425
2440
|
end
|
2441
|
+
# :nocov:
|
2442
|
+
ruby2_keywords(:add_associated_object) if respond_to?(:ruby2_keywords, true)
|
2443
|
+
# :nocov:
|
2426
2444
|
|
2427
2445
|
# Add/Set the current object to/as the given object's reciprocal association.
|
2428
2446
|
def add_reciprocal_object(opts, o)
|
@@ -2565,6 +2583,9 @@ module Sequel
|
|
2565
2583
|
associations[opts[:name]] = []
|
2566
2584
|
ret
|
2567
2585
|
end
|
2586
|
+
# :nocov:
|
2587
|
+
ruby2_keywords(:remove_all_associated_objects) if respond_to?(:ruby2_keywords, true)
|
2588
|
+
# :nocov:
|
2568
2589
|
|
2569
2590
|
# Remove the given associated object from the given association
|
2570
2591
|
def remove_associated_object(opts, o, *args)
|
@@ -2586,6 +2607,9 @@ module Sequel
|
|
2586
2607
|
run_association_callbacks(opts, :after_remove, o)
|
2587
2608
|
o
|
2588
2609
|
end
|
2610
|
+
# :nocov:
|
2611
|
+
ruby2_keywords(:remove_associated_object) if respond_to?(:ruby2_keywords, true)
|
2612
|
+
# :nocov:
|
2589
2613
|
|
2590
2614
|
# Check that the object from the associated table specified by the primary key
|
2591
2615
|
# is currently associated to the receiver. If it is associated, return the object, otherwise
|
data/lib/sequel/model/base.rb
CHANGED
@@ -508,7 +508,9 @@ module Sequel
|
|
508
508
|
|
509
509
|
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
510
510
|
end
|
511
|
+
# :nocov:
|
511
512
|
ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
|
513
|
+
# :nocov:
|
512
514
|
|
513
515
|
# Returns primary key attribute hash. If using a composite primary key
|
514
516
|
# value such be an array with values for each primary key in the correct
|
@@ -727,8 +729,14 @@ module Sequel
|
|
727
729
|
im = instance_methods
|
728
730
|
overridable_methods_module.module_eval do
|
729
731
|
meth = :"#{column}="
|
730
|
-
|
731
|
-
|
732
|
+
unless im.include?(column)
|
733
|
+
define_method(column){self[column]}
|
734
|
+
alias_method(column, column)
|
735
|
+
end
|
736
|
+
unless im.include?(meth)
|
737
|
+
define_method(meth){|v| self[column] = v}
|
738
|
+
alias_method(meth, meth)
|
739
|
+
end
|
732
740
|
end
|
733
741
|
end
|
734
742
|
|
@@ -741,8 +749,14 @@ module Sequel
|
|
741
749
|
im = instance_methods
|
742
750
|
columns.each do |column|
|
743
751
|
meth = :"#{column}="
|
744
|
-
|
745
|
-
|
752
|
+
unless im.include?(column)
|
753
|
+
overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__)
|
754
|
+
overridable_methods_module.send(:alias_method, column, column)
|
755
|
+
end
|
756
|
+
unless im.include?(meth)
|
757
|
+
overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__)
|
758
|
+
overridable_methods_module.send(:alias_method, meth, meth)
|
759
|
+
end
|
746
760
|
end
|
747
761
|
end
|
748
762
|
|
@@ -757,7 +771,10 @@ module Sequel
|
|
757
771
|
else
|
758
772
|
define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
|
759
773
|
end
|
774
|
+
singleton_class.send(:alias_method, meth, meth)
|
775
|
+
# :nocov:
|
760
776
|
singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
777
|
+
# :nocov:
|
761
778
|
end
|
762
779
|
|
763
780
|
# Get the schema from the database, fall back on checking the columns
|
@@ -1243,12 +1260,12 @@ module Sequel
|
|
1243
1260
|
# Once an object is frozen, you cannot modify it's values, changed_columns,
|
1244
1261
|
# errors, or dataset.
|
1245
1262
|
def freeze
|
1246
|
-
values.freeze
|
1247
|
-
_changed_columns.freeze
|
1248
1263
|
unless errors.frozen?
|
1249
1264
|
validate
|
1250
1265
|
errors.freeze
|
1251
1266
|
end
|
1267
|
+
values.freeze
|
1268
|
+
_changed_columns.freeze
|
1252
1269
|
this if !new? && model.primary_key
|
1253
1270
|
super
|
1254
1271
|
end
|
data/lib/sequel/model/plugins.rb
CHANGED
@@ -31,7 +31,9 @@ module Sequel
|
|
31
31
|
def self.def_dataset_methods(mod, meths)
|
32
32
|
Array(meths).each do |meth|
|
33
33
|
mod.class_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
|
34
|
+
# :nocov:
|
34
35
|
mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
|
36
|
+
# :nocov:
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
@@ -120,6 +122,7 @@ module Sequel
|
|
120
122
|
|
121
123
|
model.send(:define_method, meth, &block)
|
122
124
|
model.send(:private, meth)
|
125
|
+
model.send(:alias_method, meth, meth)
|
123
126
|
call_meth
|
124
127
|
end
|
125
128
|
|
@@ -141,6 +144,8 @@ module Sequel
|
|
141
144
|
keyword = :required
|
142
145
|
when :key, :keyrest
|
143
146
|
keyword ||= true
|
147
|
+
else
|
148
|
+
raise Error, "invalid arg_type passed to _define_sequel_method_arg_numbers: #{arg_type}"
|
144
149
|
end
|
145
150
|
end
|
146
151
|
arity = callable.arity
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
extension 'async_thread_pool'
|
5
|
+
|
6
|
+
module Plugins
|
7
|
+
# The async_thread_pool plugin makes it slightly easier to use the async_thread_pool
|
8
|
+
# Dataset extension with models. It makes Model.async return an async dataset for the
|
9
|
+
# model, and support async behavior for #destroy, #with_pk, and #with_pk! for model
|
10
|
+
# datasets:
|
11
|
+
#
|
12
|
+
# # Will load the artist with primary key 1 asynchronously
|
13
|
+
# artist = Artist.async.with_pk(1)
|
14
|
+
#
|
15
|
+
# You must load the async_thread_pool Database extension into the Database object the
|
16
|
+
# model class uses in order for async behavior to work.
|
17
|
+
#
|
18
|
+
# Usage:
|
19
|
+
#
|
20
|
+
# # Make all model subclass datasets support support async class methods and additional
|
21
|
+
# # async dataset methods
|
22
|
+
# Sequel::Model.plugin :async_thread_pool
|
23
|
+
#
|
24
|
+
# # Make the Album class support async class method and additional async dataset methods
|
25
|
+
# Album.plugin :async_thread_pool
|
26
|
+
module AsyncThreadPool
|
27
|
+
module ClassMethods
|
28
|
+
Plugins.def_dataset_methods(self, :async)
|
29
|
+
end
|
30
|
+
|
31
|
+
module DatasetMethods
|
32
|
+
[:destroy, :with_pk, :with_pk!].each do |meth|
|
33
|
+
::Sequel::Database::AsyncThreadPool::DatasetMethods.define_async_method(self, meth)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|