async_storage 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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