sequel 2.10.0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG +51 -1
  2. data/README.rdoc +2 -2
  3. data/Rakefile +2 -2
  4. data/doc/advanced_associations.rdoc +6 -18
  5. data/doc/release_notes/1.0.txt +38 -0
  6. data/doc/release_notes/1.1.txt +143 -0
  7. data/doc/release_notes/1.3.txt +101 -0
  8. data/doc/release_notes/1.4.0.txt +53 -0
  9. data/doc/release_notes/1.5.0.txt +155 -0
  10. data/doc/release_notes/2.0.0.txt +298 -0
  11. data/doc/release_notes/2.1.0.txt +271 -0
  12. data/doc/release_notes/2.10.0.txt +328 -0
  13. data/doc/release_notes/2.11.0.txt +215 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/lib/sequel_core/adapters/ado.rb +3 -0
  23. data/lib/sequel_core/adapters/db2.rb +0 -11
  24. data/lib/sequel_core/adapters/dbi.rb +0 -11
  25. data/lib/sequel_core/adapters/do.rb +0 -12
  26. data/lib/sequel_core/adapters/firebird.rb +21 -16
  27. data/lib/sequel_core/adapters/informix.rb +1 -11
  28. data/lib/sequel_core/adapters/jdbc.rb +1 -13
  29. data/lib/sequel_core/adapters/jdbc/h2.rb +3 -11
  30. data/lib/sequel_core/adapters/jdbc/mysql.rb +0 -17
  31. data/lib/sequel_core/adapters/jdbc/postgresql.rb +3 -15
  32. data/lib/sequel_core/adapters/mysql.rb +31 -27
  33. data/lib/sequel_core/adapters/odbc.rb +34 -28
  34. data/lib/sequel_core/adapters/openbase.rb +0 -11
  35. data/lib/sequel_core/adapters/oracle.rb +11 -9
  36. data/lib/sequel_core/adapters/postgres.rb +14 -17
  37. data/lib/sequel_core/adapters/shared/mssql.rb +6 -15
  38. data/lib/sequel_core/adapters/shared/mysql.rb +29 -14
  39. data/lib/sequel_core/adapters/shared/oracle.rb +4 -0
  40. data/lib/sequel_core/adapters/shared/postgres.rb +30 -35
  41. data/lib/sequel_core/adapters/shared/progress.rb +4 -0
  42. data/lib/sequel_core/adapters/shared/sqlite.rb +73 -13
  43. data/lib/sequel_core/adapters/sqlite.rb +8 -18
  44. data/lib/sequel_core/adapters/utils/date_format.rb +21 -0
  45. data/lib/sequel_core/{dataset → adapters/utils}/stored_procedures.rb +0 -0
  46. data/lib/sequel_core/{dataset → adapters/utils}/unsupported.rb +0 -0
  47. data/lib/sequel_core/core_ext.rb +1 -1
  48. data/lib/sequel_core/core_sql.rb +9 -4
  49. data/lib/sequel_core/database.rb +63 -62
  50. data/lib/sequel_core/dataset.rb +9 -4
  51. data/lib/sequel_core/dataset/convenience.rb +10 -9
  52. data/lib/sequel_core/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel_core/dataset/sql.rb +130 -36
  54. data/lib/sequel_core/schema/sql.rb +2 -2
  55. data/lib/sequel_core/sql.rb +44 -51
  56. data/lib/sequel_core/version.rb +1 -1
  57. data/lib/sequel_model/associations.rb +25 -17
  58. data/lib/sequel_model/base.rb +35 -7
  59. data/lib/sequel_model/caching.rb +1 -6
  60. data/lib/sequel_model/record.rb +23 -5
  61. data/lib/sequel_model/validations.rb +20 -5
  62. data/spec/adapters/firebird_spec.rb +6 -1
  63. data/spec/adapters/mysql_spec.rb +12 -0
  64. data/spec/adapters/postgres_spec.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +81 -2
  66. data/spec/integration/dataset_test.rb +2 -2
  67. data/spec/integration/type_test.rb +12 -2
  68. data/spec/sequel_core/core_sql_spec.rb +46 -12
  69. data/spec/sequel_core/database_spec.rb +24 -12
  70. data/spec/sequel_core/dataset_spec.rb +82 -32
  71. data/spec/sequel_core/schema_spec.rb +16 -0
  72. data/spec/sequel_model/associations_spec.rb +89 -0
  73. data/spec/sequel_model/base_spec.rb +66 -0
  74. data/spec/sequel_model/eager_loading_spec.rb +32 -0
  75. data/spec/sequel_model/record_spec.rb +9 -9
  76. data/spec/sequel_model/spec_helper.rb +3 -0
  77. data/spec/sequel_model/validations_spec.rb +63 -3
  78. metadata +41 -4
@@ -12,7 +12,7 @@ module Sequel
12
12
  SET_DEFAULT = 'SET DEFAULT'.freeze
13
13
  SET_NULL = 'SET NULL'.freeze
14
14
  TYPES = Hash.new {|h, k| k}
15
- TYPES.merge!(:double=>'double precision', String=>'varchar',
15
+ TYPES.merge!(:double=>'double precision', String=>'varchar(255)',
16
16
  Integer=>'integer', Fixnum=>'integer', Bignum=>'bigint',
17
17
  Float=>'double precision', BigDecimal=>'numeric', Numeric=>'numeric',
18
18
  Date=>'date', DateTime=>'timestamp', Time=>'timestamp', File=>'blob',
@@ -124,7 +124,7 @@ module Sequel
124
124
  # for certain databases.
125
125
  def default_index_name(table_name, columns)
126
126
  schema, table = schema_and_table(table_name)
127
- "#{"#{schema}_" if schema and schema != default_schema}#{table}_#{columns.join(UNDERSCORE)}_index"
127
+ "#{"#{schema}_" if schema and schema != default_schema}#{table}_#{columns.map{|c| c.is_one_of?(String, Symbol) ? c : literal(c).gsub(/\W/, '_')}.join(UNDERSCORE)}_index"
128
128
  end
129
129
 
130
130
  # The SQL to drop an index for the table.
@@ -181,21 +181,21 @@ module Sequel
181
181
  module CastMethods
182
182
  # Cast the reciever to the given SQL type
183
183
  def cast(sql_type)
184
- IrregularFunction.new(:cast, self, :AS, sql_type.to_s.lit)
184
+ Cast.new(self, sql_type)
185
185
  end
186
186
  alias_method :cast_as, :cast
187
187
 
188
188
  # Cast the reciever to the given SQL type (or integer if none given),
189
189
  # and return the result as a NumericExpression.
190
190
  def cast_numeric(sql_type = nil)
191
- cast(sql_type || :integer).sql_number
191
+ cast(sql_type || Integer).sql_number
192
192
  end
193
193
 
194
194
  # Cast the reciever to the given SQL type (or text if none given),
195
195
  # and return the result as a StringExpression, so you can use +
196
196
  # directly on the result for SQL string concatenation.
197
197
  def cast_string(sql_type = nil)
198
- cast(sql_type || :text).sql_string
198
+ cast(sql_type || String).sql_string
199
199
  end
200
200
  end
201
201
 
@@ -336,12 +336,12 @@ module Sequel
336
336
  module ComplexExpressionMethods
337
337
  # Extract a datetime_part (e.g. year, month) from self:
338
338
  #
339
- # :date.extract(:year) # SQL: extract(year FROM date)
339
+ # :date.extract(:year) # SQL: extract(year FROM "date")
340
340
  #
341
341
  # Also has the benefit of returning the result as a
342
342
  # NumericExpression instead of a generic ComplexExpression.
343
343
  def extract(datetime_part)
344
- IrregularFunction.new(:extract, datetime_part.to_s.lit, :FROM, self).sql_number
344
+ Function.new(:extract, PlaceholderLiteralString.new("#{datetime_part} FROM ?", [self])).sql_number
345
345
  end
346
346
 
347
347
  # Return a BooleanExpression representation of self.
@@ -400,22 +400,6 @@ module Sequel
400
400
  end
401
401
  end
402
402
 
403
- # Represents an SQL array. Added so it is possible to deal with a
404
- # ruby array of all two pairs as an SQL array instead of an ordered
405
- # hash-like conditions specifier.
406
- class SQLArray < Expression
407
- # Create an object with the given array.
408
- def initialize(array)
409
- @array = array
410
- end
411
-
412
- # Delegate the creation of the resulting SQL to the given dataset,
413
- # since it may be database dependent.
414
- def to_s(ds)
415
- ds.array_sql(@array)
416
- end
417
- end
418
-
419
403
  # Blob is used to represent binary data in the Ruby environment that is
420
404
  # stored as a blob type in the database. In PostgreSQL, the blob type is
421
405
  # called bytea. Sequel represents binary data as a Blob object because
@@ -423,9 +407,10 @@ module Sequel
423
407
  # escaped.
424
408
  class Blob < ::String
425
409
  # return self.
426
- def to_blob
410
+ def to_sequel_blob
427
411
  self
428
412
  end
413
+ alias to_blob to_sequel_blob
429
414
  end
430
415
 
431
416
  # Subclass of ComplexExpression where the expression results
@@ -516,6 +501,27 @@ module Sequel
516
501
  end
517
502
  end
518
503
 
504
+ # Represents a cast of an SQL expression to a specific type.
505
+ class Cast < GenericExpression
506
+ # The expression to cast
507
+ attr_reader :expr
508
+
509
+ # The type to which to cast the expression
510
+ attr_reader :type
511
+
512
+ # Set the attributes to the given arguments
513
+ def initialize(expr, type)
514
+ @expr = expr
515
+ @type = type
516
+ end
517
+
518
+ # Delegate the creation of the resulting SQL to the given dataset,
519
+ # since it may be database dependent.
520
+ def to_s(ds)
521
+ ds.cast_sql(expr, type)
522
+ end
523
+ end
524
+
519
525
  # Represents all columns in a given table, table.* in SQL
520
526
  class ColumnAll < SpecificExpression
521
527
  # The table containing the columns being selected
@@ -586,35 +592,6 @@ module Sequel
586
592
  end
587
593
  end
588
594
 
589
- # IrregularFunction is used for the SQL EXTRACT and CAST functions,
590
- # which don't use regular function calling syntax. The IrregularFunction
591
- # replaces the commas the regular function uses with a custom
592
- # join string.
593
- #
594
- # This shouldn't be used directly, see CastMethods#cast and
595
- # ComplexExpressionMethods#extract.
596
- class IrregularFunction < Function
597
- # The arguments to pass to the function (may be blank)
598
- attr_reader :arg1, :arg2
599
-
600
- # The SQL function to call
601
- attr_reader :f
602
-
603
- # The literal string to use in place of a comma to join arguments
604
- attr_reader :joiner
605
-
606
- # Set the attributes to the given arguments
607
- def initialize(f, arg1, joiner, arg2)
608
- @f, @arg1, @joiner, @arg2 = f, arg1, joiner, arg2
609
- end
610
-
611
- # Delegate the creation of the resulting SQL to the given dataset,
612
- # since it may be database dependent.
613
- def to_s(ds)
614
- ds.irregular_function_sql(self)
615
- end
616
- end
617
-
618
595
  # Represents an SQL JOIN clause, used for joining tables.
619
596
  class JoinClause < SpecificExpression
620
597
  # The type of join to do
@@ -785,6 +762,22 @@ module Sequel
785
762
  end
786
763
  end
787
764
 
765
+ # Represents an SQL array. Added so it is possible to deal with a
766
+ # ruby array of all two pairs as an SQL array instead of an ordered
767
+ # hash-like conditions specifier.
768
+ class SQLArray < Expression
769
+ # Create an object with the given array.
770
+ def initialize(array)
771
+ @array = array
772
+ end
773
+
774
+ # Delegate the creation of the resulting SQL to the given dataset,
775
+ # since it may be database dependent.
776
+ def to_s(ds)
777
+ ds.array_sql(@array)
778
+ end
779
+ end
780
+
788
781
  # Represents an SQL array access, with multiple possible arguments.
789
782
  class Subscript < GenericExpression
790
783
  # The SQL array column
@@ -1,6 +1,6 @@
1
1
  module Sequel
2
2
  MAJOR = 2
3
- MINOR = 10
3
+ MINOR = 11
4
4
  TINY = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, TINY].join('.')
@@ -51,6 +51,15 @@
51
51
  # one_to_many :attributes
52
52
  # has_many :attributes
53
53
  module Sequel::Model::Associations
54
+ # This module contains methods added to all association datasets
55
+ module DatasetMethods
56
+ # The model object that created the association dataset
57
+ attr_accessor :model_object
58
+
59
+ # The association reflection related to the association dataset
60
+ attr_accessor :association_reflection
61
+ end
62
+
54
63
  # Array of all association reflections for this model class
55
64
  def all_association_reflections
56
65
  association_reflections.values
@@ -100,6 +109,10 @@ module Sequel::Model::Associations
100
109
  # - :class - The associated class or its name. If not
101
110
  # given, uses the association's name, which is camelized (and
102
111
  # singularized unless the type is :many_to_one)
112
+ # - :clone - Merge the current options and block into the options and block used in defining
113
+ # the given association. Can be used to DRY up a bunch of similar associations that
114
+ # all share the same options such as :class and :key, while changing the order and block used.
115
+ # - :conditions - The conditions to use to filter the association, can be any argument passed to filter.
103
116
  # - :dataset - A proc that is instance_evaled to get the base dataset
104
117
  # to use for the _dataset method (before the other options are applied).
105
118
  # - :eager - The associations to eagerly load via EagerLoading#eager when loading the associated object(s).
@@ -126,7 +139,8 @@ module Sequel::Model::Associations
126
139
  # - :graph_block - The block to pass to join_table when eagerly loading
127
140
  # the association via eager_graph.
128
141
  # - :graph_conditions - The additional conditions to use on the SQL join when eagerly loading
129
- # the association via eager_graph. Should be a hash or an array of all two pairs.
142
+ # the association via eager_graph. Should be a hash or an array of all two pairs. If not
143
+ # specified, the :conditions option is used if it is a hash or array of all two pairs.
130
144
  # - :graph_join_type - The type of SQL join to use when eagerly loading the association via
131
145
  # eager_graph. Defaults to :left_outer.
132
146
  # - :graph_only_conditions - The conditions to use on the SQL join when eagerly loading
@@ -203,11 +217,16 @@ module Sequel::Model::Associations
203
217
  raise(Error, 'Model.associate name argument must be a symbol') unless Symbol === name
204
218
 
205
219
  # merge early so we don't modify opts
206
- opts = opts.merge(:type => type, :name => name, :block => block, :cache => true, :model => self)
220
+ orig_opts = opts.dup
221
+ orig_opts = association_reflection(opts[:clone])[:orig_opts].merge(orig_opts) if opts[:clone]
222
+ opts = orig_opts.merge(:type => type, :name => name, :cache => true, :model => self)
223
+ opts[:block] = block if block
207
224
  opts = assoc_class.new.merge!(opts)
208
225
  opts[:eager_block] = block unless opts.include?(:eager_block)
209
226
  opts[:graph_join_type] ||= :left_outer
210
227
  opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
228
+ conds = opts[:conditions]
229
+ opts[:graph_conditions] = conds if !opts.include?(:graph_conditions) and (conds.is_a?(Hash) or (conds.is_a?(Array) and conds.all_two_pairs?))
211
230
  opts[:graph_conditions] = opts[:graph_conditions] ? opts[:graph_conditions].to_a : []
212
231
  opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
213
232
  [:before_add, :before_remove, :after_add, :after_remove, :after_load, :extend].each do |cb_type|
@@ -225,6 +244,9 @@ module Sequel::Model::Associations
225
244
 
226
245
  send(:"def_#{type}", opts)
227
246
 
247
+ orig_opts.delete(:clone)
248
+ orig_opts.merge!(:class_name=>opts[:class_name], :class=>opts[:class], :block=>block)
249
+ opts[:orig_opts] = orig_opts
228
250
  # don't add to association_reflections until we are sure there are no errors
229
251
  association_reflections[name] = opts
230
252
  end
@@ -386,21 +408,7 @@ module Sequel::Model::Associations
386
408
  return if opts[:read_only]
387
409
 
388
410
  association_module_private_def(opts._setter_method){|o| send(:"#{key}=", (o.send(opts.primary_key) if o))}
389
-
390
- association_module_def(opts.setter_method) do |o|
391
- raise(Sequel::Error, "model object #{model} does not have a primary key") if o && !o.pk
392
- old_val = send(opts.association_method)
393
- return o if old_val == o
394
- return if old_val and run_association_callbacks(opts, :before_remove, old_val) == false
395
- return if o and run_association_callbacks(opts, :before_add, o) == false
396
- send(opts._setter_method, o)
397
- associations[name] = o
398
- remove_reciprocal_object(opts, old_val) if old_val
399
- add_reciprocal_object(opts, o) if o
400
- run_association_callbacks(opts, :after_add, o) if o
401
- run_association_callbacks(opts, :after_remove, old_val) if old_val
402
- o
403
- end
411
+ association_module_def(opts.setter_method){|o| set_associated_object(opts, o)}
404
412
  end
405
413
 
406
414
  # Adds one_to_many association instance methods
@@ -2,16 +2,25 @@ module Sequel
2
2
  class Model
3
3
  @allowed_columns = nil
4
4
  @association_reflections = {}
5
+ @cache_store = nil
6
+ @cache_ttl = nil
7
+ @db = nil
8
+ @db_schema = nil
5
9
  @dataset_methods = {}
6
10
  @hooks = {}
11
+ @overridable_methods_module = nil
7
12
  @primary_key = :id
8
13
  @raise_on_save_failure = true
9
14
  @raise_on_typecast_failure = true
10
15
  @restrict_primary_key = true
11
16
  @restricted_columns = nil
17
+ @simple_pk = nil
18
+ @simple_table = nil
19
+ @skip_superclass_validations = nil
12
20
  @sti_dataset = nil
13
21
  @sti_key = nil
14
22
  @strict_param_setting = true
23
+ @transform = nil
15
24
  @typecast_empty_string_to_nil = true
16
25
  @typecast_on_assignment = true
17
26
 
@@ -41,6 +50,14 @@ module Sequel
41
50
  # (default: no columns).
42
51
  metaattr_reader :restricted_columns
43
52
 
53
+ # Should be the literal primary key column name if this Model's table has a simple primary key, or
54
+ # nil if the model has a compound primary key or no primary key.
55
+ metaattr_reader :simple_pk
56
+
57
+ # Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
58
+ # or nil otherwise.
59
+ metaattr_reader :simple_table
60
+
44
61
  # The base dataset for STI, to which filters are added to get
45
62
  # only the models for the specific STI subclass.
46
63
  metaattr_reader :sti_dataset
@@ -68,15 +85,19 @@ module Sequel
68
85
  left_outer_join limit map multi_insert naked order order_by order_more
69
86
  paginate print query range reverse_order right_outer_join select
70
87
  select_all select_more server set set_graph_aliases single_value size to_csv to_hash
71
- transform union uniq unfiltered unordered update where'.map{|x| x.to_sym}
88
+ transform union uniq unfiltered unordered update where with_sql'.map{|x| x.to_sym}
72
89
 
73
90
  # Instance variables that are inherited in subclasses
74
91
  INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup, :@cache_store=>nil,
75
92
  :@cache_ttl=>nil, :@dataset_methods=>:dup, :@primary_key=>nil,
76
93
  :@raise_on_save_failure=>nil, :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
94
+ :@simple_pk=>nil, :@simple_table=>nil,
77
95
  :@sti_dataset=>nil, :@sti_key=>nil, :@strict_param_setting=>nil,
78
96
  :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
79
97
  :@raise_on_typecast_failure=>nil, :@association_reflections=>:dup}
98
+
99
+ # Empty instance variables, for -w compliance
100
+ EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@transform, :@db, :@skip_superclass_validations]
80
101
 
81
102
  # Returns the first record from the database matching the conditions.
82
103
  # If a hash is given, it is used as the conditions. If another
@@ -85,11 +106,12 @@ module Sequel
85
106
  # first before a dataset lookup is attempted unless a hash is supplied.
86
107
  def self.[](*args)
87
108
  args = args.first if (args.size == 1)
88
-
89
- if Hash === args
90
- dataset[args]
109
+ return dataset[args] if args.is_a?(Hash)
110
+ return cache_lookup(args) if @cache_store
111
+ if t = simple_table and p = simple_pk
112
+ with_sql("SELECT * FROM #{t} WHERE #{p} = #{dataset.literal(args)} LIMIT 1").first
91
113
  else
92
- @cache_store ? cache_lookup(args) : dataset[primary_key_hash(args)]
114
+ dataset[primary_key_hash(args)]
93
115
  end
94
116
  end
95
117
 
@@ -174,6 +196,7 @@ module Sequel
174
196
  # Modify and return eager loading dataset based on association options
175
197
  def self.eager_loading_dataset(opts, ds, select, associations)
176
198
  ds = ds.select(*select) if select
199
+ ds = ds.filter(opts[:conditions]) if opts[:conditions]
177
200
  ds = ds.order(*opts[:order]) if opts[:order]
178
201
  ds = ds.eager(opts[:eager]) if opts[:eager]
179
202
  ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph]
@@ -201,6 +224,7 @@ module Sequel
201
224
  def self.inherited(subclass)
202
225
  sup_class = subclass.superclass
203
226
  ivs = subclass.instance_variables.collect{|x| x.to_s}
227
+ EMPTY_INSTANCE_VARIABLES.each{|iv| subclass.instance_variable_set(iv, nil) unless ivs.include?(iv.to_s)}
204
228
  INHERITED_INSTANCE_VARIABLES.each do |iv, dup|
205
229
  next if ivs.include?(iv.to_s)
206
230
  sup_class_value = sup_class.instance_variable_get(iv)
@@ -211,7 +235,7 @@ module Sequel
211
235
  db
212
236
  begin
213
237
  if sup_class == Model
214
- subclass.set_dataset(Model.db[subclass.implicit_table_name]) unless subclass.name.blank?
238
+ subclass.set_dataset(subclass.implicit_table_name) unless subclass.name.blank?
215
239
  elsif ds = sup_class.instance_variable_get(:@dataset)
216
240
  subclass.set_dataset(sup_class.sti_key ? sup_class.sti_dataset.filter(sup_class.sti_key=>subclass.name.to_s) : ds.clone, :inherited=>true)
217
241
  end
@@ -239,7 +263,7 @@ module Sequel
239
263
  # Mark the model as not having a primary key. Not having a primary key
240
264
  # can cause issues, among which is that you won't be able to update records.
241
265
  def self.no_primary_key
242
- @primary_key = nil
266
+ @simple_pk = @primary_key = nil
243
267
  end
244
268
 
245
269
  # Returns primary key attribute hash. If using a composite primary key
@@ -317,8 +341,10 @@ module Sequel
317
341
  inherited = opts[:inherited]
318
342
  @dataset = case ds
319
343
  when Symbol
344
+ @simple_table = db.literal(ds)
320
345
  db[ds]
321
346
  when Dataset
347
+ @simple_table = nil
322
348
  @db = ds.db
323
349
  ds
324
350
  else
@@ -327,6 +353,7 @@ module Sequel
327
353
  @dataset.set_model(self)
328
354
  @dataset.transform(@transform) if @transform
329
355
  if inherited
356
+ @simple_table = sti_key ? nil : superclass.simple_table
330
357
  @columns = @dataset.columns rescue nil
331
358
  else
332
359
  @dataset.extend(DatasetMethods)
@@ -354,6 +381,7 @@ module Sequel
354
381
  # You can set it to nil to not have a primary key, but that
355
382
  # cause certain things not to work, see #no_primary_key.
356
383
  def self.set_primary_key(*key)
384
+ @simple_pk = key.length == 1 ? db.literal(key.first) : nil
357
385
  @primary_key = (key.length == 1) ? key[0] : key.flatten
358
386
  end
359
387
 
@@ -17,7 +17,7 @@ module Sequel
17
17
  def self.set_cache(store, opts = {})
18
18
  @cache_store = store
19
19
  @cache_ttl = opts[:ttl] || 3600
20
- before_save :cache_delete_unless_new
20
+ before_update :cache_delete
21
21
  before_update_values :cache_delete
22
22
  before_delete :cache_delete
23
23
  end
@@ -78,10 +78,5 @@ module Sequel
78
78
  def cache_delete
79
79
  model.send(:cache_delete, cache_key)
80
80
  end
81
-
82
- # Delete this object from the cache unless it is a new record
83
- def cache_delete_unless_new
84
- cache_delete unless new?
85
- end
86
81
  end
87
82
  end
@@ -345,8 +345,12 @@ module Sequel
345
345
  def _dataset(opts)
346
346
  raise(Sequel::Error, "model object #{model} does not have a primary key") if opts.dataset_need_primary_key? && !pk
347
347
  ds = send(opts._dataset_method)
348
+ ds.extend(Associations::DatasetMethods)
349
+ ds.model_object = self
350
+ ds.association_reflection = opts
348
351
  opts[:extend].each{|m| ds.extend(m)}
349
352
  ds = ds.select(*opts.select) if opts.select
353
+ ds = ds.filter(opts[:conditions]) if opts[:conditions]
350
354
  ds = ds.order(*opts[:order]) if opts[:order]
351
355
  ds = ds.limit(*opts[:limit]) if opts[:limit]
352
356
  ds = ds.eager(*opts[:eager]) if opts[:eager]
@@ -470,6 +474,24 @@ module Sequel
470
474
  end
471
475
  end
472
476
 
477
+ # Set the given object as the associated object for the given association
478
+ def set_associated_object(opts, o)
479
+ raise(Sequel::Error, "model object #{model} does not have a primary key") if o && !o.pk
480
+ old_val = send(opts.association_method)
481
+ return o if old_val == o
482
+ return if old_val and run_association_callbacks(opts, :before_remove, old_val) == false
483
+ return if o and run_association_callbacks(opts, :before_add, o) == false
484
+ send(opts._setter_method, o)
485
+ associations[opts[:name]] = o
486
+ remove_reciprocal_object(opts, old_val) if old_val
487
+ if o
488
+ add_reciprocal_object(opts, o)
489
+ run_association_callbacks(opts, :after_add, o)
490
+ end
491
+ run_association_callbacks(opts, :after_remove, old_val) if old_val
492
+ o
493
+ end
494
+
473
495
  # Set the columns, filtered by the only and except arrays.
474
496
  def set_restricted(hash, only, except)
475
497
  columns_not_set = model.instance_variable_get(:@columns).blank?
@@ -527,11 +549,7 @@ module Sequel
527
549
  begin
528
550
  model.db.typecast_value(col_schema[:type], value)
529
551
  rescue Error::InvalidValue
530
- if raise_on_typecast_failure
531
- raise
532
- else
533
- value
534
- end
552
+ raise_on_typecast_failure ? raise : value
535
553
  end
536
554
  end
537
555