sequel 3.5.0 → 3.6.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 (72) hide show
  1. data/CHANGELOG +108 -0
  2. data/README.rdoc +25 -14
  3. data/Rakefile +20 -1
  4. data/doc/advanced_associations.rdoc +61 -64
  5. data/doc/cheat_sheet.rdoc +16 -7
  6. data/doc/opening_databases.rdoc +3 -3
  7. data/doc/prepared_statements.rdoc +1 -1
  8. data/doc/reflection.rdoc +2 -1
  9. data/doc/release_notes/3.6.0.txt +366 -0
  10. data/doc/schema.rdoc +19 -14
  11. data/lib/sequel/adapters/amalgalite.rb +5 -27
  12. data/lib/sequel/adapters/jdbc.rb +13 -3
  13. data/lib/sequel/adapters/jdbc/h2.rb +17 -0
  14. data/lib/sequel/adapters/jdbc/mysql.rb +20 -7
  15. data/lib/sequel/adapters/mysql.rb +4 -3
  16. data/lib/sequel/adapters/oracle.rb +1 -1
  17. data/lib/sequel/adapters/postgres.rb +87 -28
  18. data/lib/sequel/adapters/shared/mssql.rb +47 -6
  19. data/lib/sequel/adapters/shared/mysql.rb +12 -31
  20. data/lib/sequel/adapters/shared/postgres.rb +15 -12
  21. data/lib/sequel/adapters/shared/sqlite.rb +18 -0
  22. data/lib/sequel/adapters/sqlite.rb +1 -16
  23. data/lib/sequel/connection_pool.rb +1 -1
  24. data/lib/sequel/core.rb +1 -1
  25. data/lib/sequel/database.rb +1 -1
  26. data/lib/sequel/database/schema_generator.rb +2 -0
  27. data/lib/sequel/database/schema_sql.rb +1 -1
  28. data/lib/sequel/dataset.rb +5 -179
  29. data/lib/sequel/dataset/actions.rb +123 -0
  30. data/lib/sequel/dataset/convenience.rb +18 -10
  31. data/lib/sequel/dataset/features.rb +65 -0
  32. data/lib/sequel/dataset/prepared_statements.rb +29 -23
  33. data/lib/sequel/dataset/query.rb +429 -0
  34. data/lib/sequel/dataset/sql.rb +67 -435
  35. data/lib/sequel/model/associations.rb +77 -13
  36. data/lib/sequel/model/base.rb +30 -8
  37. data/lib/sequel/model/errors.rb +4 -4
  38. data/lib/sequel/plugins/caching.rb +38 -15
  39. data/lib/sequel/plugins/force_encoding.rb +18 -7
  40. data/lib/sequel/plugins/hook_class_methods.rb +4 -0
  41. data/lib/sequel/plugins/many_through_many.rb +1 -1
  42. data/lib/sequel/plugins/nested_attributes.rb +40 -11
  43. data/lib/sequel/plugins/serialization.rb +17 -3
  44. data/lib/sequel/plugins/validation_helpers.rb +65 -18
  45. data/lib/sequel/sql.rb +23 -1
  46. data/lib/sequel/version.rb +1 -1
  47. data/spec/adapters/mssql_spec.rb +96 -10
  48. data/spec/adapters/mysql_spec.rb +19 -0
  49. data/spec/adapters/postgres_spec.rb +65 -2
  50. data/spec/adapters/sqlite_spec.rb +10 -0
  51. data/spec/core/core_sql_spec.rb +9 -0
  52. data/spec/core/database_spec.rb +8 -4
  53. data/spec/core/dataset_spec.rb +122 -29
  54. data/spec/core/expression_filters_spec.rb +17 -0
  55. data/spec/extensions/caching_spec.rb +43 -3
  56. data/spec/extensions/force_encoding_spec.rb +43 -1
  57. data/spec/extensions/nested_attributes_spec.rb +55 -2
  58. data/spec/extensions/validation_helpers_spec.rb +71 -0
  59. data/spec/integration/associations_test.rb +281 -0
  60. data/spec/integration/dataset_test.rb +383 -9
  61. data/spec/integration/eager_loader_test.rb +0 -65
  62. data/spec/integration/model_test.rb +110 -0
  63. data/spec/integration/plugin_test.rb +306 -0
  64. data/spec/integration/prepared_statement_test.rb +32 -0
  65. data/spec/integration/schema_test.rb +8 -3
  66. data/spec/integration/spec_helper.rb +1 -25
  67. data/spec/model/association_reflection_spec.rb +38 -0
  68. data/spec/model/associations_spec.rb +184 -8
  69. data/spec/model/eager_loading_spec.rb +23 -0
  70. data/spec/model/model_spec.rb +8 -0
  71. data/spec/model/record_spec.rb +84 -1
  72. metadata +9 -2
@@ -56,7 +56,13 @@ module Sequel
56
56
  def associated_class
57
57
  self[:class] ||= constantize(self[:class_name])
58
58
  end
59
-
59
+
60
+ # Whether this association can have associated objects, given the current
61
+ # object. Should be false if obj cannot have associated objects because
62
+ # the necessary key columns are NULL.
63
+ def can_have_associated_objects?(obj)
64
+ true
65
+ end
60
66
 
61
67
  # Name symbol for the dataset association method
62
68
  def dataset_method
@@ -126,6 +132,11 @@ module Sequel
126
132
  :"remove_#{singularize(self[:name])}"
127
133
  end
128
134
 
135
+ # Whether to check that an object to be disassociated is already associated to this object, false by default.
136
+ def remove_should_check_existing?
137
+ false
138
+ end
139
+
129
140
  # Whether this association returns an array of objects instead of a single object,
130
141
  # true by default.
131
142
  def returns_array?
@@ -152,6 +163,12 @@ module Sequel
152
163
  class ManyToOneAssociationReflection < AssociationReflection
153
164
  ASSOCIATION_TYPES[:many_to_one] = self
154
165
 
166
+ # many_to_one associations can only have associated objects if none of
167
+ # the :keys options have a nil value.
168
+ def can_have_associated_objects?(obj)
169
+ !self[:keys].any?{|k| obj.send(k).nil?}
170
+ end
171
+
155
172
  # Whether the dataset needs a primary key to function, false for many_to_one associations.
156
173
  def dataset_need_primary_key?
157
174
  false
@@ -183,6 +200,7 @@ module Sequel
183
200
  def primary_keys
184
201
  self[:primary_keys] ||= Array(primary_key)
185
202
  end
203
+ alias associated_object_keys primary_keys
186
204
 
187
205
  # Whether this association returns an array of objects instead of a single object,
188
206
  # false for a many_to_one association.
@@ -200,6 +218,17 @@ module Sequel
200
218
 
201
219
  class OneToManyAssociationReflection < AssociationReflection
202
220
  ASSOCIATION_TYPES[:one_to_many] = self
221
+
222
+ # The keys in the associated model's table related to this association
223
+ def associated_object_keys
224
+ self[:keys]
225
+ end
226
+
227
+ # one_to_many associations can only have associated objects if none of
228
+ # the :keys options have a nil value.
229
+ def can_have_associated_objects?(obj)
230
+ !self[:primary_keys].any?{|k| obj.send(k).nil?}
231
+ end
203
232
 
204
233
  # Default foreign key name symbol for key in associated table that points to
205
234
  # current table's primary key.
@@ -224,6 +253,11 @@ module Sequel
224
253
  false
225
254
  end
226
255
 
256
+ # The one_to_many association needs to check that an object to be removed already is associated.
257
+ def remove_should_check_existing?
258
+ true
259
+ end
260
+
227
261
  private
228
262
 
229
263
  # The reciprocal type of a one_to_many association is a many_to_one association.
@@ -249,6 +283,12 @@ module Sequel
249
283
  def associated_key_table
250
284
  self[:join_table]
251
285
  end
286
+
287
+ # many_to_many associations can only have associated objects if none of
288
+ # the :left_primary_keys options have a nil value.
289
+ def can_have_associated_objects?(obj)
290
+ !self[:left_primary_keys].any?{|k| obj.send(k).nil?}
291
+ end
252
292
 
253
293
  # The default associated key alias(es) to use when eager loading
254
294
  # associations via eager.
@@ -314,6 +354,7 @@ module Sequel
314
354
  def right_primary_keys
315
355
  self[:right_primary_keys] ||= Array(right_primary_key)
316
356
  end
357
+ alias associated_object_keys right_primary_keys
317
358
 
318
359
  # The columns to select when loading the association, associated_class.table_name.* by default.
319
360
  def select
@@ -432,6 +473,8 @@ module Sequel
432
473
  # - :conditions - The conditions to use to filter the association, can be any argument passed to filter.
433
474
  # - :dataset - A proc that is instance_evaled to get the base dataset
434
475
  # to use for the _dataset method (before the other options are applied).
476
+ # - :distinct - Use the DISTINCT clause when selecting associating object, both when
477
+ # lazy loading and eager loading via .eager (but not when using .eager_graph).
435
478
  # - :eager - The associations to eagerly load via #eager when loading the associated object(s).
436
479
  # For many_to_one associations, this is ignored unless this association is
437
480
  # being eagerly loaded, as it doesn't save queries unless multiple objects
@@ -597,6 +640,7 @@ module Sequel
597
640
  end
598
641
  ds = ds.order(*opts[:order]) if opts[:order]
599
642
  ds = ds.eager(opts[:eager]) if opts[:eager]
643
+ ds = ds.distinct if opts[:distinct]
600
644
  if opts[:eager_graph]
601
645
  ds = ds.eager_graph(opts[:eager_graph])
602
646
  ds = ds.add_graph_aliases(opts.associated_key_alias=>[opts.associated_class.table_name, opts.associated_key_alias, SQL::QualifiedIdentifier.new(opts.associated_key_table, opts.associated_key_column)]) if opts.eager_loading_use_associated_key?
@@ -903,7 +947,7 @@ module Sequel
903
947
 
904
948
  # Backbone behind association dataset methods
905
949
  def _dataset(opts)
906
- raise(Sequel::Error, "model object #{model} does not have a primary key") if opts.dataset_need_primary_key? && !pk
950
+ raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
907
951
  ds = send(opts._dataset_method)
908
952
  ds.extend(AssociationDatasetMethods)
909
953
  ds.model_object = self
@@ -916,6 +960,7 @@ module Sequel
916
960
  ds = ds.order(*opts[:order]) if opts[:order]
917
961
  ds = ds.limit(*opts[:limit]) if opts[:limit]
918
962
  ds = ds.eager(*opts[:eager]) if opts[:eager]
963
+ ds = ds.distinct if opts[:distinct]
919
964
  ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
920
965
  ds = send(opts.dataset_helper_method, ds) if opts[:block]
921
966
  ds
@@ -924,11 +969,11 @@ module Sequel
924
969
  # Return the associated objects from the dataset, without callbacks, reciprocals, and caching.
925
970
  def _load_associated_objects(opts)
926
971
  if opts.returns_array?
927
- send(opts.dataset_method).all
972
+ opts.can_have_associated_objects?(self) ? send(opts.dataset_method).all : []
928
973
  else
929
974
  if !opts[:key]
930
975
  send(opts.dataset_method).all.first
931
- elsif opts[:keys].all?{|k| send(k)}
976
+ elsif opts.can_have_associated_objects?(self)
932
977
  send(opts.dataset_method).first
933
978
  end
934
979
  end
@@ -936,14 +981,22 @@ module Sequel
936
981
 
937
982
  # Add the given associated object to the given association
938
983
  def add_associated_object(opts, o, *args)
939
- raise(Sequel::Error, "model object #{model} does not have a primary key") unless pk
984
+ klass = opts.associated_class
985
+ if o.is_a?(Hash)
986
+ o = klass.new(o)
987
+ elsif !o.is_a?(klass)
988
+ raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
989
+ end
990
+ raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk
940
991
  if opts.need_associated_primary_key?
941
992
  o.save(:validate=>opts[:validate]) if o.new?
942
- raise(Sequel::Error, "associated object #{o.model} does not have a primary key") unless o.pk
993
+ raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") unless o.pk
943
994
  end
944
995
  return if run_association_callbacks(opts, :before_add, o) == false
945
996
  send(opts._add_method, o, *args)
946
- associations[opts[:name]].push(o) if associations.include?(opts[:name])
997
+ if array = associations[opts[:name]] and !array.include?(o)
998
+ array.push(o)
999
+ end
947
1000
  add_reciprocal_object(opts, o)
948
1001
  run_association_callbacks(opts, :after_add, o)
949
1002
  o
@@ -982,7 +1035,7 @@ module Sequel
982
1035
 
983
1036
  # Remove all associated objects from the given association
984
1037
  def remove_all_associated_objects(opts, *args)
985
- raise(Sequel::Error, "model object #{model} does not have a primary key") unless pk
1038
+ raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk
986
1039
  send(opts._remove_all_method, *args)
987
1040
  ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
988
1041
  associations[opts[:name]] = []
@@ -991,8 +1044,18 @@ module Sequel
991
1044
 
992
1045
  # Remove the given associated object from the given association
993
1046
  def remove_associated_object(opts, o, *args)
994
- raise(Sequel::Error, "model object #{model} does not have a primary key") unless pk
995
- raise(Sequel::Error, "associated object #{o.model} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
1047
+ klass = opts.associated_class
1048
+ if o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
1049
+ key = o
1050
+ pkh = klass.primary_key_hash(key)
1051
+ raise(Sequel::Error, "no object with key(s) #{key} is currently associated to #{inspect}") unless o = (opts.remove_should_check_existing? ? send(opts.dataset_method) : klass).first(pkh)
1052
+ elsif !o.is_a?(klass)
1053
+ raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
1054
+ elsif opts.remove_should_check_existing? && send(opts.dataset_method).filter(o.pk_hash).empty?
1055
+ raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
1056
+ end
1057
+ raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk
1058
+ raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
996
1059
  return if run_association_callbacks(opts, :before_remove, o) == false
997
1060
  send(opts._remove_method, o, *args)
998
1061
  associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
@@ -1027,7 +1090,7 @@ module Sequel
1027
1090
  raise Error, "callbacks should either be Procs or Symbols"
1028
1091
  end
1029
1092
  if res == false and stop_on_false
1030
- raise(BeforeHookFailed, "Unable to modify association for record: one of the #{callback_type} hooks returned false") if raise_error
1093
+ raise(BeforeHookFailed, "Unable to modify association for #{inspect}: one of the #{callback_type} hooks returned false") if raise_error
1031
1094
  return false
1032
1095
  end
1033
1096
  end
@@ -1035,7 +1098,7 @@ module Sequel
1035
1098
 
1036
1099
  # Set the given object as the associated object for the given association
1037
1100
  def set_associated_object(opts, o)
1038
- raise(Sequel::Error, "model object #{model} does not have a primary key") if o && !o.pk
1101
+ raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if o && !o.pk
1039
1102
  old_val = send(opts.association_method)
1040
1103
  return o if old_val == o
1041
1104
  return if old_val and run_association_callbacks(opts, :before_remove, old_val) == false
@@ -1151,6 +1214,7 @@ module Sequel
1151
1214
  # call each, you will get a normal graphed result back (a hash with model object values).
1152
1215
  def eager_graph(*associations)
1153
1216
  table_name = model.table_name
1217
+
1154
1218
  ds = if @opts[:eager_graph]
1155
1219
  self
1156
1220
  else
@@ -1159,7 +1223,7 @@ module Sequel
1159
1223
  # :requirements - array of requirements for this association
1160
1224
  # :alias_association_type_map - the type of association for this association
1161
1225
  # :alias_association_name_map - the name of the association for this association
1162
- clone(:eager_graph=>{:requirements=>{}, :master=>model.table_name, :alias_association_type_map=>{}, :alias_association_name_map=>{}, :reciprocals=>{}, :cartesian_product_number=>0})
1226
+ clone(:eager_graph=>{:requirements=>{}, :master=>table_name, :alias_association_type_map=>{}, :alias_association_name_map=>{}, :reciprocals=>{}, :cartesian_product_number=>0})
1163
1227
  end
1164
1228
  ds.eager_graph_associations(ds, model, table_name, [], *associations)
1165
1229
  end
@@ -173,6 +173,7 @@ module Sequel
173
173
  # is created. Also, make sure the inherited class instance variables
174
174
  # are copied into the subclass.
175
175
  def inherited(subclass)
176
+ super
176
177
  ivs = subclass.instance_variables.collect{|x| x.to_s}
177
178
  EMPTY_INSTANCE_VARIABLES.each{|iv| subclass.instance_variable_set(iv, nil) unless ivs.include?(iv.to_s)}
178
179
  INHERITED_INSTANCE_VARIABLES.each do |iv, dup|
@@ -357,7 +358,7 @@ module Sequel
357
358
 
358
359
  # Returns name of primary table for the dataset.
359
360
  def table_name
360
- dataset.opts[:from].first
361
+ dataset.first_source_alias
361
362
  end
362
363
 
363
364
  # Allow the setting of the primary key(s) inside new/set/update.
@@ -524,10 +525,11 @@ module Sequel
524
525
  def initialize(values = {}, from_db = false)
525
526
  if from_db
526
527
  @new = false
527
- @values = values
528
+ set_values(values)
528
529
  else
529
530
  @values = {}
530
531
  @new = true
532
+ @modified = true
531
533
  set(values)
532
534
  changed_columns.clear
533
535
  yield self if block_given?
@@ -653,11 +655,23 @@ module Sequel
653
655
  @values.keys
654
656
  end
655
657
 
658
+ # Remove elements of the model object that make marshalling fail. Returns self.
659
+ def marshallable!
660
+ @this = nil
661
+ self
662
+ end
663
+
664
+ # Explicitly mark the object as modified, so save_changes/update will
665
+ # run callbacks even if no columns have changed.
666
+ def modified!
667
+ @modified = true
668
+ end
669
+
656
670
  # Whether this object has been modified since last saved, used by
657
671
  # save_changes to determine whether changes should be saved. New
658
672
  # values are always considered modified.
659
673
  def modified?
660
- new? || !changed_columns.empty?
674
+ @modified || !changed_columns.empty?
661
675
  end
662
676
 
663
677
  # Returns true if the current instance represents a new record.
@@ -715,8 +729,8 @@ module Sequel
715
729
  # Saves only changed columns if the object has been modified.
716
730
  # If the object has not been modified, returns nil. If unable to
717
731
  # save, returns false unless raise_on_save_failure is true.
718
- def save_changes
719
- save(:changed=>true) || false if modified?
732
+ def save_changes(opts={})
733
+ save(opts.merge(:changed=>true)) || false if modified?
720
734
  end
721
735
 
722
736
  # Updates the instance with the supplied values with support for virtual
@@ -750,7 +764,7 @@ module Sequel
750
764
  @this ||= model.dataset.filter(pk_hash).limit(1).naked
751
765
  end
752
766
 
753
- # Runs set with the passed hash and runs save_changes (which runs any callback methods).
767
+ # Runs set with the passed hash and then runs save_changes.
754
768
  def update(hash)
755
769
  update_restricted(hash, nil, nil)
756
770
  end
@@ -821,7 +835,7 @@ module Sequel
821
835
  # Refresh using a particular dataset, used inside save to make sure the same server
822
836
  # is used for reading newly inserted values from the database
823
837
  def _refresh(dataset)
824
- @values = dataset.first || raise(Error, "Record not found")
838
+ set_values(dataset.first || raise(Error, "Record not found"))
825
839
  changed_columns.clear
826
840
  associations.clear
827
841
  self
@@ -844,6 +858,8 @@ module Sequel
844
858
  ds = this
845
859
  ds = ds.server(:default) unless ds.opts[:server]
846
860
  _refresh(ds)
861
+ else
862
+ changed_columns.clear
847
863
  end
848
864
  else
849
865
  return save_failure(:update) if before_update == false
@@ -860,6 +876,7 @@ module Sequel
860
876
  after_save
861
877
  @columns_updated = nil
862
878
  end
879
+ @modified = false
863
880
  self
864
881
  end
865
882
 
@@ -897,7 +914,12 @@ module Sequel
897
914
  end
898
915
  self
899
916
  end
900
-
917
+
918
+ # Replace the current values with hash.
919
+ def set_values(hash)
920
+ @values = hash
921
+ end
922
+
901
923
  # Returns all methods that can be used for attribute
902
924
  # assignment (those that end with =), modified by the only
903
925
  # and except arguments:
@@ -3,11 +3,11 @@ module Sequel
3
3
  # Errors represents validation errors, a simple hash subclass
4
4
  # with a few convenience methods.
5
5
  class Errors < ::Hash
6
- ATTRIBUTE_JOINER = ' and '
6
+ ATTRIBUTE_JOINER = ' and '.freeze
7
7
 
8
8
  # Assign an array of messages for each attribute on access
9
- def initialize
10
- super{|h,k| h[k] = []}
9
+ def [](k)
10
+ has_key?(k) ? super : (self[k] = [])
11
11
  end
12
12
 
13
13
  # Adds an error for the given attribute.
@@ -32,7 +32,7 @@ module Sequel
32
32
  # Returns the array of errors for the given attribute, or nil
33
33
  # if there are no errors for the attribute.
34
34
  def on(att)
35
- self[att] if include?(att)
35
+ self[att] if has_key?(att)
36
36
  end
37
37
  end
38
38
  end
@@ -1,8 +1,8 @@
1
1
  module Sequel
2
2
  module Plugins
3
3
  # Sequel's built-in caching plugin supports caching to any object that
4
- # implements the Ruby-Memcache API. You can add caching for any model
5
- # or for all models via:
4
+ # implements the Ruby-Memcache API (or memcached API with the :ignore_exceptions
5
+ # option). You can add caching for any model or for all models via:
6
6
  #
7
7
  # Model.plugin :caching, store # Cache all models
8
8
  # MyModel.plugin :caching, store # Just cache MyModel
@@ -11,10 +11,15 @@ module Sequel
11
11
  #
12
12
  # cache_store.set(key, obj, time) # Associate the obj with the given key
13
13
  # # in the cache for the time (specified
14
- # # in seconds)
15
- # cache_store.get(key) => obj # Returns object set with same key
16
- # cache_store.get(key2) => nil # nil returned if there isn't an object
17
- # # currently in the cache with that key
14
+ # # in seconds).
15
+ # cache_store.get(key) => obj # Returns object set with same key.
16
+ # cache_store.get(key2) => nil # nil returned if there isn't an object
17
+ # # currently in the cache with that key.
18
+ #
19
+ # If the :ignore_exceptions option is true, exceptions raised by cache_store.get
20
+ # are ignored and nil is returned instead. The memcached API is to
21
+ # raise an exception for a missing record, so if you use memcached, you will
22
+ # want to use this option.
18
23
  module Caching
19
24
  # Set the cache_store and cache_ttl attributes for the given model.
20
25
  # If the :ttl option is not given, 3600 seconds is the default.
@@ -22,12 +27,16 @@ module Sequel
22
27
  model.instance_eval do
23
28
  @cache_store = store
24
29
  @cache_ttl = opts[:ttl] || 3600
30
+ @cache_ignore_exceptions = opts[:ignore_exceptions]
25
31
  end
26
32
  end
27
33
 
28
34
  module ClassMethods
35
+ # If true, ignores exceptions when gettings cached records (the memcached API).
36
+ attr_reader :cache_ignore_exceptions
37
+
29
38
  # The cache store object for the model, which should implement the
30
- # Ruby-Memcache API
39
+ # Ruby-Memcache (or memcached) API
31
40
  attr_reader :cache_store
32
41
 
33
42
  # The time to live for the cache store, in seconds.
@@ -38,38 +47,52 @@ module Sequel
38
47
  @cache_ttl = ttl
39
48
  end
40
49
 
41
- # Copy the cache_store and cache_ttl to the subclass.
50
+ # Copy the necessary class instance variables to the subclass.
42
51
  def inherited(subclass)
43
52
  super
44
53
  store = @cache_store
45
54
  ttl = @cache_ttl
55
+ cache_ignore_exceptions = @cache_ignore_exceptions
46
56
  subclass.instance_eval do
47
57
  @cache_store = store
48
58
  @cache_ttl = ttl
59
+ @cache_ignore_exceptions = cache_ignore_exceptions
49
60
  end
50
61
  end
51
62
 
52
63
  private
53
64
 
54
65
  # Delete the entry with the matching key from the cache
55
- def cache_delete(key)
56
- @cache_store.delete(key)
66
+ def cache_delete(ck)
67
+ @cache_store.delete(ck)
57
68
  nil
58
69
  end
70
+
71
+ def cache_get(ck)
72
+ if @cache_ignore_exceptions
73
+ @cache_store.get(ck) rescue nil
74
+ else
75
+ @cache_store.get(ck)
76
+ end
77
+ end
59
78
 
60
79
  # Return a key string for the pk
61
80
  def cache_key(pk)
62
81
  "#{self}:#{Array(pk).join(',')}"
63
82
  end
64
83
 
84
+ # Set the object in the cache_store with the given key for cache_ttl seconds.
85
+ def cache_set(ck, obj)
86
+ @cache_store.set(ck, obj, @cache_ttl)
87
+ end
88
+
65
89
  # Check the cache before a database lookup unless a hash is supplied.
66
90
  def primary_key_lookup(pk)
67
91
  ck = cache_key(pk)
68
- if obj = @cache_store.get(ck)
69
- return obj
70
- end
71
- if obj = super(pk)
72
- @cache_store.set(ck, obj, @cache_ttl)
92
+ unless obj = cache_get(ck)
93
+ if obj = super(pk)
94
+ cache_set(ck, obj)
95
+ end
73
96
  end
74
97
  obj
75
98
  end