familia 1.0.0.pre.rc7 → 1.2.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/.gitignore +1 -0
- data/.pre-commit-config.yaml +1 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +3 -1
- data/README.md +5 -5
- data/VERSION.yml +1 -2
- data/familia.gemspec +1 -2
- data/lib/familia/base.rb +11 -11
- data/lib/familia/connection.rb +18 -2
- data/lib/familia/errors.rb +14 -0
- data/lib/familia/features/expiration.rb +13 -2
- data/lib/familia/features/quantization.rb +1 -1
- data/lib/familia/horreum/class_methods.rb +31 -21
- data/lib/familia/horreum/commands.rb +58 -15
- data/lib/familia/horreum/relations_management.rb +9 -2
- data/lib/familia/horreum/serialization.rb +130 -29
- data/lib/familia/horreum.rb +69 -19
- data/lib/familia/redistype/commands.rb +5 -2
- data/lib/familia/redistype/serialization.rb +17 -16
- data/lib/familia/redistype/types/hashkey.rb +167 -0
- data/lib/familia/{types → redistype/types}/list.rb +19 -14
- data/lib/familia/{types → redistype/types}/sorted_set.rb +22 -19
- data/lib/familia/{types → redistype/types}/string.rb +8 -6
- data/lib/familia/{types → redistype/types}/unsorted_set.rb +16 -12
- data/lib/familia/redistype.rb +19 -9
- data/lib/familia.rb +6 -1
- data/try/10_familia_try.rb +1 -1
- data/try/20_redis_type_try.rb +1 -1
- data/try/21_redis_type_zset_try.rb +1 -1
- data/try/22_redis_type_set_try.rb +1 -1
- data/try/23_redis_type_list_try.rb +2 -2
- data/try/24_redis_type_string_try.rb +3 -3
- data/try/25_redis_type_hash_try.rb +1 -1
- data/try/26_redis_bool_try.rb +2 -2
- data/try/27_redis_horreum_try.rb +2 -2
- data/try/28_redis_horreum_serialization_try.rb +159 -0
- data/try/29_redis_horreum_initialization_try.rb +113 -0
- data/try/30_familia_object_try.rb +8 -5
- data/try/40_customer_try.rb +6 -6
- data/try/91_json_bug_try.rb +86 -0
- data/try/92_symbolize_try.rb +96 -0
- data/try/93_string_coercion_try.rb +154 -0
- metadata +20 -15
- data/lib/familia/types/hashkey.rb +0 -108
@@ -2,18 +2,21 @@
|
|
2
2
|
|
3
3
|
module Familia
|
4
4
|
class List < RedisType
|
5
|
-
|
5
|
+
|
6
|
+
# Returns the number of elements in the list
|
7
|
+
# @return [Integer] number of elements
|
8
|
+
def element_count
|
6
9
|
redis.llen rediskey
|
7
10
|
end
|
8
|
-
alias
|
11
|
+
alias size element_count
|
9
12
|
|
10
13
|
def empty?
|
11
|
-
|
14
|
+
element_count.zero?
|
12
15
|
end
|
13
16
|
|
14
17
|
def push *values
|
15
18
|
echo :push, caller(1..1).first if Familia.debug
|
16
|
-
values.flatten.compact.each { |v| redis.rpush rediskey,
|
19
|
+
values.flatten.compact.each { |v| redis.rpush rediskey, serialize_value(v) }
|
17
20
|
redis.ltrim rediskey, -@opts[:maxlength], -1 if @opts[:maxlength]
|
18
21
|
update_expiration
|
19
22
|
self
|
@@ -26,7 +29,7 @@ module Familia
|
|
26
29
|
alias add <<
|
27
30
|
|
28
31
|
def unshift *values
|
29
|
-
values.flatten.compact.each { |v| redis.lpush rediskey,
|
32
|
+
values.flatten.compact.each { |v| redis.lpush rediskey, serialize_value(v) }
|
30
33
|
# TODO: test maxlength
|
31
34
|
redis.ltrim rediskey, 0, @opts[:maxlength] - 1 if @opts[:maxlength]
|
32
35
|
update_expiration
|
@@ -35,11 +38,11 @@ module Familia
|
|
35
38
|
alias prepend unshift
|
36
39
|
|
37
40
|
def pop
|
38
|
-
|
41
|
+
deserialize_value redis.rpop(rediskey)
|
39
42
|
end
|
40
43
|
|
41
44
|
def shift
|
42
|
-
|
45
|
+
deserialize_value redis.lpop(rediskey)
|
43
46
|
end
|
44
47
|
|
45
48
|
def [](idx, count = nil)
|
@@ -57,16 +60,18 @@ module Familia
|
|
57
60
|
end
|
58
61
|
alias slice []
|
59
62
|
|
60
|
-
|
61
|
-
|
63
|
+
# Removes elements equal to value from the list
|
64
|
+
# @param value The value to remove
|
65
|
+
# @param count [Integer] Number of elements to remove (0 means all)
|
66
|
+
# @return [Integer] The number of removed elements
|
67
|
+
def remove_element(value, count = 0)
|
68
|
+
redis.lrem rediskey, count, serialize_value(value)
|
62
69
|
end
|
63
|
-
alias remove
|
64
|
-
alias rem delete
|
65
|
-
alias del delete
|
70
|
+
alias remove remove_element
|
66
71
|
|
67
72
|
def range(sidx = 0, eidx = -1)
|
68
73
|
elements = rangeraw sidx, eidx
|
69
|
-
|
74
|
+
deserialize_values(*elements)
|
70
75
|
end
|
71
76
|
|
72
77
|
def rangeraw(sidx = 0, eidx = -1)
|
@@ -119,7 +124,7 @@ module Familia
|
|
119
124
|
end
|
120
125
|
|
121
126
|
def at(idx)
|
122
|
-
|
127
|
+
deserialize_value redis.lindex(rediskey, idx)
|
123
128
|
end
|
124
129
|
|
125
130
|
def first
|
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
module Familia
|
4
4
|
class SortedSet < RedisType
|
5
|
-
|
5
|
+
# Returns the number of elements in the sorted set
|
6
|
+
# @return [Integer] number of elements
|
7
|
+
def element_count
|
6
8
|
redis.zcard rediskey
|
7
9
|
end
|
8
|
-
alias
|
10
|
+
alias size element_count
|
9
11
|
|
10
12
|
def empty?
|
11
|
-
|
13
|
+
element_count.zero?
|
12
14
|
end
|
13
15
|
|
14
16
|
# Adds a new element to the sorted set with the current timestamp as the
|
@@ -44,13 +46,13 @@ module Familia
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def add(score, val)
|
47
|
-
ret = redis.zadd rediskey, score,
|
49
|
+
ret = redis.zadd rediskey, score, serialize_value(val)
|
48
50
|
update_expiration
|
49
51
|
ret
|
50
52
|
end
|
51
53
|
|
52
54
|
def score(val)
|
53
|
-
ret = redis.zscore rediskey,
|
55
|
+
ret = redis.zscore rediskey, serialize_value(val, strict_values: false)
|
54
56
|
ret&.to_f
|
55
57
|
end
|
56
58
|
alias [] score
|
@@ -63,20 +65,20 @@ module Familia
|
|
63
65
|
|
64
66
|
# rank of member +v+ when ordered lowest to highest (starts at 0)
|
65
67
|
def rank(v)
|
66
|
-
ret = redis.zrank rediskey,
|
68
|
+
ret = redis.zrank rediskey, serialize_value(v, strict_values: false)
|
67
69
|
ret&.to_i
|
68
70
|
end
|
69
71
|
|
70
72
|
# rank of member +v+ when ordered highest to lowest (starts at 0)
|
71
73
|
def revrank(v)
|
72
|
-
ret = redis.zrevrank rediskey,
|
74
|
+
ret = redis.zrevrank rediskey, serialize_value(v, strict_values: false)
|
73
75
|
ret&.to_i
|
74
76
|
end
|
75
77
|
|
76
78
|
def members(count = -1, opts = {})
|
77
79
|
count -= 1 if count.positive?
|
78
80
|
elements = membersraw count, opts
|
79
|
-
|
81
|
+
deserialize_values(*elements)
|
80
82
|
end
|
81
83
|
alias to_a members
|
82
84
|
alias all members
|
@@ -89,7 +91,7 @@ module Familia
|
|
89
91
|
def revmembers(count = -1, opts = {})
|
90
92
|
count -= 1 if count.positive?
|
91
93
|
elements = revmembersraw count, opts
|
92
|
-
|
94
|
+
deserialize_values(*elements)
|
93
95
|
end
|
94
96
|
|
95
97
|
def revmembersraw(count = -1, opts = {})
|
@@ -132,7 +134,7 @@ module Familia
|
|
132
134
|
def range(sidx, eidx, opts = {})
|
133
135
|
echo :range, caller(1..1).first if Familia.debug
|
134
136
|
elements = rangeraw(sidx, eidx, opts)
|
135
|
-
|
137
|
+
deserialize_values(*elements)
|
136
138
|
end
|
137
139
|
|
138
140
|
def rangeraw(sidx, eidx, opts = {})
|
@@ -149,7 +151,7 @@ module Familia
|
|
149
151
|
def revrange(sidx, eidx, opts = {})
|
150
152
|
echo :revrange, caller(1..1).first if Familia.debug
|
151
153
|
elements = revrangeraw(sidx, eidx, opts)
|
152
|
-
|
154
|
+
deserialize_values(*elements)
|
153
155
|
end
|
154
156
|
|
155
157
|
def revrangeraw(sidx, eidx, opts = {})
|
@@ -160,7 +162,7 @@ module Familia
|
|
160
162
|
def rangebyscore(sscore, escore, opts = {})
|
161
163
|
echo :rangebyscore, caller(1..1).first if Familia.debug
|
162
164
|
elements = rangebyscoreraw(sscore, escore, opts)
|
163
|
-
|
165
|
+
deserialize_values(*elements)
|
164
166
|
end
|
165
167
|
|
166
168
|
def rangebyscoreraw(sscore, escore, opts = {})
|
@@ -172,7 +174,7 @@ module Familia
|
|
172
174
|
def revrangebyscore(sscore, escore, opts = {})
|
173
175
|
echo :revrangebyscore, caller(1..1).first if Familia.debug
|
174
176
|
elements = revrangebyscoreraw(sscore, escore, opts)
|
175
|
-
|
177
|
+
deserialize_values(*elements)
|
176
178
|
end
|
177
179
|
|
178
180
|
def revrangebyscoreraw(sscore, escore, opts = {})
|
@@ -201,18 +203,19 @@ module Familia
|
|
201
203
|
alias decr decrement
|
202
204
|
alias decrby decrement
|
203
205
|
|
204
|
-
|
205
|
-
|
206
|
+
# Removes a member from the sorted set
|
207
|
+
# @param value The value to remove from the sorted set
|
208
|
+
# @return [Integer] The number of members that were removed (0 or 1)
|
209
|
+
def remove_element(value)
|
210
|
+
Familia.trace :REMOVE_ELEMENT, redis, "#{value}<#{value.class}>", caller(1..1) if Familia.debug?
|
206
211
|
# We use `strict_values: false` here to allow for the deletion of values
|
207
212
|
# that are in the sorted set. If it's a horreum object, the value is
|
208
213
|
# the identifier and not a serialized version of the object. So either
|
209
214
|
# the value exists in the sorted set or it doesn't -- we don't need to
|
210
215
|
# raise an error if it's not found.
|
211
|
-
redis.zrem rediskey,
|
216
|
+
redis.zrem rediskey, serialize_value(value, strict_values: false)
|
212
217
|
end
|
213
|
-
alias remove
|
214
|
-
alias rem delete
|
215
|
-
alias del delete
|
218
|
+
alias remove remove_element # deprecated
|
216
219
|
|
217
220
|
def at(idx)
|
218
221
|
range(idx, idx).first
|
@@ -4,19 +4,21 @@ module Familia
|
|
4
4
|
class String < RedisType
|
5
5
|
def init; end
|
6
6
|
|
7
|
-
|
7
|
+
# Returns the number of elements in the list
|
8
|
+
# @return [Integer] number of elements
|
9
|
+
def char_count
|
8
10
|
to_s.size
|
9
11
|
end
|
10
|
-
alias
|
12
|
+
alias size char_count
|
11
13
|
|
12
14
|
def empty?
|
13
|
-
|
15
|
+
char_count.zero?
|
14
16
|
end
|
15
17
|
|
16
18
|
def value
|
17
19
|
echo :value, caller(0..0) if Familia.debug
|
18
20
|
redis.setnx rediskey, @opts[:default] if @opts[:default]
|
19
|
-
|
21
|
+
deserialize_value redis.get(rediskey)
|
20
22
|
end
|
21
23
|
alias content value
|
22
24
|
alias get value
|
@@ -30,7 +32,7 @@ module Familia
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def value=(val)
|
33
|
-
ret = redis.set(rediskey,
|
35
|
+
ret = redis.set(rediskey, serialize_value(val))
|
34
36
|
update_expiration
|
35
37
|
ret
|
36
38
|
end
|
@@ -38,7 +40,7 @@ module Familia
|
|
38
40
|
alias set value=
|
39
41
|
|
40
42
|
def setnx(val)
|
41
|
-
ret = redis.setnx(rediskey,
|
43
|
+
ret = redis.setnx(rediskey, serialize_value(val))
|
42
44
|
update_expiration
|
43
45
|
ret
|
44
46
|
end
|
@@ -2,17 +2,20 @@
|
|
2
2
|
|
3
3
|
module Familia
|
4
4
|
class Set < RedisType
|
5
|
-
|
5
|
+
|
6
|
+
# Returns the number of elements in the unsorted set
|
7
|
+
# @return [Integer] number of elements
|
8
|
+
def element_count
|
6
9
|
redis.scard rediskey
|
7
10
|
end
|
8
|
-
alias
|
11
|
+
alias size element_count
|
9
12
|
|
10
13
|
def empty?
|
11
|
-
|
14
|
+
element_count.zero?
|
12
15
|
end
|
13
16
|
|
14
17
|
def add *values
|
15
|
-
values.flatten.compact.each { |v| redis.sadd? rediskey,
|
18
|
+
values.flatten.compact.each { |v| redis.sadd? rediskey, serialize_value(v) }
|
16
19
|
update_expiration
|
17
20
|
self
|
18
21
|
end
|
@@ -24,7 +27,7 @@ module Familia
|
|
24
27
|
def members
|
25
28
|
echo :members, caller(1..1).first if Familia.debug
|
26
29
|
elements = membersraw
|
27
|
-
|
30
|
+
deserialize_values(*elements)
|
28
31
|
end
|
29
32
|
alias all members
|
30
33
|
alias to_a members
|
@@ -66,16 +69,17 @@ module Familia
|
|
66
69
|
end
|
67
70
|
|
68
71
|
def member?(val)
|
69
|
-
redis.sismember rediskey,
|
72
|
+
redis.sismember rediskey, serialize_value(val)
|
70
73
|
end
|
71
74
|
alias include? member?
|
72
75
|
|
73
|
-
|
74
|
-
|
76
|
+
# Removes a member from the set
|
77
|
+
# @param value The value to remove from the set
|
78
|
+
# @return [Integer] The number of members that were removed (0 or 1)
|
79
|
+
def remove_element(value)
|
80
|
+
redis.srem rediskey, serialize_value(value)
|
75
81
|
end
|
76
|
-
alias remove
|
77
|
-
alias rem delete
|
78
|
-
alias del delete
|
82
|
+
alias remove remove_element # deprecated
|
79
83
|
|
80
84
|
def intersection *setkeys
|
81
85
|
# TODO
|
@@ -90,7 +94,7 @@ module Familia
|
|
90
94
|
end
|
91
95
|
|
92
96
|
def random
|
93
|
-
|
97
|
+
deserialize_value randomraw
|
94
98
|
end
|
95
99
|
|
96
100
|
def randomraw
|
data/lib/familia/redistype.rb
CHANGED
@@ -16,17 +16,19 @@ module Familia
|
|
16
16
|
extend Familia::Features
|
17
17
|
|
18
18
|
@registered_types = {}
|
19
|
-
@valid_options = %i[class parent ttl default db key redis suffix]
|
19
|
+
@valid_options = %i[class parent ttl default db key redis suffix prefix]
|
20
20
|
@db = nil
|
21
21
|
|
22
22
|
feature :expiration
|
23
23
|
feature :quantization
|
24
24
|
|
25
25
|
class << self
|
26
|
-
attr_reader :registered_types, :valid_options
|
26
|
+
attr_reader :registered_types, :valid_options, :has_relations
|
27
27
|
attr_accessor :parent
|
28
28
|
attr_writer :db, :uri
|
29
|
+
end
|
29
30
|
|
31
|
+
module ClassMethods
|
30
32
|
# To be called inside every class that inherits RedisType
|
31
33
|
# +methname+ is the term used for the class and instance methods
|
32
34
|
# that are created for the given +klass+ (e.g. set, list, etc)
|
@@ -58,7 +60,12 @@ module Familia
|
|
58
60
|
def valid_keys_only(opts)
|
59
61
|
opts.select { |k, _| RedisType.valid_options.include? k }
|
60
62
|
end
|
63
|
+
|
64
|
+
def has_relations?
|
65
|
+
@has_relations ||= false
|
66
|
+
end
|
61
67
|
end
|
68
|
+
extend ClassMethods
|
62
69
|
|
63
70
|
attr_reader :keystring, :parent, :opts
|
64
71
|
attr_writer :dump_method, :load_method
|
@@ -89,8 +96,11 @@ module Familia
|
|
89
96
|
# :key => a hardcoded key to use instead of the deriving the from
|
90
97
|
# the name and parent (e.g. a derived key: customer:custid:secret_counter).
|
91
98
|
#
|
92
|
-
#
|
93
|
-
#
|
99
|
+
# :suffix => the suffix to use for the key (e.g. 'scores' in customer:custid:scores).
|
100
|
+
# :prefix => the prefix to use for the key (e.g. 'customer' in customer:custid:scores).
|
101
|
+
#
|
102
|
+
# Connection precendence: uses the redis connection of the parent or the
|
103
|
+
# value of opts[:redis] or Familia.redis (in that order).
|
94
104
|
def initialize(keystring, opts = {})
|
95
105
|
#Familia.ld " [initializing] #{self.class} #{opts}"
|
96
106
|
@keystring = keystring
|
@@ -210,9 +220,9 @@ module Familia
|
|
210
220
|
include Serialization
|
211
221
|
end
|
212
222
|
|
213
|
-
require_relative 'types/list'
|
214
|
-
require_relative 'types/unsorted_set'
|
215
|
-
require_relative 'types/sorted_set'
|
216
|
-
require_relative 'types/hashkey'
|
217
|
-
require_relative 'types/string'
|
223
|
+
require_relative 'redistype/types/list'
|
224
|
+
require_relative 'redistype/types/unsorted_set'
|
225
|
+
require_relative 'redistype/types/sorted_set'
|
226
|
+
require_relative 'redistype/types/hashkey'
|
227
|
+
require_relative 'redistype/types/string'
|
218
228
|
end
|
data/lib/familia.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# rubocop:disable all
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'json'
|
4
5
|
require 'redis'
|
5
6
|
require 'uri/redis'
|
6
7
|
|
@@ -35,9 +36,13 @@ module Familia
|
|
35
36
|
@members = []
|
36
37
|
|
37
38
|
class << self
|
38
|
-
|
39
|
+
attr_writer :debug
|
39
40
|
attr_reader :members
|
40
41
|
|
42
|
+
def debug
|
43
|
+
@debug ||= ENV['FAMILIA_DEBUG'].to_s.match?(/^(true|1)$/i)
|
44
|
+
end
|
45
|
+
|
41
46
|
def included(member)
|
42
47
|
raise Problem, "#{member} should subclass Familia::Horreum"
|
43
48
|
end
|
data/try/10_familia_try.rb
CHANGED
@@ -21,7 +21,7 @@ Familia::Horreum::ClassMethods.public_method_defined? :list
|
|
21
21
|
Familia::Horreum::ClassMethods.public_method_defined? :lists
|
22
22
|
#=> true
|
23
23
|
|
24
|
-
## A Familia object knows its redistype
|
24
|
+
## A Familia object knows its redistype relatives
|
25
25
|
Bone.redis_types.is_a?(Hash) && Bone.redis_types.has_key?(:owners)
|
26
26
|
#=> true
|
27
27
|
|
data/try/20_redis_type_try.rb
CHANGED
@@ -22,8 +22,8 @@ require_relative './test_helpers'
|
|
22
22
|
#=> 'DECENT!'
|
23
23
|
|
24
24
|
## Familia::String#destroy!
|
25
|
-
@a.value.
|
26
|
-
#=>
|
25
|
+
@a.value.delete!
|
26
|
+
#=> true
|
27
27
|
|
28
28
|
## Familia::String.new
|
29
29
|
@ret = Familia::String.new 'arbitrary:key'
|
@@ -63,4 +63,4 @@ require_relative './test_helpers'
|
|
63
63
|
#=> '1050bytes'
|
64
64
|
|
65
65
|
|
66
|
-
@ret.
|
66
|
+
@ret.delete!
|
data/try/26_redis_bool_try.rb
CHANGED
data/try/27_redis_horreum_try.rb
CHANGED
@@ -0,0 +1,159 @@
|
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
|
+
|
4
|
+
Familia.debug = false
|
5
|
+
|
6
|
+
@identifier = 'tryouts-28@onetimesecret.com'
|
7
|
+
@customer = Customer.new @identifier
|
8
|
+
|
9
|
+
## Basic save functionality works
|
10
|
+
@customer.name = 'John Doe'
|
11
|
+
@customer.save
|
12
|
+
#=> true
|
13
|
+
|
14
|
+
## to_h returns field hash with all Customer fields
|
15
|
+
@customer.to_h.class
|
16
|
+
#=> Hash
|
17
|
+
|
18
|
+
## to_h includes the fields we set (using symbol keys)
|
19
|
+
@customer.to_h[:name]
|
20
|
+
#=> "John Doe"
|
21
|
+
|
22
|
+
## to_h includes the key field (using symbol keys)
|
23
|
+
@customer.to_h[:key]
|
24
|
+
#=> "tryouts-28@onetimesecret.com"
|
25
|
+
|
26
|
+
## to_a returns field array in definition order
|
27
|
+
@customer.to_a.class
|
28
|
+
#=> Array
|
29
|
+
|
30
|
+
## to_a includes values in field order (name should be at index 5)
|
31
|
+
@customer.to_a[5]
|
32
|
+
#=> "John Doe"
|
33
|
+
|
34
|
+
## batch_update can update multiple fields atomically, to_h
|
35
|
+
@result = @customer.batch_update(name: 'Jane Smith', email: 'jane@example.com')
|
36
|
+
@result.to_h
|
37
|
+
#=> {:success=>true, :results=>[0, 0]}
|
38
|
+
|
39
|
+
## batch_update returns successful result, successful?
|
40
|
+
@result.successful?
|
41
|
+
#=> true
|
42
|
+
|
43
|
+
## batch_update returns successful result, tuple
|
44
|
+
@result.tuple
|
45
|
+
#=> [true, [0, 0]]
|
46
|
+
|
47
|
+
## batch_update returns successful result, to_a
|
48
|
+
@result.to_a
|
49
|
+
#=> [true, [0, 0]]
|
50
|
+
|
51
|
+
## batch_update updates object fields in memory, confirm fields changed
|
52
|
+
[@customer.name, @customer.email]
|
53
|
+
#=> ["Jane Smith", "jane@example.com"]
|
54
|
+
|
55
|
+
## batch_update persists to Redis
|
56
|
+
@customer.refresh!
|
57
|
+
[@customer.name, @customer.email]
|
58
|
+
#=> ["Jane Smith", "jane@example.com"]
|
59
|
+
|
60
|
+
## batch_update with update_expiration: false works
|
61
|
+
@customer.batch_update(name: 'Bob Jones', update_expiration: false)
|
62
|
+
@customer.refresh!
|
63
|
+
@customer.name
|
64
|
+
#=> "Bob Jones"
|
65
|
+
|
66
|
+
## apply_fields updates object in memory only (1 of 2)
|
67
|
+
@customer.apply_fields(name: 'Memory Only', email: 'memory@test.com')
|
68
|
+
[@customer.name, @customer.email]
|
69
|
+
#=> ["Memory Only", "memory@test.com"]
|
70
|
+
|
71
|
+
## apply_fields doesn't persist to Redis (2 of 2)
|
72
|
+
@customer.refresh!
|
73
|
+
[@customer.name, @customer.email]
|
74
|
+
#=> ["Bob Jones", "jane@example.com"]
|
75
|
+
|
76
|
+
## serialize_value handles strings
|
77
|
+
@customer.serialize_value("test string")
|
78
|
+
#=> "test string"
|
79
|
+
|
80
|
+
## serialize_value handles numbers
|
81
|
+
@customer.serialize_value(42)
|
82
|
+
#=> "42"
|
83
|
+
|
84
|
+
## serialize_value handles hashes as JSON
|
85
|
+
@customer.serialize_value({key: 'value', num: 123})
|
86
|
+
#=> "{\"key\":\"value\",\"num\":123}"
|
87
|
+
|
88
|
+
## serialize_value handles arrays as JSON
|
89
|
+
@customer.serialize_value([1, 2, 'three'])
|
90
|
+
#=> "[1,2,\"three\"]"
|
91
|
+
|
92
|
+
## deserialize_value handles JSON strings back to objects
|
93
|
+
@customer.deserialize_value('{"key":"value","num":123}')
|
94
|
+
#=> {:key=>"value", :num=>123}
|
95
|
+
|
96
|
+
## deserialize_value handles JSON arrays
|
97
|
+
@customer.deserialize_value('[1,2,"three"]')
|
98
|
+
#=> [1, 2, "three"]
|
99
|
+
|
100
|
+
## deserialize_value handles plain strings
|
101
|
+
@customer.deserialize_value('plain string')
|
102
|
+
#=> "plain string"
|
103
|
+
|
104
|
+
## transaction method works with block
|
105
|
+
result = @customer.transaction do |conn|
|
106
|
+
conn.hset @customer.rediskey, 'temp_field', 'temp_value'
|
107
|
+
conn.hset @customer.rediskey, 'another_field', 'another_value'
|
108
|
+
end
|
109
|
+
result.size
|
110
|
+
#=> 2
|
111
|
+
|
112
|
+
## refresh! reloads from Redis
|
113
|
+
@customer.refresh!
|
114
|
+
@customer.hget('temp_field')
|
115
|
+
#=> "temp_value"
|
116
|
+
|
117
|
+
## Empty batch_update still works
|
118
|
+
result = @customer.batch_update()
|
119
|
+
result.successful?
|
120
|
+
#=> true
|
121
|
+
|
122
|
+
## destroy! removes object from Redis (1 of 2)
|
123
|
+
@customer.destroy!
|
124
|
+
#=> true
|
125
|
+
|
126
|
+
## After destroy!, Redis key no longer exists (2 of 2)
|
127
|
+
@customer.exists?
|
128
|
+
#=> false
|
129
|
+
|
130
|
+
## destroy! removes object from Redis, not the in-memory object (2 of 2)
|
131
|
+
@customer.refresh!
|
132
|
+
@customer.name
|
133
|
+
#=> "Bob Jones"
|
134
|
+
|
135
|
+
## clear_fields! removes the in-memory object fields
|
136
|
+
@customer.clear_fields!
|
137
|
+
@customer.name
|
138
|
+
#=> nil
|
139
|
+
|
140
|
+
## Fresh customer for testing new field creation
|
141
|
+
@fresh_customer = Customer.new 'fresh-customer@test.com'
|
142
|
+
@fresh_customer.class
|
143
|
+
#=> Customer
|
144
|
+
|
145
|
+
## batch_update with new fields returns [1, 1] for new field creation
|
146
|
+
@fresh_customer.remove_field('role')
|
147
|
+
@fresh_customer.remove_field('planid')
|
148
|
+
@fresh_result = @fresh_customer.batch_update(role: 'admin', planid: 'premium')
|
149
|
+
@fresh_result.to_h
|
150
|
+
#=> {:success=>true, :results=>[1, 1]}
|
151
|
+
|
152
|
+
## Fresh customer fields are set correctly
|
153
|
+
[@fresh_customer.role, @fresh_customer.planid]
|
154
|
+
#=> ["admin", "premium"]
|
155
|
+
|
156
|
+
## Fresh customer changes persist to Redis
|
157
|
+
@fresh_customer.refresh!
|
158
|
+
[@fresh_customer.role, @fresh_customer.planid]
|
159
|
+
#=> ["admin", "premium"]
|