sequel 5.38.0 → 5.43.0

Sign up to get free protection for your applications and to get access to all the features.
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
+