cache_box 0.0.1.pre.preview8 → 0.0.1.pre.preview9
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|