announcer 0.5.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.
@@ -0,0 +1,7 @@
1
+ module Ribbon::EventBus
2
+ module Mixins
3
+ autoload(:HasInstance, 'ribbon/event_bus/mixins/has_instance')
4
+ autoload(:HasConfig, 'ribbon/event_bus/mixins/has_config')
5
+ autoload(:Serializable, 'ribbon/event_bus/mixins/serializable')
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ module Ribbon::EventBus
2
+ module Mixins
3
+ module HasConfig
4
+ def self.included(base)
5
+ raise "HasConfig requires HasInstance" unless base < HasInstance
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def config_key(key)
11
+ config_keys << key.to_sym
12
+ end
13
+
14
+ def config_keys(*keys)
15
+ unless keys.empty?
16
+ _has_config_values[:keys] = keys.map(&:to_sym)
17
+ else
18
+ _has_config_values[:keys] ||= _has_config_ancestor_keys
19
+ end
20
+ end
21
+
22
+ def _has_config_ancestor_keys
23
+ ancestors[1] < HasConfig ? ancestors[1].config_keys.dup : []
24
+ end
25
+
26
+ def _has_config_values
27
+ @__has_config_values ||= {}
28
+ end
29
+ end
30
+
31
+ def config
32
+ _has_config_config
33
+ end
34
+
35
+ def _has_config_config
36
+ @__has_config_config ||= _has_config_load_config.dup
37
+ end
38
+
39
+ def _has_config_load_config
40
+ keys = self.class.config_keys
41
+ keys.inject(instance.config) { |c, k| c.send(k) }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module Ribbon
2
+ module EventBus::Mixins
3
+ module HasInstance
4
+ def instance
5
+ (defined?(@instance) && @instance) || EventBus.instance
6
+ end
7
+
8
+ def plugins
9
+ instance.send(:plugins)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,183 @@
1
+ require 'base64'
2
+
3
+ module Ribbon::EventBus
4
+ module Mixins
5
+ module Serializable
6
+ MAGIC = :SRLZ
7
+ VERSION = 1
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+ def serialize_with(*args)
15
+ _serializable_values[:args] = args.map(&:to_sym)
16
+ end
17
+
18
+ def deserialize(encoded)
19
+ marshalled = _serializable_decode(encoded)
20
+ package = _serializable_unmarshal(marshalled)
21
+ _serializable_load_package(package)
22
+ end
23
+
24
+ def _serializable_load_package(package)
25
+ klass, args = _serializable_unpackage(package)
26
+ klass._deserialize_args(args)
27
+ end
28
+
29
+ def _deserialize_args(args)
30
+ args = args.map { |arg| _deserialize_arg(arg) }
31
+
32
+ if respond_to?(:load_from_serialized)
33
+ load_from_serialized(*args)
34
+ else
35
+ new(*args)
36
+ end
37
+ end
38
+
39
+ def _deserialize_arg(arg)
40
+ if _serializable_valid_package?(arg, false)
41
+ _serializable_load_package(arg)
42
+ else
43
+ arg
44
+ end
45
+ end
46
+
47
+ ###
48
+ # Encoding
49
+ ###
50
+ def _serializable_encode(marshalled)
51
+ Base64.strict_encode64(marshalled) # => encoded
52
+ end
53
+
54
+ def _serializable_decode(encoded)
55
+ begin
56
+ Base64.strict_decode64(encoded) # => marshalled
57
+ rescue ArgumentError
58
+ raise Errors::SerializableError, 'serialized string not encoded properly'
59
+ end
60
+ end
61
+
62
+ ###
63
+ # Marshalling
64
+ ###
65
+ def _serializable_marshal(package)
66
+ Marshal.dump(package) # => marshalled
67
+ end
68
+
69
+ def _serializable_unmarshal(marshalled)
70
+ begin
71
+ Marshal.load(marshalled) # => package
72
+ rescue TypeError
73
+ raise Errors::SerializableError, 'incorrect format'
74
+ end
75
+ end
76
+
77
+ ###
78
+ # Class Encoding
79
+ ###
80
+
81
+ def _serializable_encode_class(klass)
82
+ klass.name.to_s.sub('Ribbon::EventBus::', '').to_sym
83
+ end
84
+
85
+ def _serializable_decode_class(encoded_klass)
86
+ Ribbon::EventBus.const_get(encoded_klass.to_s)
87
+ end
88
+
89
+ ###
90
+ # Packaging
91
+ ###
92
+ def _serializable_package(klass, args)
93
+ encoded_klass = _serializable_encode_class(klass)
94
+ [MAGIC, VERSION, encoded_klass] + args # => package
95
+ end
96
+
97
+ def _serializable_valid_package?(package, noisy=true)
98
+ unless package.is_a?(Array)
99
+ if noisy
100
+ raise Errors::SerializableError, 'not a package'
101
+ else
102
+ return false
103
+ end
104
+ end
105
+
106
+ magic, version, class_name = package
107
+
108
+ # Check Magic
109
+ unless magic == MAGIC
110
+ if noisy
111
+ raise Errors::SerializableError, 'invalid serialized package'
112
+ else
113
+ return false
114
+ end
115
+ end
116
+
117
+ # Check Version
118
+ unless version == VERSION
119
+ if noisy
120
+ raise Errors::SerializableError, 'unsupported package version'
121
+ else
122
+ return false
123
+ end
124
+ end
125
+
126
+ # Check Class Name
127
+ unless class_name.is_a?(Symbol)
128
+ if noisy
129
+ raise Errors::SerializableError, 'invalid class name'
130
+ else
131
+ return false
132
+ end
133
+ end
134
+
135
+ return true
136
+ end
137
+
138
+ def _serializable_unpackage(package)
139
+ _serializable_valid_package?(package)
140
+ magic, version, encoded_klass, *args = package
141
+ klass = _serializable_decode_class(encoded_klass)
142
+ [klass, args]
143
+ end
144
+
145
+ ###
146
+ # Helpers
147
+ ###
148
+ def _serializable_values
149
+ @__serializable_values ||= {}
150
+ end
151
+
152
+ def _serializable_args
153
+ _serializable_values[:args] or
154
+ raise Errors::SerializableError, "Missing serialize_with definition"
155
+ end
156
+ end
157
+
158
+ def serialize
159
+ package = _serializable_package
160
+ marshalled = self.class._serializable_marshal(package)
161
+ self.class._serializable_encode(marshalled)
162
+ end
163
+
164
+ def _serializable_package
165
+ args = _serializable_args.map { |arg| _serialize_arg(send(arg)) }
166
+ self.class._serializable_package(self.class, args)
167
+ end
168
+
169
+ def _serialize_arg(arg)
170
+ case arg
171
+ when Serializable
172
+ arg._serializable_package
173
+ else
174
+ arg
175
+ end
176
+ end
177
+
178
+ def _serializable_args
179
+ self.class._serializable_args
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,6 @@
1
+ module Ribbon::EventBus
2
+ module Plugins
3
+ autoload(:Plugin, 'ribbon/event_bus/plugins/plugin')
4
+ autoload(:LoggingPlugin, 'ribbon/event_bus/plugins/logging_plugin')
5
+ end
6
+ end
@@ -0,0 +1,68 @@
1
+ require 'logger'
2
+
3
+ module Ribbon::EventBus
4
+ module Plugins
5
+ class LoggingPlugin < Plugin
6
+ config_key :logging
7
+
8
+ def logger
9
+ @_logger ||= _load_logger
10
+ end
11
+
12
+ around_publish do |event|
13
+ _run('Publishing', event) { publish }
14
+ end
15
+
16
+ around_resque_publish do |event|
17
+ _run('Publishing on Resque', event) { resque_publish }
18
+ end
19
+
20
+ around_subscription do |sub, event|
21
+ _run('Executing Subscription', sub) { subscription }
22
+ end
23
+
24
+ private
25
+ def _load_logger
26
+ if config.logger?
27
+ config.logger
28
+ else
29
+ Logger.new(STDOUT).tap { |logger|
30
+ logger.level = _load_level
31
+ }
32
+ end
33
+ end
34
+
35
+ def _load_level
36
+ case config.level
37
+ when :info, nil
38
+ Logger::INFO
39
+ when :warn
40
+ Logger::WARN
41
+ when :error
42
+ Logger::ERROR
43
+ when :fatal
44
+ Logger::FATAL
45
+ else
46
+ raise Errors::PluginError, "Invalid plugins.logging.level: #{config.level.inspect}"
47
+ end
48
+ end
49
+
50
+ def _run(subject, object, &block)
51
+ logger.debug("#{subject}: #{object}")
52
+
53
+ if config.log_exceptions?
54
+ begin
55
+ block.call
56
+ rescue Exception => e
57
+ logger.fatal("Exception raised when #{subject.downcase} #{object}: #{e.inspect}")
58
+ raise
59
+ end
60
+ else
61
+ block.call
62
+ end
63
+
64
+ logger.debug("Finished #{subject}: #{object}")
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,22 @@
1
+ require 'ribbon/plugins'
2
+
3
+ module Ribbon::EventBus
4
+ module Plugins
5
+ class Plugin < Ribbon::Plugins::Plugin
6
+ include Mixins::HasInstance
7
+ include Mixins::HasConfig
8
+
9
+ config_key :plugins
10
+
11
+ def initialize(plugins, params={})
12
+ super(plugins)
13
+ @instance = plugins.component
14
+ @_params = params || {}
15
+ end
16
+
17
+ def config
18
+ @__config ||= super.merge_hash!(@_params)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ module Ribbon::EventBus
2
+ module Publishers
3
+ autoload(:Publisher,
4
+ 'ribbon/event_bus/publishers/publisher')
5
+ autoload(:ProcPublisher,
6
+ 'ribbon/event_bus/publishers/proc_publisher')
7
+ autoload(:SubscriptionsPublisher,
8
+ 'ribbon/event_bus/publishers/subscriptions_publisher')
9
+ autoload(:ResquePublisher,
10
+ 'ribbon/event_bus/publishers/resque_publisher')
11
+ autoload(:RemoteResquePublisher,
12
+ 'ribbon/event_bus/publishers/remote_resque_publisher')
13
+ autoload(:AsyncResquePublisher,
14
+ 'ribbon/event_bus/publishers/async_resque_publisher')
15
+
16
+ module_function
17
+ def load_for_instance(instance)
18
+ config = instance.config
19
+ config.publish_to? ? _load_for_instance(instance, config.publish_to) : []
20
+ end
21
+
22
+ def _load_for_instance(instance, publishers)
23
+ publishers.map { |publisher| _load_with_args(publisher, instance) }
24
+ end
25
+
26
+ def _load_with_args(publisher, *args)
27
+ case publisher
28
+ when Array
29
+ _load_with_args(publisher[0], *(args + publisher[1..-1]))
30
+ else
31
+ load(publisher).new(*args)
32
+ end
33
+ end
34
+
35
+ def load(publisher)
36
+ case publisher
37
+ when String, Symbol then _load_from_string(publisher.to_s)
38
+ when Proc then _load_from_proc(publisher)
39
+ when Publisher then publisher
40
+ else raise Errors::InvalidPublisherError, publisher.inspect
41
+ end
42
+ end
43
+
44
+ def _load_from_string(publisher_name)
45
+ const_get((publisher_name.split('_').map(&:capitalize) + ['Publisher']).join)
46
+ rescue NameError
47
+ raise Errors::InvalidPublisherNameError, publisher_name
48
+ end
49
+
50
+ def _load_from_proc(publisher_proc)
51
+ ProcPublisher.new(&publisher_proc)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,81 @@
1
+ require 'resque'
2
+ require 'redis'
3
+ require 'celluloid/current'
4
+
5
+ module Ribbon::EventBus
6
+ module Publishers
7
+ class AsyncResquePublisher < Publisher
8
+ config_key :resque
9
+
10
+ attr_reader :resque
11
+
12
+ def initialize(instance=nil, params={})
13
+ super
14
+ @resque = config.resque? ? config.resque : Resque
15
+ end
16
+
17
+ def worker_id
18
+ @__worker_id ||= "event_bus_resque_worker_#{object_id}".to_sym
19
+ end
20
+
21
+ ##
22
+ # The suprvisor created in the initializer will restart the PublisherWorker
23
+ # but there can be a short period of time when the actor returned by
24
+ # Celluloid::Actor[...] is dead. To avoid that we sleep for a millisecond
25
+ # to give it time to create a new worker thread. We try three times before
26
+ # giving up.
27
+ #
28
+ # This should ensure that it's unlikely for a dead worker to be returned.
29
+ # However, if a dead worker is returned, then async calls will silently
30
+ # fail, allowing normal execution. This makes firing events best-effort.
31
+ def worker
32
+ # Retrieve the PublisherWorker or start the supervisor.
33
+ w = Celluloid::Actor[worker_id] || PublisherWorker.supervise(
34
+ args: [self],
35
+ as: worker_id
36
+ ).send(worker_id)
37
+
38
+ 3.times {
39
+ if w.dead?
40
+ sleep(0.001)
41
+ w = Celluloid::Actor[worker_id]
42
+ else
43
+ break
44
+ end
45
+ }
46
+
47
+ w
48
+ end
49
+
50
+ ##
51
+ # Needs to exist for the ResquePublisher::PublisherJob to succeed.
52
+ def subscription_queue_formatter
53
+ ResquePublisher.subscription_queue_formatter(config)
54
+ end
55
+
56
+ def publish(event)
57
+ super
58
+ worker.async.publish(event) unless event.subscriptions.empty?
59
+ end
60
+
61
+ class PublisherWorker
62
+ include Celluloid
63
+
64
+ attr_reader :publisher
65
+
66
+ def initialize(publisher)
67
+ @publisher = publisher
68
+ end
69
+
70
+ def publish(event)
71
+ publisher.resque.enqueue_to(
72
+ publisher.config.publisher_queue.to_sym,
73
+ ResquePublisher::PublisherJob,
74
+ event.serialize,
75
+ :async_resque
76
+ )
77
+ end
78
+ end # PublisherWorker
79
+ end
80
+ end
81
+ end