emittance 0.0.2 → 0.0.3
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 +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +10 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +1 -1
- data/README.md +3 -2
- data/Rakefile +5 -3
- data/bin/console +1 -0
- data/emittance.gemspec +15 -22
- data/lib/emittance.rb +12 -6
- data/lib/emittance/action.rb +51 -37
- data/lib/emittance/brokerage.rb +23 -7
- data/lib/emittance/dispatcher.rb +21 -72
- data/lib/emittance/emitter.rb +48 -40
- data/lib/emittance/errors.rb +5 -2
- data/lib/emittance/event.rb +82 -11
- data/lib/emittance/event_lookup.rb +315 -0
- data/lib/emittance/helpers/constant_helpers.rb +33 -0
- data/lib/emittance/helpers/string_helpers.rb +48 -0
- data/lib/emittance/registration.rb +3 -3
- data/lib/emittance/version.rb +2 -2
- data/lib/emittance/watcher.rb +12 -0
- metadata +20 -19
- data/lib/emittance/event/event_builder.rb +0 -154
- data/pkg/emittance-0.0.1.gem +0 -0
data/lib/emittance/emitter.rb
CHANGED
@@ -36,33 +36,33 @@ module Emittance
|
|
36
36
|
"_non_emitting_#{method_name}".to_sym
|
37
37
|
end
|
38
38
|
|
39
|
-
# @private
|
40
|
-
def emitting_method_event(emitter_klass, method_name)
|
41
|
-
Emittance::Event.event_klass_for(emitter_klass, method_name)
|
42
|
-
end
|
43
|
-
|
44
39
|
# @private
|
45
40
|
def emitter_eval(klass, *args, &blk)
|
46
41
|
if klass.respond_to? :class_eval
|
47
|
-
klass.class_eval
|
42
|
+
klass.class_eval(*args, &blk)
|
48
43
|
else
|
49
|
-
klass.singleton_class.class_eval
|
44
|
+
klass.singleton_class.class_eval(*args, &blk)
|
50
45
|
end
|
51
46
|
end
|
52
47
|
end
|
53
48
|
# :nocov:
|
54
49
|
|
50
|
+
##
|
55
51
|
# Included and extended whenever {Emittance::Emitter} is extended.
|
52
|
+
#
|
56
53
|
module ClassAndInstanceMethods
|
57
54
|
# Emits an {Emittance::Event event object} to watchers.
|
58
55
|
#
|
59
56
|
# @param identifier [Symbol, Emittance::Event] either an explicit Event object or the identifier that can be
|
60
57
|
# parsed into an Event object.
|
61
58
|
# @param payload [*] any additional information that might be helpful for an event's handler to have. Can be
|
62
|
-
# standardized on a per-event basis by pre-defining the class associated with the
|
59
|
+
# standardized on a per-event basis by pre-defining the class associated with the identifier and validating
|
60
|
+
# the payload. See {Emittance::Event} for more details.
|
61
|
+
# @param broker [Symbol] the identifier for the broker you wish to handle the event. Requires additional gems
|
62
|
+
# if not using the default.
|
63
63
|
#
|
64
64
|
# @return the payload
|
65
|
-
def emit(identifier, payload
|
65
|
+
def emit(identifier, payload: nil, broker: :synchronous)
|
66
66
|
now = Time.now
|
67
67
|
event_klass = _event_klass_for identifier
|
68
68
|
event = event_klass.new(self, now, payload)
|
@@ -76,56 +76,64 @@ module Emittance
|
|
76
76
|
#
|
77
77
|
# @param identifiers [*] anything that can be used to generate an +Event+ class.
|
78
78
|
# @param payload (@see #emit)
|
79
|
+
# @param broker (@see #emit)
|
79
80
|
def emit_with_dynamic_identifier(*identifiers, payload:, broker: :synchronous)
|
80
81
|
now = Time.now
|
81
|
-
event_klass = _event_klass_for
|
82
|
+
event_klass = _event_klass_for(*identifiers)
|
82
83
|
event = event_klass.new(self, now, payload)
|
83
84
|
_send_to_broker event, broker
|
84
85
|
|
85
86
|
payload
|
86
87
|
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
def _event_klass_for(*identifiers)
|
92
|
-
Emittance::Event.event_klass_for *identifiers
|
93
|
-
end
|
94
|
-
|
95
|
-
# @private
|
96
|
-
def _send_to_broker(event, broker)
|
97
|
-
Emittance::Brokerage.send_event event, broker
|
98
|
-
end
|
99
|
-
|
100
|
-
# Tells the class to emit an event when a any of the given set of methods. By default, the event classes are named
|
101
|
-
# accordingly: If a +Foo+ object +emits_on+ +:bar+, then the event's class will be named +FooBarEvent+, and will
|
102
|
-
# be a subclass of +Emittance::Event+.
|
89
|
+
# Tells the object to emit an event when a any of the given set of methods. By default, the event classes are
|
90
|
+
# named accordingly: If a +Foo+ object +emits_on+ +:bar+, then the event's class will be named +FooBarEvent+, and
|
91
|
+
# will be a subclass of +Emittance::Event+.
|
103
92
|
#
|
104
93
|
# The payload for this event will be the value returned from the method call.
|
105
94
|
#
|
106
95
|
# @param method_names [Symbol, String, Array<Symbol, String>] the methods whose calls emit an event
|
107
|
-
def emits_on(*method_names)
|
96
|
+
def emits_on(*method_names, identifier: nil)
|
108
97
|
method_names.each do |method_name|
|
109
98
|
non_emitting_method = Emittance::Emitter.non_emitting_method_for method_name
|
110
99
|
|
111
|
-
Emittance::Emitter.emitter_eval(self)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
100
|
+
Emittance::Emitter.emitter_eval(self, &_method_patch_block(method_name, non_emitting_method, identifier))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
116
105
|
|
117
|
-
|
106
|
+
def _method_patch_block(method_name, non_emitting_method, identifier)
|
107
|
+
lambda do |_klass|
|
108
|
+
return if method_defined?(non_emitting_method)
|
118
109
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
emit_with_dynamic_identifier self.class, __method__, payload: return_value
|
123
|
-
return_value
|
124
|
-
end
|
125
|
-
RUBY
|
126
|
-
end
|
110
|
+
alias_method non_emitting_method, method_name
|
111
|
+
|
112
|
+
module_eval _method_patch_str(method_name, non_emitting_method, identifier), __FILE__, __LINE__ + 1
|
127
113
|
end
|
128
114
|
end
|
115
|
+
|
116
|
+
def _method_patch_str(method_name, non_emitting_method, identifier)
|
117
|
+
<<~RUBY
|
118
|
+
def #{method_name}(*args, &blk)
|
119
|
+
return_value = #{non_emitting_method}(*args, &blk)
|
120
|
+
if #{!identifier.nil? ? true : false}
|
121
|
+
emit #{!identifier.nil? ? identifier : false}, payload: return_value
|
122
|
+
else
|
123
|
+
emit_with_dynamic_identifier self.class, __method__, payload: return_value
|
124
|
+
end
|
125
|
+
return_value
|
126
|
+
end
|
127
|
+
RUBY
|
128
|
+
end
|
129
|
+
|
130
|
+
def _event_klass_for(*identifiers)
|
131
|
+
Emittance::Event.event_klass_for(*identifiers)
|
132
|
+
end
|
133
|
+
|
134
|
+
def _send_to_broker(event, broker)
|
135
|
+
Emittance::Brokerage.send_event event, broker
|
136
|
+
end
|
129
137
|
end
|
130
138
|
end
|
131
139
|
end
|
data/lib/emittance/errors.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Emittance
|
4
4
|
# Raised when an identifier (for the purposes of identifying an event class) cannot be parsed, or an event class
|
5
5
|
# can otherwise not be found or generated.
|
6
6
|
class InvalidIdentifierError < StandardError; end
|
7
7
|
|
8
|
+
# Raised when an identifier couldn't be generated from a class. Typically a validation error.
|
9
|
+
class IdentifierGenerationError < StandardError; end
|
10
|
+
|
8
11
|
# Raised when an identifier registration is attempted, but there exists an event registered to the given identifiered.
|
9
|
-
class
|
12
|
+
class IdentifierCollisionError < StandardError; end
|
10
13
|
|
11
14
|
# Used when a custom event type undergoes payload validation.
|
12
15
|
class InvalidPayloadError < StandardError; end
|
data/lib/emittance/event.rb
CHANGED
@@ -26,48 +26,119 @@ module Emittance
|
|
26
26
|
# end
|
27
27
|
# end
|
28
28
|
#
|
29
|
-
# ==
|
29
|
+
# == Identifiers
|
30
|
+
#
|
31
|
+
# Events are identified by what we call "Identifiers." These come in the form of symbols, and can be used to identify
|
32
|
+
# specific event types.
|
33
|
+
#
|
34
|
+
# === Identifier Naming
|
35
|
+
#
|
36
|
+
# The naming convention for events and their identifiers goes like this: the name of an event class will be the
|
37
|
+
# CamelCase form of its identifier, plus the word +Event+. For example, +FooEvent+ can be identified with +:foo+.
|
38
|
+
# Thus, the events received by watchers of +:foo+ will be instances of `FooEvent`. Conversely, if you make an event
|
39
|
+
# class +BarEvent+ that inherits from +Emittance::Event+, its built-in identifier will be +:bar+. You can see what
|
40
|
+
# a +Emittance::Event+ subclass's identifier is by calling +.identifiers+ on it.
|
41
|
+
#
|
42
|
+
# class SomethingHappenedEvent < Emittance::Event
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# MyEvent.identifiers
|
46
|
+
# # => [:something_happened]
|
47
|
+
#
|
48
|
+
# MyEvent.new.identifiers
|
49
|
+
# # => [:something_happened]
|
50
|
+
#
|
51
|
+
# The namespace resultion operator (+::+) in an event's class name will translate to a +/+ in the identifier name:
|
52
|
+
#
|
53
|
+
# class Foo::BarEvent < Emittance::Event
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# Foo::BarEvent.identifiers
|
57
|
+
# #=> [:'foo/bar']
|
58
|
+
#
|
59
|
+
# === Custom Identifiers
|
30
60
|
#
|
31
61
|
# By default, the identifier for this event will be the snake_case form of the class name with +Event+ chopped off:
|
32
62
|
#
|
33
|
-
# FooEvent.
|
63
|
+
# FooEvent.identifiers
|
64
|
+
# # => [:foo]
|
34
65
|
#
|
35
66
|
# You can set a custom identifier for the event class like so:
|
36
67
|
#
|
37
68
|
# FooEvent.add_identifier :bar
|
69
|
+
# FooEvent.identifiers
|
70
|
+
# # => [:foo, :bar]
|
71
|
+
#
|
72
|
+
# Now, when emitters emit +:bar+, this will be the event received by watchers. +#add_identifier+ will raise an
|
73
|
+
# {Emittance::IdentifierCollisionError} if you attempt to add an identifier that has already been claimed. This
|
74
|
+
# error will also be raised if you try to add an identifier that already has an associated class. For example:
|
75
|
+
#
|
76
|
+
# class FooEvent < Emittance::Event
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# class BarEvent < Emittance::Event
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# BarEvent.add_identifier :foo
|
83
|
+
# # => Emittance::IdentifierCollisionError
|
84
|
+
#
|
85
|
+
# This error is raised because, even though we haven't explicitly add the identifier +:foo+ for +FooEvent+, Emittance
|
86
|
+
# is smart enough to know that there exists a class whose name resolves to +:foo+.
|
87
|
+
#
|
88
|
+
# It's best to use custom identifiers very sparingly. One reason for this can be illustrated like so:
|
89
|
+
#
|
90
|
+
# class FooEvent < Emittance::Event
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# FooEvent.add_identifier :bar
|
94
|
+
# FooEvent.identifiers
|
95
|
+
# # => [:foo, :bar]
|
96
|
+
#
|
97
|
+
# class BarEvent < Emittance::Event
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# BarEvent.identifiers
|
101
|
+
# # => []
|
38
102
|
#
|
39
|
-
#
|
103
|
+
# Since +BarEvent+'s default identifier was already reserved when it was created, it could not claim that identifier.
|
104
|
+
# We can manually add an identifier post-hoc, but this would nevertheless become confusing.
|
40
105
|
#
|
41
106
|
class Event
|
42
107
|
class << self
|
43
|
-
# @return [Symbol] the identifier that can be used by the {Emittance::Broker broker} to find event handlers
|
44
|
-
def
|
45
|
-
|
108
|
+
# @return [Array<Symbol>] the identifier that can be used by the {Emittance::Broker broker} to find event handlers
|
109
|
+
def identifiers
|
110
|
+
EventLookup.identifiers_for_klass(self).to_a
|
46
111
|
end
|
47
112
|
|
48
113
|
# Gives the Event object a custom identifier.
|
49
114
|
#
|
50
115
|
# @param sym [Symbol] the identifier you wish to identify this event by when emitting and watching for it
|
51
116
|
def add_identifier(sym)
|
52
|
-
|
117
|
+
raise Emittance::InvalidIdentifierError, 'Identifiers must respond to #to_sym' unless sym.respond_to?(:to_sym)
|
118
|
+
EventLookup.register_identifier self, sym.to_sym
|
53
119
|
end
|
54
120
|
|
55
|
-
# @
|
121
|
+
# @param identifiers [*] anything that can be derived into an identifier (or the event class itself) for the
|
122
|
+
# purposes of looking up an event class.
|
56
123
|
def event_klass_for(*identifiers)
|
57
|
-
|
124
|
+
EventLookup.find_event_klass(*identifiers)
|
58
125
|
end
|
59
126
|
end
|
60
127
|
|
61
128
|
attr_reader :emitter, :timestamp, :payload
|
62
129
|
|
130
|
+
# @param emitter the object that emitted the event
|
131
|
+
# @param timestamp [Time] the time at which the event occurred
|
132
|
+
# @param payload any useful data that might be of use to event watchers
|
63
133
|
def initialize(emitter, timestamp, payload)
|
64
134
|
@emitter = emitter
|
65
135
|
@timestamp = timestamp
|
66
136
|
@payload = payload
|
67
137
|
end
|
68
138
|
|
69
|
-
|
70
|
-
|
139
|
+
# @return [Array<Symbol>] all identifiers that can be used to identify the event
|
140
|
+
def identifiers
|
141
|
+
self.class.identifiers
|
71
142
|
end
|
72
143
|
end
|
73
144
|
end
|
@@ -0,0 +1,315 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Emittance
|
6
|
+
##
|
7
|
+
# For looking up event classes by their identifiers. Can perform a reverse lookup of identifiers by their associated
|
8
|
+
# event class.
|
9
|
+
#
|
10
|
+
module EventLookup
|
11
|
+
class << self
|
12
|
+
include Emittance::Helpers::StringHelpers
|
13
|
+
|
14
|
+
# Look up an {Emittance::Event} class by an identifier. Generates an Event class if no such class exists for
|
15
|
+
# that identifier.
|
16
|
+
#
|
17
|
+
# EventLookup.find_event_klass :foo
|
18
|
+
# # => FooEvent
|
19
|
+
#
|
20
|
+
# When passed subclass of {Emittance::Event}, it returns that event class.
|
21
|
+
#
|
22
|
+
# class BarEvent < Emittance::Event
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# EventLookup.find_event_klass BarEvent
|
26
|
+
# # => BarEvent
|
27
|
+
#
|
28
|
+
# Instances of an {Emittance::Event} will fetch the class of that instance.
|
29
|
+
#
|
30
|
+
# EventLookup.find_event_klass BarEvent.new(nil, nil, nil)
|
31
|
+
# # => BarEvent
|
32
|
+
#
|
33
|
+
# Can be passed multiple arguments as a composite identifier. Useful for identifying events by Class#method.
|
34
|
+
#
|
35
|
+
# # Not entirely necessary, but for illustrative purposes.
|
36
|
+
# class Baz
|
37
|
+
# def greet
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# EventLookup.find_event_klass Baz, :greet
|
42
|
+
# # => BazGreetEvent
|
43
|
+
#
|
44
|
+
# @param objs [*] anything that can be used to identify an Event class
|
45
|
+
# @return [Emittance::Event] the event class identifiable by the params
|
46
|
+
def find_event_klass(*objs)
|
47
|
+
klass = nil
|
48
|
+
|
49
|
+
klass ||= pass_klass_through(*objs)
|
50
|
+
klass ||= klass_of_event(*objs)
|
51
|
+
klass ||= find_by_identifier(*objs)
|
52
|
+
|
53
|
+
klass
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param klass [Class] a subclass of {Emittance::Event} you wish to find the identifiers for
|
57
|
+
# @return [Set<Symbol>] a collection of identifiers that can be used to identify that event class
|
58
|
+
def identifiers_for_klass(klass)
|
59
|
+
Emittance::EventLookup::Registry.identifiers_for_klass(klass)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Registers an identifier for an Event class. After registering, that identifier can be used to identify those
|
63
|
+
# events.
|
64
|
+
#
|
65
|
+
# @param klass [Class] the class you wish to register the identifier for
|
66
|
+
# @param identifier [Symbol] identifier you want to identify the class as
|
67
|
+
# @return [Class] the class for which you've just registered an identifier
|
68
|
+
def register_identifier(klass, identifier)
|
69
|
+
Emittance::EventLookup::Registry.register_identifier klass: klass, identifier: identifier
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def pass_klass_through(*objs)
|
75
|
+
objs.length == 1 && event_klass?(objs[0]) ? objs[0] : nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def klass_of_event(*objs)
|
79
|
+
objs.length == 1 && event_object?(objs[0]) ? objs[0].class : nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_by_identifier(*objs)
|
83
|
+
identifier = CompositeIdentifier.new(*objs).generate
|
84
|
+
lookup_identifier identifier
|
85
|
+
end
|
86
|
+
|
87
|
+
def event_klass?(obj)
|
88
|
+
obj.is_a?(Class) && obj < Emittance::Event
|
89
|
+
end
|
90
|
+
|
91
|
+
def event_object?(obj)
|
92
|
+
obj.is_a? Emittance::Event
|
93
|
+
end
|
94
|
+
|
95
|
+
def lookup_identifier(identifier)
|
96
|
+
Emittance::EventLookup::Registry.fetch_event_klass(identifier)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Shared behavior for things that want to convert back and forth between event classes and identifiers
|
102
|
+
#
|
103
|
+
class EventKlassConverter
|
104
|
+
include Emittance::Helpers::StringHelpers
|
105
|
+
|
106
|
+
# The thing we want to append to every event class name
|
107
|
+
KLASS_NAME_SUFFIX = 'Event'
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Converts a collection of objects to a ready-to-go identifier.
|
112
|
+
#
|
113
|
+
class CompositeIdentifier < EventKlassConverter
|
114
|
+
def initialize(*objs)
|
115
|
+
@objs = objs
|
116
|
+
end
|
117
|
+
|
118
|
+
# Compiles the objects and generates an event class name for them.
|
119
|
+
def generate
|
120
|
+
parts = objs.map { |obj| identifier_name_for obj }
|
121
|
+
compose_identifier_parts parts
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
attr_reader :objs
|
127
|
+
|
128
|
+
def identifier_name_for(obj)
|
129
|
+
name_str = obj.to_s
|
130
|
+
name_str = clean_up_punctuation name_str
|
131
|
+
name_str = snake_case name_str
|
132
|
+
|
133
|
+
name_str
|
134
|
+
end
|
135
|
+
|
136
|
+
def compose_identifier_parts(parts)
|
137
|
+
parts.join('_').to_sym
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Derives an event class name from an identifier.
|
143
|
+
#
|
144
|
+
class EventKlassName < EventKlassConverter
|
145
|
+
def initialize(identifier)
|
146
|
+
@identifier = identifier
|
147
|
+
end
|
148
|
+
|
149
|
+
# Generates an event class name for the given identifier.
|
150
|
+
def generate
|
151
|
+
base_name = camel_case identifier.to_s
|
152
|
+
decorate_klass_name base_name
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
attr_reader :identifier
|
158
|
+
|
159
|
+
def decorate_klass_name(klass_name_str)
|
160
|
+
"#{klass_name_str}#{KLASS_NAME_SUFFIX}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Derives an identifier from the name of an event class.
|
166
|
+
#
|
167
|
+
class EventIdentifier < EventKlassConverter
|
168
|
+
def initialize(klass)
|
169
|
+
@klass = klass
|
170
|
+
validate_klass
|
171
|
+
end
|
172
|
+
|
173
|
+
# Generates an identifier name for the given event class.
|
174
|
+
def generate
|
175
|
+
camel_cased_name = undecorate_klass_name(klass.name)
|
176
|
+
snake_case(camel_cased_name).to_sym
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
attr_reader :klass
|
182
|
+
|
183
|
+
def validate_klass
|
184
|
+
subklass_error_msg = "#{klass.name} is not a subclass of Emittance::Event!"
|
185
|
+
raise Emittance::IdentifierGenerationError, subklass_error_msg unless klass < Emittance::Event
|
186
|
+
end
|
187
|
+
|
188
|
+
def undecorate_klass_name(klass_name)
|
189
|
+
klass_name.gsub(/#{KLASS_NAME_SUFFIX}$/, '')
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# Caches event-to-identifier and identifier-to-event mappings. The strategy here is to lazily store/load those
|
195
|
+
# mappings. They are created on lookup. The other option would be to add a +.inherited+ method to
|
196
|
+
# {Emittance::Event} that would make subclasses register themselves, but would cause some unwanted entanglement.
|
197
|
+
#
|
198
|
+
module Registry
|
199
|
+
@identifier_to_klass_mappings = {}
|
200
|
+
@klass_to_identifier_mappings = {}
|
201
|
+
|
202
|
+
class << self
|
203
|
+
include Emittance::Helpers::ConstantHelpers
|
204
|
+
|
205
|
+
# Finds or generates the event class associated with the identifier.
|
206
|
+
#
|
207
|
+
# @param identifier [Symbol] the identifier registered to the event class you wish to fetch
|
208
|
+
# @return [Class] the event class you wish to fetch
|
209
|
+
def fetch_event_klass(identifier)
|
210
|
+
klass = nil
|
211
|
+
|
212
|
+
klass ||= identifier_to_klass_mappings[identifier]
|
213
|
+
klass ||= derive_event_klass(identifier)
|
214
|
+
|
215
|
+
klass
|
216
|
+
end
|
217
|
+
|
218
|
+
# Retrieves all identifiers associated with the event class.
|
219
|
+
#
|
220
|
+
# @param event_klass [Class] the class you want the identifiers for
|
221
|
+
# @return [Set<Symbol>] all identifiers that can be used to identify the given event class
|
222
|
+
def identifiers_for_klass(event_klass)
|
223
|
+
lookup_klass_to_identifier_mapping(event_klass) ||
|
224
|
+
(create_mapping_for_klass(event_klass) && lookup_klass_to_identifier_mapping(event_klass))
|
225
|
+
end
|
226
|
+
|
227
|
+
# Registers the given identifier for the given event class.
|
228
|
+
#
|
229
|
+
# @param klass [Class] the event class you would like to register the identifier for
|
230
|
+
# @param identifier [Symbol] the identifier with which you want to identify the event class
|
231
|
+
# @return [Class] the event class for which you've registered the identifier
|
232
|
+
def register_identifier(klass:, identifier:)
|
233
|
+
raise Emittance::InvalidIdentifierError unless valid_identifier? identifier
|
234
|
+
raise Emittance::IdentifierCollisionError if identifier_reserved? identifier, klass
|
235
|
+
|
236
|
+
identifier_to_klass_mappings[identifier] = klass
|
237
|
+
|
238
|
+
klass_to_identifier_mappings[klass] ||= empty_collection
|
239
|
+
klass_to_identifier_mappings[klass] << identifier
|
240
|
+
|
241
|
+
klass
|
242
|
+
end
|
243
|
+
|
244
|
+
# Clears all registrations.
|
245
|
+
#
|
246
|
+
# @return [Boolean] true
|
247
|
+
def clear_registrations!
|
248
|
+
identifier_to_klass_mappings.clear
|
249
|
+
klass_to_identifier_mappings.clear
|
250
|
+
end
|
251
|
+
|
252
|
+
private
|
253
|
+
|
254
|
+
attr_reader :identifier_to_klass_mappings, :klass_to_identifier_mappings
|
255
|
+
|
256
|
+
def identifier_reserved?(identifier, klass)
|
257
|
+
klass_already_exists_for_identifier?(identifier, klass) || !!identifier_to_klass_mappings[identifier]
|
258
|
+
end
|
259
|
+
|
260
|
+
def klass_already_exists_for_identifier?(identifier, klass)
|
261
|
+
derived_klass_name = klass_name_for identifier
|
262
|
+
Object.const_defined?(derived_klass_name) && klass.name != derived_klass_name
|
263
|
+
end
|
264
|
+
|
265
|
+
def lookup_klass_to_identifier_mapping(event_klass)
|
266
|
+
klass_to_identifier_mappings[event_klass]
|
267
|
+
end
|
268
|
+
|
269
|
+
def create_mapping_for_klass(event_klass)
|
270
|
+
new_identifier = derive_identifier_from_klass(event_klass)
|
271
|
+
register_identifier(identifier: new_identifier, klass: event_klass)
|
272
|
+
|
273
|
+
new_identifier
|
274
|
+
end
|
275
|
+
|
276
|
+
def valid_identifier?(identifier)
|
277
|
+
identifier.is_a? Symbol
|
278
|
+
end
|
279
|
+
|
280
|
+
def derive_event_klass(identifier)
|
281
|
+
klass_name = klass_name_for identifier
|
282
|
+
event_klass = find_or_create_event_klass klass_name
|
283
|
+
register_identifier(identifier: identifier, klass: event_klass)
|
284
|
+
|
285
|
+
event_klass
|
286
|
+
end
|
287
|
+
|
288
|
+
def derive_identifier_from_klass(event_klass)
|
289
|
+
EventIdentifier.new(event_klass).generate
|
290
|
+
end
|
291
|
+
|
292
|
+
def klass_name_for(identifier)
|
293
|
+
EventKlassName.new(identifier).generate
|
294
|
+
end
|
295
|
+
|
296
|
+
def find_or_create_event_klass(klass_name)
|
297
|
+
lookup_event_klass(klass_name) || create_event_klass(klass_name)
|
298
|
+
end
|
299
|
+
|
300
|
+
def lookup_event_klass(klass_name)
|
301
|
+
Object.const_defined?(klass_name) ? Object.const_get(klass_name) : nil
|
302
|
+
end
|
303
|
+
|
304
|
+
def create_event_klass(klass_name)
|
305
|
+
new_klass = Class.new(Emittance::Event)
|
306
|
+
set_namespaced_constant_by_name klass_name, new_klass
|
307
|
+
end
|
308
|
+
|
309
|
+
def empty_collection
|
310
|
+
Set.new
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|