async_storage 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffa57bbe53ad587cdd628ce556aaf679ed9b4ff02f5a9b8c4217be4b6ffa495d
4
- data.tar.gz: 5557aa324800a20d599a94ddcbf21894f8ffa3ccc97672f7e0329d5283090d84
3
+ metadata.gz: add79afe0dfa294abeee5fb5102e05f5d5fbecfc70d700c5b25fc4f17cf9881f
4
+ data.tar.gz: e0a8841f106ac58f7146938749732c43d99e0f56dacfcc4816d62c251bf34d2a
5
5
  SHA512:
6
- metadata.gz: f6f33f4f1d4728c5933c7b312daa602a14eb2408f40168a8b5a3f490e8853e1b84464cc698fb6a3c6721f4bc0b72fb1dae0c4341bec2c481ce46f1acb5128718
7
- data.tar.gz: 0d976a57d701639a1e46b3f9d236b3e983ac6a652e96e6ac481475f1293326251e0f4e1045ed945027ed6cd26f552fa9ec5793b2337fa6179ebc380f6fb42651
6
+ metadata.gz: 28800339448b6668b3da60bb5ad80d5e998c26ef42ebd3c06c881d49b09bfe7a8aed7893323578f3c0171183f705a5ca58fc9b871aa420425f161896e0e02d2a
7
+ data.tar.gz: dff1ad907077f87c95977f14b56c4a8c9d49f394f781a1c164a53266c5a0ee3cae45078728b41b1020d47b3bb9c7949bf9e5286f83c21fc8e39c49735a286c36
data/.gitignore CHANGED
@@ -8,4 +8,4 @@
8
8
  /tmp/
9
9
  .rspec_status
10
10
  .env
11
-
11
+ async_storage-*.gem
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- async_storage (0.1.0)
4
+ async_storage (0.0.1)
5
5
  multi_json (> 0.0.0)
6
6
  redis (> 0.0.0)
7
7
 
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'async_storage/naming'
4
+
5
+ module AsyncStorage
6
+ class Allocator
7
+ CTRL = {
8
+ enqueued: '0',
9
+ executed: '1',
10
+ missing: nil,
11
+ }.freeze
12
+
13
+ extend Forwardable
14
+ def_delegators :@repo, :resolver_class, :expires_in
15
+
16
+ attr_reader :naming
17
+
18
+ # @param repo [AsyncStorage::Repo] An instance of Repo
19
+ # @param args [Array] An array with arguments to be fowarded to resolver#call
20
+ def initialize(repo, *args)
21
+ @repo = repo
22
+ @args = args
23
+ @naming = AsyncStorage::Naming.new(repo.resolver_class, *args)
24
+ # It's different than the config.namespace.
25
+ # Thinking about a directory structure.. The global namespace would be the root directory.
26
+ # And the namespace under Repo level would be the subdirectory.
27
+ @naming.prefix = repo.namespace if repo.namespace
28
+ end
29
+
30
+ # Async get value with a given key
31
+ #
32
+ # @return [Object, NilClass] Return both stale or fresh object. If does not exist async call the retriever and return nil
33
+ def get
34
+ connection do |redis|
35
+ raw_head = redis.get(naming.head)
36
+ case raw_head
37
+ when CTRL[:executed], CTRL[:enqueued]
38
+ read_body(redis) # Try to deliver stale content
39
+ when CTRL[:missing]
40
+ return update!(redis) unless async?
41
+
42
+ perform_async(redis) # Enqueue background job to resolve content
43
+ redis.set(naming.head, CTRL[:enqueued])
44
+ read_body(redis) # Try to deliver stale content
45
+ else
46
+ raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
47
+ end
48
+ end
49
+ end
50
+
51
+ # Sync get value with a given value
52
+ #
53
+ # @return [Object] Return the result from resolver
54
+ def get!
55
+ connection do |redis|
56
+ raw_head = redis.get(naming.head)
57
+ case raw_head
58
+ when CTRL[:executed]
59
+ read_body(redis) || begin
60
+ update!(redis) unless redis.exists?(naming.body)
61
+ end
62
+ when CTRL[:missing], CTRL[:enqueued]
63
+ update!(redis)
64
+ else
65
+ raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
66
+ end
67
+ end
68
+ end
69
+
70
+ # Expire object the object with a given key. The stale object will not be removed
71
+ #
72
+ # @return [Boolean] True or False according to the object existence
73
+ def invalidate
74
+ connection do |redis|
75
+ redis.del(naming.head) == 1
76
+ end
77
+ end
78
+
79
+ # Delete object with a given key.
80
+ #
81
+ # @return [Boolean] True or False according to the object existence
82
+ def invalidate!
83
+ connection do |redis|
84
+ redis.multi do |cli|
85
+ cli.del(naming.body)
86
+ cli.del(naming.head)
87
+ end.include?(1)
88
+ end
89
+ end
90
+
91
+ # Invalidate object with the given key and update content according to the strategy
92
+ #
93
+ # @return [Object, NilClass] Stale object or nil when it does not exist
94
+ def refresh
95
+ value = get(*@args)
96
+ invalidate(*@args)
97
+ value
98
+ end
99
+
100
+ # Fetch data from resolver and store it into redis
101
+ #
102
+ # @return [Object] Return the result from resolver
103
+ def refresh!
104
+ connection { |redis| update!(redis) }
105
+ end
106
+
107
+ # Check if a fresh value exist.
108
+ #
109
+ # @return [Boolean] True or False according the object existence
110
+ def exist?
111
+ connection { |redis| redis.exists?(naming.head) && redis.exists?(naming.body) }
112
+ end
113
+
114
+ # Check if object with a given key is stale
115
+ #
116
+ # @return [NilClass, Boolean] Return nil if the object does not exist or true/false according to the object freshness state
117
+ def stale?
118
+ connection { |redis| redis.exists?(naming.body) && redis.ttl(naming.head) < 0 }
119
+ end
120
+
121
+ # Check if a fresh object exists into the storage
122
+ #
123
+ # @return [Boolean] true/false according to the object existence and freshness
124
+ def fresh?
125
+ connection { |redis| redis.exists?(naming.body) && redis.ttl(naming.head) > 0 }
126
+ end
127
+
128
+ private
129
+
130
+ def async?
131
+ false
132
+ end
133
+
134
+ def perform_async(redis)
135
+ # @TODO Enqueue a real background job here. It's only working on sync mode
136
+ # redis.set(name.head, CTRL[:enqueued])
137
+ refresh!
138
+ end
139
+
140
+ def update!(redis)
141
+ payload = resolver_class.new.(*@args)
142
+
143
+ json = AsyncStorage::JSON.dump(payload, mode: :compat)
144
+ redis.multi do |cli|
145
+ cli.set(naming.body, json)
146
+ cli.set(naming.head, CTRL[:executed])
147
+ cli.expire(naming.head, expires_in) if expires_in
148
+ end
149
+ AsyncStorage::JSON.load(json)
150
+ end
151
+
152
+ def read_body(redis)
153
+ raw = redis.get(naming.body)
154
+ return unless raw
155
+
156
+ AsyncStorage::JSON.load(raw)
157
+ end
158
+
159
+ def connection
160
+ return unless block_given?
161
+
162
+ AsyncStorage.redis_pool.with { |redis| yield(redis) }
163
+ end
164
+ end
165
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'async_storage/naming'
3
+ require 'async_storage/allocator'
4
4
 
5
5
  module AsyncStorage
6
6
  class Repo
@@ -36,150 +36,81 @@ module AsyncStorage
36
36
  #
37
37
  # @return [Object, NilClass] Return both stale or fresh object. If does not exist async call the retriever and return nil
38
38
  def get(*args)
39
- connection(*args) do |redis, naming|
40
- raw_head = redis.get(naming.head)
41
- case raw_head
42
- when CTRL[:executed], CTRL[:enqueued]
43
- read(redis, naming.body) # Try to deliver stale content
44
- when CTRL[:missing]
45
- return update!(redis, naming, *args) unless async?
46
-
47
- perform_async(*args) # Enqueue background job to resolve content
48
- redis.set(naming.head, CTRL[:enqueued])
49
- read(redis, naming.body) # Try to deliver stale content
50
- else
51
- raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
52
- end
53
- end
39
+ alloc(*args).get
54
40
  end
55
41
 
56
42
  # Sync get value with a given value
57
43
  #
58
44
  # @return [Object] Return the result from resolver
59
45
  def get!(*args)
60
- connection(*args) do |redis, naming|
61
- raw_head = redis.get(naming.head)
62
- case raw_head
63
- when CTRL[:executed]
64
- read(redis, naming.body) || begin
65
- update!(redis, naming, *args) unless redis.exists?(naming.body)
66
- end
67
- when CTRL[:missing], CTRL[:enqueued]
68
- update!(redis, naming, *args)
69
- else
70
- raise AsyncStorage::Error, format('the key %<k>s have an invalid value. Only "1" or "0" values are expected. And we got %<v>p', v: raw_head, k: naming.head)
71
- end
72
- end
46
+ alloc(*args).get!
73
47
  end
74
48
 
75
49
  # Expire object the object with a given key. The stale object will not be removed
76
50
  #
77
51
  # @return [Boolean] True or False according to the object existence
78
52
  def invalidate(*args)
79
- connection(*args) do |redis, naming|
80
- redis.del(naming.head) == 1
81
- end
53
+ alloc(*args).invalidate
82
54
  end
83
55
 
84
56
  # Delete object with a given key.
85
57
  #
86
58
  # @return [Boolean] True or False according to the object existence
87
59
  def invalidate!(*args)
88
- connection(*args) do |redis, naming|
89
- redis.multi do |cli|
90
- cli.del(naming.body)
91
- cli.del(naming.head)
92
- end.include?(1)
93
- end
60
+ alloc(*args).invalidate!
94
61
  end
95
62
 
96
63
  # Invalidate object with the given key and update content according to the strategy
97
64
  #
98
65
  # @return [Object, NilClass] Stale object or nil when it does not exist
99
66
  def refresh(*args)
100
- value = get(*args)
101
- invalidate(*args)
102
- value
67
+ alloc(*args).refresh
103
68
  end
104
69
 
105
70
  # Fetch data from resolver and store it into redis
106
71
  #
107
72
  # @return [Object] Return the result from resolver
108
73
  def refresh!(*args)
109
- connection(*args) { |redis, naming| update!(redis, naming, *args) }
74
+ alloc(*args).refresh!
110
75
  end
111
76
 
112
77
  # Check if a fresh value exist.
113
78
  #
114
79
  # @return [Boolean] True or False according the object existence
115
80
  def exist?(*args)
116
- connection(*args) { |redis, naming| redis.exists?(naming.head) && redis.exists?(naming.body) }
81
+ alloc(*args).exist?
117
82
  end
118
83
 
119
84
  # Check if object with a given key is stale
120
85
  #
121
86
  # @return [NilClass, Boolean] Return nil if the object does not exist or true/false according to the object freshness state
122
87
  def stale?(*args)
123
- connection(*args) { |redis, naming| redis.exists?(naming.body) && redis.ttl(naming.head) < 0 }
88
+ alloc(*args).stale?
124
89
  end
125
90
 
126
91
  # Check if a fresh object exists into the storage
127
92
  #
128
93
  # @return [Boolean] true/false according to the object existence and freshness
129
94
  def fresh?(*args)
130
- connection(*args) { |redis, naming| redis.exists?(naming.body) && redis.ttl(naming.head) > 0 }
131
- end
132
-
133
- private
134
-
135
- def async?
136
- false
137
- end
138
-
139
- def perform_async(*args)
140
- # @TODO Enqueue a real background job here. It's only working on sync mode
141
- # redis.set(name.head, CTRL[:enqueued])
142
- refresh!(*args)
95
+ alloc(*args).fresh?
143
96
  end
144
97
 
145
- def update!(redis, naming, *args)
146
- payload = resolver_class.new.(*args)
147
-
148
- json = AsyncStorage::JSON.dump(payload, mode: :compat)
149
- naming = build_naming(*args)
150
- redis.multi do |cli|
151
- cli.set(naming.body, json)
152
- cli.set(naming.head, CTRL[:executed])
153
- cli.expire(naming.head, expires_in) if expires_in
154
- end
155
- AsyncStorage::JSON.load(json)
156
- end
157
-
158
- def read(redis, key)
159
- return unless key
160
-
161
- raw = redis.get(key)
162
- return unless raw
163
-
164
- AsyncStorage::JSON.load(raw)
98
+ # Build an Allocator instance
99
+ #
100
+ # @param [*Array] list of parameters to be forwaded to the resolver#call
101
+ def alloc(*args)
102
+ Allocator.new(self, *args)
165
103
  end
166
104
 
167
105
  def expires_in
168
106
  @options[:expires_in] || AsyncStorage.config.expires_in
169
107
  end
170
108
 
171
- def connection(*args)
172
- return unless block_given?
173
-
174
- naming = build_naming(*args)
175
- AsyncStorage.redis_pool.with { |redis| yield(redis, naming) }
109
+ def namespace
110
+ @options[:namespace]
176
111
  end
177
112
 
178
- def build_naming(*args)
179
- naming = AsyncStorage::Naming.new(resolver_class, *args)
180
- naming.prefix = @options[:namespace] if @options[:namespace]
181
- naming
182
- end
113
+ protected
183
114
 
184
115
  def validate_resolver_class!(klass)
185
116
  unless klass.is_a?(Class)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsyncStorage
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async_storage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcos G. Zimmermann
@@ -57,6 +57,7 @@ files:
57
57
  - bin/console
58
58
  - bin/setup
59
59
  - lib/async_storage.rb
60
+ - lib/async_storage/allocator.rb
60
61
  - lib/async_storage/bath_actions.rb
61
62
  - lib/async_storage/config.rb
62
63
  - lib/async_storage/json.rb