evt-entity_cache 0.13.0.0 → 0.14.0.0

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/entity_cache.rb +17 -15
  3. data/lib/entity_cache/controls.rb +12 -3
  4. data/lib/entity_cache/controls/entity.rb +27 -1
  5. data/lib/entity_cache/controls/persist_interval.rb +2 -22
  6. data/lib/entity_cache/controls/record.rb +33 -12
  7. data/lib/entity_cache/controls/storage/persistent.rb +8 -26
  8. data/lib/entity_cache/controls/storage/persistent/example.rb +41 -0
  9. data/lib/entity_cache/controls/storage/persistent/not_implemented.rb +19 -0
  10. data/lib/entity_cache/controls/storage/persistent/write.rb +25 -0
  11. data/lib/entity_cache/controls/storage/temporary.rb +14 -2
  12. data/lib/entity_cache/controls/subject.rb +2 -4
  13. data/lib/entity_cache/controls/time.rb +1 -12
  14. data/lib/entity_cache/controls/version.rb +5 -11
  15. data/lib/entity_cache/defaults.rb +2 -2
  16. data/lib/entity_cache/record.rb +36 -30
  17. data/lib/entity_cache/record/destructure.rb +29 -0
  18. data/lib/entity_cache/record/log_text.rb +9 -0
  19. data/lib/entity_cache/record/transformer.rb +26 -0
  20. data/lib/entity_cache/store/persistent/null.rb +15 -0
  21. data/lib/entity_cache/store/persistent/substitute.rb +64 -0
  22. data/lib/entity_cache/store/persistent/telemetry.rb +16 -0
  23. data/lib/entity_cache/store/temporary.rb +47 -0
  24. data/lib/entity_cache/store/temporary/build.rb +48 -0
  25. data/lib/entity_cache/store/temporary/build/defaults.rb +28 -0
  26. data/lib/entity_cache/{storage → store}/temporary/scope/exclusive.rb +1 -1
  27. data/lib/entity_cache/store/temporary/scope/global.rb +34 -0
  28. data/lib/entity_cache/store/temporary/scope/thread.rb +35 -0
  29. data/lib/entity_cache/store/temporary/substitute.rb +36 -0
  30. data/lib/entity_cache/substitute.rb +33 -17
  31. metadata +20 -58
  32. data/lib/entity_cache/entity_cache.rb +0 -83
  33. data/lib/entity_cache/error.rb +0 -3
  34. data/lib/entity_cache/storage/persistent.rb +0 -81
  35. data/lib/entity_cache/storage/persistent/none.rb +0 -15
  36. data/lib/entity_cache/storage/persistent/substitute.rb +0 -51
  37. data/lib/entity_cache/storage/persistent/telemetry.rb +0 -38
  38. data/lib/entity_cache/storage/temporary.rb +0 -56
  39. data/lib/entity_cache/storage/temporary/build.rb +0 -42
  40. data/lib/entity_cache/storage/temporary/scope/defaults.rb +0 -32
  41. data/lib/entity_cache/storage/temporary/scope/error.rb +0 -9
  42. data/lib/entity_cache/storage/temporary/scope/shared.rb +0 -31
@@ -1,44 +1,50 @@
1
1
  class EntityCache
2
- class Record < Struct.new(:id, :entity, :version, :time, :persisted_version, :persisted_time)
3
- def versions_since_persisted
4
- persisted_version = self.persisted_version
5
- persisted_version ||= -1
6
-
7
- version - persisted_version.to_i
2
+ Record = Struct.new(
3
+ :id,
4
+ :entity,
5
+ :version,
6
+ :time,
7
+ :persisted_version,
8
+ :persisted_time
9
+ )
10
+
11
+ class Record
12
+ dependency :clock, Clock::UTC
13
+
14
+ def configure
15
+ Clock::UTC.configure(self)
8
16
  end
9
17
 
10
- def destructure(includes=nil)
11
- return entity if includes.nil?
12
-
13
- responses = [entity]
14
-
15
- includes = Array(includes)
16
-
17
- includes.each do |attribute|
18
- value = public_send attribute
19
-
20
- if value.nil? && attribute == :version
21
- value = NoStream.version
22
- end
23
-
24
- responses << value
25
- end
18
+ def self.build(id, entity, version, time, persisted_version: nil, persisted_time: nil)
19
+ instance = new(id, entity, version, time, persisted_version, persisted_time)
20
+ instance.configure
21
+ instance
22
+ end
26
23
 
27
- responses
24
+ def self.destructure(instance, includes=nil)
25
+ Destructure.(instance, includes)
28
26
  end
29
27
 
30
28
  def age_milliseconds
31
- Clock::UTC.elapsed_milliseconds(time, Clock::UTC.now)
29
+ Clock::UTC.elapsed_milliseconds(time, clock.now)
32
30
  end
33
31
 
34
- module NoStream
35
- def self.destructure(includes=nil)
36
- record = Record.new
37
- record.destructure(includes)
32
+ def persisted_age_milliseconds
33
+ if persisted_time.nil?
34
+ nil
35
+ else
36
+ Clock::UTC.elapsed_milliseconds(
37
+ persisted_time,
38
+ time
39
+ )
38
40
  end
41
+ end
39
42
 
40
- def self.version
41
- :no_stream
43
+ def persisted_age_versions
44
+ if persisted_version.nil?
45
+ nil
46
+ else
47
+ version - persisted_version
42
48
  end
43
49
  end
44
50
  end
@@ -0,0 +1,29 @@
1
+ class EntityCache
2
+ class Record
3
+ module Destructure
4
+ def self.call(record, includes=nil)
5
+ record ||= NoStream.record
6
+
7
+ return record.entity if includes.nil?
8
+
9
+ return_values = Array(includes).map do |attribute|
10
+ record.public_send(attribute)
11
+ end
12
+
13
+ return record.entity, *return_values
14
+ end
15
+
16
+ module NoStream
17
+ def self.record
18
+ @record ||= build_record
19
+ end
20
+
21
+ def self.build_record
22
+ record = Record.new
23
+ record.version = MessageStore::NoStream.name
24
+ record
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ class EntityCache
2
+ class Record
3
+ module LogText
4
+ def self.get(record)
5
+ "Entity: #{record&.entity.class}, Version: #{record&.version.inspect}, Time: #{record&.time.inspect}, Persisted Version: #{record&.persisted_version.inspect}, Persisted Time: #{record&.persisted_time.inspect}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ class EntityCache
2
+ class Record
3
+ module Transformer
4
+ def self.raw_data(instance)
5
+ raw_data = instance.to_h
6
+
7
+ entity = raw_data.delete(:entity)
8
+ copied_entity = Transform::Copy.(entity)
9
+
10
+ raw_data[:entity] = copied_entity
11
+
12
+ raw_data
13
+ end
14
+
15
+ def self.instance(raw_data)
16
+ instance = Record.new
17
+
18
+ raw_data.each do |attribute, value|
19
+ instance.public_send("#{attribute}=", value)
20
+ end
21
+
22
+ instance
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ class EntityCache
2
+ module Store
3
+ module Persistent
4
+ class Null
5
+ include Store::Persistent
6
+
7
+ def get(id)
8
+ end
9
+
10
+ def put(id, entity, version, time)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+ class EntityCache
2
+ module Store
3
+ module Persistent
4
+ module Substitute
5
+ def self.build
6
+ Persistent.build
7
+ end
8
+
9
+ class Persistent
10
+ include Store::Persistent
11
+
12
+ attr_accessor :telemetry_sink
13
+
14
+ def self.build
15
+ instance = new(subject)
16
+ instance.configure
17
+ instance
18
+ end
19
+
20
+ def configure(session: nil)
21
+ self.telemetry_sink = self.class.register_telemetry_sink(self)
22
+ end
23
+
24
+ def get(id)
25
+ record = get_records[id]
26
+
27
+ if record
28
+ return record.entity, record.version, record.time
29
+ end
30
+ end
31
+
32
+ def get_records
33
+ @get_records ||= {}
34
+ end
35
+
36
+ def add(id, entity, version, time)
37
+ record = Record.new(id, entity, version, time)
38
+
39
+ get_records[id] = record
40
+
41
+ record
42
+ end
43
+
44
+ def put(*)
45
+ end
46
+
47
+ def put?(&block)
48
+ block ||= proc { true }
49
+
50
+ telemetry_sink.recorded_put? do |record|
51
+ data = record.data
52
+
53
+ block.(data.id, data.entity, data.version, data.time)
54
+ end
55
+ end
56
+
57
+ def self.subject
58
+ :substitute
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,16 @@
1
+ class EntityCache
2
+ module Store
3
+ module Persistent
4
+ module Telemetry
5
+ Data = Struct.new(:id, :entity, :version, :time)
6
+
7
+ class Sink
8
+ include ::Telemetry::Sink
9
+
10
+ record :put
11
+ record :get
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ class EntityCache
2
+ module Store
3
+ class Temporary
4
+ include Log::Dependency
5
+
6
+ attr_accessor :subject
7
+
8
+ def self.build(subject)
9
+ instance = new
10
+ instance.subject = subject
11
+ instance
12
+ end
13
+
14
+ def self.configure(receiver, subject, scope: nil, attr_name: nil)
15
+ attr_name ||= :temporary_store
16
+
17
+ instance = Build.(subject, scope: scope)
18
+ receiver.public_send("#{attr_name}=", instance)
19
+ instance
20
+ end
21
+
22
+ abstract :records
23
+
24
+ def get(id)
25
+ logger.trace { "Getting Entity (ID: #{id.inspect})" }
26
+
27
+ record = records[id]
28
+
29
+ logger.debug { "Get entity done (ID: #{id.inspect}, #{Record::LogText.get(record)})" }
30
+
31
+ record
32
+ end
33
+
34
+ def put(record)
35
+ id = record.id
36
+
37
+ logger.trace { "Putting entity (ID: #{id.inspect}, #{Record::LogText.get(record)})" }
38
+
39
+ records[id] = record
40
+
41
+ logger.trace { "Put entity done (ID: #{id.inspect}, #{Record::LogText.get(record)})" }
42
+
43
+ record
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ class EntityCache
2
+ module Store
3
+ class Temporary
4
+ module Build
5
+ def self.call(subject, scope: nil)
6
+ scope ||= Defaults.scope
7
+
8
+ cls = scope_class(scope)
9
+
10
+ cls.build(subject)
11
+ end
12
+
13
+ def self.default_scope_class
14
+ scope_class(Defaults.scope)
15
+ end
16
+
17
+ def self.scope_class(scope)
18
+ scopes.fetch(scope) do
19
+ *scopes, final_scope = self.scopes.keys
20
+
21
+ scope_list = <<~TEXT
22
+ #{scopes.map(&:inspect) * ', '} or #{final_scope.inspect}
23
+ TEXT
24
+
25
+ error_message = %{Scope #{scope.inspect} is unknown. It must be one of: #{scope_list}}
26
+
27
+ logger.error(error_message)
28
+ raise ScopeError, error_message
29
+ end
30
+ end
31
+
32
+ def self.scopes
33
+ @scopes ||= {
34
+ :exclusive => Scope::Exclusive,
35
+ :global => Scope::Global,
36
+ :thread => Scope::Thread
37
+ }
38
+ end
39
+
40
+ def self.logger
41
+ @logger ||= Log.get(self)
42
+ end
43
+
44
+ ScopeError = Class.new(StandardError)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ class EntityCache
2
+ module Store
3
+ class Temporary
4
+ module Build
5
+ module Defaults
6
+ def self.scope
7
+ Scope.get
8
+ end
9
+
10
+ module Scope
11
+ def self.get
12
+ value = ENV.fetch(env_var, default)
13
+ value.to_sym
14
+ end
15
+
16
+ def self.env_var
17
+ 'ENTITY_CACHE_SCOPE'
18
+ end
19
+
20
+ def self.default
21
+ 'thread'
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  class EntityCache
2
- module Storage
2
+ module Store
3
3
  class Temporary
4
4
  module Scope
5
5
  class Exclusive < Temporary
@@ -0,0 +1,34 @@
1
+ class EntityCache
2
+ module Store
3
+ class Temporary
4
+ module Scope
5
+ class Global < Temporary
6
+ def records
7
+ records_by_subject[subject]
8
+ end
9
+
10
+ def records_by_subject
11
+ mutex.synchronize do
12
+ @@records_by_subject ||= {}
13
+ @@records_by_subject[subject] ||= ThreadSafeHash.new
14
+ @@records_by_subject
15
+ end
16
+ end
17
+
18
+ mutex = ::Thread::Mutex.new
19
+ define_method(:mutex) do
20
+ mutex
21
+ end
22
+
23
+ class ThreadSafeHash < ::Hash
24
+ def [](key)
25
+ value = super(key)
26
+ value = Transform::Copy.(value) unless value.nil?
27
+ value
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ class EntityCache
2
+ module Store
3
+ class Temporary
4
+ module Scope
5
+ class Thread < Temporary
6
+ def records
7
+ records_by_subject[subject]
8
+ end
9
+
10
+ def records_by_subject
11
+ current_thread = ::Thread.current
12
+
13
+ records_by_subject = current_thread.thread_variable_get(thread_variable)
14
+
15
+ if records_by_subject.nil?
16
+ records_by_subject = Hash.new do |hash, subject|
17
+ hash[subject] = {}
18
+ end
19
+
20
+ current_thread.thread_variable_set(thread_variable, records_by_subject)
21
+ end
22
+
23
+ records_by_subject
24
+ end
25
+
26
+ thread_variable = :"entity_cache_records_by_subject_#{SecureRandom.hex(7)}"
27
+
28
+ define_method(:thread_variable) do
29
+ thread_variable
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end