familia 0.10.2 → 1.0.0.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.pre-commit-config.yaml +1 -1
- data/.rubocop.yml +75 -0
- data/.rubocop_todo.yml +63 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +47 -15
- data/README.md +65 -13
- data/VERSION.yml +4 -3
- data/familia.gemspec +18 -13
- data/lib/familia/base.rb +33 -0
- data/lib/familia/connection.rb +87 -0
- data/lib/familia/core_ext.rb +119 -124
- data/lib/familia/errors.rb +33 -0
- data/lib/familia/features/api_version.rb +19 -0
- data/lib/familia/features/atomic_saves.rb +8 -0
- data/lib/familia/features/quantizer.rb +35 -0
- data/lib/familia/features/safe_dump.rb +194 -0
- data/lib/familia/features.rb +51 -0
- data/lib/familia/horreum/class_methods.rb +292 -0
- data/lib/familia/horreum/commands.rb +106 -0
- data/lib/familia/horreum/relations_management.rb +141 -0
- data/lib/familia/horreum/serialization.rb +193 -0
- data/lib/familia/horreum/settings.rb +63 -0
- data/lib/familia/horreum/utils.rb +44 -0
- data/lib/familia/horreum.rb +248 -0
- data/lib/familia/logging.rb +232 -0
- data/lib/familia/redistype/commands.rb +56 -0
- data/lib/familia/redistype/serialization.rb +110 -0
- data/lib/familia/redistype.rb +185 -0
- data/lib/familia/refinements.rb +88 -0
- data/lib/familia/settings.rb +38 -0
- data/lib/familia/types/hashkey.rb +107 -0
- data/lib/familia/types/list.rb +155 -0
- data/lib/familia/types/sorted_set.rb +234 -0
- data/lib/familia/types/string.rb +115 -0
- data/lib/familia/types/unsorted_set.rb +123 -0
- data/lib/familia/utils.rb +125 -0
- data/lib/familia/version.rb +25 -0
- data/lib/familia.rb +57 -161
- data/lib/redis_middleware.rb +109 -0
- data/try/00_familia_try.rb +5 -4
- data/try/10_familia_try.rb +21 -17
- data/try/20_redis_type_try.rb +67 -0
- data/try/{21_redis_object_zset_try.rb → 21_redis_type_zset_try.rb} +2 -2
- data/try/{22_redis_object_set_try.rb → 22_redis_type_set_try.rb} +2 -2
- data/try/{23_redis_object_list_try.rb → 23_redis_type_list_try.rb} +2 -2
- data/try/{24_redis_object_string_try.rb → 24_redis_type_string_try.rb} +6 -6
- data/try/{25_redis_object_hash_try.rb → 25_redis_type_hash_try.rb} +3 -3
- data/try/26_redis_bool_try.rb +10 -6
- data/try/27_redis_horreum_try.rb +93 -0
- data/try/30_familia_object_try.rb +21 -20
- data/try/35_feature_safedump_try.rb +83 -0
- data/try/40_customer_try.rb +140 -0
- data/try/41_customer_safedump_try.rb +86 -0
- data/try/test_helpers.rb +194 -0
- metadata +51 -47
- data/lib/familia/helpers.rb +0 -70
- data/lib/familia/object.rb +0 -533
- data/lib/familia/redisobject.rb +0 -1017
- data/lib/familia/test_helpers.rb +0 -40
- data/lib/familia/tools.rb +0 -67
- data/try/20_redis_object_try.rb +0 -44
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
require_relative '../lib/familia'
|
3
|
+
require_relative '../lib/familia/features/quantizer'
|
4
|
+
require_relative './test_helpers'
|
5
|
+
|
6
|
+
#Familia.apiversion = 'v1'
|
7
|
+
|
8
|
+
@limiter1 = Limiter.new :requests
|
9
|
+
|
10
|
+
|
11
|
+
## Redis Types are unique per instance of a Familia class
|
12
|
+
@a = Bone.new 'atoken1', :name1
|
13
|
+
@b = Bone.new 'atoken2', :name2
|
14
|
+
p [@a.object_id, @b.object_id]
|
15
|
+
p [@a.owners.parent.class, @b.owners.parent.class]
|
16
|
+
p [@a.owners.parent.object_id, @b.owners.parent.object_id]
|
17
|
+
p [@a.owners.rediskey, @b.owners.rediskey]
|
18
|
+
p [@a.token, @b.token]
|
19
|
+
p [@a.name, @b.name]
|
20
|
+
@a.owners.rediskey.eql?(@b.owners.rediskey)
|
21
|
+
#=> false
|
22
|
+
|
23
|
+
## Redis Types are frozen
|
24
|
+
@a.owners.frozen?
|
25
|
+
#=> true
|
26
|
+
|
27
|
+
|
28
|
+
## Limiter#qstamp
|
29
|
+
@limiter1.counter.qstamp(10.minutes, '%H:%M', 1302468980)
|
30
|
+
##=> '20:50'
|
31
|
+
|
32
|
+
## Redis Types can be stored to quantized stamp suffix
|
33
|
+
@limiter1.counter.rediskey
|
34
|
+
##=> "v1:limiter:requests:counter:20:50"
|
35
|
+
|
36
|
+
## Limiter#qstamp as a number
|
37
|
+
@limiter2 = Limiter.new :requests
|
38
|
+
@limiter2.counter.qstamp(10.minutes, pattern=nil, now=1302468980)
|
39
|
+
##=> 1302468600
|
40
|
+
|
41
|
+
## Redis Types can be stored to quantized numeric suffix. This
|
42
|
+
## tryouts is disabled b/c `RedisType#rediskey` takes no args
|
43
|
+
## and relies on the `class Limiter` definition in test_helpers.rb
|
44
|
+
## for the `:quantize` option. The quantized suffix for the Limiter
|
45
|
+
## class is `'%H:%M'` so its redis keys will always look like that.
|
46
|
+
@limiter2.counter.rediskey
|
47
|
+
##=> "v1:limiter:requests:counter:1302468600"
|
48
|
+
|
49
|
+
## Increment counter
|
50
|
+
@limiter1.counter.clear
|
51
|
+
@limiter1.counter.increment
|
52
|
+
#=> 1
|
53
|
+
|
54
|
+
## Check ttl
|
55
|
+
@limiter1.counter.ttl
|
56
|
+
#=> 3600.0
|
57
|
+
|
58
|
+
## Check ttl for a different instance
|
59
|
+
## (this exists to make sure options are cloned for each instance)
|
60
|
+
@limiter3 = Limiter.new :requests
|
61
|
+
@limiter3.counter.ttl
|
62
|
+
#=> 3600.0
|
63
|
+
|
64
|
+
## Check realttl
|
65
|
+
sleep 1 # Redis ttls are in seconds so we can't wait any less time than this (without mocking)
|
66
|
+
@limiter1.counter.realttl
|
67
|
+
#=> 3600-1
|
@@ -1,13 +1,13 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
3
|
|
4
|
-
Familia.apiversion = 'v1'
|
4
|
+
#Familia.apiversion = 'v1'
|
5
5
|
|
6
6
|
@a = Bone.new 'atoken2', 'akey'
|
7
7
|
|
8
8
|
## Bone#rediskey
|
9
9
|
@a.rediskey
|
10
|
-
#=> '
|
10
|
+
#=> 'bone:atoken2:akey:object'
|
11
11
|
|
12
12
|
## Familia::String#value should give default value
|
13
13
|
@a.value.value
|
@@ -39,7 +39,7 @@ Familia.apiversion = 'v1'
|
|
39
39
|
#=> '1000'
|
40
40
|
|
41
41
|
## Familia::String#increment
|
42
|
-
@ret.increment
|
42
|
+
@ret.increment
|
43
43
|
#=> 1001
|
44
44
|
|
45
45
|
## Familia::String#incrementby
|
@@ -47,7 +47,7 @@ Familia.apiversion = 'v1'
|
|
47
47
|
#=> 1100
|
48
48
|
|
49
49
|
## Familia::String#decrement
|
50
|
-
@ret.decrement
|
50
|
+
@ret.decrement
|
51
51
|
#=> 1099
|
52
52
|
|
53
53
|
## Familia::String#decrementby
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
3
|
|
4
4
|
@a = Bone.new 'atoken', 'akey'
|
5
5
|
|
@@ -21,7 +21,7 @@ require 'familia/test_helpers'
|
|
21
21
|
@a.props.has_key? 'fieldA'
|
22
22
|
#=> true
|
23
23
|
|
24
|
-
## Familia::HashKey#all
|
24
|
+
## Familia::HashKey#all
|
25
25
|
@a.props.all.class
|
26
26
|
#=> Hash
|
27
27
|
|
data/try/26_redis_bool_try.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
3
|
|
4
4
|
Familia.debug = true
|
5
5
|
|
@@ -17,10 +17,10 @@ Familia.debug = true
|
|
17
17
|
## Trying to store a boolean value to a hash key raises an exception
|
18
18
|
begin
|
19
19
|
@hashkey["test"] = true
|
20
|
-
rescue
|
20
|
+
rescue Familia::HighRiskFactor => e
|
21
21
|
e.message
|
22
22
|
end
|
23
|
-
#=> "
|
23
|
+
#=> "High risk factor for serialization bugs: true<TrueClass>"
|
24
24
|
|
25
25
|
## Boolean values are returned as strings
|
26
26
|
@hashkey["test"]
|
@@ -29,11 +29,15 @@ end
|
|
29
29
|
## Trying to store a nil value to a hash key raises an exception
|
30
30
|
begin
|
31
31
|
@hashkey["test"] = nil
|
32
|
-
rescue
|
32
|
+
rescue Familia::HighRiskFactor => e
|
33
33
|
e.message
|
34
34
|
end
|
35
|
-
#=> "
|
35
|
+
#=> "High risk factor for serialization bugs: <NilClass>"
|
36
36
|
|
37
37
|
## The exceptions prevented the hash from being updated
|
38
38
|
@hashkey["test"]
|
39
39
|
#=> "true"
|
40
|
+
|
41
|
+
## Clear the hash key
|
42
|
+
@hashkey.clear
|
43
|
+
#=> 1
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
|
+
|
4
|
+
Familia.debug = true
|
5
|
+
|
6
|
+
@identifier = 'tryouts-27@onetimesecret.com'
|
7
|
+
@customer = Customer.new @identifier
|
8
|
+
@hashkey = Familia::HashKey.new 'tryouts-27'
|
9
|
+
|
10
|
+
## Customer passed as value is returned as string identifier, on assignment as string
|
11
|
+
@hashkey["test1"] = @customer.identifier
|
12
|
+
#=> @identifier
|
13
|
+
|
14
|
+
## Customer passed as value is returned as string identifier
|
15
|
+
@hashkey["test1"]
|
16
|
+
#=> @identifier
|
17
|
+
|
18
|
+
## Trying to store a customer to a hash key implicitly converts it to string identifier
|
19
|
+
## we can't tell from the return value this way either. Here the store method return value
|
20
|
+
## is either 1 (if the key is new) or 0 (if the key already exists).
|
21
|
+
@hashkey.store "test2", @customer
|
22
|
+
#=> 1
|
23
|
+
|
24
|
+
## Trying to store a customer to a hash key implicitly converts it to string identifier
|
25
|
+
## but we can't tell that from the return value. Here the hash syntax return value
|
26
|
+
## is the value that is being assigned.
|
27
|
+
@hashkey["test2"] = @customer
|
28
|
+
#=> @customer
|
29
|
+
|
30
|
+
## Trying store again with the same key returns 0
|
31
|
+
@hashkey.store "test2", @customer
|
32
|
+
#=> 0
|
33
|
+
|
34
|
+
## Customer passed as value is returned as string identifier
|
35
|
+
@hashkey["test2"]
|
36
|
+
#=> @identifier
|
37
|
+
|
38
|
+
## Remove the key
|
39
|
+
@hashkey.clear
|
40
|
+
#=> 1
|
41
|
+
|
42
|
+
## Horreum objects can update and save their fields (1 of 2)
|
43
|
+
@customer.name = 'John Doe'
|
44
|
+
#=> "John Doe"
|
45
|
+
|
46
|
+
## Horreum objects can update and save their fields (2 of 2)
|
47
|
+
@customer.save
|
48
|
+
#=> true
|
49
|
+
|
50
|
+
## Horreum object fields have a fast writer method (1 of 2)
|
51
|
+
Familia.trace :LOAD, @customer.redis, @customer.redisuri, caller if Familia.debug?
|
52
|
+
@customer.name! 'Jane Doe'
|
53
|
+
#=> 0
|
54
|
+
|
55
|
+
## Horreum object fields have a fast writer method (2 of 2)
|
56
|
+
@customer.refresh!
|
57
|
+
@customer.name
|
58
|
+
#=> "Jane Doe"
|
59
|
+
|
60
|
+
## Unsaved changes are lost when an object reloads
|
61
|
+
@customer.name = 'John Doe'
|
62
|
+
@customer.refresh!
|
63
|
+
@customer.name
|
64
|
+
#=> "Jane Doe"
|
65
|
+
|
66
|
+
## Horreum objects can be destroyed
|
67
|
+
@customer.destroy!
|
68
|
+
#=> true
|
69
|
+
|
70
|
+
## All horrerum objects have a key field
|
71
|
+
@customer.key
|
72
|
+
#=> @identifier
|
73
|
+
|
74
|
+
## Even ones that didn't define it
|
75
|
+
@cd = CustomDomain.new "www.example.com", "@identifier"
|
76
|
+
@cd.key
|
77
|
+
#=> nil
|
78
|
+
|
79
|
+
## We can call #identifier directly if we want to "lasy load" the unique identifier
|
80
|
+
@cd.identifier
|
81
|
+
#=> "7565befd"
|
82
|
+
|
83
|
+
## The #key field will still be nil
|
84
|
+
@cd.key
|
85
|
+
#=> nil
|
86
|
+
|
87
|
+
## But once we save
|
88
|
+
@cd.save
|
89
|
+
#=> true
|
90
|
+
|
91
|
+
## The key will be set
|
92
|
+
@cd.key
|
93
|
+
#=> "7565befd"
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
Familia.apiversion = 'v1'
|
1
|
+
require_relative '../lib/familia'
|
2
|
+
require_relative './test_helpers'
|
3
|
+
#Familia.apiversion = 'v1'
|
4
4
|
|
5
5
|
@a = Bone.new 'atoken', 'akey'
|
6
6
|
|
@@ -8,8 +8,8 @@ Familia.apiversion = 'v1'
|
|
8
8
|
Bone.prefix
|
9
9
|
#=> :bone
|
10
10
|
|
11
|
-
## Familia#
|
12
|
-
@a.
|
11
|
+
## Familia#identifier
|
12
|
+
@a.identifier
|
13
13
|
#=> 'atoken:akey'
|
14
14
|
|
15
15
|
## Familia.suffix
|
@@ -18,32 +18,34 @@ Bone.suffix
|
|
18
18
|
|
19
19
|
## Familia#rediskey
|
20
20
|
@a.rediskey
|
21
|
-
#=> '
|
21
|
+
#=> 'bone:atoken:akey:object'
|
22
22
|
|
23
23
|
## Familia#rediskey
|
24
24
|
@a.rediskey
|
25
|
-
#=> '
|
25
|
+
#=> 'bone:atoken:akey:object'
|
26
26
|
|
27
27
|
## Familia#save
|
28
28
|
@cust = Customer.new :delano, "Delano Mandelbaum"
|
29
29
|
@cust.save
|
30
30
|
#=> true
|
31
31
|
|
32
|
-
## Customer.
|
33
|
-
|
34
|
-
|
32
|
+
## Customer.values (Updateing this sorted set of instance IDs is a Onetime
|
33
|
+
# Secret feature. Disabled here b/c there's no code in Familia or the test
|
34
|
+
# helpers that replicates this behaviour.) Leaving this here for reference.
|
35
|
+
Customer.values.all.collect(&:custid)
|
36
|
+
##=> ['delano']
|
35
37
|
|
36
38
|
## Familia.from_redis
|
37
39
|
obj = Customer.from_redis :delano
|
38
|
-
obj.custid
|
39
|
-
#=>
|
40
|
+
[obj.class, obj.custid]
|
41
|
+
#=> [Customer, 'delano']
|
40
42
|
|
41
43
|
## Customer.destroy
|
42
44
|
@cust.destroy!
|
43
|
-
#=>
|
45
|
+
#=> true
|
44
46
|
|
45
47
|
## Customer.instances
|
46
|
-
Customer.
|
48
|
+
Customer.values.size
|
47
49
|
#=> 0
|
48
50
|
|
49
51
|
## Familia#save with an object that expires
|
@@ -57,7 +59,7 @@ Customer.customers.class
|
|
57
59
|
|
58
60
|
## Familia class rediskey
|
59
61
|
Customer.customers.rediskey
|
60
|
-
#=> '
|
62
|
+
#=> 'customer:customers'
|
61
63
|
|
62
64
|
## Familia.class_list
|
63
65
|
Customer.customers << :delano << :tucker << :morton
|
@@ -69,19 +71,18 @@ Customer.customers.clear
|
|
69
71
|
#=> 1
|
70
72
|
|
71
73
|
|
72
|
-
## Familia class replace 1
|
74
|
+
## Familia class replace 1 of 4
|
73
75
|
Customer.message.value = "msg1"
|
74
76
|
#=> "msg1"
|
75
77
|
|
76
|
-
## Familia class replace 2
|
78
|
+
## Familia class replace 2 of 4
|
77
79
|
Customer.message.value
|
78
80
|
#=> "msg1"
|
79
81
|
|
80
|
-
## Familia class replace 3
|
82
|
+
## Familia class replace 3 of 4
|
81
83
|
Customer.message = "msg2"
|
82
84
|
#=> "msg2"
|
83
85
|
|
84
|
-
## Familia class replace 4
|
86
|
+
## Familia class replace 4 of 4
|
85
87
|
Customer.message.value
|
86
88
|
#=> "msg2"
|
87
|
-
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# These tryouts test the safe dumping functionality.
|
4
|
+
|
5
|
+
require_relative '../lib/familia'
|
6
|
+
require_relative './test_helpers'
|
7
|
+
|
8
|
+
## By default Familia::Base has no safe_dump_fields method
|
9
|
+
Familia::Base.respond_to?(:safe_dump_fields)
|
10
|
+
#=> false
|
11
|
+
|
12
|
+
## Implementing models like Customer can define safe dump fields
|
13
|
+
Customer.safe_dump_fields
|
14
|
+
#=> [:custid, :role, :verified, :updated, :created, :secrets_created, :active]
|
15
|
+
|
16
|
+
## Implementing models like Customer can safely dump their fields
|
17
|
+
@cust = Customer.new
|
18
|
+
@cust.custid = "test@example.com"
|
19
|
+
@cust.role = "user"
|
20
|
+
@cust.verified = true
|
21
|
+
@cust.created = Time.now.to_i
|
22
|
+
@cust.updated = Time.now.to_i
|
23
|
+
@safe_dump = @cust.safe_dump
|
24
|
+
@safe_dump.keys.sort
|
25
|
+
#=> [:active, :created, :custid, :role, :secrets_created, :updated, :verified]
|
26
|
+
|
27
|
+
## Implementing models like Customer do have other fields
|
28
|
+
## that are by default considered not safe to dump.
|
29
|
+
@cust1 = Customer.new
|
30
|
+
@cust1.email = "test@example.com"
|
31
|
+
|
32
|
+
@all_non_safe_fields = @cust1.instance_variables.map { |el|
|
33
|
+
el.to_s[1..-1].to_sym # slice off the leading @
|
34
|
+
}.sort
|
35
|
+
|
36
|
+
(@all_non_safe_fields - Customer.safe_dump_fields).sort
|
37
|
+
#=> [:custom_domains, :email, :password_reset, :sessions, :stripe_customer, :timeline]
|
38
|
+
|
39
|
+
## Implementing models like Customer can rest assured knowing
|
40
|
+
## any other field not in the safe list will not be dumped.
|
41
|
+
@cust2 = Customer.new
|
42
|
+
@cust2.email = "test@example.com"
|
43
|
+
@cust2.custid = "test@example.com"
|
44
|
+
@all_safe_fields = @cust2.safe_dump.keys.sort
|
45
|
+
|
46
|
+
@all_non_safe_fields = @cust2.instance_variables.map { |el|
|
47
|
+
el.to_s[1..-1].to_sym # slice off the leading @
|
48
|
+
}.sort
|
49
|
+
p [1, all_non_safe_fields: @all_non_safe_fields]
|
50
|
+
# Check if any of the non-safe fields are in the safe dump
|
51
|
+
(@all_non_safe_fields & @all_safe_fields) - [:custid, :role, :verified, :updated, :created, :secrets_created]
|
52
|
+
#=> []
|
53
|
+
|
54
|
+
## Bone does not have safe_dump feature enabled
|
55
|
+
Bone.respond_to?(:safe_dump_fields)
|
56
|
+
#=> false
|
57
|
+
|
58
|
+
## Bone instances do not have safe_dump method
|
59
|
+
@bone = Bone.new(name: "Rex", age: 3)
|
60
|
+
@bone.respond_to?(:safe_dump)
|
61
|
+
#=> false
|
62
|
+
|
63
|
+
## Blone has safe_dump feature enabled
|
64
|
+
Blone.respond_to?(:safe_dump_fields)
|
65
|
+
#=> true
|
66
|
+
|
67
|
+
## Blone has empty safe_dump_fields
|
68
|
+
Blone.safe_dump_fields
|
69
|
+
#=> []
|
70
|
+
|
71
|
+
## Blone instances have safe_dump method
|
72
|
+
@blone = Blone.new(name: "Fido", age: 5)
|
73
|
+
@blone.respond_to?(:safe_dump)
|
74
|
+
#=> true
|
75
|
+
|
76
|
+
## Blone safe_dump returns an empty hash
|
77
|
+
@blone.safe_dump
|
78
|
+
#=> {}
|
79
|
+
|
80
|
+
# Teardown
|
81
|
+
@cust2.destroy!
|
82
|
+
@bone = nil
|
83
|
+
@blone = nil
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# Customer Tryouts
|
2
|
+
require_relative '../lib/familia'
|
3
|
+
require_relative './test_helpers'
|
4
|
+
|
5
|
+
# Setup
|
6
|
+
@now = Time.now.to_f
|
7
|
+
@customer = Customer.new
|
8
|
+
@customer.custid = "test@example.com"
|
9
|
+
@customer.email = "test@example.com"
|
10
|
+
@customer.role = "user"
|
11
|
+
@customer.key = "abc123"
|
12
|
+
@customer.planid = "basic"
|
13
|
+
@customer.created = Time.now.to_i
|
14
|
+
@customer.updated = Time.now.to_i
|
15
|
+
|
16
|
+
## Customer can be saved
|
17
|
+
@customer.save
|
18
|
+
#=> true
|
19
|
+
|
20
|
+
## Customer can be retrieved by identifier
|
21
|
+
retrieved_customer = Customer.from_redis("test@example.com")
|
22
|
+
retrieved_customer.custid
|
23
|
+
#=> "test@example.com"
|
24
|
+
|
25
|
+
## Customer fields can be accessed
|
26
|
+
@customer.email
|
27
|
+
#=> "test@example.com"
|
28
|
+
|
29
|
+
## Customer role can be set and retrieved
|
30
|
+
@customer.role = "admin"
|
31
|
+
@customer.role
|
32
|
+
#=> "admin"
|
33
|
+
|
34
|
+
## Customer can update fields
|
35
|
+
@customer.planid = "premium"
|
36
|
+
@customer.save
|
37
|
+
ident = @customer.identifier
|
38
|
+
Customer.from_redis(ident).planid
|
39
|
+
#=> "premium"
|
40
|
+
|
41
|
+
## Customer can increment secrets_created counter
|
42
|
+
@customer.secrets_created.clear
|
43
|
+
@customer.secrets_created.increment
|
44
|
+
@customer.secrets_created.value
|
45
|
+
#=> '1'
|
46
|
+
|
47
|
+
## Customer can add custom domain via add method
|
48
|
+
@customer.custom_domains.add(@now, "example.org")
|
49
|
+
@customer.custom_domains.members.include?("example.org")
|
50
|
+
#=> true
|
51
|
+
|
52
|
+
## Customer can retrieve custom domain score via score method
|
53
|
+
@customer.custom_domains.score("example.org")
|
54
|
+
#=> @now
|
55
|
+
|
56
|
+
## Customer can add custom domain via []= method
|
57
|
+
@customer.custom_domains["example2.org"] = @now
|
58
|
+
@customer.custom_domains.members.include?("example2.org")
|
59
|
+
#=> true
|
60
|
+
|
61
|
+
## Customer can retrieve custom domain score via []
|
62
|
+
@customer.custom_domains["example.org"]
|
63
|
+
#=> @now
|
64
|
+
|
65
|
+
|
66
|
+
## Customer can store timeline
|
67
|
+
@customer.timeline["last_login"] = @now
|
68
|
+
@customer.timeline["last_login"].to_i.positive?
|
69
|
+
#=> true
|
70
|
+
|
71
|
+
## Customer can be added to class-level sorted set
|
72
|
+
Customer.instances << @customer
|
73
|
+
Customer.instances.member?(@customer)
|
74
|
+
#=> true
|
75
|
+
|
76
|
+
## Customer can be removed from class-level sorted set
|
77
|
+
Customer.instances.delete(@customer)
|
78
|
+
Customer.instances.member?(@customer)
|
79
|
+
#=> false
|
80
|
+
|
81
|
+
## Customer can add a session
|
82
|
+
@customer.sessions << "session123"
|
83
|
+
@customer.sessions.members.include?("session123")
|
84
|
+
#=> true
|
85
|
+
|
86
|
+
## Customer can set and get password reset information
|
87
|
+
@customer.password_reset["token"] = "reset123"
|
88
|
+
@customer.password_reset["token"]
|
89
|
+
#=> "reset123"
|
90
|
+
|
91
|
+
## Customer can be destroyed
|
92
|
+
ret = @customer.destroy!
|
93
|
+
cust = Customer.from_redis("test@example.com")
|
94
|
+
exists = Customer.exists?("test@example.com")
|
95
|
+
[ret, cust.nil?, exists]
|
96
|
+
#=> [true, true, false]
|
97
|
+
|
98
|
+
## Customer.destroy! can be called on an already destroyed object
|
99
|
+
@customer.destroy!
|
100
|
+
#=> false
|
101
|
+
|
102
|
+
## Customer.db returns the correct database number
|
103
|
+
Customer.db
|
104
|
+
#=> 15
|
105
|
+
|
106
|
+
## Customer.db returns the correct database number
|
107
|
+
@customer.db
|
108
|
+
#=> 15
|
109
|
+
|
110
|
+
## @customer.redis.connection returns the correct redis URI
|
111
|
+
@customer.redis.connection
|
112
|
+
#=> {:host=>"127.0.0.1", :port=>6379, :db=>15, :id=>"redis://127.0.0.1:6379/15", :location=>"127.0.0.1:6379"}
|
113
|
+
|
114
|
+
## @customer.redis.uri returns the correct redis URI
|
115
|
+
@customer.secrets_created.db
|
116
|
+
#=> nil
|
117
|
+
|
118
|
+
## @customer.redis.uri returns the correct redis URI
|
119
|
+
@customer.secrets_created.redis.connection
|
120
|
+
#=> {:host=>"127.0.0.1", :port=>6379, :db=>15, :id=>"redis://127.0.0.1:6379/15", :location=>"127.0.0.1:6379"}
|
121
|
+
|
122
|
+
## Customer.url is nil by default
|
123
|
+
Customer.uri
|
124
|
+
#=> nil
|
125
|
+
|
126
|
+
## Customer.destroy! makes only one call to Redis
|
127
|
+
RedisCommandCounter.count_commands { @customer.destroy! }
|
128
|
+
#=> 1
|
129
|
+
|
130
|
+
## Customer.db returns the correct database number
|
131
|
+
Customer.instances.db
|
132
|
+
#=> nil
|
133
|
+
|
134
|
+
## Customer.db returns the correct database number
|
135
|
+
Customer.instances.uri.to_s
|
136
|
+
#=> 'redis://127.0.0.1/15/'
|
137
|
+
|
138
|
+
|
139
|
+
# Teardown
|
140
|
+
Customer.instances.clear
|
@@ -0,0 +1,86 @@
|
|
1
|
+
|
2
|
+
require_relative '../lib/familia'
|
3
|
+
require_relative './test_helpers'
|
4
|
+
|
5
|
+
# Setup
|
6
|
+
@now = Time.now.to_i
|
7
|
+
@customer = Customer.new
|
8
|
+
@customer.custid = "test@example.com"
|
9
|
+
@customer.email = "test@example.com"
|
10
|
+
@customer.role = "user"
|
11
|
+
@customer.key = "abc123"
|
12
|
+
@customer.planid = "basic"
|
13
|
+
@customer.created = @now
|
14
|
+
@customer.updated = @now
|
15
|
+
@customer.verified = true
|
16
|
+
@customer.reset_requested = false
|
17
|
+
@customer.save
|
18
|
+
@safe_dump = @customer.safe_dump
|
19
|
+
|
20
|
+
## Customer can be safely dumped
|
21
|
+
@safe_dump.keys.sort
|
22
|
+
#=> [:active, :created, :custid, :role, :secrets_created, :updated, :verified]
|
23
|
+
|
24
|
+
## Safe dump includes correct custid
|
25
|
+
@safe_dump[:custid]
|
26
|
+
#=> "test@example.com"
|
27
|
+
|
28
|
+
## Safe dump includes correct role
|
29
|
+
@safe_dump[:role]
|
30
|
+
#=> "user"
|
31
|
+
|
32
|
+
## Safe dump includes correct verified status
|
33
|
+
@safe_dump[:verified]
|
34
|
+
#=> true
|
35
|
+
|
36
|
+
## Safe dump includes correct created timestamp
|
37
|
+
@safe_dump[:created]
|
38
|
+
#=> @now
|
39
|
+
|
40
|
+
## Safe dump includes correct updated timestamp
|
41
|
+
@safe_dump[:updated]
|
42
|
+
#=> @now
|
43
|
+
|
44
|
+
## Safe dump includes correct secrets_created count
|
45
|
+
@customer.secrets_created.increment
|
46
|
+
@safe_dump = @customer.safe_dump
|
47
|
+
@safe_dump[:secrets_created]
|
48
|
+
#=> "2"
|
49
|
+
|
50
|
+
## Safe dump includes correct active status when verified and not reset requested
|
51
|
+
@safe_dump[:active]
|
52
|
+
#=> true
|
53
|
+
|
54
|
+
## Safe dump includes correct active status when not verified
|
55
|
+
@customer.verified = false
|
56
|
+
@customer.save
|
57
|
+
@safe_dump = @customer.safe_dump
|
58
|
+
@safe_dump[:active]
|
59
|
+
#=> false
|
60
|
+
|
61
|
+
## Safe dump includes correct active status when reset requested
|
62
|
+
@customer.verified = true
|
63
|
+
@customer.reset_requested = true
|
64
|
+
@customer.save
|
65
|
+
@safe_dump = @customer.safe_dump
|
66
|
+
@safe_dump[:active]
|
67
|
+
#=> false
|
68
|
+
|
69
|
+
## Safe dump excludes sensitive information (email)
|
70
|
+
@safe_dump.has_key?(:email)
|
71
|
+
#=> false
|
72
|
+
|
73
|
+
## Safe dump excludes sensitive information (key)
|
74
|
+
@safe_dump.has_key?(:key)
|
75
|
+
#=> false
|
76
|
+
|
77
|
+
## Safe dump excludes sensitive information (passphrase)
|
78
|
+
@safe_dump.has_key?(:passphrase)
|
79
|
+
#=> false
|
80
|
+
|
81
|
+
## Safe dump excludes non-specified fields
|
82
|
+
@safe_dump.has_key?(:planid)
|
83
|
+
#=> false
|
84
|
+
|
85
|
+
# Teardown
|
86
|
+
@customer.destroy!
|