sohm 0.0.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sohm.rb CHANGED
@@ -33,7 +33,7 @@ module Sohm
33
33
  # Raised when trying to save an object with a `unique` index for
34
34
  # which the value already exists.
35
35
  #
36
- # Solution: rescue `Ohm::UniqueIndexViolation` during save, but
36
+ # Solution: rescue `Sohm::UniqueIndexViolation` during save, but
37
37
  # also, do some validations even before attempting to save.
38
38
  #
39
39
  class Error < StandardError; end
@@ -53,14 +53,14 @@ module Sohm
53
53
  #
54
54
  # Example:
55
55
  #
56
- # class Comment < Ohm::Model
56
+ # class Comment < Sohm::Model
57
57
  # reference :user, User # NameError undefined constant User.
58
58
  # end
59
59
  #
60
60
  # # Instead of relying on some clever `const_missing` hack, we can
61
61
  # # simply use a symbol or a string.
62
62
  #
63
- # class Comment < Ohm::Model
63
+ # class Comment < Sohm::Model
64
64
  # reference :user, :User
65
65
  # reference :post, "Post"
66
66
  # end
@@ -76,18 +76,6 @@ module Sohm
76
76
  def self.dict(arr)
77
77
  Hash[*arr]
78
78
  end
79
-
80
- def self.sort(redis, key, options)
81
- args = []
82
-
83
- args.concat(["BY", options[:by]]) if options[:by]
84
- args.concat(["GET", options[:get]]) if options[:get]
85
- args.concat(["LIMIT"] + options[:limit]) if options[:limit]
86
- args.concat(options[:order].split(" ")) if options[:order]
87
- args.concat(["STORE", options[:store]]) if options[:store]
88
-
89
- redis.call("SORT", key, *args)
90
- end
91
79
  end
92
80
 
93
81
  # Use this if you want to do quick ad hoc redis commands against the
@@ -98,14 +86,28 @@ module Sohm
98
86
  # Ohm.redis.call("SET", "foo", "bar")
99
87
  # Ohm.redis.call("FLUSH")
100
88
  #
89
+ @redis = Redic.new
101
90
  def self.redis
102
- @redis ||= Redic.new
91
+ @redis
103
92
  end
104
93
 
105
94
  def self.redis=(redis)
106
95
  @redis = redis
107
96
  end
108
97
 
98
+ # If you are using a Redis pool to override @redis above, chances are
99
+ # you won't need a mutex(since your opertions will run on different
100
+ # redis instances), so you can use this to override the default mutex
101
+ # for better performance
102
+ @mutex = Mutex.new
103
+ def self.mutex=(mutex)
104
+ @mutex = mutex
105
+ end
106
+
107
+ def self.mutex
108
+ @mutex
109
+ end
110
+
109
111
  # By default, EVALSHA is used
110
112
  def self.enable_evalsha
111
113
  defined?(@enable_evalsha) ? @enable_evalsha : true
@@ -137,7 +139,6 @@ module Sohm
137
139
  size == 0
138
140
  end
139
141
 
140
- # TODO: fix this later
141
142
  # Wraps the whole pipelining functionality.
142
143
  def fetch(ids)
143
144
  data = nil
@@ -154,7 +155,7 @@ module Sohm
154
155
 
155
156
  [].tap do |result|
156
157
  data.each_with_index do |atts, idx|
157
- unless attrs.empty?
158
+ unless atts.empty?
158
159
  result << model.new(Utils.dict(atts).update(:id => ids[idx]))
159
160
  end
160
161
  end
@@ -195,10 +196,10 @@ module Sohm
195
196
  #
196
197
  # Example:
197
198
  #
198
- # class Comment < Ohm::Model
199
+ # class Comment < Sohm::Model
199
200
  # end
200
201
  #
201
- # class Post < Ohm::Model
202
+ # class Post < Sohm::Model
202
203
  # list :comments, :Comment
203
204
  # end
204
205
  #
@@ -246,10 +247,10 @@ module Sohm
246
247
  #
247
248
  # Example:
248
249
  #
249
- # class Comment < Ohm::Model
250
+ # class Comment < Sohm::Model
250
251
  # end
251
252
  #
252
- # class Post < Ohm::Model
253
+ # class Post < Sohm::Model
253
254
  # list :comments, :Comment
254
255
  # end
255
256
  #
@@ -272,10 +273,10 @@ module Sohm
272
273
 
273
274
  # Returns an array with all the ID's of the list.
274
275
  #
275
- # class Comment < Ohm::Model
276
+ # class Comment < Sohm::Model
276
277
  # end
277
278
  #
278
- # class Post < Ohm::Model
279
+ # class Post < Sohm::Model
279
280
  # list :comments, :Comment
280
281
  # end
281
282
  #
@@ -305,57 +306,6 @@ module Sohm
305
306
  class BasicSet
306
307
  include Collection
307
308
 
308
- # Allows you to sort by any attribute in the hash, this doesn't include
309
- # the +id+. If you want to sort by ID, use #sort.
310
- #
311
- # class User < Ohm::Model
312
- # attribute :name
313
- # end
314
- #
315
- # User.all.sort_by(:name, :order => "ALPHA")
316
- # User.all.sort_by(:name, :order => "ALPHA DESC")
317
- # User.all.sort_by(:name, :order => "ALPHA DESC", :limit => [0, 10])
318
- #
319
- # Note: This is slower compared to just doing `sort`, specifically
320
- # because Redis has to read each individual hash in order to sort
321
- # them.
322
- #
323
- def sort_by(att, options = {})
324
- sort(options.merge(:by => to_key(att)))
325
- end
326
-
327
- # Allows you to sort your models using their IDs. This is much
328
- # faster than `sort_by`. If you simply want to get records in
329
- # ascending or descending order, then this is the best method to
330
- # do that.
331
- #
332
- # Example:
333
- #
334
- # class User < Ohm::Model
335
- # attribute :name
336
- # end
337
- #
338
- # User.create(:name => "John")
339
- # User.create(:name => "Jane")
340
- #
341
- # User.all.sort.map(&:id) == ["1", "2"]
342
- # # => true
343
- #
344
- # User.all.sort(:order => "ASC").map(&:id) == ["1", "2"]
345
- # # => true
346
- #
347
- # User.all.sort(:order => "DESC").map(&:id) == ["2", "1"]
348
- # # => true
349
- #
350
- def sort(options = {})
351
- if options.has_key?(:get)
352
- options[:get] = to_key(options[:get])
353
- return execute { |key| Utils.sort(redis, key, options) }
354
- end
355
-
356
- fetch(execute { |key| Utils.sort(redis, key, options) })
357
- end
358
-
359
309
  # Check if a model is included in this set.
360
310
  #
361
311
  # Example:
@@ -377,34 +327,28 @@ module Sohm
377
327
  execute { |key| redis.call("SCARD", key) }
378
328
  end
379
329
 
380
- # Syntactic sugar for `sort_by` or `sort` when you only need the
381
- # first element.
330
+ # SMEMBERS then choosing the first will take too much memory in case data
331
+ # grow big enough, which will be slow in this case.
332
+ # Providing +sample+ only gives a hint that we won't preserve any order
333
+ # for this, there will be 2 cases:
382
334
  #
383
- # Example:
335
+ # 1. Anyone in the set will do, this is the original use case of +sample*
336
+ # 2. For some reasons(maybe due to filters), we only have 1 element left
337
+ # in this set, using +sample+ will do the trick
384
338
  #
385
- # User.all.first ==
386
- # User.all.sort(:limit => [0, 1]).first
387
- #
388
- # User.all.first(:by => :name, "ALPHA") ==
389
- # User.all.sort_by(:name, :order => "ALPHA", :limit => [0, 1]).first
390
- #
391
- def first(options = {})
392
- opts = options.dup
393
- opts.merge!(:limit => [0, 1])
394
-
395
- if opts[:by]
396
- sort_by(opts.delete(:by), opts).first
397
- else
398
- sort(opts).first
399
- end
339
+ # For all the other cases, we won't be able to fetch a single element
340
+ # without fetching all elements first(in other words, doing this
341
+ # efficiently)
342
+ def sample
343
+ model[execute { |key| redis.call("SRANDMEMBER", key) }]
400
344
  end
401
345
 
402
346
  # Returns an array with all the ID's of the set.
403
347
  #
404
- # class Post < Ohm::Model
348
+ # class Post < Sohm::Model
405
349
  # end
406
350
  #
407
- # class User < Ohm::Model
351
+ # class User < Sohm::Model
408
352
  # attribute :name
409
353
  # index :name
410
354
  #
@@ -442,10 +386,10 @@ module Sohm
442
386
  #
443
387
  # Example:
444
388
  #
445
- # class Post < Ohm::Model
389
+ # class Post < Sohm::Model
446
390
  # end
447
391
  #
448
- # class User < Ohm::Model
392
+ # class User < Sohm::Model
449
393
  # set :posts, :Post
450
394
  # end
451
395
  #
@@ -459,15 +403,6 @@ module Sohm
459
403
  def exists?(id)
460
404
  execute { |key| redis.call("SISMEMBER", key, id) == 1 }
461
405
  end
462
-
463
- private
464
- def to_key(att)
465
- if model.counters.include?(att)
466
- namespace["*:counters->%s" % att]
467
- else
468
- namespace["*->%s" % att]
469
- end
470
- end
471
406
  end
472
407
 
473
408
  class Set < BasicSet
@@ -583,13 +518,13 @@ module Sohm
583
518
  #
584
519
  # Example:
585
520
  #
586
- # User.all.kind_of?(Ohm::Set)
521
+ # User.all.kind_of?(Sohm::Set)
587
522
  # # => true
588
523
  #
589
- # User.find(:name => "John").kind_of?(Ohm::Set)
524
+ # User.find(:name => "John").kind_of?(Sohm::Set)
590
525
  # # => true
591
526
  #
592
- # User.find(:name => "John", :age => 30).kind_of?(Ohm::MultiSet)
527
+ # User.find(:name => "John", :age => 30).kind_of?(Sohm::MultiSet)
593
528
  # # => true
594
529
  #
595
530
  class MultiSet < BasicSet
@@ -704,7 +639,7 @@ module Sohm
704
639
  #
705
640
  # Example:
706
641
  #
707
- # class User < Ohm::Model
642
+ # class User < Sohm::Model
708
643
  # attribute :name
709
644
  # index :name
710
645
  #
@@ -740,7 +675,7 @@ module Sohm
740
675
  #
741
676
  # Next we increment points:
742
677
  #
743
- # HINCR User:1:counters points 1
678
+ # HINCR User:1:_counters points 1
744
679
  #
745
680
  # And then we add a Post to the `posts` set.
746
681
  # (For brevity, let's assume the Post created has an ID of 1).
@@ -757,7 +692,7 @@ module Sohm
757
692
  end
758
693
 
759
694
  def self.mutex
760
- @@mutex ||= Mutex.new
695
+ Sohm.mutex
761
696
  end
762
697
 
763
698
  def self.synchronize(&block)
@@ -768,7 +703,7 @@ module Sohm
768
703
  #
769
704
  # Example:
770
705
  #
771
- # class User < Ohm::Model
706
+ # class User < Sohm::Model
772
707
  # end
773
708
  #
774
709
  # User.key == "User"
@@ -782,7 +717,7 @@ module Sohm
782
717
  # http://github.com/soveran/nido
783
718
  #
784
719
  def self.key
785
- @key ||= Nido.new(self.name)
720
+ Nido.new(self.name)
786
721
  end
787
722
 
788
723
  # Retrieve a record by ID.
@@ -807,7 +742,7 @@ module Sohm
807
742
  # Note: The use of this should be a last resort for your actual
808
743
  # application runtime, or for simply debugging in your console. If
809
744
  # you care about performance, you should pipeline your reads. For
810
- # more information checkout the implementation of Ohm::List#fetch.
745
+ # more information checkout the implementation of Sohm::List#fetch.
811
746
  #
812
747
  def self.to_proc
813
748
  lambda { |id| self[id] }
@@ -822,7 +757,7 @@ module Sohm
822
757
  #
823
758
  # Example:
824
759
  #
825
- # class User < Ohm::Model
760
+ # class User < Sohm::Model
826
761
  # attribute :email
827
762
  #
828
763
  # attribute :name
@@ -860,9 +795,9 @@ module Sohm
860
795
  keys = filters(dict)
861
796
 
862
797
  if keys.size == 1
863
- Ohm::Set.new(keys.first, key, self)
798
+ Sohm::Set.new(keys.first, key, self)
864
799
  else
865
- Ohm::MultiSet.new(key, self, Command.new(:sinterstore, *keys))
800
+ Sohm::MultiSet.new(key, self, Command.new(:sinterstore, *keys))
866
801
  end
867
802
  end
868
803
 
@@ -882,11 +817,11 @@ module Sohm
882
817
  indices << attribute unless indices.include?(attribute)
883
818
  end
884
819
 
885
- # Declare an Ohm::Set with the given name.
820
+ # Declare an Sohm::Set with the given name.
886
821
  #
887
822
  # Example:
888
823
  #
889
- # class User < Ohm::Model
824
+ # class User < Sohm::Model
890
825
  # set :posts, :Post
891
826
  # end
892
827
  #
@@ -895,7 +830,7 @@ module Sohm
895
830
  # # => true
896
831
  #
897
832
  # Note: You can't use the set until you save the model. If you try
898
- # to do it, you'll receive an Ohm::MissingID error.
833
+ # to do it, you'll receive an Sohm::MissingID error.
899
834
  #
900
835
  def self.set(name, model)
901
836
  track(name)
@@ -903,18 +838,18 @@ module Sohm
903
838
  define_method name do
904
839
  model = Utils.const(self.class, model)
905
840
 
906
- Ohm::MutableSet.new(key[name], model.key, model)
841
+ Sohm::MutableSet.new(key[name], model.key, model)
907
842
  end
908
843
  end
909
844
 
910
- # Declare an Ohm::List with the given name.
845
+ # Declare an Sohm::List with the given name.
911
846
  #
912
847
  # Example:
913
848
  #
914
- # class Comment < Ohm::Model
849
+ # class Comment < Sohm::Model
915
850
  # end
916
851
  #
917
- # class Post < Ohm::Model
852
+ # class Post < Sohm::Model
918
853
  # list :comments, :Comment
919
854
  # end
920
855
  #
@@ -925,7 +860,7 @@ module Sohm
925
860
  # # => true
926
861
  #
927
862
  # Note: You can't use the list until you save the model. If you try
928
- # to do it, you'll receive an Ohm::MissingID error.
863
+ # to do it, you'll receive an Sohm::MissingID error.
929
864
  #
930
865
  def self.list(name, model)
931
866
  track(name)
@@ -933,24 +868,24 @@ module Sohm
933
868
  define_method name do
934
869
  model = Utils.const(self.class, model)
935
870
 
936
- Ohm::List.new(key[name], model.key, model)
871
+ Sohm::List.new(key[name], model.key, model)
937
872
  end
938
873
  end
939
874
 
940
875
  # A macro for defining a method which basically does a find.
941
876
  #
942
877
  # Example:
943
- # class Post < Ohm::Model
878
+ # class Post < Sohm::Model
944
879
  # reference :user, :User
945
880
  # end
946
881
  #
947
- # class User < Ohm::Model
882
+ # class User < Sohm::Model
948
883
  # collection :posts, :Post
949
884
  # end
950
885
  #
951
886
  # # is the same as
952
887
  #
953
- # class User < Ohm::Model
888
+ # class User < Sohm::Model
954
889
  # def posts
955
890
  # Post.find(:user_id => self.id)
956
891
  # end
@@ -968,27 +903,25 @@ module Sohm
968
903
  #
969
904
  # Example:
970
905
  #
971
- # class Post < Ohm::Model
906
+ # class Post < Sohm::Model
972
907
  # reference :user, :User
973
908
  # end
974
909
  #
975
910
  # # It's the same as:
976
911
  #
977
- # class Post < Ohm::Model
912
+ # class Post < Sohm::Model
978
913
  # attribute :user_id
979
914
  # index :user_id
980
915
  #
981
916
  # def user
982
- # @_memo[:user] ||= User[user_id]
917
+ # User[user_id]
983
918
  # end
984
919
  #
985
920
  # def user=(user)
986
921
  # self.user_id = user.id
987
- # @_memo[:user] = user
988
922
  # end
989
923
  #
990
924
  # def user_id=(user_id)
991
- # @_memo.delete(:user_id)
992
925
  # self.user_id = user_id
993
926
  # end
994
927
  # end
@@ -1006,20 +939,16 @@ module Sohm
1006
939
  end
1007
940
 
1008
941
  define_method(writer) do |value|
1009
- @_memo.delete(name)
1010
942
  @attributes[reader] = value
1011
943
  end
1012
944
 
1013
945
  define_method(:"#{name}=") do |value|
1014
- @_memo.delete(name)
1015
946
  send(writer, value ? value.id : nil)
1016
947
  end
1017
948
 
1018
949
  define_method(name) do
1019
- @_memo[name] ||= begin
1020
- model = Utils.const(self.class, model)
1021
- model[send(reader)]
1022
- end
950
+ model = Utils.const(self.class, model)
951
+ model[send(reader)]
1023
952
  end
1024
953
  end
1025
954
 
@@ -1027,7 +956,7 @@ module Sohm
1027
956
  # persisted attributes. All attributes are stored on the Redis
1028
957
  # hash.
1029
958
  #
1030
- # class User < Ohm::Model
959
+ # class User < Sohm::Model
1031
960
  # attribute :name
1032
961
  # end
1033
962
  #
@@ -1042,7 +971,7 @@ module Sohm
1042
971
  # A +lambda+ can be passed as a second parameter to add
1043
972
  # typecasting support to the attribute.
1044
973
  #
1045
- # class User < Ohm::Model
974
+ # class User < Sohm::Model
1046
975
  # attribute :age, ->(x) { x.to_i }
1047
976
  # end
1048
977
  #
@@ -1089,10 +1018,15 @@ module Sohm
1089
1018
 
1090
1019
  if cast
1091
1020
  define_method(name) do
1021
+ # NOTE: This is a temporary solution, since we might use
1022
+ # composite objects (such as arrays), which won't always
1023
+ # do a reset
1024
+ @serial_attributes_changed = true
1092
1025
  cast[@serial_attributes[name]]
1093
1026
  end
1094
1027
  else
1095
1028
  define_method(name) do
1029
+ @serial_attributes_changed = true
1096
1030
  @serial_attributes[name]
1097
1031
  end
1098
1032
  end
@@ -1111,7 +1045,7 @@ module Sohm
1111
1045
  #
1112
1046
  # Example:
1113
1047
  #
1114
- # class User < Ohm::Model
1048
+ # class User < Sohm::Model
1115
1049
  # counter :points
1116
1050
  # end
1117
1051
  #
@@ -1122,7 +1056,7 @@ module Sohm
1122
1056
  # # => 1
1123
1057
  #
1124
1058
  # Note: You can't use counters until you save the model. If you
1125
- # try to do it, you'll receive an Ohm::MissingID error.
1059
+ # try to do it, you'll receive an Sohm::MissingID error.
1126
1060
  #
1127
1061
  def self.counter(name)
1128
1062
  counters << name unless counters.include?(name)
@@ -1130,7 +1064,7 @@ module Sohm
1130
1064
  define_method(name) do
1131
1065
  return 0 if new?
1132
1066
 
1133
- redis.call("HGET", key[:counters], name).to_i
1067
+ redis.call("HGET", key[:_counters], name).to_i
1134
1068
  end
1135
1069
  end
1136
1070
 
@@ -1142,11 +1076,11 @@ module Sohm
1142
1076
  # Create a new model, notice that under Sohm's circumstances,
1143
1077
  # this is no longer a syntactic sugar for Model.new(atts).save
1144
1078
  def self.create(atts = {})
1145
- new(atts).save(create: true)
1079
+ new(atts).save
1146
1080
  end
1147
1081
 
1148
1082
  # Returns the namespace for the keys generated using this model.
1149
- # Check `Ohm::Model.key` documentation for more details.
1083
+ # Check `Sohm::Model.key` documentation for more details.
1150
1084
  def key
1151
1085
  model.key[id]
1152
1086
  end
@@ -1160,7 +1094,6 @@ module Sohm
1160
1094
  def initialize(atts = {})
1161
1095
  @attributes = {}
1162
1096
  @serial_attributes = {}
1163
- @_memo = {}
1164
1097
  @serial_attributes_changed = false
1165
1098
  update_attributes(atts)
1166
1099
  end
@@ -1170,7 +1103,7 @@ module Sohm
1170
1103
  #
1171
1104
  # Example:
1172
1105
  #
1173
- # class User < Ohm::Model; end
1106
+ # class User < Sohm::Model; end
1174
1107
  #
1175
1108
  # u = User.create
1176
1109
  # u.id
@@ -1201,52 +1134,16 @@ module Sohm
1201
1134
  # Preload all the attributes of this model from Redis. Used
1202
1135
  # internally by `Model::[]`.
1203
1136
  def load!
1204
- update_attributes(Utils.dict(redis.call("HGETALL", key))) unless new?
1137
+ update_attributes(Utils.dict(redis.call("HGETALL", key))) if id
1205
1138
  @serial_attributes_changed = false
1206
1139
  return self
1207
1140
  end
1208
1141
 
1209
- # Read an attribute remotely from Redis. Useful if you want to get
1210
- # the most recent value of the attribute and not rely on locally
1211
- # cached value.
1212
- #
1213
- # Example:
1214
- #
1215
- # User.create(:name => "A")
1216
- #
1217
- # Session 1 | Session 2
1218
- # --------------|------------------------
1219
- # u = User[1] | u = User[1]
1220
- # u.name = "B" |
1221
- # u.save |
1222
- # | u.name == "A"
1223
- # | u.get(:name) == "B"
1224
- #
1225
- def get(att)
1226
- @attributes[att] = redis.call("HGET", key, att)
1227
- end
1228
-
1229
- # Update an attribute value atomically. The best usecase for this
1230
- # is when you simply want to update one value.
1231
- #
1232
- # Note: This method is dangerous because it doesn't update indices
1233
- # and uniques. Use it wisely. The safe equivalent is `update`.
1234
- #
1235
- def set(att, val)
1236
- if val.to_s.empty?
1237
- redis.call("HDEL", key, att)
1238
- else
1239
- redis.call("HSET", key, att, val)
1240
- end
1241
-
1242
- @attributes[att] = val
1243
- end
1244
-
1245
1142
  # Returns +true+ if the model is not persisted. Otherwise, returns +false+.
1246
1143
  #
1247
1144
  # Example:
1248
1145
  #
1249
- # class User < Ohm::Model
1146
+ # class User < Sohm::Model
1250
1147
  # attribute :name
1251
1148
  # end
1252
1149
  #
@@ -1258,12 +1155,12 @@ module Sohm
1258
1155
  # u.new?
1259
1156
  # # => false
1260
1157
  def new?
1261
- !model.exists?(id)
1158
+ !(defined?(@id) && model.exists?(id))
1262
1159
  end
1263
1160
 
1264
1161
  # Increment a counter atomically. Internally uses HINCRBY.
1265
1162
  def incr(att, count = 1)
1266
- redis.call("HINCRBY", key[:counters], att, count)
1163
+ redis.call("HINCRBY", key[:_counters], att, count)
1267
1164
  end
1268
1165
 
1269
1166
  # Decrement a counter atomically. Internally uses HINCRBY.
@@ -1294,7 +1191,7 @@ module Sohm
1294
1191
  #
1295
1192
  # Example:
1296
1193
  #
1297
- # class User < Ohm::Model
1194
+ # class User < Sohm::Model
1298
1195
  # attribute :name
1299
1196
  # end
1300
1197
  #
@@ -1316,7 +1213,7 @@ module Sohm
1316
1213
  #
1317
1214
  # Example:
1318
1215
  #
1319
- # class User < Ohm::Model
1216
+ # class User < Sohm::Model
1320
1217
  # attribute :name
1321
1218
  # end
1322
1219
  #
@@ -1326,7 +1223,7 @@ module Sohm
1326
1223
  #
1327
1224
  # In order to add additional attributes, you can override `to_hash`:
1328
1225
  #
1329
- # class User < Ohm::Model
1226
+ # class User < Sohm::Model
1330
1227
  # attribute :name
1331
1228
  #
1332
1229
  # def to_hash
@@ -1351,7 +1248,7 @@ module Sohm
1351
1248
  #
1352
1249
  # Example:
1353
1250
  #
1354
- # class User < Ohm::Model
1251
+ # class User < Sohm::Model
1355
1252
  # attribute :name
1356
1253
  # end
1357
1254
  #
@@ -1362,7 +1259,7 @@ module Sohm
1362
1259
  def save
1363
1260
  if serial_attributes_changed
1364
1261
  response = script(LUA_SAVE, 1, key,
1365
- serial_attributes.to_msgpack,
1262
+ sanitize_attributes(serial_attributes).to_msgpack,
1366
1263
  cas_token)
1367
1264
 
1368
1265
  if response.is_a?(RuntimeError)
@@ -1377,7 +1274,8 @@ module Sohm
1377
1274
  @serial_attributes_changed = false
1378
1275
  end
1379
1276
 
1380
- redis.call("HSET", key, "_ndata", attributes.to_msgpack)
1277
+ redis.call("HSET", key, "_ndata",
1278
+ sanitize_attributes(attributes).to_msgpack)
1381
1279
 
1382
1280
  refresh_indices
1383
1281
 
@@ -1387,18 +1285,21 @@ module Sohm
1387
1285
  # Delete the model, including all the following keys:
1388
1286
  #
1389
1287
  # - <Model>:<id>
1390
- # - <Model>:<id>:counters
1288
+ # - <Model>:<id>:_counters
1391
1289
  # - <Model>:<id>:<set name>
1392
1290
  #
1393
1291
  # If the model has uniques or indices, they're also cleaned up.
1394
1292
  #
1395
1293
  def delete
1396
1294
  memo_key = key["_indices"]
1397
- commands = [["DEL", key], ["DEL", memo_key]]
1295
+ commands = [["DEL", key], ["DEL", memo_key], ["DEL", key["_counters"]]]
1398
1296
  index_list = redis.call("SMEMBERS", memo_key)
1399
1297
  index_list.each do |index_key|
1400
1298
  commands << ["SREM", index_key, id]
1401
1299
  end
1300
+ model.tracked.each do |tracked_key|
1301
+ commands << ["DEL", key[tracked_key]]
1302
+ end
1402
1303
 
1403
1304
  model.synchronize do
1404
1305
  commands.each do |command|
@@ -1459,24 +1360,33 @@ module Sohm
1459
1360
  downcase.to_sym
1460
1361
  end
1461
1362
 
1363
+ # Workaround to JRuby's concurrency problem
1364
+ def self.inherited(subclass)
1365
+ subclass.instance_variable_set(:@indices, [])
1366
+ subclass.instance_variable_set(:@counters, [])
1367
+ subclass.instance_variable_set(:@tracked, [])
1368
+ subclass.instance_variable_set(:@attributes, [])
1369
+ subclass.instance_variable_set(:@serial_attributes, [])
1370
+ end
1371
+
1462
1372
  def self.indices
1463
- @indices ||= []
1373
+ @indices
1464
1374
  end
1465
1375
 
1466
1376
  def self.counters
1467
- @counters ||= []
1377
+ @counters
1468
1378
  end
1469
1379
 
1470
1380
  def self.tracked
1471
- @tracked ||= []
1381
+ @tracked
1472
1382
  end
1473
1383
 
1474
1384
  def self.attributes
1475
- @attributes ||= []
1385
+ @attributes
1476
1386
  end
1477
1387
 
1478
1388
  def self.serial_attributes
1479
- @serial_attributes ||= []
1389
+ @serial_attributes
1480
1390
  end
1481
1391
 
1482
1392
  def self.filters(dict)
@@ -1493,9 +1403,9 @@ module Sohm
1493
1403
  raise IndexNotFound unless indices.include?(att)
1494
1404
 
1495
1405
  if val.kind_of?(Enumerable)
1496
- val.map { |v| key[:indices][att][v] }
1406
+ val.map { |v| key[:_indices][att][v] }
1497
1407
  else
1498
- [key[:indices][att][val]]
1408
+ [key[:_indices][att][val]]
1499
1409
  end
1500
1410
  end
1501
1411
 
@@ -1511,7 +1421,7 @@ module Sohm
1511
1421
  # Add new indices first
1512
1422
  commands = fetch_indices.each_pair.map do |field, vals|
1513
1423
  vals.map do |val|
1514
- index_key = key["_indices"][field][val]
1424
+ index_key = model.key["_indices"][field][val]
1515
1425
  [["SADD", memo_key, index_key], ["SADD", index_key, id]]
1516
1426
  end
1517
1427
  end.flatten(2)
@@ -1529,7 +1439,7 @@ module Sohm
1529
1439
  index_set = ::Set.new(redis.call("SMEMBERS", memo_key))
1530
1440
  valid_list = model[id].send(:fetch_indices).each_pair.map do |field, vals|
1531
1441
  vals.map do |val|
1532
- key["_indices"][field][val]
1442
+ model.key["_indices"][field][val]
1533
1443
  end
1534
1444
  end.flatten(1)
1535
1445
  valid_set = ::Set.new(valid_list)
@@ -1565,6 +1475,10 @@ module Sohm
1565
1475
  attrs
1566
1476
  end
1567
1477
 
1478
+ def sanitize_attributes(attributes)
1479
+ attributes.select { |key, val| val }
1480
+ end
1481
+
1568
1482
  def model
1569
1483
  self.class
1570
1484
  end