dispatch-rider 0.0.7 → 0.1.0

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