hoodoo 1.13.0 → 1.14.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/lib/hoodoo.rb +1 -0
- data/lib/hoodoo/services/services/session.rb +8 -104
- data/lib/hoodoo/transient_store.rb +19 -0
- data/lib/hoodoo/transient_store/mocks/dalli_client.rb +148 -0
- data/lib/hoodoo/transient_store/mocks/redis.rb +138 -0
- data/lib/hoodoo/transient_store/transient_store.rb +344 -0
- data/lib/hoodoo/transient_store/transient_store/base.rb +81 -0
- data/lib/hoodoo/transient_store/transient_store/memcached.rb +116 -0
- data/lib/hoodoo/transient_store/transient_store/memcached_redis_mirror.rb +181 -0
- data/lib/hoodoo/transient_store/transient_store/redis.rb +126 -0
- data/lib/hoodoo/version.rb +1 -1
- data/spec/active/active_record/support_spec.rb +3 -9
- data/spec/active/active_record/translated_spec.rb +2 -5
- data/spec/logger/writers/file_writer_spec.rb +1 -4
- data/spec/logger/writers/stream_writer_spec.rb +2 -9
- data/spec/services/middleware/middleware_logging_spec.rb +1 -4
- data/spec/services/middleware/middleware_permissions_spec.rb +2 -2
- data/spec/services/services/interface_spec.rb +2 -2
- data/spec/services/services/session_spec.rb +26 -19
- data/spec/transient_store/transient_store/base_spec.rb +52 -0
- data/spec/transient_store/transient_store/memcached_redis_mirror_spec.rb +380 -0
- data/spec/transient_store/transient_store/memcached_spec.rb +244 -0
- data/spec/transient_store/transient_store/mocks/dalli_client_spec.rb +44 -0
- data/spec/transient_store/transient_store/mocks/redis_spec.rb +28 -0
- data/spec/transient_store/transient_store/redis_spec.rb +242 -0
- data/spec/transient_store/transient_store_spec.rb +448 -0
- metadata +31 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f2eb0d49452cfe11be6f7953e2915139a799389
|
4
|
+
data.tar.gz: 2d9ca68a6fc2f064c7e8c1a3473ef3028369ff02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3091aedf879cae316a5bbf757bca8f6a01967bd41608359d7ab01f8b54330d2faf482ac0d5d80e222d88ffa9425d15fec98e02e6c976aa9223adbd1ff536816
|
7
|
+
data.tar.gz: 2194d4e534790c220d2bdfadc2b9efc42a8ba408741b1933a6f0c66edd47e2e40103aa04a6a7a22e66e78576395d035a70e111425aad4c0bf59030590ff50019
|
data/lib/hoodoo.rb
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
|
11
11
|
require 'ostruct'
|
12
12
|
require 'dalli'
|
13
|
+
require 'hoodoo/transient_store/mocks/dalli_client'
|
13
14
|
|
14
15
|
module Hoodoo
|
15
16
|
module Services
|
@@ -634,111 +635,14 @@ module Hoodoo
|
|
634
635
|
)
|
635
636
|
end
|
636
637
|
|
637
|
-
#
|
638
|
-
#
|
639
|
-
#
|
638
|
+
# Before Hoodoo::TransientStore was created, the Session system was
|
639
|
+
# directly tied into Memcached and had a mock backend used for tests
|
640
|
+
# without a Redis dependency. This now lives in
|
641
|
+
# Hoodoo::TransientStore::Mocks::DalliClient, but for any code out in
|
642
|
+
# the wild which might use the old Session namespace version, we add
|
643
|
+
# what amounts to a class alias here.
|
640
644
|
#
|
641
|
-
|
642
|
-
#
|
643
|
-
# ...whenever you need to stub out real Memcached. You will
|
644
|
-
# probably want to add:
|
645
|
-
#
|
646
|
-
# before :all do # (or ":each")
|
647
|
-
# Hoodoo::Services::Session::MockDalliClient.reset()
|
648
|
-
# end
|
649
|
-
#
|
650
|
-
# ...to "clean out Memcached" before or between tests. You can
|
651
|
-
# check the contents of mock Memcached by examining ::store's
|
652
|
-
# hash of data.
|
653
|
-
#
|
654
|
-
class MockDalliClient
|
655
|
-
@@store = {}
|
656
|
-
|
657
|
-
# For test analysis, return the hash of 'memcached' mock data.
|
658
|
-
#
|
659
|
-
# Entries are referenced by the key you used to originally
|
660
|
-
# store them; values are hashes with ":expires_at" giving an
|
661
|
-
# expiry time or "nil" and ":value" giving your stored value.
|
662
|
-
#
|
663
|
-
def self.store
|
664
|
-
@@store
|
665
|
-
end
|
666
|
-
|
667
|
-
# Wipe out all saved data.
|
668
|
-
#
|
669
|
-
def self.reset
|
670
|
-
@@store = {}
|
671
|
-
end
|
672
|
-
|
673
|
-
# Pass +true+ to bypass the mock client (subject to the caller
|
674
|
-
# reading ::bypass?) to e.g. get test code coverage on real
|
675
|
-
# Memcached. Pass +false+ otherwise.
|
676
|
-
#
|
677
|
-
def self.bypass( bypass_boolean )
|
678
|
-
@@bypass = bypass_boolean
|
679
|
-
end
|
680
|
-
|
681
|
-
@@bypass = false
|
682
|
-
|
683
|
-
# If +true+, bypass this class and use real Dalli::Client; else
|
684
|
-
# don't. Default return value is +false+.
|
685
|
-
#
|
686
|
-
def self.bypass?
|
687
|
-
@@bypass
|
688
|
-
end
|
689
|
-
|
690
|
-
# Get the data stored under the given key. Returns +nil+ if
|
691
|
-
# not found / expired.
|
692
|
-
#
|
693
|
-
# +key+:: Key to look up (see #set).
|
694
|
-
#
|
695
|
-
def get( key )
|
696
|
-
data = @@store[ key ]
|
697
|
-
return nil if data.nil?
|
698
|
-
|
699
|
-
expires_at = data[ :expires_at ]
|
700
|
-
return nil unless expires_at.nil? || Time.now < expires_at
|
701
|
-
|
702
|
-
return data[ :value ]
|
703
|
-
end
|
704
|
-
|
705
|
-
# Set data for a given key.
|
706
|
-
#
|
707
|
-
# +key+:: Key under which to store data.
|
708
|
-
#
|
709
|
-
# +value+:: Data to store.
|
710
|
-
#
|
711
|
-
# +ttl+:: (Optional) time-to-live ('live' as in living, not as in
|
712
|
-
# 'live TV') - a value in seconds, after which the data is
|
713
|
-
# considered expired. If omitted, the data does not expire.
|
714
|
-
#
|
715
|
-
def set( key, value, ttl = nil )
|
716
|
-
data = {
|
717
|
-
:expires_at => ttl.nil? ? nil : Time.now.utc + ttl,
|
718
|
-
:value => value
|
719
|
-
}
|
720
|
-
|
721
|
-
@@store[ key ] = data
|
722
|
-
true
|
723
|
-
end
|
724
|
-
|
725
|
-
# Remove data for the given key.
|
726
|
-
#
|
727
|
-
def delete( key )
|
728
|
-
if @@store.has_key?( key )
|
729
|
-
@@store.delete( key )
|
730
|
-
true
|
731
|
-
else
|
732
|
-
false
|
733
|
-
end
|
734
|
-
end
|
735
|
-
|
736
|
-
# Mock 'stats' health check.
|
737
|
-
#
|
738
|
-
def stats
|
739
|
-
true
|
740
|
-
end
|
741
|
-
end
|
645
|
+
MockDalliClient = Hoodoo::TransientStore::Mocks::DalliClient
|
742
646
|
end
|
743
647
|
end
|
744
648
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: transient_store.rb
|
3
|
+
# (C):: Loyalty New Zealand 2017
|
4
|
+
#
|
5
|
+
# Purpose:: Include temporary/transient storage abstraction layers.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 01-Feb-2017 (ADH): Created
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
# Core abstraction
|
11
|
+
|
12
|
+
require 'hoodoo/transient_store/transient_store'
|
13
|
+
require 'hoodoo/transient_store/transient_store/base'
|
14
|
+
|
15
|
+
# Storage engine plugins
|
16
|
+
|
17
|
+
require 'hoodoo/transient_store/transient_store/memcached'
|
18
|
+
require 'hoodoo/transient_store/transient_store/redis'
|
19
|
+
require 'hoodoo/transient_store/transient_store/memcached_redis_mirror'
|
@@ -0,0 +1,148 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: dalli_client.rb
|
3
|
+
# (C):: Loyalty New Zealand 2017
|
4
|
+
#
|
5
|
+
# Purpose:: A mock/fake Dalli::Client minimal implementation as an
|
6
|
+
# alternative test back-end for Memcached-independent tests.
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 02-Feb-2017 (ADH): Created.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
module Hoodoo
|
12
|
+
class TransientStore
|
13
|
+
|
14
|
+
# Mock back-end code used by tests to allow them to run without a
|
15
|
+
# dependency on the real engine (though the real engine is always
|
16
|
+
# recommended and Hoodoo core tests always cover both).
|
17
|
+
#
|
18
|
+
class Mocks
|
19
|
+
|
20
|
+
# Mock known uses of Dalli::Client with test implementations.
|
21
|
+
# Use explicitly, or as an RSpec implicit mock via something like
|
22
|
+
# this:
|
23
|
+
#
|
24
|
+
# allow( Dalli::Client ).to(
|
25
|
+
# receive( :new ).
|
26
|
+
# and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
27
|
+
# )
|
28
|
+
#
|
29
|
+
# ...whenever you need to stub out real Memcached. You will
|
30
|
+
# probably want to add:
|
31
|
+
#
|
32
|
+
# before :all do # (or ":each")
|
33
|
+
# Hoodoo::TransientStore::Mocks::DalliClient.reset()
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# ...to "clean out Memcached" before or between tests. You can
|
37
|
+
# check the contents of mock Memcached by examining ::store's
|
38
|
+
# hash of data.
|
39
|
+
#
|
40
|
+
# The test coverage for Hoodoo::TransientStore selects this backend in
|
41
|
+
# passing. Generally speaking you should favour Hoodoo::TransientStore
|
42
|
+
# over hard-coding to a storage engine available by the Hoodoo
|
43
|
+
# abstraction and, as a result, may never need this mock class at all.
|
44
|
+
#
|
45
|
+
class DalliClient
|
46
|
+
@@store = {}
|
47
|
+
|
48
|
+
# For test analysis, return the hash of 'memcached' mock data.
|
49
|
+
#
|
50
|
+
# Entries are referenced by the key you used to originally
|
51
|
+
# store them; values are hashes with ":expires_at" giving an
|
52
|
+
# expiry time or "nil" and ":value" giving your stored value.
|
53
|
+
#
|
54
|
+
def self.store
|
55
|
+
@@store
|
56
|
+
end
|
57
|
+
|
58
|
+
# Wipe out all saved data.
|
59
|
+
#
|
60
|
+
def self.reset
|
61
|
+
@@store = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Pass +true+ to bypass the mock client (subject to the caller
|
65
|
+
# reading ::bypass?) to e.g. get test code coverage on real
|
66
|
+
# Memcached. Pass +false+ otherwise.
|
67
|
+
#
|
68
|
+
# (Deprecated, but indefinitely maintained).
|
69
|
+
#
|
70
|
+
def self.bypass( bypass_boolean )
|
71
|
+
@@bypass = bypass_boolean
|
72
|
+
end
|
73
|
+
|
74
|
+
@@bypass = false
|
75
|
+
|
76
|
+
# If +true+, bypass this class and use real Dalli::Client; else
|
77
|
+
# don't. Default return value is +false+.
|
78
|
+
#
|
79
|
+
# (Deprecated, but indefinitely maintained).
|
80
|
+
#
|
81
|
+
def self.bypass?
|
82
|
+
@@bypass
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get the data stored under the given key. Returns +nil+ if
|
86
|
+
# not found / expired.
|
87
|
+
#
|
88
|
+
# +key+:: Key to look up (see #set).
|
89
|
+
#
|
90
|
+
def get( key )
|
91
|
+
data = @@store[ key ]
|
92
|
+
return nil if data.nil?
|
93
|
+
|
94
|
+
expires_at = data[ :expires_at ]
|
95
|
+
return nil unless expires_at.nil? || Time.now < expires_at
|
96
|
+
|
97
|
+
return data[ :value ]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set data for a given key.
|
101
|
+
#
|
102
|
+
# +key+:: Key under which to store data.
|
103
|
+
#
|
104
|
+
# +value+:: Data to store.
|
105
|
+
#
|
106
|
+
# +ttl+:: (Optional) time-to-live ('live' as in living, not as in
|
107
|
+
# 'live TV') - a value in seconds, after which the data is
|
108
|
+
# considered expired. If omitted, the data does not expire.
|
109
|
+
#
|
110
|
+
def set( key, value, ttl = nil )
|
111
|
+
data = {
|
112
|
+
:expires_at => ttl.nil? ? nil : Time.now.utc + ttl,
|
113
|
+
:value => value
|
114
|
+
}
|
115
|
+
|
116
|
+
@@store[ key ] = data
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
# Remove data for the given key.
|
121
|
+
#
|
122
|
+
def delete( key )
|
123
|
+
if @@store.has_key?( key )
|
124
|
+
@@store.delete( key )
|
125
|
+
true
|
126
|
+
else
|
127
|
+
false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Stub for 'closing' a connection.
|
132
|
+
#
|
133
|
+
def close; end
|
134
|
+
|
135
|
+
# Mock 'stats' health check.
|
136
|
+
#
|
137
|
+
def stats
|
138
|
+
|
139
|
+
# Should really be a Hash, but kept as 'true' in case any existing
|
140
|
+
# client depends on this; it isn't too important either way.
|
141
|
+
#
|
142
|
+
true
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: redis.rb
|
3
|
+
# (C):: Loyalty New Zealand 2017
|
4
|
+
#
|
5
|
+
# Purpose:: A mock/fake Redis client minimal implementation as an
|
6
|
+
# alternative test back-end for Redis-independent tests.
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 02-Feb-2017 (ADH): Created.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
module Hoodoo
|
12
|
+
class TransientStore
|
13
|
+
class Mocks
|
14
|
+
|
15
|
+
# Mock known uses of Redis with test implementations. Use explicitly,
|
16
|
+
# or as an RSpec implicit mock via something like this:
|
17
|
+
#
|
18
|
+
# allow( Redis ).to(
|
19
|
+
# receive( :new ).
|
20
|
+
# and_return( Hoodoo::TransientStore::Mocks::Redis.new )
|
21
|
+
# )
|
22
|
+
#
|
23
|
+
# ...whenever you need to stub out real Memcached. You will
|
24
|
+
# probably want to add:
|
25
|
+
#
|
26
|
+
# before :all do # (or ":each")
|
27
|
+
# Hoodoo::TransientStore::Mocks::Redis.reset()
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# ...to "clean out Memcached" before or between tests. You can
|
31
|
+
# check the contents of mock Memcached by examining ::store's
|
32
|
+
# hash of data.
|
33
|
+
#
|
34
|
+
# The test coverage for Hoodoo::TransientStore selects this backend in
|
35
|
+
# passing. Generally speaking you should favour Hoodoo::TransientStore
|
36
|
+
# over hard-coding to a storage engine available by the Hoodoo
|
37
|
+
# abstraction and, as a result, may never need this mock class at all.
|
38
|
+
#
|
39
|
+
class Redis
|
40
|
+
@@store = {}
|
41
|
+
|
42
|
+
# For test analysis, return the hash of 'memcached' mock data.
|
43
|
+
#
|
44
|
+
# Entries are referenced by the key you used to originally
|
45
|
+
# store them; values are hashes with ":expires_at" giving an
|
46
|
+
# expiry time or "nil" and ":value" giving your stored value.
|
47
|
+
#
|
48
|
+
def self.store
|
49
|
+
@@store
|
50
|
+
end
|
51
|
+
|
52
|
+
# Wipe out all saved data.
|
53
|
+
#
|
54
|
+
def self.reset
|
55
|
+
@@store = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get the data stored under the given key. Returns +nil+ if
|
59
|
+
# not found / expired.
|
60
|
+
#
|
61
|
+
# +key+:: Key to look up (see #set).
|
62
|
+
#
|
63
|
+
def get( key )
|
64
|
+
data = @@store[ key ]
|
65
|
+
return nil if data.nil?
|
66
|
+
|
67
|
+
expires_at = data[ :expires_at ]
|
68
|
+
return nil unless expires_at.nil? || Time.now < expires_at
|
69
|
+
|
70
|
+
return data[ :value ]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Alias for #get.
|
74
|
+
#
|
75
|
+
def []( key )
|
76
|
+
get( key )
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set data for a given key.
|
80
|
+
#
|
81
|
+
# +key+:: Key under which to store data.
|
82
|
+
#
|
83
|
+
# +value+:: Data to store.
|
84
|
+
#
|
85
|
+
# +ttl+:: (Optional) time-to-live ('live' as in living, not as in
|
86
|
+
# 'live TV') - a value in seconds, after which the data is
|
87
|
+
# considered expired. If omitted, the data does not expire.
|
88
|
+
#
|
89
|
+
def set( key, value, ttl = nil )
|
90
|
+
@@store[ key ] = { :value => value }
|
91
|
+
'OK'
|
92
|
+
end
|
93
|
+
|
94
|
+
# Alias for #set.
|
95
|
+
#
|
96
|
+
def []=( key, value )
|
97
|
+
set( key, value )
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set expiry time for a given key, which must exist.
|
101
|
+
#
|
102
|
+
# +ttl+:: time-to-live ('live' as in living, not as in 'live TV').
|
103
|
+
# A value in seconds, after which the data is considered
|
104
|
+
# expired.
|
105
|
+
#
|
106
|
+
def expire( key, ttl )
|
107
|
+
unless @@store.has_key?( key )
|
108
|
+
raise "Hoodoo::TransientStore::Mocks::Redis\#expire: Cannot find key '#{ key }'"
|
109
|
+
end
|
110
|
+
|
111
|
+
@@store[ key ][ :expires_at ] = Time.now.utc + ttl
|
112
|
+
true
|
113
|
+
end
|
114
|
+
|
115
|
+
# Remove data for the given key.
|
116
|
+
#
|
117
|
+
def del( key )
|
118
|
+
if @@store.has_key?( key )
|
119
|
+
@@store.delete( key )
|
120
|
+
1
|
121
|
+
else
|
122
|
+
0
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Stub for 'closing' a connection.
|
127
|
+
#
|
128
|
+
def quit; end
|
129
|
+
|
130
|
+
# Mock 'info' health check.
|
131
|
+
#
|
132
|
+
def info( command )
|
133
|
+
{ :alive => command }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|