batsir 0.1.0 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
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/.travis.yml CHANGED
@@ -4,7 +4,15 @@ jdk:
4
4
  - oraclejdk7
5
5
 
6
6
  rvm:
7
- - 1.9.2
8
7
  - 1.9.3
8
+ - 2.0.0
9
9
  - jruby-19mode
10
10
  - rbx-19mode
11
+ - ruby-head
12
+ - jruby-head
13
+
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: rbx-19mode
17
+ - rvm: ruby-head
18
+ - rvm: jruby-head
data/CHANGES.md ADDED
@@ -0,0 +1,54 @@
1
+ 0.3.7
2
+ -----------
3
+ - AMQP queues are now durable by default.
4
+ This prevents queued messages getting lost when batsir is restarted (and the last consumer disconnects). It can be configured by setting 'Batsir::Config.amqp_durable'.
5
+ - Now requires bunny 0.10.7
6
+
7
+
8
+ 0.3.6
9
+ -----------
10
+ - Fixes bug in conditional notifier
11
+ - Constrained Celluloid version to 0.14.x, until we can support 0.15.x and above
12
+
13
+
14
+ 0.3.5
15
+ -----------
16
+ - NOTICE: This version removes sidekiq/celluloid gem contraints. Be sure to set these accordingly in your own project's Gemfile!
17
+
18
+
19
+ 0.3.4
20
+ -----------
21
+ - Fixed issue when using bunny 0.9.0.pre9. Bunny::Session#start now returns ifself instead of its channel.
22
+
23
+
24
+ 0.3.3
25
+ -----------
26
+ - Bunny :heartbeat option is now explicitly set. It defaults to 0; the original AMQP 0.8 setting.
27
+
28
+
29
+ 0.3.2
30
+ -----------
31
+ - Filters can now be passed initialisation options
32
+
33
+
34
+ 0.3.1
35
+ -----------
36
+ - Renamed config variable 'connection_pool_size' to a more apt 'amqp_pool_size'
37
+
38
+
39
+ 0.3.0
40
+ -----------
41
+ - AMQPAcceptor and AMQPNotifier classes now only support Bunny 0.9 and up.
42
+ This fixes a bug that occurred when talking to an AMQP 0.9 server with a Bunny 0.8 client.
43
+ High message rates would, at some point, cause the server to reject all messages.
44
+
45
+
46
+ 0.2.1
47
+ -----------
48
+ - Notifiers will not modify the message
49
+ - FieldTransformers now always return message hashes with strings for keys
50
+
51
+
52
+ 0.2.0
53
+ -----------
54
+ - first production-ready code
data/Gemfile CHANGED
@@ -1,17 +1,14 @@
1
- source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
1
+ source "https://rubygems.org"
5
2
 
6
- # Add dependencies to develop your gem here.
7
- # Include everything needed to run rake, tests, features, etc.
8
3
  gem "bundler", "> 1.0.0"
9
4
  gem "jeweler"
10
- gem "blockenspiel", "~> 0.4.3"
11
- gem "celluloid"
12
- gem "sidekiq"
13
- gem "bunny", :git => "https://github.com/ruby-amqp/bunny.git"
5
+ gem "rdoc"
6
+ gem "blockenspiel", ">= 0.4.3"
7
+ gem "celluloid", "~> 0.14.1"
8
+ gem "sidekiq", ">= 2.5.4"
9
+ gem "bunny", :github => "ruby-amqp/bunny", :tag => "0.10.7"
14
10
  gem "json"
11
+ gem "log4r"
15
12
  #gem "hot_bunnies", :git => "https://github.com/ruby-amqp/hot_bunnies.git"
16
13
 
17
14
  group :development do
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
+ [![Gem Version](https://badge.fury.io/rb/batsir.png)](http://badge.fury.io/rb/batsir)
2
+ [![Code Climate](https://codeclimate.com/github/jwkoelewijn/batsir.png)](https://codeclimate.com/github/jwkoelewijn/batsir)
1
3
  [![Build Status](https://secure.travis-ci.org/jwkoelewijn/batsir.png?branch=master)](http://travis-ci.org/jwkoelewijn/batsir)
2
4
 
3
-
4
5
  ![Batsir Logo](/jwkoelewijn/batsir/raw/master/batsir.png)
5
6
 
6
7
  # Batsir
@@ -43,27 +44,70 @@ Batsir.create_and_start do
43
44
  end
44
45
  ```
45
46
 
46
- This example creates 2 stages, 'stage 1' and 'stage 2'. The first stage creates and AMQPAcceptor,
47
+ This example creates 2 stages, 'stage 1' and 'stage 2'. The first stage creates and AMQPAcceptor,
47
48
  that will connect to a AMQP Broker on localhost and will listen for messages on the 'some_queue' queue.
48
49
  When a message is received, the message will be offered to the SumFilter first. The result of the
49
50
  SumFilter is then sent to the #execute method of the AverageFilter. The result of this filter will
50
- than be sent as an AMQP message on the 'queue_2' queue.
51
+ then be sent as an AMQP message on the 'queue_2' queue.
51
52
 
52
53
  The inbound AMQPAcceptor of the second stage will then receive the message and its filters will be
53
54
  invoked (the PrintFilter in this example).
54
55
 
56
+ # Conditional Notifiers
57
+
58
+ It is possible to create conditional notifiers. These accept a list of regular Notifiers which will
59
+ send a message only if the provided condition evaluates to true. The condition is given as a String,
60
+ which is evaluated in the context of a Proc at runtime. This Proc is always supplied with a 'message'
61
+ parameter, which is available when the condition is evaluated.
62
+
63
+ ## Example of Conditional Notifier
64
+
65
+ ```ruby
66
+ Batsir.create_and_start do
67
+ stage "stage 1" do
68
+ inbound do
69
+ acceptor AMQPAcceptor, :queue => 'some_queue', :host => 'localhost'
70
+ end
71
+ filter SumFilter
72
+ filter AverageFilter
73
+ outbound do
74
+ conditional do
75
+ notify_if "message.to_i > 2", AMQPNotifier, :queue => 'gt_2'
76
+ notify_if "message.to_i < 2", AMQPNotifier, :queue => 'lt_3'
77
+ end
78
+ end
79
+ end
80
+
81
+ stage "stage 2" do
82
+ inbound do
83
+ acceptor AMQPAcceptor, :queue => 'gt_2'
84
+ end
85
+ filter PrintFilter
86
+ end
87
+
88
+ stage "stage 3" do
89
+ inbound do
90
+ acceptor AMQPAcceptor, :queue => 'lt_3'
91
+ end
92
+ end
93
+ end
94
+ ```
95
+
96
+ In this example the message is sent to queue 'gt_2' if the integer value of the message is greater than 2,
97
+ and to queue 'lt_2' when the integer value of the message is less than 2.
98
+
55
99
  # Sidekiq & Celluloid
56
100
  Batsir acts as both a Sidekiq server and client at the same time. When Batsir#create_and_start is invoked,
57
101
  Batsir will create Sidekiq workers on the fly, with instantiated filters on the workers. These workers will
58
102
  be deployed in the Sidekiq server, so that they will be available for processing. The workers also register
59
103
  themselves in a registry, where they can be requested using the stage name.
60
104
 
61
- The inbound acceptors will listen as a Celluloid Actor on the client side of Sidekiq. When a message is
105
+ The inbound acceptors will listen as a Celluloid Actor on the client side of Sidekiq. When a message is
62
106
  received, it will look up the corresponding StageWorker in the registry and it will invoke the
63
107
  StageWorker asynchronously using Sidekiq.
64
108
 
65
109
  ## Contributing to batsir
66
-
110
+
67
111
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
68
112
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
69
113
  * Fork the project
data/Rakefile CHANGED
@@ -9,28 +9,36 @@ rescue Bundler::BundlerError => e
9
9
  $stderr.puts "Run `bundle install` to install missing gems"
10
10
  exit e.status_code
11
11
  end
12
- require 'rake'
13
12
 
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
14
+
15
+ require 'rake'
14
16
  require 'jeweler'
17
+ require 'batsir/version'
18
+
19
+ version = Batsir::VERSION
20
+
15
21
  Jeweler::Tasks.new do |gem|
16
22
  # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
23
  gem.name = "batsir"
24
+ gem.version = version
18
25
  gem.homepage = "http://github.com/jwkoelewijn/batsir"
19
26
  gem.license = "MIT"
20
27
  gem.summary = %Q{Batsir is an execution platform for stage based operation queue execution}
21
- gem.description = %Q{Batsir uses so called stages to define operation queues. These operation queus
22
- consist of several operations that will be executed one after the other. Each stage
23
- is defined by its name and the queue on which it will listen. Once a message is received
24
- on the queue, it is dispatched to a worker in a seperate thread that will pass the message
25
- to each operation in the operation queue.
26
- Operation queues can have 4 different operations, 1 common operation type, and 3 special
27
- purpose operations: retrieval operations (which are always executed before all other operations),
28
- persistence operations (which are always executed after the common operations, but before the
29
- notification operations) and notification operations (which will always be executed last)
30
- This makes it possible to create chains of stages to perform tasks that depend on each
31
- other, but otherwise have a low coupling}
28
+ gem.description = %Q{Batsir uses so called stages to define operation queues. These operation queues
29
+ consist of several operations that will be executed one after the other. Each stage
30
+ is defined by its name and the queue on which it will listen. Once a message is received
31
+ on the queue, it is dispatched to a worker in a seperate thread that will pass the message
32
+ to each operation in the operation queue.
33
+ Operation queues can have 4 different operations, 1 common operation type, and 3 special
34
+ purpose operations: retrieval operations (which are always executed before all other operations),
35
+ persistence operations (which are always executed after the common operations, but before the
36
+ notification operations) and notification operations (which will always be executed last)
37
+ This makes it possible to create chains of stages to perform tasks that depend on each
38
+ other, but otherwise have a low coupling}
32
39
  gem.email = "jwkoelewijn@gmail.com"
33
- gem.authors = ["J.W. Koelewijn"]
40
+ gem.authors = ["J.W. Koelewijn", "Bram de Vries"]
41
+ gem.extra_rdoc_files << "CHANGES.md"
34
42
  # dependencies defined in Gemfile
35
43
  end
36
44
  Jeweler::RubygemsDotOrgTasks.new
@@ -44,12 +52,11 @@ end
44
52
 
45
53
  task :default => :spec
46
54
 
47
- require 'rake/rdoctask'
55
+ require 'rdoc/task'
48
56
  Rake::RDocTask.new do |rdoc|
49
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
-
51
57
  rdoc.rdoc_dir = 'rdoc'
52
58
  rdoc.title = "batsir #{version}"
53
59
  rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('CHANGES*')
54
61
  rdoc.rdoc_files.include('lib/**/*.rb')
55
62
  end
data/batsir.gemspec CHANGED
@@ -5,14 +5,15 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "batsir"
8
- s.version = "0.1.0"
8
+ s.version = "0.3.7"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["J.W. Koelewijn"]
12
- s.date = "2012-06-28"
13
- s.description = "Batsir uses so called stages to define operation queues. These operation queus\n consist of several operations that will be executed one after the other. Each stage\n is defined by its name and the queue on which it will listen. Once a message is received\n on the queue, it is dispatched to a worker in a seperate thread that will pass the message\n to each operation in the operation queue.\n Operation queues can have 4 different operations, 1 common operation type, and 3 special \n purpose operations: retrieval operations (which are always executed before all other operations),\n persistence operations (which are always executed after the common operations, but before the\n notification operations) and notification operations (which will always be executed last)\n This makes it possible to create chains of stages to perform tasks that depend on each\n other, but otherwise have a low coupling"
11
+ s.authors = ["J.W. Koelewijn", "Bram de Vries"]
12
+ s.date = "2013-10-24"
13
+ s.description = "Batsir uses so called stages to define operation queues. These operation queues\n consist of several operations that will be executed one after the other. Each stage\n is defined by its name and the queue on which it will listen. Once a message is received\n on the queue, it is dispatched to a worker in a seperate thread that will pass the message\n to each operation in the operation queue.\n Operation queues can have 4 different operations, 1 common operation type, and 3 special\n purpose operations: retrieval operations (which are always executed before all other operations),\n persistence operations (which are always executed after the common operations, but before the\n notification operations) and notification operations (which will always be executed last)\n This makes it possible to create chains of stages to perform tasks that depend on each\n other, but otherwise have a low coupling"
14
14
  s.email = "jwkoelewijn@gmail.com"
15
15
  s.extra_rdoc_files = [
16
+ "CHANGES.md",
16
17
  "LICENSE.txt",
17
18
  "README.md"
18
19
  ]
@@ -20,43 +21,64 @@ Gem::Specification.new do |s|
20
21
  ".document",
21
22
  ".rspec",
22
23
  ".travis.yml",
24
+ "CHANGES.md",
23
25
  "Gemfile",
24
26
  "LICENSE.txt",
25
27
  "README.md",
26
28
  "Rakefile",
27
- "VERSION",
28
29
  "batsir.gemspec",
29
30
  "batsir.png",
30
31
  "lib/batsir.rb",
31
32
  "lib/batsir/acceptors/acceptor.rb",
32
33
  "lib/batsir/acceptors/amqp_acceptor.rb",
33
34
  "lib/batsir/amqp.rb",
35
+ "lib/batsir/amqp_consumer.rb",
34
36
  "lib/batsir/chain.rb",
37
+ "lib/batsir/compiler/stage_worker_compiler.rb",
35
38
  "lib/batsir/config.rb",
39
+ "lib/batsir/dsl/conditional_notifier_declaration.rb",
36
40
  "lib/batsir/dsl/dsl_mappings.rb",
41
+ "lib/batsir/errors.rb",
37
42
  "lib/batsir/filter.rb",
38
43
  "lib/batsir/filter_queue.rb",
44
+ "lib/batsir/log.rb",
45
+ "lib/batsir/logger.rb",
39
46
  "lib/batsir/logo.rb",
40
47
  "lib/batsir/notifiers/amqp_notifier.rb",
48
+ "lib/batsir/notifiers/conditional_notifier.rb",
41
49
  "lib/batsir/notifiers/notifier.rb",
42
50
  "lib/batsir/registry.rb",
43
51
  "lib/batsir/stage.rb",
44
52
  "lib/batsir/stage_worker.rb",
53
+ "lib/batsir/strategies/retry_strategy.rb",
54
+ "lib/batsir/strategies/strategy.rb",
45
55
  "lib/batsir/transformers/field_transformer.rb",
46
56
  "lib/batsir/transformers/json_input_transformer.rb",
47
57
  "lib/batsir/transformers/json_output_transformer.rb",
48
58
  "lib/batsir/transformers/transformer.rb",
59
+ "lib/batsir/version.rb",
49
60
  "spec/batsir/acceptors/acceptor_spec.rb",
50
61
  "spec/batsir/acceptors/amqp_acceptor_spec.rb",
62
+ "spec/batsir/acceptors/shared_examples.rb",
63
+ "spec/batsir/amqp_spec.rb",
51
64
  "spec/batsir/chain_spec.rb",
65
+ "spec/batsir/config_spec.rb",
52
66
  "spec/batsir/dsl/chain_mapping_spec.rb",
67
+ "spec/batsir/dsl/conditional_notifier_mapping_spec.rb",
53
68
  "spec/batsir/dsl/stage_mapping_spec.rb",
54
69
  "spec/batsir/filter_queue_spec.rb",
55
70
  "spec/batsir/filter_spec.rb",
71
+ "spec/batsir/log_spec.rb",
72
+ "spec/batsir/logger_spec.rb",
56
73
  "spec/batsir/notifiers/amqp_notifier_spec.rb",
74
+ "spec/batsir/notifiers/conditional_notifier_spec.rb",
57
75
  "spec/batsir/notifiers/notifier_spec.rb",
76
+ "spec/batsir/notifiers/shared_examples.rb",
77
+ "spec/batsir/registry_spec.rb",
58
78
  "spec/batsir/stage_spec.rb",
59
79
  "spec/batsir/stage_worker_spec.rb",
80
+ "spec/batsir/strategies/retry_strategy_spec.rb",
81
+ "spec/batsir/strategies/strategy_spec.rb",
60
82
  "spec/batsir/support/bunny_mocks.rb",
61
83
  "spec/batsir/support/mock_filters.rb",
62
84
  "spec/batsir/transformers/field_transformer_spec.rb",
@@ -68,7 +90,7 @@ Gem::Specification.new do |s|
68
90
  s.homepage = "http://github.com/jwkoelewijn/batsir"
69
91
  s.licenses = ["MIT"]
70
92
  s.require_paths = ["lib"]
71
- s.rubygems_version = "1.8.22"
93
+ s.rubygems_version = "1.8.25"
72
94
  s.summary = "Batsir is an execution platform for stage based operation queue execution"
73
95
 
74
96
  if s.respond_to? :specification_version then
@@ -77,28 +99,34 @@ Gem::Specification.new do |s|
77
99
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
78
100
  s.add_runtime_dependency(%q<bundler>, ["> 1.0.0"])
79
101
  s.add_runtime_dependency(%q<jeweler>, [">= 0"])
80
- s.add_runtime_dependency(%q<blockenspiel>, ["~> 0.4.3"])
81
- s.add_runtime_dependency(%q<celluloid>, [">= 0"])
82
- s.add_runtime_dependency(%q<sidekiq>, [">= 0"])
102
+ s.add_runtime_dependency(%q<rdoc>, [">= 0"])
103
+ s.add_runtime_dependency(%q<blockenspiel>, [">= 0.4.3"])
104
+ s.add_runtime_dependency(%q<celluloid>, ["~> 0.14.1"])
105
+ s.add_runtime_dependency(%q<sidekiq>, [">= 2.5.4"])
83
106
  s.add_runtime_dependency(%q<bunny>, [">= 0"])
84
107
  s.add_runtime_dependency(%q<json>, [">= 0"])
108
+ s.add_runtime_dependency(%q<log4r>, [">= 0"])
85
109
  else
86
110
  s.add_dependency(%q<bundler>, ["> 1.0.0"])
87
111
  s.add_dependency(%q<jeweler>, [">= 0"])
88
- s.add_dependency(%q<blockenspiel>, ["~> 0.4.3"])
89
- s.add_dependency(%q<celluloid>, [">= 0"])
90
- s.add_dependency(%q<sidekiq>, [">= 0"])
112
+ s.add_dependency(%q<rdoc>, [">= 0"])
113
+ s.add_dependency(%q<blockenspiel>, [">= 0.4.3"])
114
+ s.add_dependency(%q<celluloid>, ["~> 0.14.1"])
115
+ s.add_dependency(%q<sidekiq>, [">= 2.5.4"])
91
116
  s.add_dependency(%q<bunny>, [">= 0"])
92
117
  s.add_dependency(%q<json>, [">= 0"])
118
+ s.add_dependency(%q<log4r>, [">= 0"])
93
119
  end
94
120
  else
95
121
  s.add_dependency(%q<bundler>, ["> 1.0.0"])
96
122
  s.add_dependency(%q<jeweler>, [">= 0"])
97
- s.add_dependency(%q<blockenspiel>, ["~> 0.4.3"])
98
- s.add_dependency(%q<celluloid>, [">= 0"])
99
- s.add_dependency(%q<sidekiq>, [">= 0"])
123
+ s.add_dependency(%q<rdoc>, [">= 0"])
124
+ s.add_dependency(%q<blockenspiel>, [">= 0.4.3"])
125
+ s.add_dependency(%q<celluloid>, ["~> 0.14.1"])
126
+ s.add_dependency(%q<sidekiq>, [">= 2.5.4"])
100
127
  s.add_dependency(%q<bunny>, [">= 0"])
101
128
  s.add_dependency(%q<json>, [">= 0"])
129
+ s.add_dependency(%q<log4r>, [">= 0"])
102
130
  end
103
131
  end
104
132
 
@@ -18,27 +18,53 @@ module Batsir
18
18
  @transformer_queue << transformer
19
19
  end
20
20
 
21
+ #
21
22
  # This method is called automatically when the stage is
22
23
  # started, it is here that you set up the accepting
23
24
  # logic. Make sure that somewhere within this logic
24
25
  # the #start_filter_chain(msg) is called to start
25
- # actual processing
26
+ # actual processing.
26
27
  #
27
28
  # Note that this method will be invoked asynchronously
28
29
  # using the Celluloid actor semantics.
30
+ #
29
31
  def start
30
-
32
+ raise NotImplementedError.new
31
33
  end
32
34
 
35
+ #
33
36
  # When a message is accepted by an Acceptor, this method
34
37
  # should be invoked with the received payload to start
35
- # processing of the filter chain
38
+ # processing of the filter chain.
39
+ #
36
40
  def start_filter_chain(message)
37
41
  klazz = Batsir::Registry.get(stage_name)
42
+ message = transform(message)
43
+ klazz.perform_async(message) if klazz
44
+ end
45
+
46
+ #
47
+ # Call each of the transformers in the transformer_queue
48
+ # on the message, in order
49
+ #
50
+ def transform(message)
38
51
  transformer_queue.each do |transformer|
39
- message = transformer.transform(message)
52
+ begin
53
+ message = transformer.transform(message)
54
+ rescue Batsir::Errors::TransformError => e
55
+ message = process_message_error(message, e)
56
+ end
40
57
  end
41
- klazz.perform_async(message) if klazz
58
+ message
59
+ end
60
+
61
+ #
62
+ # This method is called after an error is thrown.
63
+ # Can be overridden to implement error handling.
64
+ # Returns a message
65
+ #
66
+ def process_message_error(message, error)
67
+ message
42
68
  end
43
69
  end
44
70
  end
@@ -4,16 +4,44 @@ module Batsir
4
4
  module Acceptors
5
5
  class AMQPAcceptor < Acceptor
6
6
  include Batsir::AMQP
7
- def start
8
- Bunny.run( bunny_options ) do |bunny|
9
- q = bunny.queue( queue )
10
- exc = bunny.exchange( exchange )
11
- q.bind( exc, :key => queue)
12
- q.subscribe(:cancellator => cancellator) do |msg|
13
- start_filter_chain(msg[:payload])
14
- end
7
+ include Batsir::Log
8
+
9
+ attr_reader :consumer
10
+ attr_writer :consumer_source
11
+
12
+ def initialize(options={})
13
+ super
14
+ @bunny = Bunny.new(bunny_options).start
15
+ @channel = @bunny.channel
16
+ @q = @bunny.queue( queue, :durable => durable )
17
+ @x = @bunny.exchange( exchange )
18
+ @q.bind( @x, :routing_key => queue)
19
+
20
+ @consumer = consumer_source.call(self, @channel, @q)
21
+ @consumer.on_delivery() do |delivery_info, metadata, payload|
22
+ handle_delivery(payload)
15
23
  end
16
24
  end
25
+
26
+ def start
27
+ @q.subscribe_with(@consumer)
28
+ end
29
+
30
+ def handle_delivery(payload)
31
+ start_filter_chain(payload)
32
+ end
33
+
34
+ def process_message_error(message, error)
35
+ log.error error.message
36
+ nil
37
+ end
38
+
39
+ private
40
+
41
+ def consumer_source
42
+ @consumer_source ||= Batsir::AMQPConsumer.public_method(:new)
43
+ end
44
+
17
45
  end
18
46
  end
19
47
  end
data/lib/batsir/amqp.rb CHANGED
@@ -5,8 +5,10 @@ module Batsir
5
5
  attr_accessor :port
6
6
  attr_accessor :username
7
7
  attr_accessor :password
8
+ attr_accessor :durable
8
9
  attr_accessor :vhost
9
10
  attr_accessor :exchange
11
+ attr_accessor :heartbeat
10
12
 
11
13
  def bunny_options
12
14
  {
@@ -14,32 +16,58 @@ module Batsir
14
16
  :port => port,
15
17
  :user => username,
16
18
  :pass => password,
17
- :vhost => vhost
19
+ :vhost => vhost,
20
+ :heartbeat => heartbeat
18
21
  }
19
22
  end
20
23
 
21
24
  def host
22
- @host || 'localhost'
25
+ @host ||= Batsir::Config.fetch(:amqp_host, 'localhost')
23
26
  end
24
27
 
25
28
  def port
26
- @port || 5672
29
+ @port ||= Batsir::Config.fetch(:amqp_port, 5672)
27
30
  end
28
31
 
29
32
  def username
30
- @username || 'guest'
33
+ @username ||= Batsir::Config.fetch(:amqp_user, 'guest')
31
34
  end
32
35
 
33
36
  def password
34
- @password || 'guest'
37
+ @password ||= Batsir::Config.fetch(:amqp_pass, 'guest')
35
38
  end
36
39
 
37
40
  def vhost
38
- @vhost || '/'
41
+ @vhost ||= Batsir::Config.fetch(:amqp_vhost, '/')
39
42
  end
40
43
 
41
44
  def exchange
42
- @exchange || 'amq.direct'
45
+ @exchange ||= Batsir::Config.fetch(:amqp_exchange, 'amq.direct')
46
+ end
47
+
48
+ def heartbeat
49
+ @heartbeat ||= Batsir::Config.fetch(:amqp_heartbeat, 0) # default to AMQP 0.8 heartbeat setting
50
+ end
51
+
52
+ def bunny_pool_size
53
+ @bunny_pool_size ||= Batsir::Config.ampq_pool_size
54
+ end
55
+
56
+ def durable
57
+ @durable ||= Batsir::Config.fetch(:amqp_durable, true)
58
+ end
59
+
60
+ def bunny_pool_key
61
+ "bunny_pool_for_#{host}_#{port}_#{vhost}"
62
+ end
63
+
64
+ def bunny_pool
65
+ @bunny_pool = Batsir::Registry.get(bunny_pool_key)
66
+ if !@bunny_pool
67
+ pool = ConnectionPool.new(:size => bunny_pool_size) { Bunny.new(bunny_options).start }
68
+ @bunny_pool = Batsir::Registry.register(bunny_pool_key, pool)
69
+ end
70
+ @bunny_pool
43
71
  end
44
72
  end
45
73
  end
@@ -0,0 +1,8 @@
1
+ module Batsir
2
+ class AMQPConsumer < Bunny::Consumer
3
+ def initialize(acceptor, channel, queue)
4
+ super(channel, queue)
5
+ @acceptor = acceptor
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,86 @@
1
+ module Batsir
2
+ module Compiler
3
+ class StageWorkerCompiler
4
+ attr_accessor :stage
5
+
6
+ def initialize(stage)
7
+ @stage = stage
8
+ end
9
+
10
+ def compile
11
+ klazz_name = "#{stage.name.capitalize.gsub(' ','')}Worker"
12
+ code = <<-EOF
13
+ class #{klazz_name}
14
+ def self.stage_name
15
+ "#{stage.name}"
16
+ end
17
+
18
+ def initialize
19
+ @filter_queue = self.class.filter_queue
20
+ end
21
+
22
+ def self.filter_queue
23
+ @filter_queue
24
+ end
25
+
26
+ def self.initialize_filter_queue
27
+ @filter_queue = Batsir::FilterQueue.new
28
+ EOF
29
+
30
+ stage.filter_declarations.each do |filter_declaration|
31
+ code << <<-EOF
32
+ @filter_queue.add #{filter_declaration.filter.to_s}.new(#{filter_declaration.options.to_s})
33
+ EOF
34
+ end
35
+
36
+ stage.conditional_notifiers.each do |conditional_notifier_declaration|
37
+ conditional_notifier_declaration.compile(code, self)
38
+ end
39
+
40
+ stage.notifiers.each do |notifier, options_set|
41
+ options_set.each do |options|
42
+ code << <<-EOF
43
+ notifier = #{notifier.to_s}.new(#{options.to_s})
44
+ EOF
45
+
46
+ self.add_transformers_to_notifier("notifier", code)
47
+
48
+ self.add_notifier("notifier", code)
49
+ end
50
+ end
51
+
52
+ code << <<-EOF
53
+ end
54
+
55
+ include Sidekiq::Worker
56
+ include Batsir::StageWorker
57
+ end
58
+
59
+ #{klazz_name}.sidekiq_options(:queue => Batsir::Config.sidekiq_queue)
60
+ #{klazz_name}
61
+ EOF
62
+ code
63
+ end
64
+
65
+ def add_transformers_to_notifier(notifier_name, code)
66
+ if stage.notifier_transformers.any?
67
+ stage.notifier_transformers.each do |transformer_declaration|
68
+ code << <<-EOF
69
+ #{notifier_name}.add_transformer #{transformer_declaration.transformer}.new(#{transformer_declaration.options.to_s})
70
+ EOF
71
+ end
72
+ else
73
+ code << <<-EOF
74
+ #{notifier_name}.add_transformer Batsir::Transformers::JSONOutputTransformer.new
75
+ EOF
76
+ end
77
+ end
78
+
79
+ def add_notifier(notifier_name, code)
80
+ code << <<-EOF
81
+ @filter_queue.add_notifier #{notifier_name}
82
+ EOF
83
+ end
84
+ end
85
+ end
86
+ end