sequel 5.38.0 → 5.43.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +54 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/doc/cheat_sheet.rdoc +5 -5
  6. data/doc/code_order.rdoc +0 -12
  7. data/doc/fork_safety.rdoc +84 -0
  8. data/doc/postgresql.rdoc +1 -1
  9. data/doc/querying.rdoc +3 -3
  10. data/doc/release_notes/5.39.0.txt +19 -0
  11. data/doc/release_notes/5.40.0.txt +40 -0
  12. data/doc/release_notes/5.41.0.txt +25 -0
  13. data/doc/release_notes/5.42.0.txt +136 -0
  14. data/doc/release_notes/5.43.0.txt +98 -0
  15. data/doc/sql.rdoc +1 -1
  16. data/doc/testing.rdoc +2 -0
  17. data/lib/sequel/adapters/ado.rb +16 -16
  18. data/lib/sequel/adapters/jdbc.rb +2 -2
  19. data/lib/sequel/adapters/shared/mssql.rb +21 -1
  20. data/lib/sequel/adapters/shared/postgres.rb +5 -3
  21. data/lib/sequel/adapters/shared/sqlite.rb +35 -1
  22. data/lib/sequel/database/misc.rb +1 -2
  23. data/lib/sequel/database/schema_generator.rb +16 -1
  24. data/lib/sequel/database/schema_methods.rb +19 -5
  25. data/lib/sequel/database/transactions.rb +1 -1
  26. data/lib/sequel/dataset/features.rb +10 -0
  27. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  28. data/lib/sequel/dataset/sql.rb +32 -10
  29. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  30. data/lib/sequel/extensions/blank.rb +8 -0
  31. data/lib/sequel/extensions/date_arithmetic.rb +7 -9
  32. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  33. data/lib/sequel/extensions/inflector.rb +8 -0
  34. data/lib/sequel/extensions/migration.rb +2 -0
  35. data/lib/sequel/extensions/named_timezones.rb +5 -1
  36. data/lib/sequel/extensions/pg_array.rb +1 -0
  37. data/lib/sequel/extensions/pg_interval.rb +34 -8
  38. data/lib/sequel/extensions/pg_row.rb +1 -0
  39. data/lib/sequel/extensions/query.rb +2 -0
  40. data/lib/sequel/model/associations.rb +28 -4
  41. data/lib/sequel/model/base.rb +23 -6
  42. data/lib/sequel/model/plugins.rb +5 -0
  43. data/lib/sequel/plugins/association_proxies.rb +2 -0
  44. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  45. data/lib/sequel/plugins/auto_validations.rb +15 -1
  46. data/lib/sequel/plugins/column_encryption.rb +711 -0
  47. data/lib/sequel/plugins/composition.rb +7 -2
  48. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  49. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  50. data/lib/sequel/plugins/json_serializer.rb +37 -22
  51. data/lib/sequel/plugins/nested_attributes.rb +8 -3
  52. data/lib/sequel/plugins/pg_array_associations.rb +4 -0
  53. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  54. data/lib/sequel/plugins/serialization.rb +8 -3
  55. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  56. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  57. data/lib/sequel/plugins/tree.rb +9 -4
  58. data/lib/sequel/plugins/validation_helpers.rb +6 -2
  59. data/lib/sequel/timezones.rb +8 -3
  60. data/lib/sequel/version.rb +1 -1
  61. 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
- interval = if interval.is_a?(Hash)
53
- h = {}
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
- DateAdd.new(expr, interval, opts)
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)
@@ -55,6 +55,8 @@ module Sequel
55
55
 
56
56
  module SQL
57
57
  class Expression
58
+ alias inspect inspect
59
+
58
60
  # Attempt to produce a string suitable for eval, such that:
59
61
  #
60
62
  # eval(obj.inspect) == obj
@@ -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 += 31557600 * v
74
- parts << [:years, v]
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 += 2592000 * v
79
- parts << [:months, v]
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 << [:days, v]
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 << [:seconds, seconds]
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 << [:seconds, seconds]
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
@@ -482,6 +482,7 @@ module Sequel
482
482
  row_type(db_type, v)
483
483
  end
484
484
  private meth
485
+ alias_method(meth, meth)
485
486
  end
486
487
 
487
488
  nil
@@ -74,7 +74,9 @@ module Sequel
74
74
  raise(Sequel::Error, "method #{method.inspect} did not return a dataset") unless @dataset.is_a?(Dataset)
75
75
  self
76
76
  end
77
+ # :nocov:
77
78
  ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
79
+ # :nocov:
78
80
  end
79
81
  end
80
82
 
@@ -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).send(:define_method, name, &block)
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
- association_module_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
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
- association_module_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
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
- association_module_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
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
@@ -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
- define_method(column){self[column]} unless im.include?(column)
731
- define_method(meth){|v| self[column] = v} unless im.include?(meth)
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
- overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__) unless im.include?(column)
745
- overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__) unless im.include?(meth)
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
@@ -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
@@ -99,7 +99,9 @@ module Sequel
99
99
  end
100
100
  v.public_send(meth, *args, &block)
101
101
  end
102
+ # :nocov:
102
103
  ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
104
+ # :nocov:
103
105
  end
104
106
 
105
107
  module ClassMethods
@@ -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
+