lowevent 0.8.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 58811c1dffb8954d8dd1ef1e40e322ffb2b6ea6a9d824e1205c05d1803e6c565
4
+ data.tar.gz: 12afa112f58ad6f0aa1425572db2f8d38a32416d9f48d58be3bf172a0e7d2b40
5
+ SHA512:
6
+ metadata.gz: da3ccf1f9f03e42f3172237b1c70196485d512146ad4e0d5a4d7e1aa27d6357fbcc381285115d79c241052f8dec7bbe3d52d97d786f00d821e5e5811aaa4b4de
7
+ data.tar.gz: 6d03f85b9cc65fe1cecbb0a9254d5a31a636a8e85b557f37fe20814f70b4c4095c7c182037692cf3c3b16c15cdbecbfc5b2b06c3196bc2f6437084e9782dad9b
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lowtype'
4
+
5
+ module Low
6
+ module Events
7
+ # An event that happens on event tree branching and isn't added to the event tree/sequence.
8
+ class HiddenEvent
9
+ include LowType
10
+
11
+ attr_reader :key, :action, :created_at
12
+
13
+ def initialize(key:, action: nil)
14
+ @key = key
15
+ @action = action
16
+ @created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
17
+ end
18
+
19
+ def trigger
20
+ event_tree = branch
21
+ key = Observers::Keys[@key]
22
+ key.trigger(event: self) { restore_level(event_tree:) }
23
+ end
24
+
25
+ class << self
26
+ def trigger(**kwargs)
27
+ new(**kwargs).trigger
28
+ end
29
+
30
+ def inherited(child)
31
+ child.include LowType
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Low
4
+ module Events
5
+ RenderEvent = Data.define(:action, :props) do
6
+ # TODO: Duplicate props when used in a parallelized situation (antlers: "<{ :parallelize }>")
7
+ def initialize(action: :render, props: {})
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../interfaces/event'
4
+
5
+ module Low
6
+ module Events
7
+ class RequestEvent < Event
8
+ attr_reader :request
9
+
10
+ def initialize(request:, action: :request)
11
+ super(key: self.class, action:)
12
+
13
+ @request = request
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'protocol/http'
4
+ require_relative '../interfaces/event'
5
+
6
+ module Low
7
+ module Events
8
+ class ResponseEvent < Event
9
+ attr_reader :response
10
+
11
+ # TODO: Type: "response: Protocol::HTTP::Response"
12
+ def initialize(response: nil)
13
+ super(key: self.class, action: :respond)
14
+
15
+ @response = response
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../interfaces/event'
4
+
5
+ module Low
6
+ module Events
7
+ class StatusEvent < Event
8
+ attr_reader :status, :request
9
+
10
+ def initialize(status:, request:, key: nil, action: :render)
11
+ super(key: key || status, action:)
12
+
13
+ @status = status
14
+ @request = request
15
+ end
16
+
17
+ # Status is a complex type that can be represented by both "Status" generically and "Status[:code]" specifically.
18
+ class << self
19
+ def trigger(status:, **kwargs)
20
+ new(status:, **kwargs).trigger || new(status:, key: Status, **kwargs).trigger
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'protocol/http'
4
+
5
+ module Low
6
+ module Events
7
+ class ResponseFactory
8
+ class << self
9
+ def response(body:)
10
+ headers = Protocol::HTTP::Headers.new(['content-type', 'text/html'])
11
+ body = Protocol::HTTP::Body::Buffered.wrap(body.strip)
12
+
13
+ Protocol::HTTP::Response.new('http/1.1', 200, headers, body)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Low
4
+ module Events
5
+ module Definable
6
+ def define(key = self)
7
+ yield observers(key)
8
+ end
9
+
10
+ class << self
11
+ def included(klass)
12
+ klass.extend Definable
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lowtype'
4
+ require 'observers'
5
+ require_relative 'definable'
6
+ require_relative '../support/value_object'
7
+
8
+ module Low
9
+ # An event represents what is currently happening; the past, present and future.
10
+ #
11
+ # They are action-driven, representing inputs and outputs in a linear pipeline-like flow,
12
+ # which end up as children of the event... which is why events are mutable (except for RenderEvent).
13
+ # They are one-to-many with a return value.
14
+ #
15
+ # Integrations:
16
+ # - Observers for observer pattern which we wrap in an event-centric API
17
+ # - EventPool for a tree of events and their child events (defined in this gem)
18
+ # - LowState for state machines to trigger multiple ordered actions [UNLRELEASED]
19
+ class Event
20
+ include LowType
21
+ include Observers
22
+ include Events::Definable
23
+ include Support::ValueObject
24
+
25
+ attr_reader :key, :action, :actions, :created_at
26
+ attr_accessor :children
27
+
28
+ # Subclass provides a key such as "self.class".
29
+ # Subclass defines default action or actions, but not both.
30
+ def initialize(key:, action: nil, actions: [], children: [])
31
+ @key = key
32
+ @action = action
33
+ @actions = actions
34
+ @children = children
35
+
36
+ @created_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
37
+ end
38
+
39
+ def trigger
40
+ event_tree = branch
41
+ key = Observers::Keys[@key]
42
+ key.trigger(event: self) { restore_level(event_tree:) }
43
+ end
44
+
45
+ def take
46
+ event_tree = branch
47
+ key = Observers::Keys[@key]
48
+ key.take(event: self) { restore_level(event_tree:) }
49
+ end
50
+
51
+ def branch
52
+ event_tree = Providers['low.event.pool'].current_event_tree(event: self)
53
+ event_tree.branch(event: self)
54
+ end
55
+
56
+ private
57
+
58
+ def restore_level(event_tree:)
59
+ event_tree.current_event = self if event_tree.respond_to?(:current_event)
60
+ end
61
+
62
+ class << self
63
+ def trigger(**kwargs) = new(**kwargs).trigger
64
+ def take(**kwargs) = new(**kwargs).take
65
+
66
+ def inherited(child)
67
+ child.include LowType
68
+ increase_count
69
+ add_event(child)
70
+ end
71
+
72
+ def count
73
+ @count ||= 0
74
+ end
75
+
76
+ def increase_count
77
+ @count = count + 1
78
+ end
79
+
80
+ def events
81
+ @events ||= []
82
+ end
83
+
84
+ def add_event(event)
85
+ events << event
86
+ end
87
+ end
88
+ end
89
+ end
data/lib/lowevent.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'providers'
4
+
5
+ require_relative 'events/render_event'
6
+ require_relative 'events/request_event'
7
+ require_relative 'events/response_event'
8
+ require_relative 'events/status_event'
9
+ require_relative 'factories/response_factory' # TODO: Find out who's using this and require it there.
10
+ require_relative 'interfaces/event'
11
+ require_relative 'pool/event_pool'
12
+
13
+ Providers.define('low.event.pool') do
14
+ Low::Events::EventPool.new
15
+ end
16
+
17
+ LowEvent = Low::Event
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../events/hidden_event'
4
+
5
+ module Low
6
+ module Events
7
+ class BranchEvent < HiddenEvent
8
+ attr_reader :event_tree, :event
9
+
10
+ def initialize(event_tree:, event:)
11
+ super(key: BranchEvent, action: :branch)
12
+
13
+ @event_tree = event_tree
14
+ @event = event
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'observers'
4
+
5
+ require_relative '../events/request_event'
6
+ require_relative 'event_tree'
7
+ require_relative '../support/pool_hash'
8
+
9
+ module Low
10
+ module Events
11
+ class EventPool
12
+ include Observers
13
+
14
+ BUFFER_SIZE = 100
15
+
16
+ def initialize
17
+ @event_trees = Support::PoolHash.new(BUFFER_SIZE)
18
+ @request_counts = Support::PoolHash.new(BUFFER_SIZE)
19
+ end
20
+
21
+ def current_event_tree(event:)
22
+ request_id = request_id(event:)
23
+
24
+ return @event_trees[request_id] if @event_trees[request_id]
25
+
26
+ event_tree = @event_trees.add(request_id, EventTree.new(request_id:))
27
+ trigger action: :new_event_tree, event: event_tree
28
+
29
+ event_tree
30
+ end
31
+
32
+ def event_trees
33
+ @event_trees
34
+ end
35
+
36
+ private
37
+
38
+ def request_id(event:)
39
+ fiber_id = Fiber.current.object_id
40
+ increment_request_counts(fiber_id:) if event.is_a?(RequestEvent)
41
+ "#{fiber_id}-#{@request_counts[fiber_id]}"
42
+ end
43
+
44
+ def increment_request_counts(fiber_id:)
45
+ @request_counts[fiber_id] ||= 0
46
+ @request_counts[fiber_id] += 1
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'observers'
4
+ require_relative 'branch_event'
5
+
6
+ module Low
7
+ module Events
8
+ class EventTree
9
+ include Observers
10
+
11
+ attr_reader :request_id, :root_event, :sequence
12
+ attr_accessor :current_event
13
+
14
+ def initialize(request_id: nil)
15
+ @request_id = request_id
16
+ @root_event = nil
17
+ @current_event = nil
18
+ @sequence = []
19
+ end
20
+
21
+ def branch(event:)
22
+ @sequence << event
23
+
24
+ if @root_event.nil?
25
+ @root_event = event
26
+ @current_event = event
27
+ else
28
+ @current_event.children << event
29
+ end
30
+
31
+ trigger action: :branch, event: BranchEvent.new(event_tree: self, event:)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Low
4
+ module Support
5
+ class PoolHash < Hash
6
+ def initialize(max_size)
7
+ @max_size = max_size
8
+
9
+ super()
10
+ end
11
+
12
+ def add(key, value)
13
+ # Prune the hash when a new item added.
14
+ if size >= @max_size && !key?(key)
15
+ # TODO: Notify event pool that item was removed.
16
+ _old_key, _old_value = shift
17
+ end
18
+
19
+ self[key] = value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Low
4
+ module Support
5
+ module ValueObject
6
+ def ==(other) = other.class == self.class
7
+ def eql?(other) = self == other
8
+ def hash = [self.class].hash
9
+ end
10
+ end
11
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Low
4
+ EVENT_VERSION = '0.8.1'
5
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lowevent
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.1
5
+ platform: ruby
6
+ authors:
7
+ - maedi
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: lowtype
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.4'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.4'
26
+ - !ruby/object:Gem::Dependency
27
+ name: observers
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: protocol-http
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: providers
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ description: Events and observers for event-driven applications
69
+ email:
70
+ - maediprichard@gmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/events/hidden_event.rb
76
+ - lib/events/render_event.rb
77
+ - lib/events/request_event.rb
78
+ - lib/events/response_event.rb
79
+ - lib/events/status_event.rb
80
+ - lib/factories/response_factory.rb
81
+ - lib/interfaces/definable.rb
82
+ - lib/interfaces/event.rb
83
+ - lib/lowevent.rb
84
+ - lib/pool/branch_event.rb
85
+ - lib/pool/event_pool.rb
86
+ - lib/pool/event_tree.rb
87
+ - lib/support/pool_hash.rb
88
+ - lib/support/value_object.rb
89
+ - lib/version.rb
90
+ homepage: https://github.com/low-rb/lowevent
91
+ licenses: []
92
+ metadata:
93
+ homepage_uri: https://github.com/low-rb/lowevent
94
+ source_code_uri: https://github.com/low-rb/lowevent/src/branch/main
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 3.3.0
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 4.0.6
110
+ specification_version: 4
111
+ summary: Observable events
112
+ test_files: []