batsir 0.1.0 → 0.3.7

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.
Files changed (63) hide show
  1. data/.travis.yml +9 -1
  2. data/CHANGES.md +54 -0
  3. data/Gemfile +7 -10
  4. data/README.md +49 -5
  5. data/Rakefile +23 -16
  6. data/batsir.gemspec +43 -15
  7. data/lib/batsir/acceptors/acceptor.rb +31 -5
  8. data/lib/batsir/acceptors/amqp_acceptor.rb +36 -8
  9. data/lib/batsir/amqp.rb +35 -7
  10. data/lib/batsir/amqp_consumer.rb +8 -0
  11. data/lib/batsir/compiler/stage_worker_compiler.rb +86 -0
  12. data/lib/batsir/config.rb +208 -24
  13. data/lib/batsir/dsl/conditional_notifier_declaration.rb +31 -0
  14. data/lib/batsir/dsl/dsl_mappings.rb +27 -2
  15. data/lib/batsir/errors.rb +18 -0
  16. data/lib/batsir/filter.rb +5 -0
  17. data/lib/batsir/log.rb +7 -0
  18. data/lib/batsir/logger.rb +37 -0
  19. data/lib/batsir/logo.rb +3 -8
  20. data/lib/batsir/notifiers/amqp_notifier.rb +14 -4
  21. data/lib/batsir/notifiers/conditional_notifier.rb +29 -0
  22. data/lib/batsir/notifiers/notifier.rb +6 -5
  23. data/lib/batsir/registry.rb +6 -2
  24. data/lib/batsir/stage.rb +9 -4
  25. data/lib/batsir/stage_worker.rb +3 -56
  26. data/lib/batsir/strategies/retry_strategy.rb +35 -0
  27. data/lib/batsir/strategies/strategy.rb +20 -0
  28. data/lib/batsir/transformers/field_transformer.rb +2 -3
  29. data/lib/batsir/transformers/json_input_transformer.rb +6 -2
  30. data/lib/batsir/transformers/json_output_transformer.rb +6 -2
  31. data/lib/batsir/transformers/transformer.rb +5 -1
  32. data/lib/batsir/version.rb +10 -0
  33. data/lib/batsir.rb +31 -13
  34. data/spec/batsir/acceptors/acceptor_spec.rb +7 -78
  35. data/spec/batsir/acceptors/amqp_acceptor_spec.rb +55 -66
  36. data/spec/batsir/acceptors/shared_examples.rb +102 -0
  37. data/spec/batsir/amqp_spec.rb +58 -0
  38. data/spec/batsir/chain_spec.rb +4 -4
  39. data/spec/batsir/config_spec.rb +97 -0
  40. data/spec/batsir/dsl/chain_mapping_spec.rb +5 -6
  41. data/spec/batsir/dsl/conditional_notifier_mapping_spec.rb +80 -0
  42. data/spec/batsir/dsl/stage_mapping_spec.rb +38 -20
  43. data/spec/batsir/filter_queue_spec.rb +9 -15
  44. data/spec/batsir/filter_spec.rb +4 -5
  45. data/spec/batsir/log_spec.rb +10 -0
  46. data/spec/batsir/logger_spec.rb +46 -0
  47. data/spec/batsir/notifiers/amqp_notifier_spec.rb +43 -22
  48. data/spec/batsir/notifiers/conditional_notifier_spec.rb +62 -0
  49. data/spec/batsir/notifiers/notifier_spec.rb +4 -66
  50. data/spec/batsir/notifiers/shared_examples.rb +100 -0
  51. data/spec/batsir/registry_spec.rb +48 -0
  52. data/spec/batsir/stage_spec.rb +91 -85
  53. data/spec/batsir/stage_worker_spec.rb +13 -13
  54. data/spec/batsir/strategies/retry_strategy_spec.rb +58 -0
  55. data/spec/batsir/strategies/strategy_spec.rb +28 -0
  56. data/spec/batsir/support/bunny_mocks.rb +78 -5
  57. data/spec/batsir/transformers/field_transformer_spec.rb +22 -22
  58. data/spec/batsir/transformers/json_input_transformer_spec.rb +8 -3
  59. data/spec/batsir/transformers/json_output_transformer_spec.rb +2 -2
  60. data/spec/batsir/transformers/transformer_spec.rb +18 -4
  61. data/spec/spec_helper.rb +6 -2
  62. metadata +78 -23
  63. data/VERSION +0 -1
data/lib/batsir/config.rb CHANGED
@@ -1,34 +1,218 @@
1
1
  module Batsir
2
2
  class Config
3
- def initialize(data={})
4
- @data = {}
5
- update!(data)
6
- end
3
+ class << self
7
4
 
8
- def update!(data)
9
- data.each do |key, value|
10
- self[key] = value
5
+ # Adapted from Merb::Config class
6
+
7
+ attr_accessor :config
8
+
9
+ def defaults
10
+ @defaults ||= {
11
+ :amqp_host => 'localhost',
12
+ :amqp_port => 5672,
13
+ :amqp_user => 'guest',
14
+ :amqp_pass => 'guest',
15
+ :amqp_vhost => '/',
16
+ :amqp_durable => true,
17
+ :ampq_pool_size => 5,
18
+ :redis_host => 'localhost',
19
+ :redis_port => 6379,
20
+ :redis_database => 0,
21
+ :redis_namespace => 'batsir',
22
+ :sidekiq_queue => 'batsir',
23
+ :log_name => 'batsir'
24
+ }
25
+ end
26
+
27
+ # Returns the current configuration or sets it up
28
+ def configuration
29
+ @config ||= setup
30
+ end
31
+
32
+ # Yields the configuration.
33
+ #
34
+ # ==== Block parameters
35
+ # c<Hash>:: The configuration parameters.
36
+ #
37
+ # ==== Examples
38
+ # Batsir::Config.use do |config|
39
+ # config[:exception_details] = false
40
+ # config[:log_stream] = STDOUT
41
+ # end
42
+ #
43
+ # ==== Returns
44
+ # nil
45
+ #
46
+ # :api: public
47
+ def use
48
+ yield configuration
49
+ nil
50
+ end
51
+
52
+ # Detects whether the provided key is in the config.
53
+ #
54
+ # ==== Parameters
55
+ # key<Object>:: The key to check.
56
+ #
57
+ # ==== Returns
58
+ # Boolean:: True if the key exists in the config.
59
+ #
60
+ # :api: public
61
+ def key?(key)
62
+ configuration.key?(key)
63
+ end
64
+
65
+ # Retrieve the value of a config entry.
66
+ #
67
+ # ==== Parameters
68
+ # key<Object>:: The key to retrieve the parameter for.
69
+ #
70
+ # ==== Returns
71
+ # Object:: The value of the configuration parameter.
72
+ #
73
+ # :api: public
74
+ def [](key)
75
+ configuration[key]
76
+ end
77
+
78
+ # Set the value of a config entry.
79
+ #
80
+ # ==== Parameters
81
+ # key<Object>:: The key to set the parameter for.
82
+ # val<Object>:: The value of the parameter.
83
+ #
84
+ # :api: public
85
+ def []=(key, val)
86
+ configuration[key] = val
87
+ end
88
+
89
+ # Remove the value of a config entry.
90
+ #
91
+ # ==== Parameters
92
+ # key<Object>:: The key of the parameter to delete.
93
+ #
94
+ # ==== Returns
95
+ # Object:: The value of the removed entry.
96
+ #
97
+ # :api: public
98
+ def delete(key)
99
+ configuration.delete(key)
11
100
  end
12
- end
13
101
 
14
- def [](key)
15
- @data[key.to_sym]
16
- end
102
+ # Resets the configuration to its default state
103
+ #
104
+ def reset
105
+ setup
106
+ end
107
+
108
+ # Retrieve the value of a config entry, returning the provided default if the key is not present
109
+ #
110
+ # ==== Parameters
111
+ # key<Object>:: The key to retrieve the parameter for.
112
+ # default<Object>::
113
+ # The default value to return if the parameter is not set.
114
+ #
115
+ # ==== Returns
116
+ # Object:: The value of the configuration parameter or the default.
117
+ #
118
+ # :api: public
119
+ def fetch(key, default = nil)
120
+ configuration.fetch(key, default)
121
+ end
122
+
123
+ # Returns the configuration as a hash.
124
+ #
125
+ # ==== Returns
126
+ # Hash:: The config as a hash.
127
+ #
128
+ # :api: public
129
+ def to_hash
130
+ configuration
131
+ end
132
+
133
+ # Sets up the configuration by storing the given settings.
134
+ #
135
+ # ==== Parameters
136
+ # settings<Hash>::
137
+ # Configuration settings to use. These are merged with the defaults.
138
+ #
139
+ # ==== Returns
140
+ # The configuration as a hash.
141
+ #
142
+ # :api: private
143
+ def setup(settings = {})
144
+ @config = defaults.merge(settings)
145
+ end
17
146
 
18
- def []=(key, value)
19
- if value.class == Hash
20
- @data[key.to_sym] = Config.new(value)
21
- else
22
- @data[key.to_sym] = value
147
+ # Set configuration parameters from a code block, where each method
148
+ # evaluates to a config parameter.
149
+ #
150
+ # ==== Parameters
151
+ # &block:: Configuration parameter block.
152
+ #
153
+ # ==== Examples
154
+ # # Set environment and log level.
155
+ # Batsir::Config.configure do
156
+ # environment "development"
157
+ # log_level "debug"
158
+ # end
159
+ #
160
+ # ==== Returns
161
+ # nil
162
+ #
163
+ # :api: public
164
+ def configure(&block)
165
+ ConfigBlock.new(self, &block) if block_given?
166
+ nil
23
167
  end
24
- end
25
168
 
26
- def method_missing(sym, *args)
27
- if sym.to_s =~ /(.+)=$/
28
- self[$1] = args.first
29
- else
30
- self[sym]
169
+ # Allows retrieval of single key config values via Batsir::Config.<key>
170
+ # Allows single key assignment via Merb.config.<key> = ...
171
+ #
172
+ # ==== Parameters
173
+ # method<~to_s>:: Method name as hash key value.
174
+ # *args:: Value to set the configuration parameter to.
175
+ #
176
+ # ==== Returns
177
+ # The value of the entry fetched or assigned to.
178
+ #
179
+ # :api: public
180
+ def method_missing(method, *args)
181
+ if method.to_s[-1,1] == '='
182
+ self[method.to_s.tr('=','').to_sym] = args.first
183
+ else
184
+ self[method]
185
+ end
31
186
  end
32
- end
33
- end
187
+
188
+ end # class << self
189
+
190
+ class ConfigBlock
191
+
192
+ # Evaluates the provided block, where any call to a method causes
193
+ # #[]= to be called on klass with the method name as the key and the arguments
194
+ # as the value.
195
+ #
196
+ # ==== Parameters
197
+ # klass<Object~[]=>:: The object on which to assign values.
198
+ # &block:: The block which specifies the config values to set.
199
+ #
200
+ # ==== Returns
201
+ # nil
202
+ #
203
+ # :api: private
204
+ def initialize(klass, &block)
205
+ @klass = klass
206
+ instance_eval(&block)
207
+ end
208
+
209
+ # Assign args as the value of the entry keyed by method.
210
+ #
211
+ # :api: private
212
+ def method_missing(method, *args)
213
+ @klass[method] = args.first
214
+ end
215
+
216
+ end # ConfigBlock
217
+ end # Config
34
218
  end
@@ -0,0 +1,31 @@
1
+ module Batsir
2
+ module DSL
3
+ class ConditionalNotifierDeclaration
4
+ attr_reader :notifier_declarations
5
+
6
+ NotifierConditionDeclaration = Struct.new(:condition, :notifier, :options)
7
+
8
+ def initialize
9
+ @notifier_declarations = []
10
+ end
11
+
12
+ def add_conditional(condition, notifier_class, options)
13
+ @notifier_declarations << NotifierConditionDeclaration.new(condition, notifier_class, options)
14
+ end
15
+
16
+ def compile(output, stage_worker)
17
+ output << <<-EOF
18
+ conditional_notifier = Batsir::Notifiers::ConditionalNotifier.new
19
+ EOF
20
+ notifier_declarations.each do |notifier_declaration|
21
+ output << <<-EOF
22
+ condition = ->(message){#{notifier_declaration.condition}}
23
+ conditional_notifier.add_notifier condition, #{notifier_declaration.notifier}.new(#{notifier_declaration.options.to_s})
24
+ EOF
25
+ end
26
+ stage_worker.add_transformers_to_notifier("conditional_notifier", output)
27
+ stage_worker.add_notifier( "conditional_notifier", output)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -32,8 +32,8 @@ module Batsir
32
32
  @stage
33
33
  end
34
34
 
35
- def filter(operation)
36
- @stage.add_filter(operation)
35
+ def filter(filter_class, options = {})
36
+ @stage.add_filter(filter_class, options)
37
37
  end
38
38
 
39
39
  def inbound(&block)
@@ -43,6 +43,7 @@ module Batsir
43
43
  def outbound(&block)
44
44
  ::Blockenspiel.invoke(block, Batsir::DSL::OutboundMapping.new(@stage))
45
45
  end
46
+
46
47
  end
47
48
 
48
49
  class InboundMapping < ::Blockenspiel::Base
@@ -78,6 +79,14 @@ module Batsir
78
79
  ::Blockenspiel.invoke(block, Batsir::DSL::OutboundTransformerMapping.new(@stage))
79
80
  end
80
81
 
82
+ def conditional(&block)
83
+ new_block = ::Proc.new do
84
+ conditional &block
85
+ end
86
+ conditional = ::Blockenspiel.invoke(new_block, Batsir::DSL::ConditionalNotifierMapping.new)
87
+ @stage.add_conditional_notifier(conditional)
88
+ end
89
+
81
90
  def notifier(notifier_class, options = {})
82
91
  @stage.add_notifier(notifier_class, options)
83
92
  end
@@ -92,5 +101,21 @@ module Batsir
92
101
  @stage.add_notifier_transformer(transformer, options)
93
102
  end
94
103
  end
104
+
105
+ class ConditionalNotifierMapping < ::Blockenspiel::Base
106
+ def initialize
107
+ @notifier = nil
108
+ end
109
+
110
+ def conditional(&block)
111
+ @notifier = Batsir::DSL::ConditionalNotifierDeclaration.new
112
+ ::Blockenspiel.invoke(block, self)
113
+ @notifier
114
+ end
115
+
116
+ def notify_if(condition, notifier, options = {})
117
+ @notifier.add_conditional(condition, notifier, options)
118
+ end
119
+ end
95
120
  end
96
121
  end
@@ -0,0 +1,18 @@
1
+ module Batsir
2
+ module Errors
3
+
4
+ class BatsirError < RuntimeError; end
5
+ class NotifierError < BatsirError; end
6
+ class TransformError < BatsirError; end
7
+ class StrategyError < BatsirError; end
8
+
9
+ class ExecuteMethodNotImplementedError < BatsirError; end
10
+
11
+ class NotifierConnectionError < NotifierError; end
12
+
13
+ class JSONInputTransformError < TransformError; end
14
+ class JSONOutputTransformError < TransformError; end
15
+
16
+ class RetryStrategyFailed < StrategyError; end
17
+ end
18
+ end
data/lib/batsir/filter.rb CHANGED
@@ -6,7 +6,12 @@ module Batsir
6
6
  end
7
7
  end
8
8
 
9
+ def filter(message)
10
+ execute(message)
11
+ end
12
+
9
13
  def execute(message)
14
+ raise NotImplementedError.new
10
15
  end
11
16
  end
12
17
  end
data/lib/batsir/log.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Batsir
2
+ module Log
3
+ def log
4
+ Batsir::Logger
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ require 'log4r'
2
+
3
+ module Batsir
4
+ module Logger
5
+ DEBUG = 1
6
+ INFO = 2
7
+ WARN = 3
8
+ ERROR = 4
9
+ FATAL = 5
10
+
11
+ class << self
12
+
13
+ DEFAULT_OUTPUT = 'stdout'
14
+
15
+ def log
16
+ @logger ||= setup
17
+ end
18
+
19
+ def setup
20
+ logger = Log4r::Logger.new(Batsir::Config.fetch(:log_name, "batsir"))
21
+ logger.level = Batsir::Config.fetch(:log_level, WARN)
22
+ logger.outputters = Batsir::Config.fetch(:log_outputter, DEFAULT_OUTPUT)
23
+ logger
24
+ end
25
+
26
+ def reset
27
+ @logger = nil
28
+ end
29
+
30
+ # makes this respond like a Log4r::Logger
31
+ def method_missing(sym, *args, &block)
32
+ log.send sym, *args, &block
33
+ end
34
+
35
+ end
36
+ end
37
+ end
data/lib/batsir/logo.rb CHANGED
@@ -2,10 +2,6 @@ module Batsir
2
2
  def self.logo
3
3
  <<EOF
4
4
 
5
-
6
-
7
-
8
-
9
5
  E ,.
10
6
  ,,,agB8@f ,8v ,8L J88&gg,,,
11
7
  .,a88888888@^ |8&,,,,,,88k ,88888888&g,,
@@ -26,10 +22,9 @@ module Batsir
26
22
  `?9@888&, j88f ,d88$@9l'
27
23
  `|?9t |f `9T"`
28
24
 
29
-
30
-
31
-
32
-
25
+ ______ _______ _______ _______ _____ ______
26
+ |_____] |_____| | |______ | |_____/
27
+ |_____] | | | ______| __|__ | \\_
33
28
 
34
29
  EOF
35
30
  end
@@ -5,11 +5,21 @@ module Batsir
5
5
  class AMQPNotifier < Notifier
6
6
  include Batsir::AMQP
7
7
 
8
+ attr_reader :error_strategy
9
+
10
+ def initialize(options = {}, error_strategy = Batsir::Strategies::RetryStrategy)
11
+ super(options)
12
+ @error_strategy = error_strategy.new(self)
13
+ @bunny = Bunny.new(bunny_options).start
14
+ @x = @bunny.exchange( exchange )
15
+ end
16
+
8
17
  def execute(message)
9
- Bunny.run( bunny_options )do |bunny|
10
- exc = bunny.exchange( exchange )
11
- exc.publish( message, :key => queue )
12
- end
18
+ @x.publish(message, :routing_key => queue)
19
+ end
20
+
21
+ def handle_error(message, error)
22
+ @error_strategy.execute(message, error)
13
23
  end
14
24
  end
15
25
  end
@@ -0,0 +1,29 @@
1
+ module Batsir
2
+ module Notifiers
3
+ class ConditionalNotifier < Notifier
4
+ attr_accessor :notifiers
5
+
6
+ NotifierCondition = Struct.new(:condition, :notifier, :options)
7
+
8
+ def initialize(options = {})
9
+ super
10
+ @notifiers = []
11
+ end
12
+
13
+ def add_notifier( condition, notifier_class, options = {})
14
+ self.notifiers << NotifierCondition.new(condition, notifier_class, options)
15
+ end
16
+
17
+ def execute(message)
18
+ self.notifiers.each do |notifier_condition|
19
+ if notifier_condition.condition.call(message)
20
+ notifier = notifier_condition.notifier
21
+ options = notifier_condition.options
22
+ notifier.notify(message)
23
+ end
24
+ end
25
+ message
26
+ end
27
+ end
28
+ end
29
+ end
@@ -21,11 +21,8 @@ module Batsir
21
21
  end
22
22
 
23
23
  def notify(message)
24
- execute(transform(message))
25
- end
26
-
27
- def execute(message)
28
-
24
+ execute(transform(message.clone))
25
+ message
29
26
  end
30
27
 
31
28
  def transform(message)
@@ -34,6 +31,10 @@ module Batsir
34
31
  end
35
32
  message
36
33
  end
34
+
35
+ def execute(message)
36
+ raise NotImplementedError.new
37
+ end
37
38
  end
38
39
  end
39
40
  end
@@ -5,11 +5,15 @@ module Batsir
5
5
  end
6
6
 
7
7
  def self.registry
8
- @registry ||= {}
8
+ @registry || reset
9
9
  end
10
10
 
11
11
  def self.get(name)
12
- registry.fetch(name)
12
+ registry.fetch(name, nil)
13
+ end
14
+
15
+ def self.reset
16
+ @registry = {}
13
17
  end
14
18
  end
15
19
  end
data/lib/batsir/stage.rb CHANGED
@@ -10,6 +10,7 @@ module Batsir
10
10
  attr_accessor :cancellators
11
11
  attr_reader :filter_declarations
12
12
  attr_reader :notifiers
13
+ attr_reader :conditional_notifiers
13
14
  attr_reader :acceptors
14
15
  attr_reader :running_acceptors
15
16
  attr_reader :notifier_transformers
@@ -25,6 +26,7 @@ module Batsir
25
26
  @acceptors = {}
26
27
  @filter_declarations = []
27
28
  @notifiers = {}
29
+ @conditional_notifiers = []
28
30
  @notifier_transformers = []
29
31
  @built = false
30
32
  end
@@ -50,6 +52,10 @@ module Batsir
50
52
  @filter_declarations.map{ |filter_declaration| filter_declaration.filter }
51
53
  end
52
54
 
55
+ def add_conditional_notifier(notifier_declaration)
56
+ @conditional_notifiers << notifier_declaration
57
+ end
58
+
53
59
  def add_notifier(notifier, options = {})
54
60
  @notifiers[notifier] ||= Set.new
55
61
  @notifiers[notifier] << options
@@ -72,10 +78,9 @@ module Batsir
72
78
  def start
73
79
  acceptors.each do |acceptor_class, options|
74
80
  options.each do |acceptor_options|
75
- cancellator_reader, cancellator_writer = ::IO.pipe
76
- acceptor_options.merge!(:stage_name => self.name, :cancellator => cancellator_reader)
77
- @cancellators << cancellator_writer
81
+ acceptor_options.merge!(:stage_name => self.name)
78
82
  acceptor = acceptor_class.new(acceptor_options)
83
+
79
84
  if acceptor_transformers.any?
80
85
  acceptor_transformers.each do |transformer_declaration|
81
86
  transformer = transformer_declaration.transformer.new(transformer_declaration.options)
@@ -85,7 +90,7 @@ module Batsir
85
90
  acceptor.add_transformer(Batsir::Transformers::JSONInputTransformer.new)
86
91
  end
87
92
  @running_acceptors << acceptor
88
- acceptor.start!
93
+ acceptor.async.start
89
94
  end
90
95
  end
91
96
  true
@@ -14,9 +14,10 @@ module Batsir
14
14
  end
15
15
 
16
16
  def execute(message)
17
+ return false if message.nil?
17
18
  return false unless @filter_queue
18
19
  @filter_queue.filters.each do |filter|
19
- message = filter.execute(message)
20
+ message = filter.filter(message)
20
21
  return false if message.nil?
21
22
  end
22
23
  @filter_queue.notifiers.each do |notifier|
@@ -26,61 +27,7 @@ module Batsir
26
27
  end
27
28
 
28
29
  def self.compile_from(stage)
29
- code = <<-EOF
30
- class #{stage.name.capitalize.gsub(' ','')}Worker
31
- def self.stage_name
32
- "#{stage.name}"
33
- end
34
-
35
- def initialize
36
- @filter_queue = self.class.filter_queue
37
- end
38
-
39
- def self.filter_queue
40
- @filter_queue
41
- end
42
-
43
- def self.initialize_filter_queue
44
- @filter_queue = Batsir::FilterQueue.new
45
- EOF
46
-
47
- stage.filter_declarations.each do |filter_declaration|
48
- code << <<-EOF
49
- @filter_queue.add #{filter_declaration.filter.to_s}.new(#{filter_declaration.options.to_s})
50
- EOF
51
- end
52
-
53
- stage.notifiers.each do |notifier, options_set|
54
- options_set.each do |options|
55
- code << <<-EOF
56
- notifier = #{notifier.to_s}.new(#{options.to_s})
57
- EOF
58
-
59
- if stage.notifier_transformers.any?
60
- stage.notifier_transformers.each do |transformer_declaration|
61
- code << <<-EOF
62
- notifier.add_transformer #{transformer_declaration.transformer}.new(#{transformer_declaration.options.to_s})
63
- EOF
64
- end
65
- else
66
- code << <<-EOF
67
- notifier.add_transformer Batsir::Transformers::JSONOutputTransformer.new
68
- EOF
69
- end
70
- code << <<-EOF
71
- @filter_queue.add_notifier notifier
72
- EOF
73
- end
74
- end
75
-
76
- code << <<-EOF
77
- end
78
-
79
- include Sidekiq::Worker
80
- include Batsir::StageWorker
81
- end
82
- EOF
83
- code
30
+ Batsir::Compiler::StageWorkerCompiler.new(stage).compile
84
31
  end
85
32
  end
86
33
  end
@@ -0,0 +1,35 @@
1
+ module Batsir
2
+ module Strategies
3
+ class RetryStrategy < Strategy
4
+ include Batsir::Log
5
+
6
+ attr_reader :retries, :attempts
7
+
8
+ def initialize(context, retries = 3)
9
+ super(context)
10
+ @retries = retries
11
+ @attempts = {}
12
+ end
13
+
14
+ def execute(message, error)
15
+ @attempts[message] ? @attempts[message] += 1 : @attempts[message] = 0
16
+
17
+ if @attempts[message] >= @retries
18
+ error_msg = "Tried to send '#{message}' #{@attempts[message]} times and failed"
19
+ reset_attempts(message)
20
+ log.error error_msg
21
+ raise Batsir::Errors::RetryStrategyFailed.new error_msg
22
+ else
23
+ log.warn "Recovering from #{error}. Making another attempt (##{@attempts[message]+1})"
24
+ result = @context.execute(message)
25
+ reset_attempts(message)
26
+ return result
27
+ end
28
+ end
29
+
30
+ def reset_attempts(message)
31
+ @attempts.delete(message)
32
+ end
33
+ end
34
+ end
35
+ end