dispatch-rider 0.0.7 → 0.1.0

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
+ SHA1:
3
+ metadata.gz: 7bbb3c1f98b4bafe69fae92ea7508cd67345275a
4
+ data.tar.gz: b849394b852851fa7d29f25338ef17a1870ad8b3
5
+ SHA512:
6
+ metadata.gz: dc1538bee183a3e7f03e0e06e141704e1abb7cdc7e765d9e69918ada0bd90fcc836a29f6440822a83c16fe7d7469bbe95735ffeb753995974beed6485698e34f
7
+ data.tar.gz: bcbe7f23996a3dcf324b64598db790b02886c83b1afa37c0085711cd688a0aad63096849c0fbf35753f6fba7ba231014590390b1168389754af8d08e3b1d2e96
data/README.md CHANGED
@@ -166,6 +166,31 @@ News.create!(:headlines => [
166
166
 
167
167
  ### Subscriber
168
168
 
169
+ ### Configuration
170
+
171
+ You can configure the subscription side of DispatchRider by using the built in configuration object.
172
+
173
+ ```ruby
174
+ DispatchRider.config do |config|
175
+ config.before(:initialize) do
176
+ # code to run before initialize
177
+ end
178
+
179
+ config.after(:process) do
180
+ # code to run after process
181
+ end
182
+
183
+ config.error_handler = DefaultErrorHandler # an object that responds to .call(message, exception)
184
+
185
+ config.queue_kind = :sqs
186
+ config.queue_info = { name: "queue-production" }
187
+
188
+ config.handler_path = Rails.root + "app/handlers" # path to handler files to be autoloaded
189
+ end
190
+ ```
191
+
192
+ ### Manual Setup
193
+
169
194
  To setup a subscriber you'll need message handlers. The handlers are named the same as the message subjects.
170
195
 
171
196
  Sample message handler:
@@ -197,23 +222,39 @@ subscriber.process
197
222
  Sample subscriber dispatch error handling (optional):
198
223
 
199
224
  ```ruby
200
- # using blocks
225
+ # using objects
201
226
 
202
- subscriber.on_dispatch_error do |message, exception|
203
- # put your error handling code here
227
+ module ErrorHandler
228
+
229
+ def self.call(message, exception)
230
+ # put your error handling code here
231
+
232
+ return false # or return true to permanently remove the message
233
+ end
204
234
 
205
- return false # or return true to permanently remove the message
206
235
  end
207
236
 
208
- # using methods
237
+ subscriber.setup_demultiplexer(kind, ErrorHandler)
209
238
 
210
- def handle_dispatch_error(message, exception)
239
+ # using lambdas
240
+
241
+ error_handler = ->(message, exception) do
211
242
  # put your error handling code here
212
243
 
213
244
  return false # or return true to permanently remove the message
214
245
  end
215
246
 
216
- subscriber.on_dispatch_error &method(:handle_dispatch_error)
247
+ subscriber.setup_demultiplexer(kind, error_handler)
248
+ ```
249
+
250
+ #### Airbrake Support
251
+ Airbrake is supported out of the box. All you need to do is:
252
+
253
+ 1. Install and configure the [airbrake gem](https://github.com/airbrake/airbrake).
254
+ 2. Use the `AirbrakeErrorHandler`.
255
+
256
+ ```ruby
257
+ subscriber.setup_demultiplexer(kind, AirbrakeErrorHandler)
217
258
  ```
218
259
 
219
260
  ## Contributing
@@ -0,0 +1,29 @@
1
+ module DispatchRider
2
+ module Callbacks
3
+ class Access
4
+ attr_reader :callbacks
5
+
6
+ def initialize(callbacks)
7
+ @callbacks = callbacks
8
+ end
9
+
10
+ def invoke(event, *args)
11
+ begin
12
+ invoke_callbacks :before, event, *args
13
+ yield
14
+ ensure
15
+ invoke_callbacks :after, event, *args
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def invoke_callbacks(modifier, event, *args)
22
+ callbacks.for(modifier, event).each do |callback|
23
+ callback.call(*args)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module DispatchRider
2
+ module Callbacks
3
+ class Storage
4
+
5
+ def initialize
6
+ @callbacks = {}
7
+ end
8
+
9
+ def before(event, block_param = nil, &block)
10
+ add_callback :before, event, block_param, &block
11
+ end
12
+
13
+ def after(event, block_param = nil, &block)
14
+ add_callback :after, event, block_param, &block
15
+ end
16
+
17
+ def for(modifier, event)
18
+ @callbacks[[modifier, event]] || []
19
+ end
20
+
21
+ private
22
+
23
+ def add_callback(modifier, event, block_param = nil, &block)
24
+ block = block || block_param
25
+ @callbacks[[modifier, event]] ||= []
26
+ @callbacks[[modifier, event]] << block
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module DispatchRider
2
+ module Callbacks
3
+ end
4
+ end
5
+
6
+ require 'dispatch-rider/callbacks/access'
7
+ require 'dispatch-rider/callbacks/storage'
@@ -0,0 +1,32 @@
1
+ module DispatchRider
2
+ class Configuration
3
+ attr_accessor :handler_path, :error_handler, :queue_info, :queue_kind, :subscriber
4
+ attr_reader :callbacks
5
+
6
+ def initialize
7
+ @handler_path = Dir.getwd + "/app/handlers"
8
+ @error_handler = DispatchRider::DefaultErrorHandler
9
+ @queue_kind = :file_system
10
+ @queue_info = { path: "tmp/dispatch-rider-queue" }
11
+ @callbacks = Callbacks::Storage.new
12
+ @subscriber = DispatchRider::Subscriber
13
+ end
14
+
15
+ delegate :before, :after, :to => :callbacks
16
+
17
+ def handlers
18
+ @handlers ||= begin
19
+ load_handler_files
20
+ DispatchRider::Handlers::Base.subclasses.map{ |klass| klass.name.underscore.to_sym }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def load_handler_files
27
+ Dir["#{@handler_path}/*.rb"].each do |filename|
28
+ require filename.gsub(/\.rb$/, '')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -5,21 +5,22 @@
5
5
  # The demultiplexer can be stopped by calling the Demultiplexer#stop method.
6
6
  module DispatchRider
7
7
  class Demultiplexer
8
- attr_reader :queue, :dispatcher
8
+ attr_reader :queue, :dispatcher, :error_handler
9
9
 
10
- def initialize(queue, dispatcher)
10
+ def initialize(queue, dispatcher, error_handler)
11
11
  @queue = queue
12
12
  @dispatcher = dispatcher
13
+ @error_handler = error_handler
13
14
  @continue = true
14
15
  end
15
16
 
16
17
  def start
17
- catch(:done) do
18
- loop do
19
- throw :done unless @continue
20
- queue.pop do |message|
21
- dispatch_message(message)
22
- end
18
+ do_loop do
19
+ begin
20
+ handle_next_queue_item
21
+ rescue Exception => exception
22
+ error_handler.call(Message.new(subject: "TopLevelError", body: {}), exception)
23
+ throw :done
23
24
  end
24
25
  end
25
26
  self
@@ -29,8 +30,28 @@ module DispatchRider
29
30
  @continue = false
30
31
  end
31
32
 
33
+ private
34
+
32
35
  def dispatch_message(message)
33
36
  dispatcher.dispatch(message)
37
+ rescue Exception => exception
38
+ error_handler.call(message, exception)
34
39
  end
40
+
41
+ def do_loop
42
+ catch(:done) do
43
+ loop do
44
+ throw :done unless @continue
45
+ yield
46
+ end
47
+ end
48
+ end
49
+
50
+ def handle_next_queue_item
51
+ queue.pop do |message|
52
+ dispatch_message(message)
53
+ end
54
+ end
55
+
35
56
  end
36
57
  end
@@ -7,37 +7,18 @@ module DispatchRider
7
7
  class Dispatcher
8
8
  extend Forwardable
9
9
 
10
- require 'dispatch-rider/dispatcher/named_process'
11
- include NamedProcess
12
-
13
10
  attr_reader :handler_registrar
14
11
 
15
12
  def_delegators :handler_registrar, :register, :fetch, :unregister
16
13
 
17
14
  def initialize
18
15
  @handler_registrar = Registrars::Handler.new
19
- @error_handler = method(:default_error_handler)
20
- end
21
-
22
- def on_error(&block)
23
- @error_handler = block
24
16
  end
25
17
 
26
18
  def dispatch(message)
27
- with_named_process(message.subject) do
28
- handler_registrar.fetch(message.subject).process(message.body)
29
- end
30
-
19
+ handler_registrar.fetch(message.subject).new.do_process(message.body)
31
20
  true # success => true (delete message)
32
- rescue Exception => exception
33
- @error_handler.call(message, exception)
34
- false # failure => false (put message back on queue)
35
21
  end
36
22
 
37
- private
38
-
39
- def default_error_handler(message, exception)
40
- raise exception
41
- end
42
23
  end
43
24
  end
@@ -0,0 +1,18 @@
1
+ module DispatchRider
2
+
3
+ # This is the default error handler for dispatch rider.
4
+ # It simply re-raises the exception.
5
+ module DefaultErrorHandler
6
+ def self.call(message, exception)
7
+ raise exception
8
+ end
9
+ end
10
+
11
+ # This error handler integrates with airbrake.io, i
12
+ # sending the mesage and environment details.
13
+ module AirbrakeErrorHandler
14
+ def self.call(message, exception)
15
+ Airbrake.notify(exception, controller: "DispatchRider", action: message.subject, parameters: message.attributes, cgi_data: ENV)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module DispatchRider
2
+ module Handlers
3
+ class Base
4
+ include NamedProcess
5
+ extend InheritanceTracking
6
+
7
+ def do_process(options)
8
+ with_named_process(self.class.name) do
9
+ process(options)
10
+ end
11
+ end
12
+
13
+ def process(options)
14
+ raise NotImplementedError, "Method 'process' not overridden in subclass!"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # This module tracks which classes inherit from the class that includes
2
+ # the module, and provides an accessor to it.
3
+
4
+ module DispatchRider
5
+ module Handlers
6
+ module InheritanceTracking
7
+
8
+ def inherited(subclass)
9
+ subclasses << subclass
10
+ super
11
+ end
12
+
13
+ def subclasses
14
+ @subclasses ||= Set.new
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module DispatchRider
2
+ module Handlers
3
+ module NamedProcess
4
+
5
+ def with_named_process(subject)
6
+ original_program_name = $0
7
+ begin
8
+ $0 += " - #{subject}"
9
+ yield
10
+ ensure
11
+ $0 = original_program_name
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module DispatchRider
2
+ module Handlers
3
+ end
4
+ end
5
+
6
+ require 'dispatch-rider/handlers/named_process'
7
+ require 'dispatch-rider/handlers/inheritance_tracking'
8
+ require 'dispatch-rider/handlers/base'
@@ -0,0 +1,53 @@
1
+ module DispatchRider
2
+ class Runner
3
+
4
+ def self.run
5
+ new.process
6
+ end
7
+
8
+ def initialize
9
+ callbacks.invoke(:initialize) do
10
+ ready
11
+ set_queue_from_config
12
+ end
13
+ end
14
+ private_class_method :new
15
+
16
+ def process
17
+ callbacks.invoke(:process) do
18
+ puts "Running..."
19
+ @subscriber.process
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def config
26
+ DispatchRider.config
27
+ end
28
+
29
+ def callbacks
30
+ @callbacks ||= Callbacks::Access.new(config.callbacks)
31
+ end
32
+
33
+ def ready
34
+ puts "Creating subscriber..."
35
+ @subscriber = config.subscriber.new
36
+
37
+ config.handlers.each do |handler_name|
38
+ puts "Registering #{handler_name} handler..."
39
+ @subscriber.register_handler(handler_name)
40
+ end
41
+ end
42
+
43
+ def set_queue_from_config
44
+ kind = config.queue_kind
45
+ info = config.queue_info
46
+
47
+ puts "Setting #{kind} queue @ #{info.to_json} ..."
48
+ @subscriber.register_queue(kind, info)
49
+ @subscriber.setup_demultiplexer(kind, config.error_handler)
50
+ end
51
+
52
+ end
53
+ end
@@ -23,13 +23,9 @@ module DispatchRider
23
23
  self
24
24
  end
25
25
 
26
- def on_dispatch_error(&block)
27
- dispatcher.on_error &block
28
- end
29
-
30
- def setup_demultiplexer(queue_name)
26
+ def setup_demultiplexer(queue_name, error_handler = DispatchRider::DefaultErrorHandler)
31
27
  queue = queue_service_registrar.fetch(queue_name)
32
- @demultiplexer ||= DispatchRider::Demultiplexer.new(queue, dispatcher)
28
+ @demultiplexer ||= DispatchRider::Demultiplexer.new(queue, dispatcher, error_handler)
33
29
  self
34
30
  end
35
31
 
@@ -1,4 +1,4 @@
1
1
  # This file specifies the current version of the gem.
2
2
  module DispatchRider
3
- VERSION = "0.0.7"
3
+ VERSION = "0.1.0"
4
4
  end
@@ -7,15 +7,31 @@ require "active_support/json"
7
7
  require "active_support/core_ext/array/conversions"
8
8
  require "active_model"
9
9
 
10
+ require "dispatch-rider/configuration"
11
+
10
12
  module DispatchRider
13
+ class << self
14
+ def configure
15
+ yield configuration
16
+ end
17
+
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+ alias_method :config, :configuration
22
+ end
11
23
  end
12
24
 
13
25
  require "dispatch-rider/errors"
26
+ require "dispatch-rider/error_handlers"
27
+ require "dispatch-rider/handlers"
28
+ require "dispatch-rider/callbacks"
14
29
  require "dispatch-rider/message"
15
30
  require "dispatch-rider/registrars"
16
31
  require "dispatch-rider/notification_services"
17
32
  require "dispatch-rider/queue_services"
18
33
  require "dispatch-rider/dispatcher"
19
34
  require "dispatch-rider/demultiplexer"
35
+ require "dispatch-rider/runner"
20
36
  require "dispatch-rider/publisher"
21
37
  require "dispatch-rider/subscriber"
metadata CHANGED
@@ -1,84 +1,74 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dispatch-rider
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Suman Mukherjee
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-09-23 00:00:00.000000000 Z
11
+ date: 2013-10-24 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activesupport
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: activemodel
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: daemons
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: bundler
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: jeweler
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ~>
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ~>
92
81
  - !ruby/object:Gem::Version
@@ -94,65 +83,57 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rake
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: travis-lint
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - '>='
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: rspec
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
- - - ! '>='
115
+ - - '>='
132
116
  - !ruby/object:Gem::Version
133
117
  version: '0'
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
- - - ! '>='
122
+ - - '>='
140
123
  - !ruby/object:Gem::Version
141
124
  version: '0'
142
125
  - !ruby/object:Gem::Dependency
143
126
  name: debugger
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
- - - ! '>='
129
+ - - '>='
148
130
  - !ruby/object:Gem::Version
149
131
  version: '0'
150
132
  type: :development
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
- - - ! '>='
136
+ - - '>='
156
137
  - !ruby/object:Gem::Version
157
138
  version: '0'
158
139
  description: Messaging system that is customizable based on which queueing system
@@ -166,11 +147,19 @@ extra_rdoc_files:
166
147
  - README.md
167
148
  files:
168
149
  - lib/dispatch-rider.rb
150
+ - lib/dispatch-rider/callbacks.rb
151
+ - lib/dispatch-rider/callbacks/access.rb
152
+ - lib/dispatch-rider/callbacks/storage.rb
169
153
  - lib/dispatch-rider/command.rb
154
+ - lib/dispatch-rider/configuration.rb
170
155
  - lib/dispatch-rider/demultiplexer.rb
171
156
  - lib/dispatch-rider/dispatcher.rb
172
- - lib/dispatch-rider/dispatcher/named_process.rb
157
+ - lib/dispatch-rider/error_handlers.rb
173
158
  - lib/dispatch-rider/errors.rb
159
+ - lib/dispatch-rider/handlers.rb
160
+ - lib/dispatch-rider/handlers/base.rb
161
+ - lib/dispatch-rider/handlers/inheritance_tracking.rb
162
+ - lib/dispatch-rider/handlers/named_process.rb
174
163
  - lib/dispatch-rider/message.rb
175
164
  - lib/dispatch-rider/notification_services.rb
176
165
  - lib/dispatch-rider/notification_services/aws_sns.rb
@@ -195,6 +184,7 @@ files:
195
184
  - lib/dispatch-rider/registrars/publishing_destination.rb
196
185
  - lib/dispatch-rider/registrars/queue_service.rb
197
186
  - lib/dispatch-rider/registrars/sns_channel.rb
187
+ - lib/dispatch-rider/runner.rb
198
188
  - lib/dispatch-rider/subscriber.rb
199
189
  - lib/dispatch-rider/version.rb
200
190
  - LICENSE.txt
@@ -202,30 +192,26 @@ files:
202
192
  homepage: ''
203
193
  licenses:
204
194
  - MIT
195
+ metadata: {}
205
196
  post_install_message:
206
197
  rdoc_options: []
207
198
  require_paths:
208
199
  - lib
209
200
  required_ruby_version: !ruby/object:Gem::Requirement
210
- none: false
211
201
  requirements:
212
- - - ! '>='
202
+ - - '>='
213
203
  - !ruby/object:Gem::Version
214
204
  version: '0'
215
- segments:
216
- - 0
217
- hash: -1276525084671356042
218
205
  required_rubygems_version: !ruby/object:Gem::Requirement
219
- none: false
220
206
  requirements:
221
- - - ! '>='
207
+ - - '>='
222
208
  - !ruby/object:Gem::Version
223
209
  version: '0'
224
210
  requirements: []
225
211
  rubyforge_project:
226
- rubygems_version: 1.8.25
212
+ rubygems_version: 2.0.3
227
213
  signing_key:
228
- specification_version: 3
214
+ specification_version: 4
229
215
  summary: Messaging system based on the reactor patter. You can publish messages to
230
216
  a queue and then a demultiplexer runs an event loop which pops items from the queue
231
217
  and hands it over to a dispatcher. The dispatcher hands over the message to the
@@ -1,13 +0,0 @@
1
- module DispatchRider
2
- module Dispatcher::NamedProcess
3
- def with_named_process(subject)
4
- original_program_name = $0
5
- begin
6
- $0 += " - #{subject}"
7
- yield
8
- ensure
9
- $0 = original_program_name
10
- end
11
- end
12
- end
13
- end