sohm 0.0.1 → 0.9.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.
- checksums.yaml +4 -4
- data/.travis.gemfile +9 -0
- data/.travis.yml +10 -0
- data/README.md +2 -0
- data/lib/sohm/auto_id.rb +13 -0
- data/lib/sohm/command.rb +1 -1
- data/lib/sohm/index_all.rb +18 -0
- data/lib/sohm/json.rb +1 -1
- data/lib/sohm.rb +121 -207
- data/sohm.gemspec +1 -1
- data/test/association.rb +6 -12
- data/test/command.rb +6 -6
- data/test/connection.rb +5 -5
- data/test/core.rb +4 -1
- data/test/counters.rb +2 -1
- data/test/enumerable.rb +9 -3
- data/test/filtering.rb +10 -13
- data/test/hash_key.rb +3 -1
- data/test/helper.rb +5 -3
- data/test/indices.rb +6 -36
- data/test/json.rb +8 -3
- data/test/list.rb +6 -11
- data/test/model.rb +100 -179
- data/test/set.rb +6 -2
- data/test/sohm.rb +44 -0
- data/test/to_hash.rb +5 -2
- metadata +7 -6
- data/lib/sample.rb +0 -14
- data/lib/sohm/lua/delete.lua +0 -72
- data/test/thread_safety.rb +0 -67
- data/test/uniques.rb +0 -98
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 `
|
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 <
|
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 <
|
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
|
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
|
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 <
|
199
|
+
# class Comment < Sohm::Model
|
199
200
|
# end
|
200
201
|
#
|
201
|
-
# class Post <
|
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 <
|
250
|
+
# class Comment < Sohm::Model
|
250
251
|
# end
|
251
252
|
#
|
252
|
-
# class Post <
|
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 <
|
276
|
+
# class Comment < Sohm::Model
|
276
277
|
# end
|
277
278
|
#
|
278
|
-
# class Post <
|
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
|
-
#
|
381
|
-
#
|
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
|
-
#
|
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
|
-
#
|
386
|
-
#
|
387
|
-
#
|
388
|
-
|
389
|
-
|
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 <
|
348
|
+
# class Post < Sohm::Model
|
405
349
|
# end
|
406
350
|
#
|
407
|
-
# class User <
|
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 <
|
389
|
+
# class Post < Sohm::Model
|
446
390
|
# end
|
447
391
|
#
|
448
|
-
# class User <
|
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?(
|
521
|
+
# User.all.kind_of?(Sohm::Set)
|
587
522
|
# # => true
|
588
523
|
#
|
589
|
-
# User.find(:name => "John").kind_of?(
|
524
|
+
# User.find(:name => "John").kind_of?(Sohm::Set)
|
590
525
|
# # => true
|
591
526
|
#
|
592
|
-
# User.find(:name => "John", :age => 30).kind_of?(
|
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 <
|
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:
|
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
|
-
|
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 <
|
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
|
-
|
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
|
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 <
|
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
|
-
|
798
|
+
Sohm::Set.new(keys.first, key, self)
|
864
799
|
else
|
865
|
-
|
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
|
820
|
+
# Declare an Sohm::Set with the given name.
|
886
821
|
#
|
887
822
|
# Example:
|
888
823
|
#
|
889
|
-
# class User <
|
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
|
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
|
-
|
841
|
+
Sohm::MutableSet.new(key[name], model.key, model)
|
907
842
|
end
|
908
843
|
end
|
909
844
|
|
910
|
-
# Declare an
|
845
|
+
# Declare an Sohm::List with the given name.
|
911
846
|
#
|
912
847
|
# Example:
|
913
848
|
#
|
914
|
-
# class Comment <
|
849
|
+
# class Comment < Sohm::Model
|
915
850
|
# end
|
916
851
|
#
|
917
|
-
# class Post <
|
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
|
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
|
-
|
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 <
|
878
|
+
# class Post < Sohm::Model
|
944
879
|
# reference :user, :User
|
945
880
|
# end
|
946
881
|
#
|
947
|
-
# class User <
|
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 <
|
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 <
|
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 <
|
912
|
+
# class Post < Sohm::Model
|
978
913
|
# attribute :user_id
|
979
914
|
# index :user_id
|
980
915
|
#
|
981
916
|
# def user
|
982
|
-
#
|
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
|
-
|
1020
|
-
|
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 <
|
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 <
|
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 <
|
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
|
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[:
|
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
|
1079
|
+
new(atts).save
|
1146
1080
|
end
|
1147
1081
|
|
1148
1082
|
# Returns the namespace for the keys generated using this model.
|
1149
|
-
# Check `
|
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 <
|
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)))
|
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 <
|
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[:
|
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 <
|
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 <
|
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 <
|
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 <
|
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",
|
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>:
|
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[:
|
1406
|
+
val.map { |v| key[:_indices][att][v] }
|
1497
1407
|
else
|
1498
|
-
[key[:
|
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
|