-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathobject_manager.rb
executable file
·165 lines (149 loc) · 6.05 KB
/
object_manager.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
require 'set'
require_relative 'configurable/object_manager_configuration'
module Shark
class ObjectManager
class << self
include Configurable
use_configuration_type ObjectManagerConfiguration
end
include Configurable
inherit_configuration_from self
configuration.schema do
required :object_type
required :update_frequency
# ObjectManagers must gather data from at least one source. This
# requirement will be met as long as the configuration includes
# at least one `sources_from` statement.
required :sources
end
# A human name for this manager. Not used internally, but useful for
# reflecting on instances within an Agency.
attr_accessor :name
# A hash of objects that this manager is currently dealing with. Objects in
# this hash will participate in all activities that this manager performs.
# Objects not in this hash but in `known_objects` will not participate in
# these activities.
attr_accessor :active_objects
# The agency that this manager belongs to. All events that this manager
# creates will be pushed out to this agency.
attr_accessor :agency
# A reference to the storage adapter being used. This is generally a global
# value, but having a local reference to it simplifies code lines, and
# allows for overrides if necessary.
attr_accessor :storage
# The type that all objects that this manager creates will take on.
attr_accessor :klass
# The frequency at which this manager should perform update cycles. That
# is, how often the sources will be polled and events will be published.
# Note that the owner of the manager is responsible for scheduling; this
# attribute is simply here to match how frequencies are defined in the
# configuration.
attr_accessor :update_frequency
# An array of Source objects that will be used to update the attributes of
# each active object.
attr_accessor :sources
# Instantiate a new ObjectManager, first calling the configurator to apply
# any instance-level configurations, then applying those configurations to
# attributes of this class.
def initialize name, agency, &configurator
# Apply the optional configurator's options
configure &configurator if block_given?
@name = name
@agency = agency
@active_objects = Set.new
# TODO: Include `storage` in the configuration
@storage = Storage.adapter
@klass = configuration.object_type
@update_frequency = configuration.update_frequency
@namespace = configuration.namespace
# Create the source instances by lookup based on the given name, and the
# object_type specified for thie ObjectManager. Additionally, apply any
# additional configuration that was given for each Source.
@sources = configuration.sources.map do |name, config|
Source.create(name, @klass, config)
end
end
# Add an object to `active_objects`. If the object is not already in
# `storage`, add it there as well.
def activate object
@active_objects << object.identifier
@storage.create(object.identifier, object)
end
# Remove an object from `active_objects`, but keep its entry in
# `storage`.
def deactivate object
@active_objects.delete object.identifier
end
# Remove all objects from `active_objects`. Entries in `storage` will be
# preserved.
def deactivate_all
@active_objects.clear
end
# Completely remove an object from this manager. Its entries in both
# `active_objects` and `storage` will be deleted.
def remove object
@active_objects.delete object.identifier
@storage.remove(object.identifier)
end
# Return the object matching the key of the given object in `@storage`, or
# nil if no match exists.
def find identifier
@storage.find(identifier) || nil
end
# Return the object matching the key of the given object in `@storage`, or
# create a new instance if no match exists.
def find_or_new identifier
find(identifier) || @klass.new
end
# Call the given block once for each active object, passing that object as
# a parameter.
def each &block
@active_objects.each &block
end
# Update the state of this manager by deactivating all objects, and polling
# all sources to determine the new set of active objects.
def update
# Remember which objects were previously active
previously_active = @active_objects.clone
# Deactivate all objects to avoid keeping stale objects active
deactivate_all
# Poll all of the sources (in order) to update all objects and determine
# the active set
@sources.each do |source|
source.refresh
source.update self
end
# If any new objects were activated in this session, publish an
# `activate` event
(@active_objects - previously_active).each do |identifier|
fire(:activate, @storage.find(identifier))
end
# Do the same for any objects that are no longer active
(previously_active - @active_objects).each do |identifier|
fire(:deactivate, @storage.find(identifier))
end
# Publish update events for each currently active object
@active_objects.each do |identifier|
fire(:update, @storage.find(identifier))
end
end
protected
# Create an event to be sent out from the agency. The event originating
# here consists of:
# - event_type: the type of event being sent.
# - channel: the namespace the event should be broadcast in.
# - args: a single argument, the object the event relates to.
# - originator: who is responsible for initiating the event.
def fire event_type, object
agency.call(Event.new(
topic: object.identifier,
type: event_type.to_sym,
args: [object],
kwargs: {
version: object.version
},
originator: object.identifier
))
end
end
end