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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2121d192ceadcf66d3ddcea5bbed36e4b90d5a34749e50b3f23672f61cb8a7ea
4
- data.tar.gz: b5c009712c162fa0b761ff4b8998645aed7a09e467e6569fbc785b76ac166b78
3
+ metadata.gz: ca77451aab70af003b3741b676a17715213ffef2fc7caeeca3fce5aa960001a9
4
+ data.tar.gz: d43ccd11cb493f00c84131cf22eb69bf1e45794736ecf7bd650c043260effeb0
5
5
  SHA512:
6
- metadata.gz: c4c45937029f9096bc8a8224decec4ab72faa98569043b1292d19e050826a92a8be57bdf26612e2800f82712654a444b03c66327e9a4baa5f8110444967397c3
7
- data.tar.gz: 97dece56e4b22094415960285047136b15a1e02bea647b0c8b41eb2b762b661ef301f81b7acc376b27cc9027a4c86c7403971656cf824e9e8debc59439922ad0
6
+ metadata.gz: 7e83cbd1efe8a0027d8ba3c8032d94400b75c968d78df484390a9168dded13fcd9b8e2625f9ce2a1c605bf672c967018375f18f0ca35e2fb85f82e6d0e4ae489
7
+ data.tar.gz: 55771fa58ca7e96b81b45e26ba7164e45435d04bea0010f8ce76696861e056d6a3c9ca06633673834d28b1bd8eaf59a5ecaa90b421d348f2355cbc5054fef790
@@ -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-preview8'
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
@@ -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
- # logger = Logger
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: self
29
- def add(edge)
30
- validate_edge!(edge)
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
- name_o = extract_name(edge)
44
- name_s = name_o.to_s
45
- if @edges.key?(name_s)
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
- add_to_plan(name_s, needs_s)
21
+ unless unit.has?(name_s)
22
+ needs = @needs[name_s]
23
+ needs.reverse_each { |need| plan << need }
24
+ end
56
25
 
57
- @leaves[name_s] = name_o
58
- needs_s.each do |need_s|
59
- @leaves.delete(need_s)
26
+ plan_index += 1
27
+ break if plan_index >= plan.size
60
28
  end
61
29
 
62
- self
63
- end
30
+ sequence = plan.reverse
31
+ sequence.each_with_index do |name_s, index|
32
+ next if unit.has?(name_s)
64
33
 
65
- # Input:
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[@names[need]] = unit.result(need)
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 @edges[item].include?(need)
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, name_o|
57
+ @leaves.each do |name_s|
99
58
  next unless unit.has?(name_s)
100
59
 
101
- result[name_o] = unit.result(name_s)
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
- FileUtils.remove_entry_secure(@path, true)
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
- file = ::File.join(@path, name)
41
- return unless ::File.exist?(file)
44
+ LOCK.synchronize do
45
+ file = ::File.join(@path, name)
46
+ return unless ::File.exist?(file)
42
47
 
43
- Marshal.load(::File.read(file))
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
- ::File.write(file, content)
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
- file = ::File.join(@path, name)
74
- FileUtils.remove_entry_secure(file, true)
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
- file = ::File.join(@path, name)
88
- ::File.file?(file)
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
- @state = state || {}
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
- @state[name]
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
- @state[name] = data
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
- @state.delete(name)
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
- @state.key?(name)
91
+ LOCK.synchronize do
92
+ @state.key?(name)
93
+ end
82
94
  end
83
95
 
84
96
  private
@@ -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
- @namespace = namespace
17
- @logger = logger || Logger.new(STDOUT, level: Logger::INFO)
18
- @storage = storage || ::CacheBox::Storage::File.new(namespace: @namespace)
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
- @state = { result: {}, stash: {} }
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
- return @state[:result][name_s] if @state[:result].key?(name_s)
45
+ LOCK.synchronize do
46
+ return @state[:result][name_s] if @state[:result].key?(name_s)
40
47
 
41
- data = @storage.read!(name_s)
42
- if data&.key?(:result)
43
- @state[:result][name_s] = data[:result]
48
+ data = @storage.read!(name_s)
49
+ if data&.key?(:result)
50
+ @state[:result][name_s] = data[:result]
44
51
 
45
- return data[:result]
46
- elsif data&.key?(:stash)
47
- @state[:stash][name_s] = data[:stash]
48
- end
52
+ return data[:result]
53
+ elsif data&.key?(:stash)
54
+ @state[:stash][name_s] = data[:stash]
55
+ end
49
56
 
50
- box = ::CacheBox::Box.new(
51
- namespace: @namespace.dup,
52
- name: name_o.dup,
53
- args: args,
54
- input: input,
55
- stash: @state[:stash][name] || {},
56
- logger: @logger
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
- @state[:result][name_s] = block.call(box)
68
+ result = block.call(box)
69
+
70
+ LOCK.synchronize do
71
+ @state[:result][name_s] = result
72
+ end
61
73
  ensure
62
- if @state[:result].key?(name_s)
63
- @state[:stash].delete(name_s)
64
- @storage.write!(name_s, { result: @state[:result][name_s] })
65
- else
66
- stash = box.stash
67
- dump = { stash: stash }
68
-
69
- @state[:stash][name_s] = stash
70
- @storage.write!(name_s, dump)
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
- return true if @state[:result].key?(name)
101
+ LOCK.synchronize do
102
+ return true if @state[:result].key?(name)
86
103
 
87
- data = @storage.read!(name)
104
+ data = @storage.read!(name)
88
105
 
89
- if data&.key?(:result)
90
- @state[:result][name] = data[:result]
91
- elsif data&.key?(:stash)
92
- @state[:stash][name] = data[:stash]
93
- end
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
- @state[:result].key?(name)
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
- return @state[:result][name] if @state[:result].key?(name)
126
+ LOCK.synchronize do
127
+ return @state[:result][name] if @state[:result].key?(name)
109
128
 
110
- data = @storage.read!(name)
129
+ data = @storage.read!(name)
111
130
 
112
- if data&.key?(:result)
113
- @state[:result][name] = data[:result]
114
- elsif data&.key?(:stash)
115
- @state[:stash][name] = data[:stash]
116
- end
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
- @state[:result][name]
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
- @state = { result: {}, stash: {} }
150
+ LOCK.synchronize do
151
+ @state = { result: ::CacheBox::Stash.new, stash: ::CacheBox::Stash.new }
131
152
 
132
- @storage.reset!
153
+ @storage.reset!
154
+ end
133
155
  else
134
- names.each do |name|
135
- name = name.to_s
136
- @state[:result].delete(name)
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
- @storage.delete!(name)
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
- @state = { result: {}, stash: {} }
180
+ LOCK.synchronize do
181
+ @state = { result: ::CacheBox::Stash.new, stash: ::CacheBox::Stash.new }
182
+ end
156
183
  else
157
- names.each do |name_o|
158
- name_s = name_o.to_s
184
+ LOCK.synchronize do
185
+ names.each do |name_o|
186
+ name_s = name_o.to_s
159
187
 
160
- @state[:result].delete(name_s)
161
- @state[:stash].delete(name_s)
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.preview8
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-07-26 00:00:00.000000000 Z
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
- description:
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.3
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: []