cache_box 0.0.1.pre.preview8 → 0.0.1.pre.preview9
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/cache_box.gemspec +5 -1
- data/lib/cache_box.rb +2 -0
- data/lib/cache_box/scheduler/base.rb +109 -0
- data/lib/cache_box/scheduler/concurrent.rb +91 -0
- data/lib/cache_box/scheduler/serial.rb +27 -120
- data/lib/cache_box/stash.rb +43 -0
- data/lib/cache_box/storage/file.rb +23 -11
- data/lib/cache_box/storage/memory.rb +17 -5
- data/lib/cache_box/unit.rb +87 -58
- metadata +24 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca77451aab70af003b3741b676a17715213ffef2fc7caeeca3fce5aa960001a9
|
4
|
+
data.tar.gz: d43ccd11cb493f00c84131cf22eb69bf1e45794736ecf7bd650c043260effeb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e83cbd1efe8a0027d8ba3c8032d94400b75c968d78df484390a9168dded13fcd9b8e2625f9ce2a1c605bf672c967018375f18f0ca35e2fb85f82e6d0e4ae489
|
7
|
+
data.tar.gz: 55771fa58ca7e96b81b45e26ba7164e45435d04bea0010f8ce76696861e056d6a3c9ca06633673834d28b1bd8eaf59a5ecaa90b421d348f2355cbc5054fef790
|
data/cache_box.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'cache_box'
|
5
|
-
spec.version = '0.0.1-
|
5
|
+
spec.version = '0.0.1-preview9'
|
6
6
|
spec.authors = ['Codruț Constantin Gușoi']
|
7
7
|
spec.email = ['codrut.gusoi+rubygems.org@gmail.com']
|
8
8
|
|
@@ -20,12 +20,15 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.files = [
|
21
21
|
'lib/cache_box.rb',
|
22
22
|
'lib/cache_box/helper/validate.rb',
|
23
|
+
'lib/cache_box/scheduler/base.rb',
|
24
|
+
'lib/cache_box/scheduler/concurrent.rb',
|
23
25
|
'lib/cache_box/scheduler/serial.rb',
|
24
26
|
'lib/cache_box/storage/file.rb',
|
25
27
|
'lib/cache_box/storage/memory.rb',
|
26
28
|
'lib/cache_box/box.rb',
|
27
29
|
'lib/cache_box/chain.rb',
|
28
30
|
'lib/cache_box/graph.rb',
|
31
|
+
'lib/cache_box/stash.rb',
|
29
32
|
'lib/cache_box/unit.rb',
|
30
33
|
'LICENSE',
|
31
34
|
'cache_box.gemspec'
|
@@ -36,4 +39,5 @@ Gem::Specification.new do |spec|
|
|
36
39
|
spec.add_development_dependency 'pry-byebug', '~> 3.9'
|
37
40
|
spec.add_development_dependency 'rake', '~> 13.0'
|
38
41
|
spec.add_development_dependency 'rubocop', '~> 0.88'
|
42
|
+
spec.add_development_dependency 'simplecov', '~> 0.18'
|
39
43
|
end
|
data/lib/cache_box.rb
CHANGED
@@ -8,9 +8,11 @@ require_relative 'cache_box/helper/validate'
|
|
8
8
|
require_relative 'cache_box/storage/file'
|
9
9
|
require_relative 'cache_box/storage/memory'
|
10
10
|
|
11
|
+
require_relative 'cache_box/stash'
|
11
12
|
require_relative 'cache_box/box'
|
12
13
|
require_relative 'cache_box/unit'
|
13
14
|
|
15
|
+
require_relative 'cache_box/scheduler/concurrent'
|
14
16
|
require_relative 'cache_box/scheduler/serial'
|
15
17
|
|
16
18
|
require_relative 'cache_box/chain'
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CacheBox
|
4
|
+
module Scheduler
|
5
|
+
class Base
|
6
|
+
include CacheBox::Helper::Validate
|
7
|
+
|
8
|
+
# Input:
|
9
|
+
#
|
10
|
+
# logger = Logger
|
11
|
+
#
|
12
|
+
# Output: N/A
|
13
|
+
def initialize(logger: nil)
|
14
|
+
@logger = logger || Logger.new(STDOUT, level: Logger::INFO)
|
15
|
+
|
16
|
+
@lookup = {}
|
17
|
+
@needs = {}
|
18
|
+
@feeds = {}
|
19
|
+
@roots = []
|
20
|
+
@leaves = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Input:
|
24
|
+
#
|
25
|
+
# edge = String |
|
26
|
+
# Symbol |
|
27
|
+
# Hash{String | Symbol => String | Symbol | Array[String | Symbol]}
|
28
|
+
#
|
29
|
+
# Output: self
|
30
|
+
def add(edge)
|
31
|
+
validate_edge!(edge)
|
32
|
+
|
33
|
+
needs_o = extract_needs(edge)
|
34
|
+
needs_s = needs_o.map(&:to_s)
|
35
|
+
needs_s.each_with_index do |arg, index|
|
36
|
+
next if @lookup.key?(arg)
|
37
|
+
|
38
|
+
raise(
|
39
|
+
ArgumentError,
|
40
|
+
"Graph does not contain required node #{needs_o[index].inspect}"
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
name_o = extract_name(edge)
|
45
|
+
name_s = name_o.to_s
|
46
|
+
if @lookup.key?(name_s)
|
47
|
+
raise(
|
48
|
+
ArgumentError,
|
49
|
+
"Graph already contains a node named #{name_o.inspect}"
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
@lookup[name_s] = name_o
|
54
|
+
@needs[name_s] = needs_s
|
55
|
+
|
56
|
+
@leaves << name_s
|
57
|
+
needs_s.each do |need_s|
|
58
|
+
existing = @feeds[need_s]
|
59
|
+
|
60
|
+
@feeds[need_s] = existing ? existing << name_s : [name_s]
|
61
|
+
@leaves.delete(need_s)
|
62
|
+
end
|
63
|
+
@roots << name_s if needs_s == []
|
64
|
+
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Input:
|
69
|
+
#
|
70
|
+
# nodes = Hash{...String => Array[String | Symbol, Proc(Object)]}
|
71
|
+
# unit = CacheBox::Unit
|
72
|
+
#
|
73
|
+
# Output: Hash{...String | Symbol => Object}
|
74
|
+
def run!(_nodes, _unit)
|
75
|
+
raise(
|
76
|
+
NotImplementedError,
|
77
|
+
'The `#run/2` method needs to be implemented in your subclass!'
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Input: String | Symbol | Hash[1 String | Symbol => Object]
|
84
|
+
#
|
85
|
+
# Output: String | Symbol
|
86
|
+
def extract_name(edge)
|
87
|
+
return edge.dup if edge.is_a?(Symbol) || edge.is_a?(String)
|
88
|
+
|
89
|
+
edge.keys.first.dup
|
90
|
+
end
|
91
|
+
|
92
|
+
# Input: ?
|
93
|
+
#
|
94
|
+
# Output: Array[...String | Symbol]
|
95
|
+
def extract_needs(edge)
|
96
|
+
return [] unless edge.is_a?(Hash)
|
97
|
+
|
98
|
+
name = edge.keys.first
|
99
|
+
needs = edge[name]
|
100
|
+
|
101
|
+
if needs.is_a?(Array)
|
102
|
+
needs.map(&:dup)
|
103
|
+
else
|
104
|
+
[needs.dup]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
class CacheBox
|
6
|
+
module Scheduler
|
7
|
+
class Concurrent < Base
|
8
|
+
|
9
|
+
# TODO: write and use a thread pool
|
10
|
+
|
11
|
+
# Input:
|
12
|
+
#
|
13
|
+
# nodes = Hash{...String => Array[String | Symbol, Proc(Object)]}
|
14
|
+
# unit = CacheBox::Unit
|
15
|
+
#
|
16
|
+
# Output: Hash{...String | Symbol => Object}
|
17
|
+
def run!(nodes, unit)
|
18
|
+
work = @roots.dup
|
19
|
+
threads = []
|
20
|
+
|
21
|
+
condition = ConditionVariable.new
|
22
|
+
|
23
|
+
condition_lock = Mutex.new
|
24
|
+
scheduler_lock = Mutex.new
|
25
|
+
|
26
|
+
scheduler = Thread.new do
|
27
|
+
condition_lock.synchronize do
|
28
|
+
index = 0
|
29
|
+
loop do
|
30
|
+
stop = false
|
31
|
+
|
32
|
+
scheduler_lock.synchronize do
|
33
|
+
loop do
|
34
|
+
break if index >= work.size
|
35
|
+
|
36
|
+
name_s = work[index]
|
37
|
+
thread = Thread.new do
|
38
|
+
callable = nodes[name_s][1]
|
39
|
+
needs = @needs[name_s]
|
40
|
+
input = {}
|
41
|
+
|
42
|
+
scheduler_lock.synchronize do
|
43
|
+
needs.each do |need|
|
44
|
+
next unless unit.has?(need)
|
45
|
+
|
46
|
+
input[@lookup[need]] = unit.result(need)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
callable.call(input)
|
51
|
+
|
52
|
+
scheduler_lock.synchronize do
|
53
|
+
@feeds[name_s]&.each do |feed|
|
54
|
+
work.push(feed) unless work.include?(feed)
|
55
|
+
end
|
56
|
+
|
57
|
+
threads.delete(Thread.current)
|
58
|
+
end
|
59
|
+
|
60
|
+
condition_lock.synchronize do
|
61
|
+
condition.signal
|
62
|
+
end
|
63
|
+
end
|
64
|
+
threads << thread
|
65
|
+
|
66
|
+
index += 1
|
67
|
+
end
|
68
|
+
|
69
|
+
stop = true if threads.empty?
|
70
|
+
end
|
71
|
+
|
72
|
+
break if stop
|
73
|
+
|
74
|
+
condition.wait(condition_lock)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
scheduler.join
|
80
|
+
|
81
|
+
result = {}
|
82
|
+
@leaves.each do |name_s|
|
83
|
+
next unless unit.has?(name_s)
|
84
|
+
|
85
|
+
result[@lookup[name_s]] = unit.result(name_s)
|
86
|
+
end
|
87
|
+
result
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -1,93 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'base'
|
4
|
+
|
3
5
|
class CacheBox
|
4
6
|
module Scheduler
|
5
|
-
class Serial
|
6
|
-
include CacheBox::Helper::Validate
|
7
|
-
|
7
|
+
class Serial < Base
|
8
8
|
# Input:
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Output: N/A
|
13
|
-
def initialize(logger: nil)
|
14
|
-
@logger = logger || Logger.new(STDOUT, level: Logger::INFO)
|
15
|
-
|
16
|
-
@edges = {}
|
17
|
-
@names = {}
|
18
|
-
@leaves = {}
|
19
|
-
@plan = []
|
20
|
-
end
|
21
|
-
|
22
|
-
# Input:
|
23
|
-
#
|
24
|
-
# edge = String |
|
25
|
-
# Symbol |
|
26
|
-
# Hash{String | Symbol => String | Symbol | Array[String | Symbol]}
|
10
|
+
# nodes = Hash{...String => Array[String | Symbol, Proc(Object)]}
|
11
|
+
# unit = CacheBox::Unit
|
27
12
|
#
|
28
|
-
# Output:
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
needs_o = extract_needs(edge)
|
33
|
-
needs_s = needs_o.map(&:to_s)
|
34
|
-
needs_s.each_with_index do |arg, index|
|
35
|
-
next if @edges.key?(arg)
|
36
|
-
|
37
|
-
raise(
|
38
|
-
ArgumentError,
|
39
|
-
"Graph does not contain required node #{needs_o[index].inspect}"
|
40
|
-
)
|
41
|
-
end
|
13
|
+
# Output: Hash{...String | Symbol => Object}
|
14
|
+
def run!(nodes, unit)
|
15
|
+
plan = @leaves.dup.reverse
|
42
16
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise(
|
47
|
-
ArgumentError,
|
48
|
-
"Graph already contains a node named #{name_o.inspect}"
|
49
|
-
)
|
50
|
-
else
|
51
|
-
@edges[name_s] = needs_s
|
52
|
-
end
|
53
|
-
@names[name_s] = name_o
|
17
|
+
plan_index = 0
|
18
|
+
loop do
|
19
|
+
name_s = plan[plan_index]
|
54
20
|
|
55
|
-
|
21
|
+
unless unit.has?(name_s)
|
22
|
+
needs = @needs[name_s]
|
23
|
+
needs.reverse_each { |need| plan << need }
|
24
|
+
end
|
56
25
|
|
57
|
-
|
58
|
-
|
59
|
-
@leaves.delete(need_s)
|
26
|
+
plan_index += 1
|
27
|
+
break if plan_index >= plan.size
|
60
28
|
end
|
61
29
|
|
62
|
-
|
63
|
-
|
30
|
+
sequence = plan.reverse
|
31
|
+
sequence.each_with_index do |name_s, index|
|
32
|
+
next if unit.has?(name_s)
|
64
33
|
|
65
|
-
|
66
|
-
|
67
|
-
# nodes = Hash{...String => Array[String | Symbol, Proc(Object)]}
|
68
|
-
# unit = CacheBox::Unit
|
69
|
-
#
|
70
|
-
# Output: Hash{...String | Symbol => Object}
|
71
|
-
def run!(nodes, unit)
|
72
|
-
sequence = @plan.flatten
|
73
|
-
sequence.each_with_index do |name, index|
|
74
|
-
next if unit.has?(name)
|
75
|
-
|
76
|
-
callable = nodes[name][1]
|
77
|
-
needs = @edges[name]
|
34
|
+
callable = nodes[name_s][1]
|
35
|
+
needs = @needs[name_s]
|
78
36
|
input = {}
|
79
37
|
|
80
38
|
needs.each do |need|
|
81
39
|
next unless unit.has?(need)
|
82
40
|
|
83
|
-
input[@
|
41
|
+
input[@lookup[need]] = unit.result(need)
|
84
42
|
end
|
85
43
|
callable.call(input)
|
86
44
|
|
87
45
|
needs.each do |need|
|
88
46
|
needed = false
|
47
|
+
|
89
48
|
sequence[(index + 1)..-1].each do |item|
|
90
|
-
needed = true if @
|
49
|
+
needed = true if @needs[item].include?(need)
|
91
50
|
end
|
92
51
|
|
93
52
|
unit.clear(need) unless needed
|
@@ -95,65 +54,13 @@ class CacheBox
|
|
95
54
|
end
|
96
55
|
|
97
56
|
result = {}
|
98
|
-
@leaves.each do |name_s
|
57
|
+
@leaves.each do |name_s|
|
99
58
|
next unless unit.has?(name_s)
|
100
59
|
|
101
|
-
result[
|
60
|
+
result[@lookup[name_s]] = unit.result(name_s)
|
102
61
|
end
|
103
62
|
result
|
104
63
|
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
# Input: ?
|
109
|
-
#
|
110
|
-
# Output: ?
|
111
|
-
def add_to_plan(name, from)
|
112
|
-
result = 0
|
113
|
-
|
114
|
-
from.each do |node|
|
115
|
-
@plan.each_with_index do |row, index|
|
116
|
-
next unless row.include?(node)
|
117
|
-
|
118
|
-
result = index >= result ? index + 1 : result
|
119
|
-
break
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
@plan.each do |row|
|
124
|
-
next unless row.include?(from)
|
125
|
-
|
126
|
-
row.push(name)
|
127
|
-
break
|
128
|
-
end
|
129
|
-
|
130
|
-
if (row = @plan[result])
|
131
|
-
row.push(name)
|
132
|
-
else
|
133
|
-
@plan.push([name])
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# Input: String | Symbol | Hash[1 String | Symbol => Object]
|
138
|
-
#
|
139
|
-
# Output: String | Symbol
|
140
|
-
def extract_name(edge)
|
141
|
-
return edge if edge.is_a?(Symbol) || edge.is_a?(String)
|
142
|
-
|
143
|
-
edge.keys.first
|
144
|
-
end
|
145
|
-
|
146
|
-
# Input: ?
|
147
|
-
#
|
148
|
-
# Output: Array[...String | Symbol]
|
149
|
-
def extract_needs(edge)
|
150
|
-
return [] unless edge.is_a?(Hash)
|
151
|
-
|
152
|
-
name = edge.keys.first
|
153
|
-
needs = edge[name]
|
154
|
-
|
155
|
-
needs.is_a?(Array) ? needs : [needs]
|
156
|
-
end
|
157
64
|
end
|
158
65
|
end
|
159
66
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CacheBox
|
4
|
+
# A thread safe, in memory, key value storage.
|
5
|
+
#
|
6
|
+
# The object itself is thread safe but it's contents is not.
|
7
|
+
class Stash
|
8
|
+
def initialize(state = nil)
|
9
|
+
@lock = Mutex.new
|
10
|
+
@state = state || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
@lock.synchronize do
|
15
|
+
@state[key]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
@lock.synchronize do
|
21
|
+
@state[key] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete(key)
|
26
|
+
@lock.synchronize do
|
27
|
+
@state.delete(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def key?(key)
|
32
|
+
@lock.synchronize do
|
33
|
+
@state.key?(key)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def with
|
38
|
+
@lock.synchronize do
|
39
|
+
yield(@state)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -4,6 +4,8 @@ class CacheBox
|
|
4
4
|
module Storage
|
5
5
|
# A storage backed by files.
|
6
6
|
class File
|
7
|
+
LOCK = Mutex.new
|
8
|
+
|
7
9
|
# Input:
|
8
10
|
#
|
9
11
|
# namespace = Symbol | String # Default: 'namespace'
|
@@ -22,7 +24,9 @@ class CacheBox
|
|
22
24
|
|
23
25
|
# Output: self
|
24
26
|
def reset!
|
25
|
-
|
27
|
+
LOCK.synchronize do
|
28
|
+
FileUtils.remove_entry_secure(@path, true)
|
29
|
+
end
|
26
30
|
|
27
31
|
self
|
28
32
|
end
|
@@ -37,10 +41,12 @@ class CacheBox
|
|
37
41
|
def read!(name)
|
38
42
|
validate_string!(name, 'name')
|
39
43
|
|
40
|
-
|
41
|
-
|
44
|
+
LOCK.synchronize do
|
45
|
+
file = ::File.join(@path, name)
|
46
|
+
return unless ::File.exist?(file)
|
42
47
|
|
43
|
-
|
48
|
+
Marshal.load(::File.read(file))
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
46
52
|
# Input:
|
@@ -52,12 +58,14 @@ class CacheBox
|
|
52
58
|
def write!(name, data)
|
53
59
|
validate_string!(name, 'name')
|
54
60
|
|
55
|
-
FileUtils.mkdir_p(@path) unless Dir.exist?(@path)
|
56
|
-
|
57
61
|
content = Marshal.dump(data)
|
58
62
|
file = ::File.join(@path, name)
|
59
63
|
|
60
|
-
|
64
|
+
LOCK.synchronize do
|
65
|
+
FileUtils.mkdir_p(@path)
|
66
|
+
|
67
|
+
::File.write(file, content)
|
68
|
+
end
|
61
69
|
|
62
70
|
self
|
63
71
|
end
|
@@ -70,8 +78,10 @@ class CacheBox
|
|
70
78
|
def delete!(name)
|
71
79
|
validate_string!(name, 'name')
|
72
80
|
|
73
|
-
|
74
|
-
|
81
|
+
LOCK.synchronize do
|
82
|
+
file = ::File.join(@path, name)
|
83
|
+
FileUtils.remove_entry_secure(file, true)
|
84
|
+
end
|
75
85
|
|
76
86
|
self
|
77
87
|
end
|
@@ -84,8 +94,10 @@ class CacheBox
|
|
84
94
|
def has?(name)
|
85
95
|
validate_string!(name, 'name')
|
86
96
|
|
87
|
-
|
88
|
-
|
97
|
+
LOCK.synchronize do
|
98
|
+
file = ::File.join(@path, name)
|
99
|
+
::File.file?(file)
|
100
|
+
end
|
89
101
|
end
|
90
102
|
|
91
103
|
private
|
@@ -4,6 +4,8 @@ class CacheBox
|
|
4
4
|
module Storage
|
5
5
|
# A storage backed by an in memory Hash.
|
6
6
|
class Memory
|
7
|
+
LOCK = Mutex.new
|
8
|
+
|
7
9
|
# Accepts a Hash or any Hash-like object as argument. Will use a plain Hash
|
8
10
|
# if none provided.
|
9
11
|
#
|
@@ -15,7 +17,9 @@ class CacheBox
|
|
15
17
|
def initialize(state = nil)
|
16
18
|
validate!(state)
|
17
19
|
|
18
|
-
|
20
|
+
LOCK.synchronize do
|
21
|
+
@state = state || {}
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
# Accepts a Hash or any Hash-like object as argument. Will use a plain Hash
|
@@ -40,7 +44,9 @@ class CacheBox
|
|
40
44
|
def read!(name)
|
41
45
|
validate_string!(name, 'name')
|
42
46
|
|
43
|
-
|
47
|
+
LOCK.synchronize do
|
48
|
+
@state[name]
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
46
52
|
# Input:
|
@@ -52,7 +58,9 @@ class CacheBox
|
|
52
58
|
def write!(name, data)
|
53
59
|
validate_string!(name, 'name')
|
54
60
|
|
55
|
-
|
61
|
+
LOCK.synchronize do
|
62
|
+
@state[name] = data
|
63
|
+
end
|
56
64
|
|
57
65
|
self
|
58
66
|
end
|
@@ -65,7 +73,9 @@ class CacheBox
|
|
65
73
|
def delete!(name)
|
66
74
|
validate_string!(name, 'name')
|
67
75
|
|
68
|
-
|
76
|
+
LOCK.synchronize do
|
77
|
+
@state.delete(name)
|
78
|
+
end
|
69
79
|
|
70
80
|
self
|
71
81
|
end
|
@@ -78,7 +88,9 @@ class CacheBox
|
|
78
88
|
def has?(name)
|
79
89
|
validate_string!(name, 'name')
|
80
90
|
|
81
|
-
|
91
|
+
LOCK.synchronize do
|
92
|
+
@state.key?(name)
|
93
|
+
end
|
82
94
|
end
|
83
95
|
|
84
96
|
private
|
data/lib/cache_box/unit.rb
CHANGED
@@ -4,6 +4,8 @@ class CacheBox
|
|
4
4
|
class Unit
|
5
5
|
include CacheBox::Helper::Validate
|
6
6
|
|
7
|
+
LOCK = Mutex.new
|
8
|
+
|
7
9
|
# Input:
|
8
10
|
#
|
9
11
|
# namespace = String | Symbol # Default: :namespace
|
@@ -13,11 +15,14 @@ class CacheBox
|
|
13
15
|
namespace ||= :namespace
|
14
16
|
validate_string_or_symbol!(namespace, 'namespace')
|
15
17
|
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@
|
18
|
+
@namespace_o = namespace
|
19
|
+
@namespace_s = namespace.to_s
|
20
|
+
@logger = logger || Logger.new(STDOUT, level: Logger::INFO)
|
21
|
+
@storage = storage || ::CacheBox::Storage::File.new(namespace: @namespace_o)
|
19
22
|
|
20
|
-
|
23
|
+
LOCK.synchronize do
|
24
|
+
@state = { result: ::CacheBox::Stash.new, stash: ::CacheBox::Stash.new }
|
25
|
+
end
|
21
26
|
end
|
22
27
|
|
23
28
|
# Input:
|
@@ -35,39 +40,50 @@ class CacheBox
|
|
35
40
|
|
36
41
|
name_o = name
|
37
42
|
name_s = name.to_s
|
43
|
+
box = nil
|
38
44
|
|
39
|
-
|
45
|
+
LOCK.synchronize do
|
46
|
+
return @state[:result][name_s] if @state[:result].key?(name_s)
|
40
47
|
|
41
|
-
|
42
|
-
|
43
|
-
|
48
|
+
data = @storage.read!(name_s)
|
49
|
+
if data&.key?(:result)
|
50
|
+
@state[:result][name_s] = data[:result]
|
44
51
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
52
|
+
return data[:result]
|
53
|
+
elsif data&.key?(:stash)
|
54
|
+
@state[:stash][name_s] = data[:stash]
|
55
|
+
end
|
49
56
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
box = ::CacheBox::Box.new(
|
58
|
+
namespace: @namespace_o.dup,
|
59
|
+
name: name_o.dup,
|
60
|
+
args: args,
|
61
|
+
input: input,
|
62
|
+
stash: @state[:stash][name] || ::CacheBox::Stash.new,
|
63
|
+
logger: @logger
|
64
|
+
)
|
65
|
+
end
|
58
66
|
|
59
67
|
begin
|
60
|
-
|
68
|
+
result = block.call(box)
|
69
|
+
|
70
|
+
LOCK.synchronize do
|
71
|
+
@state[:result][name_s] = result
|
72
|
+
end
|
61
73
|
ensure
|
62
|
-
|
63
|
-
@state[:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
74
|
+
LOCK.synchronize do
|
75
|
+
if @state[:result].key?(name_s)
|
76
|
+
@state[:stash].delete(name_s)
|
77
|
+
@storage.write!(name_s, { result: @state[:result][name_s] })
|
78
|
+
else
|
79
|
+
stash = box.stash
|
80
|
+
stash.with do |state|
|
81
|
+
dump = { stash: state }
|
82
|
+
|
83
|
+
@state[:stash][name_s] = stash
|
84
|
+
@storage.write!(name_s, dump)
|
85
|
+
end
|
86
|
+
end
|
71
87
|
end
|
72
88
|
end
|
73
89
|
end
|
@@ -82,17 +98,19 @@ class CacheBox
|
|
82
98
|
validate_string_or_symbol!(name, 'name')
|
83
99
|
name = name.to_s
|
84
100
|
|
85
|
-
|
101
|
+
LOCK.synchronize do
|
102
|
+
return true if @state[:result].key?(name)
|
86
103
|
|
87
|
-
|
104
|
+
data = @storage.read!(name)
|
88
105
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
106
|
+
if data&.key?(:result)
|
107
|
+
@state[:result][name] = data[:result]
|
108
|
+
elsif data&.key?(:stash)
|
109
|
+
@state[:stash][name] = data[:stash]
|
110
|
+
end
|
94
111
|
|
95
|
-
|
112
|
+
@state[:result].key?(name)
|
113
|
+
end
|
96
114
|
end
|
97
115
|
|
98
116
|
# Input:
|
@@ -105,17 +123,19 @@ class CacheBox
|
|
105
123
|
validate_string_or_symbol!(name, 'name')
|
106
124
|
name = name.to_s
|
107
125
|
|
108
|
-
|
126
|
+
LOCK.synchronize do
|
127
|
+
return @state[:result][name] if @state[:result].key?(name)
|
109
128
|
|
110
|
-
|
129
|
+
data = @storage.read!(name)
|
111
130
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
131
|
+
if data&.key?(:result)
|
132
|
+
@state[:result][name] = data[:result]
|
133
|
+
elsif data&.key?(:stash)
|
134
|
+
@state[:stash][name] = data[:stash]
|
135
|
+
end
|
117
136
|
|
118
|
-
|
137
|
+
@state[:result][name]
|
138
|
+
end
|
119
139
|
end
|
120
140
|
|
121
141
|
# Input:
|
@@ -127,16 +147,21 @@ class CacheBox
|
|
127
147
|
validate_array_of_string_or_symbol!(names, 'names')
|
128
148
|
|
129
149
|
if names.empty?
|
130
|
-
|
150
|
+
LOCK.synchronize do
|
151
|
+
@state = { result: ::CacheBox::Stash.new, stash: ::CacheBox::Stash.new }
|
131
152
|
|
132
|
-
|
153
|
+
@storage.reset!
|
154
|
+
end
|
133
155
|
else
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
@state[:stash].delete(name)
|
156
|
+
LOCK.synchronize do
|
157
|
+
names.each do |name_o|
|
158
|
+
name_s = name_o.to_s
|
138
159
|
|
139
|
-
|
160
|
+
@state[:result].delete(name_s)
|
161
|
+
@state[:stash].delete(name_s)
|
162
|
+
|
163
|
+
@storage.delete!(name_s)
|
164
|
+
end
|
140
165
|
end
|
141
166
|
end
|
142
167
|
|
@@ -152,13 +177,17 @@ class CacheBox
|
|
152
177
|
validate_array_of_string_or_symbol!(names, 'names')
|
153
178
|
|
154
179
|
if names.empty?
|
155
|
-
|
180
|
+
LOCK.synchronize do
|
181
|
+
@state = { result: ::CacheBox::Stash.new, stash: ::CacheBox::Stash.new }
|
182
|
+
end
|
156
183
|
else
|
157
|
-
|
158
|
-
|
184
|
+
LOCK.synchronize do
|
185
|
+
names.each do |name_o|
|
186
|
+
name_s = name_o.to_s
|
159
187
|
|
160
|
-
|
161
|
-
|
188
|
+
@state[:result].delete(name_s)
|
189
|
+
@state[:stash].delete(name_s)
|
190
|
+
end
|
162
191
|
end
|
163
192
|
end
|
164
193
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cache_box
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.pre.
|
4
|
+
version: 0.0.1.pre.preview9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Codruț Constantin Gușoi
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -66,7 +66,21 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0.88'
|
69
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.18'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.18'
|
83
|
+
description:
|
70
84
|
email:
|
71
85
|
- codrut.gusoi+rubygems.org@gmail.com
|
72
86
|
executables: []
|
@@ -80,7 +94,10 @@ files:
|
|
80
94
|
- lib/cache_box/chain.rb
|
81
95
|
- lib/cache_box/graph.rb
|
82
96
|
- lib/cache_box/helper/validate.rb
|
97
|
+
- lib/cache_box/scheduler/base.rb
|
98
|
+
- lib/cache_box/scheduler/concurrent.rb
|
83
99
|
- lib/cache_box/scheduler/serial.rb
|
100
|
+
- lib/cache_box/stash.rb
|
84
101
|
- lib/cache_box/storage/file.rb
|
85
102
|
- lib/cache_box/storage/memory.rb
|
86
103
|
- lib/cache_box/unit.rb
|
@@ -90,7 +107,7 @@ licenses:
|
|
90
107
|
metadata:
|
91
108
|
homepage_uri: https://gitlab.com/sdwolfz/cache_box_rb
|
92
109
|
source_code_uri: https://gitlab.com/sdwolfz/cache_box_rb
|
93
|
-
post_install_message:
|
110
|
+
post_install_message:
|
94
111
|
rdoc_options: []
|
95
112
|
require_paths:
|
96
113
|
- lib
|
@@ -105,8 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
122
|
- !ruby/object:Gem::Version
|
106
123
|
version: 1.3.1
|
107
124
|
requirements: []
|
108
|
-
rubygems_version: 3.1.
|
109
|
-
signing_key:
|
125
|
+
rubygems_version: 3.1.2
|
126
|
+
signing_key:
|
110
127
|
specification_version: 4
|
111
128
|
summary: A simple, fast, and easy to use local cache.
|
112
129
|
test_files: []
|