rabbit_feed 2.4.4 → 3.0.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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +81 -0
- data/README.md +39 -5
- data/Rakefile +3 -19
- data/bin/rabbit_feed +0 -1
- data/example/non_rails_app/Gemfile.lock +29 -32
- data/example/non_rails_app/Rakefile +1 -1
- data/example/non_rails_app/bin/benchmark +3 -3
- data/example/non_rails_app/lib/non_rails_app/event_handler.rb +1 -1
- data/example/non_rails_app/spec/lib/non_rails_app/event_handler_spec.rb +3 -4
- data/example/non_rails_app/spec/lib/non_rails_app/event_routing_spec.rb +3 -3
- data/example/rails_app/Gemfile +4 -16
- data/example/rails_app/Gemfile.lock +131 -137
- data/example/rails_app/app/assets/javascripts/application.js +0 -1
- data/example/rails_app/app/controllers/application_controller.rb +0 -3
- data/example/rails_app/app/controllers/beavers_controller.rb +14 -15
- data/example/rails_app/bin/rails +1 -1
- data/example/rails_app/config/environments/development.rb +1 -1
- data/example/rails_app/config/environments/test.rb +2 -2
- data/example/rails_app/config/initializers/cookies_serializer.rb +1 -1
- data/example/rails_app/config/unicorn.rb +1 -1
- data/example/rails_app/config.ru +1 -1
- data/example/rails_app/db/schema.rb +5 -7
- data/example/rails_app/lib/event_handler.rb +1 -1
- data/example/rails_app/spec/controllers/beavers_controller_spec.rb +9 -10
- data/example/rails_app/spec/event_routing_spec.rb +1 -2
- data/example/rails_app/test/controllers/beavers_controller_test.rb +12 -12
- data/lib/dsl.rb +4 -4
- data/lib/rabbit_feed/client.rb +17 -23
- data/lib/rabbit_feed/configuration.rb +10 -9
- data/lib/rabbit_feed/connection.rb +3 -3
- data/lib/rabbit_feed/console_consumer.rb +22 -24
- data/lib/rabbit_feed/consumer.rb +2 -2
- data/lib/rabbit_feed/consumer_connection.rb +21 -22
- data/lib/rabbit_feed/event.rb +8 -28
- data/lib/rabbit_feed/event_definitions.rb +14 -15
- data/lib/rabbit_feed/event_routing.rb +26 -27
- data/lib/rabbit_feed/json_log_formatter.rb +1 -1
- data/lib/rabbit_feed/producer.rb +13 -13
- data/lib/rabbit_feed/producer_connection.rb +8 -9
- data/lib/rabbit_feed/testing_support/rspec_matchers/publish_event.rb +52 -89
- data/lib/rabbit_feed/testing_support/test_rabbit_feed_consumer.rb +1 -2
- data/lib/rabbit_feed/testing_support/testing_helpers.rb +0 -1
- data/lib/rabbit_feed/testing_support.rb +5 -6
- data/lib/rabbit_feed/version.rb +1 -1
- data/lib/rabbit_feed.rb +12 -13
- data/rabbit_feed.gemspec +16 -14
- data/run_benchmark +4 -3
- data/run_example +1 -1
- data/spec/features/step_definitions/connectivity_steps.rb +6 -9
- data/spec/lib/rabbit_feed/client_spec.rb +8 -9
- data/spec/lib/rabbit_feed/configuration_spec.rb +20 -23
- data/spec/lib/rabbit_feed/console_consumer_spec.rb +11 -13
- data/spec/lib/rabbit_feed/consumer_connection_spec.rb +26 -28
- data/spec/lib/rabbit_feed/event_definitions_spec.rb +31 -31
- data/spec/lib/rabbit_feed/event_routing_spec.rb +35 -62
- data/spec/lib/rabbit_feed/event_spec.rb +40 -87
- data/spec/lib/rabbit_feed/producer_connection_spec.rb +11 -7
- data/spec/lib/rabbit_feed/producer_spec.rb +16 -19
- data/spec/lib/rabbit_feed/testing_support/rspec_matchers/publish_event_spec.rb +82 -87
- data/spec/lib/rabbit_feed/testing_support/testing_helper_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -10
- metadata +67 -45
- data/example/rails_app/README.rdoc +0 -28
data/lib/rabbit_feed/client.rb
CHANGED
@@ -10,30 +10,27 @@ module RabbitFeed
|
|
10
10
|
require_path: '.',
|
11
11
|
config_file: 'config/rabbit_feed.yml',
|
12
12
|
logfile: 'log/rabbit_feed.log',
|
13
|
-
pidfile: 'tmp/pids/rabbit_feed.pid'
|
14
|
-
}
|
13
|
+
pidfile: 'tmp/pids/rabbit_feed.pid'
|
14
|
+
}.freeze
|
15
15
|
DEFAULTS.freeze
|
16
16
|
|
17
17
|
attr_reader :command, :options
|
18
18
|
validates_presence_of :command, :options
|
19
|
-
validates :command, inclusion: { in: %w(consume produce shutdown console), message:
|
19
|
+
validates :command, inclusion: { in: %w(consume produce shutdown console), message: '%{value} is not a valid command' }
|
20
20
|
validate :log_file_path_exists
|
21
21
|
validate :config_file_exists
|
22
22
|
validate :require_path_valid, unless: :console?
|
23
23
|
validate :pidfile_path_exists, if: :daemonize?
|
24
24
|
validate :environment_specified
|
25
25
|
|
26
|
-
def initialize
|
26
|
+
def initialize(arguments = ARGV)
|
27
27
|
@command = arguments[0]
|
28
28
|
@options = parse_options arguments
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
set_configuration
|
35
|
-
load_dependancies unless console?
|
36
|
-
end
|
29
|
+
return if shutdown?
|
30
|
+
validate!
|
31
|
+
set_logging
|
32
|
+
set_configuration
|
33
|
+
load_dependancies unless console?
|
37
34
|
end
|
38
35
|
|
39
36
|
def run
|
@@ -43,25 +40,24 @@ module RabbitFeed
|
|
43
40
|
private
|
44
41
|
|
45
42
|
def validate!
|
46
|
-
raise Error
|
43
|
+
raise Error, errors.messages if invalid?
|
47
44
|
end
|
48
45
|
|
49
46
|
def log_file_path_exists
|
50
|
-
errors.add(:options, "log file path not found: '#{options[:logfile]}', specify this using the --logfile option") unless Dir.
|
47
|
+
errors.add(:options, "log file path not found: '#{options[:logfile]}', specify this using the --logfile option") unless Dir.exist?(File.dirname(options[:logfile]))
|
51
48
|
end
|
52
49
|
|
53
50
|
def config_file_exists
|
54
|
-
errors.add(:options, "configuration file not found: '#{options[:config_file]}', specify this using the --config option") unless File.
|
51
|
+
errors.add(:options, "configuration file not found: '#{options[:config_file]}', specify this using the --config option") unless File.exist?(options[:config_file])
|
55
52
|
end
|
56
53
|
|
57
54
|
def require_path_valid
|
58
|
-
|
59
|
-
|
60
|
-
end
|
55
|
+
return unless require_rails? && !File.exist?("#{options[:require_path]}/config/application.rb")
|
56
|
+
errors.add(:options, 'point rabbit_feed to a Rails 3/4 application or a Ruby file to load your worker classes with --require')
|
61
57
|
end
|
62
58
|
|
63
59
|
def pidfile_path_exists
|
64
|
-
errors.add(:options, "pid file path not found: '#{options[:pidfile]}', specify this using the --pidfile option") unless Dir.
|
60
|
+
errors.add(:options, "pid file path not found: '#{options[:pidfile]}', specify this using the --pidfile option") unless Dir.exist?(File.dirname(options[:pidfile]))
|
65
61
|
end
|
66
62
|
|
67
63
|
def environment_specified
|
@@ -145,11 +141,10 @@ module RabbitFeed
|
|
145
141
|
File.directory?(options[:require_path])
|
146
142
|
end
|
147
143
|
|
148
|
-
def parse_options
|
144
|
+
def parse_options(argv)
|
149
145
|
opts = {}
|
150
146
|
|
151
147
|
parser = OptionParser.new do |o|
|
152
|
-
|
153
148
|
o.on '-a', '--application VAL', 'Name of the application' do |arg|
|
154
149
|
opts[:application] = arg
|
155
150
|
end
|
@@ -190,7 +185,7 @@ module RabbitFeed
|
|
190
185
|
opts[:pidfile] = arg
|
191
186
|
end
|
192
187
|
|
193
|
-
o.on '-V', '--version', 'Print version and exit' do |
|
188
|
+
o.on '-V', '--version', 'Print version and exit' do |_arg|
|
194
189
|
puts "RabbitFeed #{RabbitFeed::VERSION}"
|
195
190
|
exit 0
|
196
191
|
end
|
@@ -204,6 +199,5 @@ module RabbitFeed
|
|
204
199
|
parser.parse! argv
|
205
200
|
DEFAULTS.merge opts
|
206
201
|
end
|
207
|
-
|
208
202
|
end
|
209
203
|
end
|
@@ -7,8 +7,8 @@ module RabbitFeed
|
|
7
7
|
:consumer_exit_after_fail
|
8
8
|
validates_presence_of :application, :environment, :exchange
|
9
9
|
|
10
|
-
def initialize
|
11
|
-
RabbitFeed.log.info {{ event: :initialize_configuration, options: options.merge(
|
10
|
+
def initialize(options)
|
11
|
+
RabbitFeed.log.info { { event: :initialize_configuration, options: options.merge(password: :redacted) } }
|
12
12
|
|
13
13
|
@host = options[:host]
|
14
14
|
@hosts = options[:hosts]
|
@@ -28,10 +28,10 @@ module RabbitFeed
|
|
28
28
|
validate!
|
29
29
|
end
|
30
30
|
|
31
|
-
def self.load
|
32
|
-
RabbitFeed.log.info {{ event: :load_configuration_file, file_path: file_path, environment: environment, application: application }}
|
31
|
+
def self.load(file_path, environment, application)
|
32
|
+
RabbitFeed.log.info { { event: :load_configuration_file, file_path: file_path, environment: environment, application: application } }
|
33
33
|
|
34
|
-
raise ConfigurationError
|
34
|
+
raise ConfigurationError, "The RabbitFeed configuration file path specified does not exist: #{file_path}" unless File.exist? file_path
|
35
35
|
|
36
36
|
options = read_configuration_file file_path, environment
|
37
37
|
options[:environment] = environment
|
@@ -48,7 +48,7 @@ module RabbitFeed
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def connection_options
|
51
|
-
|
51
|
+
{}.tap do |options|
|
52
52
|
options[:heartbeat] = heartbeat if heartbeat
|
53
53
|
options[:connect_timeout] = connect_timeout if connect_timeout
|
54
54
|
options[:host] = host if host
|
@@ -65,13 +65,14 @@ module RabbitFeed
|
|
65
65
|
|
66
66
|
private
|
67
67
|
|
68
|
-
def self.read_configuration_file
|
68
|
+
def self.read_configuration_file(file_path, environment)
|
69
69
|
raw_configuration = YAML.load(ERB.new(File.read(file_path)).result)
|
70
|
-
HashWithIndifferentAccess.new
|
70
|
+
HashWithIndifferentAccess.new(raw_configuration[environment] || {})
|
71
71
|
end
|
72
|
+
private_class_method :read_configuration_file
|
72
73
|
|
73
74
|
def validate!
|
74
|
-
raise ConfigurationError
|
75
|
+
raise ConfigurationError, errors.messages if invalid?
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
@@ -3,10 +3,10 @@ module RabbitFeed
|
|
3
3
|
include Singleton
|
4
4
|
|
5
5
|
def initialize
|
6
|
-
RabbitFeed.log.info {{ event: :connecting_to_rabbitmq, options: RabbitFeed.configuration.connection_options.merge(
|
6
|
+
RabbitFeed.log.info { { event: :connecting_to_rabbitmq, options: RabbitFeed.configuration.connection_options.merge(password: :redacted, logger: :redacted) } }
|
7
7
|
@connection = Bunny.new RabbitFeed.configuration.connection_options
|
8
8
|
@connection.start
|
9
|
-
RabbitFeed.log.info {{ event: :connected_to_rabbitmq }}
|
9
|
+
RabbitFeed.log.info { { event: :connected_to_rabbitmq } }
|
10
10
|
@channel = @connection.create_channel
|
11
11
|
@mutex = Mutex.new
|
12
12
|
end
|
@@ -15,7 +15,7 @@ module RabbitFeed
|
|
15
15
|
|
16
16
|
attr_reader :channel, :mutex
|
17
17
|
|
18
|
-
def synchronized
|
18
|
+
def synchronized
|
19
19
|
mutex.synchronize do
|
20
20
|
yield
|
21
21
|
end
|
@@ -2,7 +2,7 @@ module RabbitFeed
|
|
2
2
|
module ConsoleConsumer
|
3
3
|
extend self
|
4
4
|
|
5
|
-
APPLICATION_NAME = 'rabbit_feed_console'
|
5
|
+
APPLICATION_NAME = 'rabbit_feed_console'.freeze
|
6
6
|
|
7
7
|
def init
|
8
8
|
@event_count = 0
|
@@ -10,10 +10,10 @@ module RabbitFeed
|
|
10
10
|
route_all_events
|
11
11
|
puts welcome_message
|
12
12
|
ask_to_purge_queue unless queue_empty?
|
13
|
-
puts
|
13
|
+
puts 'Ready. Press CTRL+C to exit.'
|
14
14
|
end
|
15
15
|
|
16
|
-
def formatted
|
16
|
+
def formatted(event)
|
17
17
|
Formatter.new(event).to_s
|
18
18
|
end
|
19
19
|
|
@@ -28,10 +28,9 @@ module RabbitFeed
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def welcome_message
|
31
|
-
"
|
32
|
-
Environment: #{RabbitFeed.environment}
|
33
|
-
Queue: #{RabbitFeed.configuration.queue}
|
34
|
-
"""
|
31
|
+
"RabbitFeed console starting at #{Time.now.utc}...\n"\
|
32
|
+
"Environment: #{RabbitFeed.environment}\n"\
|
33
|
+
"Queue: #{RabbitFeed.configuration.queue}"
|
35
34
|
end
|
36
35
|
|
37
36
|
def queue_empty?
|
@@ -39,15 +38,15 @@ Queue: #{RabbitFeed.configuration.queue}
|
|
39
38
|
end
|
40
39
|
|
41
40
|
def ask_to_purge_queue
|
42
|
-
puts "There are currently #{ConsumerConnection.instance.queue_depth} message(s) in the console's queue.\n"
|
43
|
-
|
41
|
+
puts "There are currently #{ConsumerConnection.instance.queue_depth} message(s) in the console's queue.\n"\
|
42
|
+
'Would you like to purge the queue before proceeding? (y/N)>'
|
44
43
|
response = STDIN.gets.chomp
|
45
44
|
purge_queue if response == 'y'
|
46
45
|
end
|
47
46
|
|
48
47
|
def purge_queue
|
49
48
|
ConsumerConnection.instance.purge_queue
|
50
|
-
puts
|
49
|
+
puts 'Queue purged.'
|
51
50
|
end
|
52
51
|
|
53
52
|
def route_all_events
|
@@ -56,7 +55,7 @@ Queue: #{RabbitFeed.configuration.queue}
|
|
56
55
|
accept_from(:any) do
|
57
56
|
event(:any) do |event|
|
58
57
|
scope.increment_event_count
|
59
|
-
puts
|
58
|
+
puts scope.formatted(event)
|
60
59
|
puts scope.event_count_message
|
61
60
|
end
|
62
61
|
end
|
@@ -68,15 +67,14 @@ Queue: #{RabbitFeed.configuration.queue}
|
|
68
67
|
end
|
69
68
|
|
70
69
|
class Formatter
|
71
|
-
|
72
70
|
BORDER_WIDTH = 100
|
73
|
-
BORDER_CHAR =
|
74
|
-
DIVIDER_CHAR =
|
75
|
-
NEWLINE = "\n"
|
71
|
+
BORDER_CHAR = '-'.freeze
|
72
|
+
DIVIDER_CHAR = '*'.freeze
|
73
|
+
NEWLINE = "\n".freeze
|
76
74
|
|
77
75
|
attr_reader :event
|
78
76
|
|
79
|
-
def initialize
|
77
|
+
def initialize(event)
|
80
78
|
@event = event
|
81
79
|
end
|
82
80
|
|
@@ -88,12 +86,12 @@ Queue: #{RabbitFeed.configuration.queue}
|
|
88
86
|
|
89
87
|
def header
|
90
88
|
event_detail = "#{event.name}: #{event.created_at_utc}"
|
91
|
-
border_filler = BORDER_CHAR*((BORDER_WIDTH - event_detail.length)/2)
|
92
|
-
border_filler+event_detail+border_filler
|
89
|
+
border_filler = BORDER_CHAR * ((BORDER_WIDTH - event_detail.length) / 2)
|
90
|
+
border_filler + event_detail + border_filler
|
93
91
|
end
|
94
92
|
|
95
93
|
def footer
|
96
|
-
BORDER_CHAR*BORDER_WIDTH
|
94
|
+
BORDER_CHAR * BORDER_WIDTH
|
97
95
|
end
|
98
96
|
|
99
97
|
def metadata
|
@@ -101,18 +99,18 @@ Queue: #{RabbitFeed.configuration.queue}
|
|
101
99
|
end
|
102
100
|
|
103
101
|
def divider
|
104
|
-
DIVIDER_CHAR*BORDER_WIDTH
|
102
|
+
DIVIDER_CHAR * BORDER_WIDTH
|
105
103
|
end
|
106
104
|
|
107
105
|
def payload
|
108
106
|
pretty_print_hash 'Event payload', event.payload
|
109
107
|
end
|
110
108
|
|
111
|
-
def pretty_print_hash
|
109
|
+
def pretty_print_hash(description, hash)
|
112
110
|
'#' + description + NEWLINE +
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
hash.keys.sort.map do |key|
|
112
|
+
"#{key}: #{hash[key]}"
|
113
|
+
end.join(NEWLINE)
|
116
114
|
end
|
117
115
|
end
|
118
116
|
end
|
data/lib/rabbit_feed/consumer.rb
CHANGED
@@ -7,9 +7,9 @@ module RabbitFeed
|
|
7
7
|
def run
|
8
8
|
ConsumerConnection.instance.consume do |raw_event|
|
9
9
|
event = Event.deserialize raw_event
|
10
|
-
RabbitFeed.log.info {{ event: :message_received, metadata: event.metadata }}
|
10
|
+
RabbitFeed.log.info { { event: :message_received, metadata: event.metadata } }
|
11
11
|
event_routing.handle_event event
|
12
|
-
RabbitFeed.log.info {{ event: :message_processed, metadata: event.metadata }}
|
12
|
+
RabbitFeed.log.info { { event: :message_processed, metadata: event.metadata } }
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module RabbitFeed
|
2
2
|
class ConsumerConnection < RabbitFeed::Connection
|
3
|
-
|
4
3
|
SUBSCRIPTION_OPTIONS = {
|
5
4
|
consumer_tag: Socket.gethostname, # Use the host name of the server
|
6
5
|
manual_ack: true, # Manually acknowledge messages once they've been processed
|
@@ -15,30 +14,30 @@ module RabbitFeed
|
|
15
14
|
arguments: {
|
16
15
|
'x-ha-policy' => 'all', # Apply the queue on all mirrors
|
17
16
|
'x-expires' => SEVEN_DAYS_IN_MS, # Auto-delete the queue after a period of inactivity (in ms)
|
18
|
-
|
17
|
+
}
|
19
18
|
}.freeze
|
20
19
|
|
21
20
|
def initialize
|
22
21
|
super
|
23
22
|
channel.prefetch(1)
|
24
23
|
@queue = channel.queue RabbitFeed.configuration.queue, queue_options
|
25
|
-
RabbitFeed.log.info {{ event: :queue_declared, queue: RabbitFeed.configuration.queue, options: queue_options }}
|
24
|
+
RabbitFeed.log.info { { event: :queue_declared, queue: RabbitFeed.configuration.queue, options: queue_options } }
|
26
25
|
bind_on_accepted_routes
|
27
26
|
end
|
28
27
|
|
29
|
-
def consume
|
28
|
+
def consume(&block)
|
30
29
|
raise 'This connection already has a consumer subscribed' if connection_in_use?
|
31
30
|
synchronized do
|
32
31
|
begin
|
33
|
-
RabbitFeed.log.info {{ event: :subscribe_to_queue, queue: RabbitFeed.configuration.queue }}
|
32
|
+
RabbitFeed.log.info { { event: :subscribe_to_queue, queue: RabbitFeed.configuration.queue } }
|
34
33
|
|
35
|
-
consumer = queue.subscribe(SUBSCRIPTION_OPTIONS) do |delivery_info,
|
34
|
+
consumer = queue.subscribe(SUBSCRIPTION_OPTIONS) do |delivery_info, _properties, payload|
|
36
35
|
handle_message delivery_info, payload, &block
|
37
36
|
end
|
38
37
|
|
39
38
|
sleep # Sleep indefinitely, as the consumer runs in its own thread
|
40
|
-
rescue SystemExit,
|
41
|
-
RabbitFeed.log.info {{ event: :unsubscribe_from_queue, queue: RabbitFeed.configuration.queue }}
|
39
|
+
rescue SystemExit, SignalException
|
40
|
+
RabbitFeed.log.info { { event: :unsubscribe_from_queue, queue: RabbitFeed.configuration.queue } }
|
42
41
|
ensure
|
43
42
|
(cancel_consumer consumer) if consumer.present?
|
44
43
|
end
|
@@ -59,29 +58,29 @@ module RabbitFeed
|
|
59
58
|
|
60
59
|
def queue_options
|
61
60
|
{
|
62
|
-
auto_delete: RabbitFeed.configuration.auto_delete_queue
|
61
|
+
auto_delete: RabbitFeed.configuration.auto_delete_queue
|
63
62
|
}.merge QUEUE_OPTIONS
|
64
63
|
end
|
65
64
|
|
66
65
|
def bind_on_accepted_routes
|
67
66
|
if RabbitFeed::Consumer.event_routing.present?
|
68
67
|
RabbitFeed::Consumer.event_routing.accepted_routes.each do |accepted_route|
|
69
|
-
queue.bind(RabbitFeed.configuration.exchange,
|
70
|
-
RabbitFeed.log.info {{ event: :queue_bound, queue: RabbitFeed.configuration.queue, exchange: RabbitFeed.configuration.exchange, routing_key: accepted_route }}
|
68
|
+
queue.bind(RabbitFeed.configuration.exchange, routing_key: accepted_route)
|
69
|
+
RabbitFeed.log.info { { event: :queue_bound, queue: RabbitFeed.configuration.queue, exchange: RabbitFeed.configuration.exchange, routing_key: accepted_route } }
|
71
70
|
end
|
72
71
|
else
|
73
72
|
queue.bind(RabbitFeed.configuration.exchange)
|
74
|
-
RabbitFeed.log.info {{ event: :queue_bound, queue: RabbitFeed.configuration.queue, exchange: RabbitFeed.configuration.exchange }}
|
73
|
+
RabbitFeed.log.info { { event: :queue_bound, queue: RabbitFeed.configuration.queue, exchange: RabbitFeed.configuration.exchange } }
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
|
-
def acknowledge
|
77
|
+
def acknowledge(delivery_info)
|
79
78
|
queue.channel.ack(delivery_info.delivery_tag)
|
80
|
-
RabbitFeed.log.debug {{ event: :acknowledge, delivery_tag: delivery_info.delivery_tag }}
|
79
|
+
RabbitFeed.log.debug { { event: :acknowledge, delivery_tag: delivery_info.delivery_tag } }
|
81
80
|
end
|
82
81
|
|
83
|
-
def handle_message
|
84
|
-
RabbitFeed.log.debug {{ event: :handling_message, delivery_tag: delivery_info.delivery_tag }}
|
82
|
+
def handle_message(delivery_info, payload)
|
83
|
+
RabbitFeed.log.debug { { event: :handling_message, delivery_tag: delivery_info.delivery_tag } }
|
85
84
|
begin
|
86
85
|
yield payload
|
87
86
|
acknowledge delivery_info
|
@@ -91,21 +90,21 @@ module RabbitFeed
|
|
91
90
|
end
|
92
91
|
end
|
93
92
|
|
94
|
-
def cancel_consumer
|
93
|
+
def cancel_consumer(consumer)
|
95
94
|
cancel_ok = consumer.cancel
|
96
|
-
RabbitFeed.log.debug {{ event: :consumer_cancelled, status: cancel_ok, queue: RabbitFeed.configuration.queue }}
|
95
|
+
RabbitFeed.log.debug { { event: :consumer_cancelled, status: cancel_ok, queue: RabbitFeed.configuration.queue } }
|
97
96
|
end
|
98
97
|
|
99
|
-
def negative_acknowledge
|
98
|
+
def negative_acknowledge(delivery_info)
|
100
99
|
# Tell rabbit that we were unable to process the message
|
101
100
|
# This will re-queue the message
|
102
101
|
queue.channel.nack(delivery_info.delivery_tag, false, true)
|
103
|
-
RabbitFeed.log.debug {{ event: :negative_acknowledge, delivery_tag: delivery_info.delivery_tag }}
|
102
|
+
RabbitFeed.log.debug { { event: :negative_acknowledge, delivery_tag: delivery_info.delivery_tag } }
|
104
103
|
end
|
105
104
|
|
106
|
-
def handle_processing_exception
|
105
|
+
def handle_processing_exception(delivery_info, exception)
|
107
106
|
negative_acknowledge delivery_info
|
108
|
-
RabbitFeed.log.error {{ event: :processing_exception, delivery_tag: delivery_info.delivery_tag, message: exception.message, backtrace: exception.backtrace.join(',') }}
|
107
|
+
RabbitFeed.log.error { { event: :processing_exception, delivery_tag: delivery_info.delivery_tag, message: exception.message, backtrace: exception.backtrace.join(',') } }
|
109
108
|
RabbitFeed.exception_notify exception
|
110
109
|
end
|
111
110
|
end
|
data/lib/rabbit_feed/event.rb
CHANGED
@@ -2,14 +2,14 @@ module RabbitFeed
|
|
2
2
|
class Event
|
3
3
|
include ActiveModel::Validations
|
4
4
|
|
5
|
-
SCHEMA_VERSION = '2.0.0'
|
5
|
+
SCHEMA_VERSION = '2.0.0'.freeze
|
6
6
|
|
7
7
|
attr_reader :schema, :payload, :metadata, :sensitive_fields
|
8
8
|
validates :metadata, presence: true
|
9
9
|
validates :payload, length: { minimum: 0, allow_nil: false, message: 'can\'t be nil' }
|
10
10
|
validate :required_metadata
|
11
11
|
|
12
|
-
def initialize
|
12
|
+
def initialize(metadata, payload = {}, schema = nil, sensitive_fields = [])
|
13
13
|
@schema = schema
|
14
14
|
@payload = payload.with_indifferent_access if payload
|
15
15
|
@metadata = metadata.with_indifferent_access if metadata
|
@@ -40,8 +40,7 @@ module RabbitFeed
|
|
40
40
|
end
|
41
41
|
|
42
42
|
class << self
|
43
|
-
|
44
|
-
def deserialize serialized_event
|
43
|
+
def deserialize(serialized_event)
|
45
44
|
datum_reader = Avro::IO::DatumReader.new
|
46
45
|
reader = Avro::DataFile::Reader.new (StringIO.new serialized_event), datum_reader
|
47
46
|
event_hash = nil
|
@@ -49,25 +48,7 @@ module RabbitFeed
|
|
49
48
|
event_hash = datum
|
50
49
|
end
|
51
50
|
reader.close
|
52
|
-
|
53
|
-
new_from_version_1 event_hash, datum_reader.readers_schema
|
54
|
-
else
|
55
|
-
new event_hash['metadata'], event_hash['payload'], datum_reader.readers_schema
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def version_1? event_hash
|
62
|
-
%w(metadata payload).none?{|key| event_hash.has_key? key}
|
63
|
-
end
|
64
|
-
|
65
|
-
def new_from_version_1 metadata_and_payload, schema
|
66
|
-
metadata = {}
|
67
|
-
%w(application name host version environment created_at_utc).each do |field|
|
68
|
-
metadata[field] = metadata_and_payload.delete field
|
69
|
-
end
|
70
|
-
new metadata, metadata_and_payload, schema
|
51
|
+
new event_hash['metadata'], event_hash['payload'], datum_reader.readers_schema
|
71
52
|
end
|
72
53
|
end
|
73
54
|
|
@@ -75,18 +56,17 @@ module RabbitFeed
|
|
75
56
|
|
76
57
|
def sensitive_proof_payload
|
77
58
|
sensitive_fields.each_with_object(payload.dup) do |field, clean_payload|
|
78
|
-
clean_payload[field] =
|
59
|
+
clean_payload[field] = '[REMOVED]' if clean_payload.key?(field)
|
79
60
|
end
|
80
61
|
end
|
81
62
|
|
82
63
|
def validate!
|
83
|
-
raise Error
|
64
|
+
raise Error, "Invalid event: #{errors.messages}" if invalid?
|
84
65
|
end
|
85
66
|
|
86
67
|
def required_metadata
|
87
|
-
|
88
|
-
|
89
|
-
end
|
68
|
+
return unless metadata
|
69
|
+
errors.add(:metadata, 'name field is required') if metadata[:name].blank?
|
90
70
|
end
|
91
71
|
end
|
92
72
|
end
|
@@ -1,13 +1,12 @@
|
|
1
1
|
module RabbitFeed
|
2
2
|
class EventDefinitions
|
3
|
-
|
4
3
|
class Field
|
5
4
|
include ActiveModel::Validations
|
6
5
|
|
7
6
|
attr_reader :name, :type, :definition
|
8
7
|
validates_presence_of :name, :type, :definition
|
9
8
|
|
10
|
-
def initialize
|
9
|
+
def initialize(name, type, definition)
|
11
10
|
@name = name
|
12
11
|
@type = type
|
13
12
|
@definition = definition
|
@@ -21,7 +20,7 @@ module RabbitFeed
|
|
21
20
|
private
|
22
21
|
|
23
22
|
def validate!
|
24
|
-
raise ConfigurationError
|
23
|
+
raise ConfigurationError, "Bad field specification for #{name}: #{errors.messages}" if invalid?
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
@@ -33,24 +32,24 @@ module RabbitFeed
|
|
33
32
|
validate :schema_parseable
|
34
33
|
validates :version, format: { with: /\A\d+\.\d+\.\d+\z/, message: 'must be in *.*.* format' }
|
35
34
|
|
36
|
-
def initialize
|
35
|
+
def initialize(name, version)
|
37
36
|
@name = name
|
38
37
|
@version = version
|
39
38
|
@fields = []
|
40
39
|
@sensitive_fields = []
|
41
40
|
end
|
42
41
|
|
43
|
-
def payload_contains
|
44
|
-
|
42
|
+
def payload_contains(&block)
|
43
|
+
instance_eval(&block)
|
45
44
|
end
|
46
45
|
|
47
|
-
def field
|
46
|
+
def field(name, options)
|
48
47
|
sensitive_fields << name.to_s if options.delete(:sensitive)
|
49
48
|
fields << (Field.new name, options[:type], options[:definition])
|
50
49
|
end
|
51
50
|
|
52
|
-
def defined_as
|
53
|
-
@definition =
|
51
|
+
def defined_as(&block)
|
52
|
+
@definition = yield if block.present?
|
54
53
|
end
|
55
54
|
|
56
55
|
def payload_schema
|
@@ -65,23 +64,23 @@ module RabbitFeed
|
|
65
64
|
(Field.new 'version', 'string', 'The version of the event payload'),
|
66
65
|
(Field.new 'schema_version', 'string', 'The version of the event schema'),
|
67
66
|
(Field.new 'name', 'string', 'The name of the event'),
|
68
|
-
(Field.new 'created_at_utc', 'string', 'The UTC time that the event was created')
|
67
|
+
(Field.new 'created_at_utc', 'string', 'The UTC time that the event was created')
|
69
68
|
].map(&:schema) }
|
70
69
|
end
|
71
70
|
|
72
71
|
def event_schema
|
73
72
|
[
|
74
73
|
{ name: 'payload', type: payload_schema, doc: 'The event payload (defined by the source system)' },
|
75
|
-
{ name: 'metadata', type: metadata_schema, doc: 'The event metadata (defined by rabbit feed)' }
|
74
|
+
{ name: 'metadata', type: metadata_schema, doc: 'The event metadata (defined by rabbit feed)' }
|
76
75
|
]
|
77
76
|
end
|
78
77
|
|
79
78
|
def schema
|
80
|
-
@schema ||=
|
79
|
+
@schema ||= Avro::Schema.parse({ name: name, type: 'record', doc: definition, fields: event_schema }.to_json)
|
81
80
|
end
|
82
81
|
|
83
82
|
def validate!
|
84
|
-
raise ConfigurationError
|
83
|
+
raise ConfigurationError, "Bad event specification for #{name}: #{errors.messages}" if invalid?
|
85
84
|
end
|
86
85
|
|
87
86
|
private
|
@@ -99,13 +98,13 @@ module RabbitFeed
|
|
99
98
|
@events = {}
|
100
99
|
end
|
101
100
|
|
102
|
-
def define_event
|
101
|
+
def define_event(name, options, &block)
|
103
102
|
events[name] = Event.new name, options[:version]
|
104
103
|
events[name].instance_eval(&block)
|
105
104
|
events[name].validate!
|
106
105
|
end
|
107
106
|
|
108
|
-
def []
|
107
|
+
def [](name)
|
109
108
|
events[name]
|
110
109
|
end
|
111
110
|
end
|