sequel 5.13.0 → 5.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc4068290931cd51d12ec42e40a1e383ffa9cc8448501970230ec0863fc1cc21
4
- data.tar.gz: 16c549d0adcd8700bfb498bc2a459437a37c57a85d9048099336105efff376ad
3
+ metadata.gz: a950f8be723b867d5ecc782005c2374fa84a05a6b54c4ba55652f5442de1c90b
4
+ data.tar.gz: c5b2c92ee3580933b9fcd0525fdb67a79f5915f8b8e337e4731b9046f771dbf0
5
5
  SHA512:
6
- metadata.gz: 3f6acb6af9670774bf87b19349e778cb7b967ac529bbd192ea3ce2b6790080c21afa4445faabe18de148cfcef894e5457924782fb5462289dc38ff9af4ab5fcb
7
- data.tar.gz: e03cf594244024d41de805ef57bcdc545bd9bb247e34fcd48e1d6fba8c558f8dacc49828b5f529e465b9a5a6089e66adc5e1a8d43cf7029225556429aa97441b
6
+ metadata.gz: 4671d49935656b0ec907f10d3d4a6b25e29815b328b7661e55d95e4bfabbf84818de2b3550d92d86f444fbd69a561f099c859a5b1156303e10d58a7402641ebc
7
+ data.tar.gz: 5488428f7290a548344849919a7895dfd1e8d7cbe7990c2b261b4118e2a46845d9bde30b00673928511e2e650b3e4098f81cb2087d8f42227653607d3ae90c6f
data/CHANGELOG CHANGED
@@ -1,3 +1,23 @@
1
+ === 5.14.0 (2018-11-01)
2
+
3
+ * Drop defaulting the :port option to 5432 in the postgres adapter, so that setting the :service option in :driver_options works (jeremyevans) (#1558)
4
+
5
+ * Do not cache values for columns without parseable defaults when using :cache option in defaults_setter plugin (jeremyevans)
6
+
7
+ * Emulate NULLS FIRST/LAST ordering on databases that do not natively support it (jeremyevans)
8
+
9
+ * Do not modify boolean expressions created from string or array if string or array is modified (jeremyevans)
10
+
11
+ * Make roots and roots_dataset dataset methods instead of class methods in the tree plugin (JelF) (#1554)
12
+
13
+ * Do not cache dataset SQL if dataset uses subquery that cannot cache SQL (jeremyevans)
14
+
15
+ * Make Model#=== work correctly for models with composite primary keys (jeremyevans)
16
+
17
+ * Add Model#pk_equal? as a more descriptive name for Model#=== (AlexWayfer) (#1550)
18
+
19
+ * Do not push down expression inversion in cases where it may result in incorrect behavior (e.g. ANY/SOME/ALL operators) (jeremyevans) (#1549)
20
+
1
21
  === 5.13.0 (2018-10-01)
2
22
 
3
23
  * Support :single_value type in prepared statements (rintaun) (#1547)
@@ -63,6 +63,14 @@ into the Database instance:
63
63
  DB.extension :pg_array
64
64
  Sequel.extension :pg_array_ops
65
65
 
66
+ With regard to common database types, please note that the generic String type
67
+ is +text+ on PostgreSQL and not <tt>varchar(255)</tt> as it is on some other
68
+ databases. +text+ is PostgreSQL's recommended type for storage of text data,
69
+ and is more similar to Ruby's String type as it allows for unlimited length.
70
+ If you want to set a maximum size for a text column, you must specify a
71
+ <tt>:size</tt> option. This will use a <tt>varchar($size)</tt> type and
72
+ impose a maximum size for the column.
73
+
66
74
  == PostgreSQL-specific DDL Support
67
75
 
68
76
  === Exclusion Constraints
@@ -301,6 +309,18 @@ can pass an +:update+ option with a hash of values to update. You must pass eit
301
309
  DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
302
310
  # INSERT INTO TABLE (a, b) VALUES (1, 2)
303
311
  # ON CONFLICT (a) DO UPDATE SET b = excluded.b
312
+
313
+ If you want to update existing rows but using the current value of the column, you can build
314
+ the desired calculation using <tt>Sequel[]</tt>
315
+
316
+ DB[:table]
317
+ .insert_conflict(
318
+ target: :a,
319
+ update: {b: Sequel[:excluded][:b] + Sequel[:table][:a]}
320
+ )
321
+ .import([:a, :b], [ [1, 2] ])
322
+ # INSERT INTO TABLE (a, b) VALUES (1, 2)
323
+ # ON CONFLICT (a) DO UPDATE SET b = (excluded.b + table.a)
304
324
 
305
325
  Additionally, if you only want to do the update in certain cases, you can specify an
306
326
  +:update_where+ option, which will be used as a filter. If the row doesn't match the
@@ -0,0 +1,63 @@
1
+ = New Features
2
+
3
+ * The :nulls option when creating ordered expressions is now supported
4
+ on all databases that Sequel ships support for. For databases that
5
+ do not support NULLS FIRST/NULLS LAST, support is emulated.
6
+
7
+ ds.order(Sequel.asc(:name, :nulls=>:last))
8
+ # When emulated:
9
+ # ORDER BY (CASE WHEN (name IS NULL) THEN 2 ELSE 1 END), name ASC
10
+
11
+ * Model#pk_equal? has been added as a more descriptive name for
12
+ Model#===. Model#=== is now an alias of Model#pk_equal?.
13
+
14
+ * The roots and roots_dataset class methods in the tree plugin are now
15
+ also available as dataset methods.
16
+
17
+ = Other Improvements
18
+
19
+ * Inverting expressions using the ANY/SOME/ALL SQL operators now works
20
+ correctly:
21
+
22
+ # Sequel <5.14.0
23
+ Sequel.~(:a=>Sequel.function(:any, :x))
24
+ # "(a != any(x))"
25
+
26
+ # Sequel >=5.14.0
27
+ Sequel.~(:a=>Sequel.function(:any, :x))
28
+ # "NOT (a = any(x))"
29
+
30
+ Sequel has always tried to push inversion down to create SQL that is
31
+ easier to reason about. However, inversion cannot be pushed down if
32
+ an ANY/SOME/ALL SQL operator is used, because that is a different
33
+ type of operation that just happens to use the same syntax. Sequel
34
+ now avoids inversion push down for boolean operators where the
35
+ right hand side is an SQL::Function, LiteralString, or
36
+ SQL::PlaceholderLiteralString.
37
+
38
+ * When creating a boolean expression from a hash or array of pairs, if
39
+ the right hand side is an unfrozen array and string, use a frozen
40
+ copy in the expression, so that mutating the array or string
41
+ argument later does not affect the expression.
42
+
43
+ * When using the defaults_setter plugin with the :cache option, do not
44
+ cache values for columns without parseable defaults. If the default
45
+ value exists but is not parseable, caching such values could result
46
+ in incorrect behavior if the model instance is saved later.
47
+
48
+ * For models with composite primary keys, Model#=== now returns false
49
+ if any primary key value is nil, mirroring the behavior for the
50
+ scalar primary key case.
51
+
52
+ * Model datasets no longer cache SQL if they include a subquery that
53
+ cannot cache SQL.
54
+
55
+ * The SQL used for constraints in the constraint_validations
56
+ extension when the :allow_nil option is used is now clearer and
57
+ easier to understand.
58
+
59
+ * The postgres adapter no longer specifies a default port when using
60
+ the pg driver, in order to work with configurations where the
61
+ :service option is used in the :driver_options hash. The pg driver
62
+ defaults to port 5432 if no port is given, so this should not affect
63
+ backwards compatibility.
@@ -194,7 +194,7 @@ module Sequel
194
194
  if USES_PG
195
195
  connection_params = {
196
196
  :host => opts[:host],
197
- :port => opts[:port] || 5432,
197
+ :port => opts[:port],
198
198
  :dbname => opts[:database],
199
199
  :user => opts[:user],
200
200
  :password => opts[:password],
@@ -234,6 +234,11 @@ module Sequel
234
234
  end
235
235
  end
236
236
 
237
+ # Access does not natively support NULLS FIRST/LAST.
238
+ def requires_emulating_nulls_first?
239
+ true
240
+ end
241
+
237
242
  # Access doesn't support ESCAPE for LIKE.
238
243
  def requires_like_escape?
239
244
  false
@@ -418,6 +418,11 @@ module Sequel
418
418
  false
419
419
  end
420
420
 
421
+ # At least some versions of DB do not support NULLS FIRST/LAST.
422
+ def requires_emulating_nulls_first?
423
+ true
424
+ end
425
+
421
426
  # Modify the sql to limit the number of rows returned.
422
427
  # Uses :offset_strategy Database option to determine how to format the
423
428
  # limit and offset.
@@ -1046,6 +1046,11 @@ module Sequel
1046
1046
  end
1047
1047
  end
1048
1048
 
1049
+ # MSSQL does not natively support NULLS FIRST/LAST.
1050
+ def requires_emulating_nulls_first?
1051
+ true
1052
+ end
1053
+
1049
1054
  # MSSQL supports 100-nsec precision for time columns, but ruby by
1050
1055
  # default only supports usec precision.
1051
1056
  def sqltime_precision
@@ -1008,6 +1008,11 @@ module Sequel
1008
1008
  super || key == :insert_ignore || key == :update_ignore || key == :on_duplicate_key_update
1009
1009
  end
1010
1010
 
1011
+ # MySQL does not natively support NULLS FIRST/LAST.
1012
+ def requires_emulating_nulls_first?
1013
+ true
1014
+ end
1015
+
1011
1016
  def select_only_offset_sql(sql)
1012
1017
  sql << " LIMIT "
1013
1018
  literal_append(sql, @opts[:offset])
@@ -397,6 +397,11 @@ module Sequel
397
397
  :values
398
398
  end
399
399
 
400
+ # SQLAnywhere does not natively support NULLS FIRST/LAST.
401
+ def requires_emulating_nulls_first?
402
+ true
403
+ end
404
+
400
405
  def select_into_sql(sql)
401
406
  if i = @opts[:into]
402
407
  sql << " INTO "
@@ -825,6 +825,11 @@ module Sequel
825
825
  end
826
826
  end
827
827
 
828
+ # SQLite does not natively support NULLS FIRST/LAST.
829
+ def requires_emulating_nulls_first?
830
+ true
831
+ end
832
+
828
833
  # SQLite does not support FOR UPDATE, but silently ignore it
829
834
  # instead of raising an error for compatibility with other
830
835
  # databases.
@@ -220,6 +220,11 @@ module Sequel
220
220
  true
221
221
  end
222
222
 
223
+ # Whether ORDER BY col NULLS FIRST/LAST must be emulated.
224
+ def requires_emulating_nulls_first?
225
+ false
226
+ end
227
+
223
228
  # Whether common table expressions are supported in UNION/INTERSECT/EXCEPT clauses.
224
229
  def supports_cte_in_compounds?
225
230
  supports_cte_in_subqueries?
@@ -559,13 +559,30 @@ module Sequel
559
559
 
560
560
  # Append literalization of ordered expression to SQL string.
561
561
  def ordered_expression_sql_append(sql, oe)
562
+ if emulate = requires_emulating_nulls_first?
563
+ case oe.nulls
564
+ when :first
565
+ null_order = 0
566
+ when :last
567
+ null_order = 2
568
+ end
569
+
570
+ if null_order
571
+ literal_append(sql, Sequel.case({{oe.expression=>nil}=>null_order}, 1))
572
+ sql << ", "
573
+ end
574
+ end
575
+
562
576
  literal_append(sql, oe.expression)
563
577
  sql << (oe.descending ? ' DESC' : ' ASC')
564
- case oe.nulls
565
- when :first
566
- sql << " NULLS FIRST"
567
- when :last
568
- sql << " NULLS LAST"
578
+
579
+ unless emulate
580
+ case oe.nulls
581
+ when :first
582
+ sql << " NULLS FIRST"
583
+ when :last
584
+ sql << " NULLS LAST"
585
+ end
569
586
  end
570
587
  end
571
588
 
@@ -1553,7 +1570,13 @@ module Sequel
1553
1570
 
1554
1571
  # Append literalization of the subselect to SQL string.
1555
1572
  def subselect_sql_append(sql, ds)
1556
- subselect_sql_dataset(sql, ds).sql
1573
+ sds = subselect_sql_dataset(sql, ds)
1574
+ sds.sql
1575
+ unless sds.send(:cache_sql?)
1576
+ # If subquery dataset does not allow caching SQL,
1577
+ # then this dataset should not allow caching SQL.
1578
+ disable_sql_caching!
1579
+ end
1557
1580
  end
1558
1581
 
1559
1582
  def subselect_sql_dataset(sql, ds)
@@ -355,6 +355,18 @@ module Sequel
355
355
  super
356
356
  end
357
357
 
358
+ def constraint_validation_expression(cols, allow_nil)
359
+ exprs = cols.map do |c|
360
+ expr = yield c
361
+ if allow_nil
362
+ Sequel.|({c=>nil}, expr)
363
+ else
364
+ Sequel.&(Sequel.~(c=>nil), expr)
365
+ end
366
+ end
367
+ Sequel.&(*exprs)
368
+ end
369
+
358
370
  # For the given table, generator, and validations, add constraints
359
371
  # to the generator for each of the validations, as well as adding
360
372
  # validation metadata to the constraint validations table.
@@ -365,28 +377,44 @@ module Sequel
365
377
 
366
378
  case validation_type
367
379
  when :presence
368
- string_check = columns.select{|c| generator_string_column?(generator, table, c)}.map{|c| [Sequel.trim(c), blank_string_value]}
369
- generator_add_constraint_from_validation(generator, val, (Sequel.negate(string_check) unless string_check.empty?))
380
+ strings, non_strings = columns.partition{|c| generator_string_column?(generator, table, c)}
381
+ if !non_strings.empty? && !allow_nil
382
+ non_strings_expr = Sequel.&(*non_strings.map{|c| Sequel.~(c=>nil)})
383
+ end
384
+
385
+ unless strings.empty?
386
+ strings_expr = constraint_validation_expression(strings, allow_nil){|c| Sequel.~(Sequel.trim(c) => blank_string_value)}
387
+ end
388
+
389
+ expr = if non_strings_expr && strings_expr
390
+ Sequel.&(strings_expr, non_strings_expr)
391
+ else
392
+ strings_expr || non_strings_expr
393
+ end
394
+
395
+ if expr
396
+ generator.constraint(constraint, expr)
397
+ end
370
398
  when :exact_length
371
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| {Sequel.char_length(c) => arg}}))
399
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| {Sequel.char_length(c) => arg}})
372
400
  when :min_length
373
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| Sequel.char_length(c) >= arg}))
401
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.char_length(c) >= arg})
374
402
  when :max_length
375
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| Sequel.char_length(c) <= arg}))
403
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.char_length(c) <= arg})
376
404
  when *REVERSE_OPERATOR_MAP.keys
377
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| Sequel.identifier(c).public_send(REVERSE_OPERATOR_MAP[validation_type], arg)}))
405
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.identifier(c).public_send(REVERSE_OPERATOR_MAP[validation_type], arg)})
378
406
  when :length_range
379
407
  op = arg.exclude_end? ? :< : :<=
380
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| (Sequel.char_length(c) >= arg.begin) & Sequel.char_length(c).public_send(op, arg.end)}))
408
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| (Sequel.char_length(c) >= arg.begin) & Sequel.char_length(c).public_send(op, arg.end)})
381
409
  arg = "#{arg.begin}..#{'.' if arg.exclude_end?}#{arg.end}"
382
410
  when :format
383
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| {c => arg}}))
411
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| {c => arg}})
384
412
  if arg.casefold?
385
413
  validation_type = :iformat
386
414
  end
387
415
  arg = arg.source
388
416
  when :includes
389
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| {c => arg}}))
417
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| {c => arg}})
390
418
  if arg.is_a?(Range)
391
419
  if arg.begin.is_a?(Integer) && arg.end.is_a?(Integer)
392
420
  validation_type = :includes_int_range
@@ -407,7 +435,7 @@ module Sequel
407
435
  raise Error, "validates includes only supports arrays and ranges currently, cannot handle: #{arg.inspect}"
408
436
  end
409
437
  when :like, :ilike
410
- generator_add_constraint_from_validation(generator, val, Sequel.&(*columns.map{|c| Sequel.public_send(validation_type, c, arg)}))
438
+ generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.public_send(validation_type, c, arg)})
411
439
  when :unique
412
440
  generator.unique(columns, :name=>constraint)
413
441
  columns = [columns.join(',')]
@@ -438,23 +466,6 @@ module Sequel
438
466
  ds.multi_insert(rows.flatten)
439
467
  end
440
468
 
441
- # Add the constraint to the generator, including a NOT NULL constraint
442
- # for all columns unless the :allow_nil option is given.
443
- def generator_add_constraint_from_validation(generator, val, cons)
444
- if val[:allow_nil]
445
- nil_cons = Sequel[val[:columns].map{|c| [c, nil]}]
446
- cons = Sequel.|(nil_cons, cons) if cons
447
- else
448
- nil_cons = Sequel.negate(val[:columns].map{|c| [c, nil]})
449
- cons = cons ? Sequel.&(nil_cons, cons) : nil_cons
450
- end
451
-
452
- if cons
453
- generator.constraint(val[:name], cons)
454
- end
455
- end
456
-
457
-
458
469
  # Introspect the generator to determine if column
459
470
  # created is a string or not.
460
471
  def generator_string_column?(generator, table, c)
@@ -20,7 +20,7 @@
20
20
  #
21
21
  module Sequel
22
22
  module Integer64
23
- # Use timestamptz by default for generic timestamp value.
23
+ # Use same type as used for :Bignum by default for generic integer value.
24
24
  def type_literal_generic_integer(column)
25
25
  type_literal_generic_bignum_symbol(column)
26
26
  end
@@ -1103,16 +1103,33 @@ module Sequel
1103
1103
  eql?(obj)
1104
1104
  end
1105
1105
 
1106
- # If pk is not nil, true only if the objects have the same class and pk.
1107
- # If pk is nil, false.
1106
+ # Case equality. By default, checks equality of the primary key value, see
1107
+ # pk_equal?.
1108
1108
  #
1109
- # Artist[1] === Artist[1] # true
1110
- # Artist.new === Artist.new # false
1111
- # Artist[1].set(:name=>'Bob') == Artist[1] # => true
1109
+ # Artist[1] === Artist[1] # => true
1110
+ # Artist.new === Artist.new # => false
1111
+ # Artist[1].set(:name=>'Bob') === Artist[1] # => true
1112
1112
  def ===(obj)
1113
- pk.nil? ? false : (obj.class == model) && (obj.pk == pk)
1113
+ case pkv = pk
1114
+ when nil
1115
+ return false
1116
+ when Array
1117
+ return false if pk.any?(&:nil?)
1118
+ end
1119
+
1120
+ (obj.class == model) && (obj.pk == pkv)
1114
1121
  end
1115
-
1122
+
1123
+ # If the receiver has a primary key value, returns true if the objects have
1124
+ # the same class and primary key value.
1125
+ # If the receiver's primary key value is nil or is an array containing
1126
+ # nil, returns false.
1127
+ #
1128
+ # Artist[1].pk_equal?(Artist[1]) # => true
1129
+ # Artist.new.pk_equal?(Artist.new) # => false
1130
+ # Artist[1].set(:name=>'Bob').pk_equal?(Artist[1]) # => true
1131
+ alias pk_equal? ===
1132
+
1116
1133
  # class is defined in Object, but it is also a keyword,
1117
1134
  # and since a lot of instance methods call class methods,
1118
1135
  # this alias makes it so you can use model instead of
@@ -116,7 +116,7 @@ module Sequel
116
116
  # Use default value for a new record if values doesn't already contain an entry for it.
117
117
  def [](k)
118
118
  if new? && !values.has_key?(k)
119
- v = model.default_values[k]
119
+ v = model.default_values.fetch(k){return}
120
120
  v = v.call if v.respond_to?(:call)
121
121
  values[k] = v if model.cache_default_values?
122
122
  v
@@ -70,6 +70,7 @@ module Sequel
70
70
  attr_reader :children_association_name
71
71
 
72
72
  Plugins.inherited_instance_variables(self, :@parent_column=>nil, :@tree_order=>nil, :@parent_association_name=>nil, :@children_association_name=>nil)
73
+ Plugins.def_dataset_methods(self, [:roots, :roots_dataset])
73
74
 
74
75
  # Should freeze tree order if it is an array when freezing the model class.
75
76
  def freeze
@@ -77,22 +78,6 @@ module Sequel
77
78
 
78
79
  super
79
80
  end
80
-
81
- # Returns list of all root nodes (those with no parent nodes).
82
- #
83
- # TreeClass.roots # => [root1, root2]
84
- def roots
85
- roots_dataset.all
86
- end
87
-
88
- # Returns the dataset for retrieval of all root nodes
89
- #
90
- # TreeClass.roots_dataset # => Sequel::Dataset instance
91
- def roots_dataset
92
- ds = where(Sequel.or(Array(parent_column).zip([])))
93
- ds = ds.order(*tree_order) if tree_order
94
- ds
95
- end
96
81
  end
97
82
 
98
83
  module InstanceMethods
@@ -154,6 +139,24 @@ module Sequel
154
139
  end
155
140
  end
156
141
 
142
+ module DatasetMethods
143
+ # Returns list of all root nodes (those with no parent nodes).
144
+ #
145
+ # TreeClass.roots # => [root1, root2]
146
+ def roots
147
+ roots_dataset.all
148
+ end
149
+
150
+ # Returns the dataset for retrieval of all root nodes
151
+ #
152
+ # TreeClass.roots_dataset # => Sequel::Dataset instance
153
+ def roots_dataset
154
+ ds = where(Sequel.or(Array(model.parent_column).zip([])))
155
+ ds = ds.order(*model.tree_order) if model.tree_order
156
+ ds
157
+ end
158
+ end
159
+
157
160
  # Plugin included when :single_root option is passed.
158
161
  module SingleRoot
159
162
  module ClassMethods
@@ -265,12 +265,12 @@ module Sequel
265
265
  # methods overlap with the standard +BooleanMethods methods+, and they only
266
266
  # make sense for integers, they are only included in +NumericExpression+.
267
267
  #
268
- # :a.sql_number & :b # "a" & "b"
269
- # :a.sql_number | :b # "a" | "b"
270
- # :a.sql_number ^ :b # "a" ^ "b"
271
- # :a.sql_number << :b # "a" << "b"
272
- # :a.sql_number >> :b # "a" >> "b"
273
- # ~:a.sql_number # ~"a"
268
+ # Sequel[:a].sql_number & :b # "a" & "b"
269
+ # Sequel[:a].sql_number | :b # "a" | "b"
270
+ # Sequel[:a].sql_number ^ :b # "a" ^ "b"
271
+ # Sequel[:a].sql_number << :b # "a" << "b"
272
+ # Sequel[:a].sql_number >> :b # "a" >> "b"
273
+ # ~Sequel[:a].sql_number # ~"a"
274
274
  module BitwiseMethods
275
275
  ComplexExpression::BITWISE_OPERATORS.each do |o|
276
276
  module_eval("def #{o}(o) NumericExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
@@ -354,9 +354,15 @@ module Sequel
354
354
  end
355
355
 
356
356
  # Return an <tt>SQL::CaseExpression</tt> created with the given arguments.
357
+ # The first argument are the <tt>WHEN</tt>/<tt>THEN</tt> conditions,
358
+ # specified as an array or a hash. The second argument is the
359
+ # <tt>ELSE</tt> default value. The third optional argument is the
360
+ # <tt>CASE</tt> expression.
357
361
  #
358
- # Sequel.case([[{a: [2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
362
+ # Sequel.case({a: 1}, 0) # SQL: CASE WHEN a THEN 1 ELSE 0 END
359
363
  # Sequel.case({a: 1}, 0, :b) # SQL: CASE b WHEN a THEN 1 ELSE 0 END
364
+ # Sequel.case({{a: [2,3]} => 1}, 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
365
+ # Sequel.case([[{a: [2,3]}, 1]], 0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
360
366
  def case(*args)
361
367
  SQL::CaseExpression.new(*args)
362
368
  end
@@ -745,10 +751,10 @@ module Sequel
745
751
  # This module includes the inequality methods (>, <, >=, <=) that are defined on objects that can be
746
752
  # used in a numeric or string context in SQL.
747
753
  #
748
- # Sequel.lit('a') > :b # a > "b"
749
- # Sequel.lit('a') < :b # a > "b"
750
- # Sequel.lit('a') >= :b # a >= "b"
751
- # Sequel.lit('a') <= :b # a <= "b"
754
+ # Sequel[:a] > :b # a > "b"
755
+ # Sequel[:a] < :b # a > "b"
756
+ # Sequel[:a] >= :b # a >= "b"
757
+ # Sequel[:a] <= :b # a <= "b"
752
758
  module InequalityMethods
753
759
  ComplexExpression::INEQUALITY_OPERATORS.each do |o|
754
760
  module_eval("def #{o}(o) BooleanExpression.new(#{o.inspect}, self, o) end", __FILE__, __LINE__)
@@ -759,10 +765,10 @@ module Sequel
759
765
  # that are defined on objects that can be used in a numeric context in SQL
760
766
  # (+Symbol+, +LiteralString+, and +SQL::GenericExpression+).
761
767
  #
762
- # :a + :b # "a" + "b"
763
- # :a - :b # "a" - "b"
764
- # :a * :b # "a" * "b"
765
- # :a / :b # "a" / "b"
768
+ # Sequel[:a] + :b # "a" + "b"
769
+ # Sequel[:a] - :b # "a" - "b"
770
+ # Sequel[:a] * :b # "a" * "b"
771
+ # Sequel[:a] / :b # "a" / "b"
766
772
  #
767
773
  # One exception to this is if + is called with a +String+ or +StringExpression+,
768
774
  # in which case the || operator is used instead of the + operator:
@@ -1079,7 +1085,13 @@ module Sequel
1079
1085
  expr = new(:AND, expr, new(r.exclude_end? ? :< : :<=, l, r.end))
1080
1086
  end
1081
1087
  expr
1082
- when ::Array, ::Sequel::Dataset
1088
+ when ::Array
1089
+ r = r.dup.freeze unless r.frozen?
1090
+ new(:IN, l, r)
1091
+ when ::String
1092
+ r = r.dup.freeze unless r.frozen?
1093
+ new(:'=', l, r)
1094
+ when ::Sequel::Dataset
1083
1095
  new(:IN, l, r)
1084
1096
  when NegativeBooleanConstant
1085
1097
  new(:"IS NOT", l, r.constant)
@@ -1111,8 +1123,21 @@ module Sequel
1111
1123
  case op = ce.op
1112
1124
  when :AND, :OR
1113
1125
  BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1114
- else
1126
+ when :IN, :"NOT IN"
1115
1127
  BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1128
+ else
1129
+ if ce.args.length == 2
1130
+ case ce.args[1]
1131
+ when Function, LiteralString, PlaceholderLiteralString
1132
+ # Special behavior to not push down inversion in this case because doing so
1133
+ # can result in incorrect behavior for ANY/SOME/ALL operators.
1134
+ BooleanExpression.new(:NOT, ce)
1135
+ else
1136
+ BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1137
+ end
1138
+ else
1139
+ BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1140
+ end
1116
1141
  end
1117
1142
  when StringExpression, NumericExpression
1118
1143
  raise(Sequel::Error, "cannot invert #{ce.inspect}")
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 13
9
+ MINOR = 14
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
@@ -1411,6 +1411,10 @@ describe "Dataset#order" do
1411
1411
  @dataset.order(Sequel.asc(:name, :nulls=>:last), Sequel.desc(:price, :nulls=>:first)).sql.must_equal 'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
1412
1412
  end
1413
1413
 
1414
+ it "should emulate :nulls options for asc and desc if not natively supported" do
1415
+ @dataset.with_extend{def requires_emulating_nulls_first?; true end}.order(Sequel.asc(:name, :nulls=>:last), Sequel.desc(:price, :nulls=>:first)).sql.must_equal 'SELECT * FROM test ORDER BY (CASE WHEN (name IS NULL) THEN 2 ELSE 1 END), name ASC, (CASE WHEN (price IS NULL) THEN 0 ELSE 1 END), price DESC'
1416
+ end
1417
+
1414
1418
  it "should override a previous ordering" do
1415
1419
  @dataset.order(:name).order(:stamp).sql.must_equal 'SELECT * FROM test ORDER BY stamp'
1416
1420
  end
@@ -2344,6 +2348,14 @@ describe "Dataset#from_self" do
2344
2348
  @ds.server(:blah).from_self(:alias=>:some_name).opts[:server].must_equal :blah
2345
2349
  end
2346
2350
 
2351
+ it "should work correctly when a delayed evaluation is used " do
2352
+ a = true
2353
+ ds = @ds.where(Sequel.delay{a}).from_self
2354
+ ds.sql.must_equal "SELECT * FROM (SELECT name FROM test WHERE 't' LIMIT 1) AS t1"
2355
+ a = false
2356
+ ds.sql.must_equal "SELECT * FROM (SELECT name FROM test WHERE 'f' LIMIT 1) AS t1"
2357
+ end
2358
+
2347
2359
  it "should hoist WITH clauses in current dataset if dataset doesn't support WITH in subselect" do
2348
2360
  ds = Sequel.mock.dataset
2349
2361
  ds = ds.with_extend do
@@ -54,6 +54,22 @@ describe "Blockless Ruby Filters" do
54
54
  @d.l(~~Sequel.|(:x, :y)).must_equal '(x OR y)'
55
55
  end
56
56
 
57
+ it "should not modifying boolean expression created from array if array is modified" do
58
+ a = [1]
59
+ expr = Sequel.expr(:b=>a)
60
+ @d.l(expr).must_equal '(b IN (1))'
61
+ a << 2
62
+ @d.l(expr).must_equal '(b IN (1))'
63
+ end
64
+
65
+ it "should not modifying boolean expression created from string if string is modified" do
66
+ a = '1'.dup
67
+ expr = Sequel.expr(:b=>a)
68
+ @d.l(expr).must_equal "(b = '1')"
69
+ a << '2'
70
+ @d.l(expr).must_equal "(b = '1')"
71
+ end
72
+
57
73
  it "should support = via Hash" do
58
74
  @d.l(:x => 100).must_equal '(x = 100)'
59
75
  @d.l(:x => 'a').must_equal '(x = \'a\')'
@@ -78,6 +94,12 @@ describe "Blockless Ruby Filters" do
78
94
  @d.l(~Sequel.expr(:x => false)).must_equal '(x IS NOT FALSE)'
79
95
  @d.l(~Sequel.expr(:x => nil)).must_equal '(x IS NOT NULL)'
80
96
  end
97
+
98
+ it "should use NOT for inverting boolean expressions where right hand side is function or literal strings" do
99
+ @d.l(~Sequel.expr(:x => Sequel.function(:any))).must_equal 'NOT (x = any())'
100
+ @d.l(~Sequel.expr(:x => Sequel.lit('any()'))).must_equal 'NOT (x = any())'
101
+ @d.l(~Sequel.expr(:x => Sequel.lit('any(?)', 1))).must_equal 'NOT (x = any(1))'
102
+ end
81
103
 
82
104
  it "should support = and similar operations via =~ method" do
83
105
  @d.l{x =~ 100}.must_equal '(x = 100)'
@@ -107,12 +107,38 @@ describe "constraint_validations extension" do
107
107
  sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CONSTRAINT cons CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
108
108
  end
109
109
 
110
- it "should handle multiple columns when adding validations" do
110
+ it "should handle multiple string columns when adding presence validations" do
111
111
  @db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar]}}
112
112
  sqls = @db.sqls
113
113
  parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo")
114
114
  parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo")
115
- sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (bar IS NOT NULL) AND (trim(name) != '') AND (trim(bar) != '')))"]
115
+ sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '') AND (bar IS NOT NULL) AND (trim(bar) != '')))"]
116
+ end
117
+
118
+ it "should handle multiple string columns when adding presence validations with :allow_nil" do
119
+ @db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar], :allow_nil=>true}}
120
+ sqls = @db.sqls
121
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t')
122
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo", :allow_nil=>'t')
123
+ sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK (((name IS NULL) OR (trim(name) != '')) AND ((bar IS NULL) OR (trim(bar) != ''))))"]
124
+ end
125
+
126
+ it "should handle multiple string columns when adding presence validations" do
127
+ @db.create_table(:foo){String :name; Integer :x; String :bar; validate{presence [:name, :x, :bar]}}
128
+ sqls = @db.sqls
129
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo")
130
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"x", :table=>"foo")
131
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo")
132
+ sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), x integer, bar varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '') AND (bar IS NOT NULL) AND (trim(bar) != '') AND (x IS NOT NULL)))"]
133
+ end
134
+
135
+ it "should handle multiple string columns when adding presence validations with :allow_nil" do
136
+ @db.create_table(:foo){String :name; Integer :x; String :bar; validate{presence [:name, :x, :bar], :allow_nil=>true}}
137
+ sqls = @db.sqls
138
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t')
139
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"x", :table=>"foo", :allow_nil=>'t')
140
+ parse_insert(sqls.slice!(1)).must_equal(:validation_type=>"presence", :column=>"bar", :table=>"foo", :allow_nil=>'t')
141
+ sqls.must_equal ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), x integer, bar varchar(255), CHECK (((name IS NULL) OR (trim(name) != '')) AND ((bar IS NULL) OR (trim(bar) != ''))))"]
116
142
  end
117
143
 
118
144
  it "should handle presence validation on non-String columns" do
@@ -78,6 +78,15 @@ describe "Sequel::Plugins::DefaultsSetter" do
78
78
  o.a.must_be_same_as(o.a)
79
79
  end
80
80
 
81
+ it "should not cache default values if :cache plugin option is used and there is no default values" do
82
+ @c.plugin :defaults_setter, :cache => true
83
+ o = @c.new
84
+ o.a.must_be_nil
85
+ o.values.must_be_empty
86
+ o.a.must_be_nil
87
+ o.a.must_be_same_as(o.a)
88
+ end
89
+
81
90
  it "should not override a given value" do
82
91
  @pr.call(2)
83
92
  @c.new('a'=>3).a.must_equal 3
@@ -63,10 +63,13 @@ describe Sequel::Model, "tree plugin" do
63
63
  @c.dataset = @c.dataset.with_fetch([{:id=>1, :parent_id=>nil, :name=>'r'}])
64
64
  @c.roots.must_equal [@c.load(:id=>1, :parent_id=>nil, :name=>'r')]
65
65
  @db.sqls.must_equal ["SELECT * FROM nodes WHERE (parent_id IS NULL)"]
66
+ @c.exclude(id: 2).roots.must_equal [@c.load(:id=>1, :parent_id=>nil, :name=>'r')]
67
+ @db.sqls.must_equal ["SELECT * FROM nodes WHERE ((id != 2) AND (parent_id IS NULL))"]
66
68
  end
67
69
 
68
70
  it "should have roots_dataset be a dataset representing the tree's roots" do
69
71
  @c.roots_dataset.sql.must_equal "SELECT * FROM nodes WHERE (parent_id IS NULL)"
72
+ @c.exclude(id: 2).roots_dataset.sql.must_equal "SELECT * FROM nodes WHERE ((id != 2) AND (parent_id IS NULL))"
70
73
  end
71
74
 
72
75
  it "should have ancestors return the ancestors of the current node" do
@@ -39,6 +39,15 @@ describe "Simple Dataset operations" do
39
39
  @ds.order(:id).all.must_equal [{:id=>1, :number=>10}, {:id=>100, :number=>20}]
40
40
  end
41
41
 
42
+ it "should support ordering considering NULLS" do
43
+ @ds.insert(:number=>20)
44
+ @ds.insert(:number=>nil)
45
+ @ds.order(Sequel[:number].asc(:nulls=>:first)).select_map(:number).must_equal [nil, 10, 20]
46
+ @ds.order(Sequel[:number].asc(:nulls=>:last)).select_map(:number).must_equal [10, 20, nil]
47
+ @ds.order(Sequel[:number].desc(:nulls=>:first)).select_map(:number).must_equal [nil, 20, 10]
48
+ @ds.order(Sequel[:number].desc(:nulls=>:last)).select_map(:number).must_equal [20, 10, nil]
49
+ end
50
+
42
51
  it "should have insert return primary key value" do
43
52
  @ds.insert(:number=>20).must_equal 2
44
53
  @ds.filter(:id=>2).first[:number].must_equal 20
@@ -1709,7 +1709,9 @@ describe "Sequel::Plugins::Tree" do
1709
1709
 
1710
1710
  it "iterate top-level nodes in order" do
1711
1711
  @Node.roots_dataset.count.must_equal 5
1712
- @Node.roots.map{|p| p.name}.must_equal %w'one two three four five'
1712
+ @Node.roots.map(&:name).must_equal %w'one two three four five'
1713
+ @Node.where(:name=>%w'one two.one').roots_dataset.count.must_equal 1
1714
+ @Node.where(:name=>%w'one two.one').roots.map(&:name).must_equal %w'one'
1713
1715
  end
1714
1716
 
1715
1717
  it "should have children" do
@@ -2026,8 +2028,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
2026
2028
  max_length 6, :minlen, opts.merge(:name=>:maxl2)
2027
2029
  operator :<, 'm', :exactlen, opts.merge(:name=>:lt)
2028
2030
  operator :>=, 5, :num, opts.merge(:name=>:gte)
2031
+ presence [:m1, :m2, :m3], opts.merge(:name=>:pm)
2029
2032
  end
2030
- @valid_row = {:pre=>'a', :exactlen=>'12345', :minlen=>'12345', :maxlen=>'12345', :lenrange=>'1234', :lik=>'fooabc', :ilik=>'FooABC', :inc=>'abc', :uniq=>'u', :num=>5}
2033
+ @valid_row = {:pre=>'a', :exactlen=>'12345', :minlen=>'12345', :maxlen=>'12345', :lenrange=>'1234', :lik=>'fooabc', :ilik=>'FooABC', :inc=>'abc', :uniq=>'u', :num=>5, :m1=>'a', :m2=>1, :m3=>'a'}
2031
2034
  @violations = [
2032
2035
  [:pre, [nil, '', ' ']],
2033
2036
  [:exactlen, [nil, '', '1234', '123456', 'n1234']],
@@ -2068,6 +2071,41 @@ describe "Sequel::Plugins::ConstraintValidations" do
2068
2071
  end
2069
2072
  end
2070
2073
 
2074
+ try = @valid_row.dup
2075
+ if @validation_opts[:allow_nil]
2076
+ [:m1, :m2, :m3].each do |c|
2077
+ @ds.insert(try.merge(c=>nil))
2078
+ @ds.delete
2079
+ end
2080
+ @ds.insert(try.merge(:m1=>nil, :m2=>nil))
2081
+ @ds.delete
2082
+ @ds.insert(try.merge(:m1=>nil, :m3=>nil))
2083
+ @ds.delete
2084
+ @ds.insert(try.merge(:m2=>nil, :m3=>nil))
2085
+ @ds.delete
2086
+ @ds.insert(try.merge(:m1=>nil, :m2=>nil, :m3=>nil))
2087
+ @ds.delete
2088
+ else
2089
+ [:m1, :m2, :m3].each do |c|
2090
+ proc{@ds.insert(try.merge(c=>nil))}.must_raise(Sequel::DatabaseError)
2091
+ end
2092
+ proc{@ds.insert(try.merge(:m1=>nil, :m2=>nil))}.must_raise(Sequel::DatabaseError)
2093
+ proc{@ds.insert(try.merge(:m1=>nil, :m3=>nil))}.must_raise(Sequel::DatabaseError)
2094
+ proc{@ds.insert(try.merge(:m2=>nil, :m3=>nil))}.must_raise(Sequel::DatabaseError)
2095
+ proc{@ds.insert(try.merge(:m1=>nil, :m2=>nil, :m3=>nil))}.must_raise(Sequel::DatabaseError)
2096
+ end
2097
+
2098
+ unless @db.database_type == :oracle
2099
+ [:m1, :m3].each do |c|
2100
+ proc{@ds.insert(try.merge(c=>''))}.must_raise(Sequel::DatabaseError)
2101
+ end
2102
+ proc{@ds.insert(try.merge(:m1=>'', :m3=>''))}.must_raise(Sequel::DatabaseError)
2103
+ proc{@ds.insert(try.merge(:m1=>'', :m2=>nil))}.must_raise(Sequel::DatabaseError)
2104
+ proc{@ds.insert(try.merge(:m1=>nil, :m3=>''))}.must_raise(Sequel::DatabaseError)
2105
+ proc{@ds.insert(try.merge(:m2=>nil, :m3=>''))}.must_raise(Sequel::DatabaseError)
2106
+ proc{@ds.insert(try.merge(:m1=>'', :m2=>nil, :m3=>''))}.must_raise(Sequel::DatabaseError)
2107
+ end
2108
+
2071
2109
  # Test for dropping of constraint
2072
2110
  @db.alter_table(:cv_test){validate{drop :maxl2}}
2073
2111
  @ds.insert(@valid_row.merge(:minlen=>'1234567'))
@@ -2092,6 +2130,34 @@ describe "Sequel::Plugins::ConstraintValidations" do
2092
2130
  c.new(try).wont_be :valid?
2093
2131
  end
2094
2132
  end
2133
+
2134
+ try = @valid_row.dup
2135
+ if @validation_opts[:allow_nil]
2136
+ [:m1, :m2, :m3].each do |col|
2137
+ c.new(try.merge(col=>nil)).must_be :valid?
2138
+ end
2139
+ c.new(try.merge(:m1=>nil, :m2=>nil)).must_be :valid?
2140
+ c.new(try.merge(:m1=>nil, :m3=>nil)).must_be :valid?
2141
+ c.new(try.merge(:m2=>nil, :m3=>nil)).must_be :valid?
2142
+ c.new(try.merge(:m1=>nil, :m2=>nil, :m3=>nil)).must_be :valid?
2143
+ else
2144
+ [:m1, :m2, :m3].each do |col|
2145
+ c.new(try.merge(col=>nil)).wont_be :valid?
2146
+ end
2147
+ c.new(try.merge(:m1=>nil, :m2=>nil)).wont_be :valid?
2148
+ c.new(try.merge(:m1=>nil, :m3=>nil)).wont_be :valid?
2149
+ c.new(try.merge(:m2=>nil, :m3=>nil)).wont_be :valid?
2150
+ c.new(try.merge(:m1=>nil, :m2=>nil, :m3=>nil)).wont_be :valid?
2151
+ end
2152
+ c.new(try.merge(:m1=>'', :m2=>nil)).wont_be :valid?
2153
+ c.new(try.merge(:m1=>nil, :m3=>'')).wont_be :valid?
2154
+ c.new(try.merge(:m2=>nil, :m3=>'')).wont_be :valid?
2155
+ c.new(try.merge(:m1=>'', :m2=>nil, :m3=>'')).wont_be :valid?
2156
+ [:m1, :m3].each do |col|
2157
+ c.new(try.merge(col=>'')).wont_be :valid?
2158
+ end
2159
+ c.new(try.merge(:m1=>'', :m3=>'')).wont_be :valid?
2160
+
2095
2161
  c.db.constraint_validations = nil
2096
2162
  end
2097
2163
  end
@@ -2116,6 +2182,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
2116
2182
  String :inc
2117
2183
  String :uniq, :null=>false
2118
2184
  Integer :num
2185
+ String :m1
2186
+ Integer :m2
2187
+ String :m3
2119
2188
  validate(&validate_block)
2120
2189
  end
2121
2190
  end
@@ -2162,6 +2231,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
2162
2231
  add_column :form, String
2163
2232
  end
2164
2233
  add_column :num, Integer
2234
+ add_column :m1, String
2235
+ add_column :m2, Integer
2236
+ add_column :m3, String
2165
2237
  validate(&validate_block)
2166
2238
  end
2167
2239
  end
@@ -1445,35 +1445,46 @@ describe Sequel::Model, "#==" do
1445
1445
  end
1446
1446
  end
1447
1447
 
1448
- describe Sequel::Model, "#===" do
1449
- it "should compare instances by class and pk if pk is not nil" do
1450
- z = Class.new(Sequel::Model)
1451
- z.columns :id, :x
1452
- y = Class.new(Sequel::Model)
1453
- y.columns :id, :x
1454
- a = z.load(:id => 1, :x => 3)
1455
- b = z.load(:id => 1, :x => 4)
1456
- c = z.load(:id => 2, :x => 3)
1457
- d = y.load(:id => 1, :x => 3)
1458
-
1459
- a.must_be :===, b
1460
- a.wont_be :===, c
1461
- a.wont_be :===, d
1462
- end
1448
+ [:===, :pk_equal?].each do |method_name|
1449
+ describe Sequel::Model, "##{method_name}" do
1450
+ it "should compare instances by class and pk if pk is not nil" do
1451
+ z = Class.new(Sequel::Model)
1452
+ z.columns :id, :x
1453
+ y = Class.new(Sequel::Model)
1454
+ y.columns :id, :x
1455
+ a = z.load(:id => 1, :x => 3)
1456
+ b = z.load(:id => 1, :x => 4)
1457
+ c = z.load(:id => 2, :x => 3)
1458
+ d = y.load(:id => 1, :x => 3)
1459
+
1460
+ a.must_be method_name, b
1461
+ a.wont_be method_name, c
1462
+ a.wont_be method_name, d
1463
+ end
1463
1464
 
1464
- it "should always be false if the primary key is nil" do
1465
- z = Class.new(Sequel::Model)
1466
- z.columns :id, :x
1467
- y = Class.new(Sequel::Model)
1468
- y.columns :id, :x
1469
- a = z.new(:x => 3)
1470
- b = z.new(:x => 4)
1471
- c = z.new(:x => 3)
1472
- d = y.new(:x => 3)
1473
-
1474
- a.wont_be :===, b
1475
- a.wont_be :===, c
1476
- a.wont_be :===, d
1465
+ it "should always be false if the primary key is nil" do
1466
+ z = Class.new(Sequel::Model)
1467
+ z.columns :id, :x
1468
+ y = Class.new(Sequel::Model)
1469
+ y.columns :id, :x
1470
+ a = z.new(:x => 3)
1471
+ b = z.new(:x => 4)
1472
+ c = z.new(:x => 3)
1473
+ d = y.new(:x => 3)
1474
+
1475
+ a.wont_be method_name, b
1476
+ a.wont_be method_name, c
1477
+ a.wont_be method_name, d
1478
+ end
1479
+
1480
+ it "should always be false if the primary key is an array containing nil" do
1481
+ z = Class.new(Sequel::Model)
1482
+ z.columns :id, :x
1483
+ z.set_primary_key [:id, :x]
1484
+ z.load(:id => nil, :x => nil).wont_be method_name, z.load(:id => nil, :x => nil)
1485
+ z.load(:id => 1, :x => nil).wont_be method_name, z.load(:id => 1, :x => nil)
1486
+ z.load(:id => nil, :x => 2).wont_be method_name, z.load(:id => nil, :x => 2)
1487
+ end
1477
1488
  end
1478
1489
  end
1479
1490
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.13.0
4
+ version: 5.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-01 00:00:00.000000000 Z
11
+ date: 2018-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -197,6 +197,7 @@ extra_rdoc_files:
197
197
  - doc/release_notes/5.11.0.txt
198
198
  - doc/release_notes/5.12.0.txt
199
199
  - doc/release_notes/5.13.0.txt
200
+ - doc/release_notes/5.14.0.txt
200
201
  files:
201
202
  - CHANGELOG
202
203
  - MIT-LICENSE
@@ -280,6 +281,7 @@ files:
280
281
  - doc/release_notes/5.11.0.txt
281
282
  - doc/release_notes/5.12.0.txt
282
283
  - doc/release_notes/5.13.0.txt
284
+ - doc/release_notes/5.14.0.txt
283
285
  - doc/release_notes/5.2.0.txt
284
286
  - doc/release_notes/5.3.0.txt
285
287
  - doc/release_notes/5.4.0.txt
@@ -805,7 +807,12 @@ files:
805
807
  homepage: http://sequel.jeremyevans.net
806
808
  licenses:
807
809
  - MIT
808
- metadata: {}
810
+ metadata:
811
+ bug_tracker_uri: https://github.com/jeremyevans/sequel/issues
812
+ changelog_uri: http://sequel.jeremyevans.net/rdoc/files/CHANGELOG.html
813
+ documentation_uri: http://sequel.jeremyevans.net/documentation.html
814
+ mailing_list_uri: https://groups.google.com/forum/#!forum/sequel-talk
815
+ source_code_uri: https://github.com/jeremyevans/sequel
809
816
  post_install_message:
810
817
  rdoc_options:
811
818
  - "--quiet"