announcer 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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