banter 1.2.3 → 1.2.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b13b654e7432d2615637212aa89c5e2982132c4f
4
- data.tar.gz: 7574a9284911946d6ea20f7dab4c5edb4f6cd132
3
+ metadata.gz: 07b71e3cd5e4d50cdbadff4bfc6aeb90cffa749c
4
+ data.tar.gz: 541920f865408bedb92d9b9bd0b0490adf7f13d1
5
5
  SHA512:
6
- metadata.gz: 9b0e607a13ddfc3f25f018dc99c756c37969ed1cbd2054a297811e9aa87ed9d2d8377d97fad4bcb7fec4947aece16969d19ec1f6bd8a62ee016e115cf459fb85
7
- data.tar.gz: b22048fb8320d300d739b46ad5cfb4b77a06bba4e80384824924ba105a574a82ac1ad92c2d8ff5db2f72c9de56da2642eb1b03842c6886621cb4c2fb14dd35fb
6
+ metadata.gz: 656c84442869c91309fcbb93c1303b527c4b03b107d53cc4354154928e7a0a0273e9e9ad5bdbeafc0b841141d8f7c94e40e5adb0b7cc43952db1a832cc8ade44
7
+ data.tar.gz: 943010a912bd147ca77ec5d5c6658b89688c74d2c4b194a9eee2bcbbd5777bbcf935e6cc19234a28c6ca7937dc4e500420b13891b59296371a0fb5b2adc17b53
@@ -1,33 +1,32 @@
1
- # Internal log class that is used to log messages before sending, after receiving, and failure to send
1
+ # encoding: utf-8
2
+ # Internal log class that is used to log envelopes before sending, after receiving, and failure to send
2
3
  module Banter
3
4
 
4
5
  class RabbitLogger
6
+ @@log_map = [:debug, :info, :warn, :error, :fatal]
5
7
  def self.enabled?
6
- Banter::Configuration.logging_enabled
8
+ true
9
+ # Banter::Configuration.logging_enabled
7
10
  end
8
11
 
9
- def self.log_publish(routing_key, message)
10
- return unless enabled?
11
- tags = ["BANTER PUBLISH", message[:ts], message[:pub], message[:v], routing_key]
12
- logger.tagged(tags) { logger.warn message[:payload].as_json }
12
+ def self.log_publish(routing_key, envelope)
13
+ internal_log(:warn, "BANTER PUBLISH", envelope, routing_key, envelope[:payload])
13
14
  end
14
15
 
15
- def self.failed_publish(routing_key, properties, message)
16
- return unless enabled?
17
- tags = ["BANTER FAILED_SEND", message[:ts], message[:pub], message[:v], routing_key]
18
- logger.tagged(tags) { logger.error( { properties: properties, payload:message[:payload] }.as_json ) }
16
+ def self.failed_publish(routing_key, properties, envelope)
17
+ internal_log(:error, "BANTER FAILED_SEND", envelope, routing_key, { properties: properties, payload:envelope[:payload] })
19
18
  end
20
19
 
21
- def self.log_receive(routing_key, message)
22
- return unless enabled?
23
- tags = ["BANTER RECEIVED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
24
- logger.tagged(tags) { logger.warn message[:payload].as_json }
20
+ def self.log_receive(routing_key, envelope)
21
+ internal_log(:warn, "BANTER RECEIVED", envelope, routing_key, envelope[:payload])
25
22
  end
26
23
 
27
- def self.log_complete(routing_key, message)
28
- return unless enabled?
29
- tags = ["BANTER COMPLETED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
30
- logger.tagged(tags) { logger.warn message[:payload].as_json }
24
+ def self.log_complete(routing_key, envelope)
25
+ internal_log(:warn, "BANTER COMPLETED", envelope, routing_key, envelope[:payload])
26
+ end
27
+
28
+ def self.log_subscriber_failed(routing_key, delivery_info, properties, envelope)
29
+ internal_log(:warn, "BANTER SUBSCRIBER FAILED", envelope, routing_key, { delivery_info: delivery_info, properties: properties, contents: envelope[:payload]} )
31
30
  end
32
31
 
33
32
  def self.log_service(service_name, message)
@@ -36,15 +35,24 @@ module Banter
36
35
  logger.tagged(tags) { logger.info message.as_json}
37
36
  end
38
37
 
39
- @@log_map = [:debug, :info, :warn, :error, :fatal]
40
38
  def self.log(log_level, message)
41
- tags = ["BANTER LOG_LEVEL:#{@@log_map[log_level].capitalize.to_s}", Process::pid]
42
- logger.tagged(tags) { logger.send(@@log_map[log_level], message.as_json ) }
39
+ tags = ["BANTER LOG_LEVEL:#{@@log_map[log_level].capitalize.to_s}", Process::pid.to_s].map{|x| utf(x)}
40
+ logger.tagged(tags) { logger.send(@@log_map[log_level], utf(message.as_json.to_s) ) }
41
+ end
42
+
43
+ def self.internal_log(log_level, log_type, envelope, routing_key, payload)
44
+ return unless enabled?
45
+ tags = [log_type, envelope[:ts].to_s, envelope[:pub], envelope[:v].to_s, routing_key].map{|x| utf(x)}
46
+ logger.tagged(tags) { logger.send(log_level, utf(payload.to_s)) }
43
47
  end
44
48
 
45
49
  def self.logger
46
50
  Banter.logger
47
51
  end
48
52
 
53
+ def self.utf(field)
54
+ field.force_encoding('utf-8')
55
+ end
56
+
49
57
  end
50
58
  end
@@ -19,7 +19,7 @@ module Banter
19
19
  end
20
20
 
21
21
  def start
22
- @subscriber.start(false) do |delivery_info, properties, envelope|
22
+ @subscriber.start do |delivery_info, properties, envelope|
23
23
  message_received(delivery_info, properties, envelope)
24
24
  true
25
25
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Banter
2
4
  module Server
3
5
 
@@ -23,15 +25,13 @@ module Banter
23
25
  @queue_name = queue_name
24
26
  end
25
27
 
26
- # pass in a lambda for this method to work. We might only want to expose the content instead of
27
- # all 3 chunks.
28
- def start(blocking=false)
28
+ def start(&block)
29
29
  @connection = Bunny.new(Configuration.connection)
30
30
  begin
31
31
  @connection.start
32
32
  rescue => e
33
33
  Banter::RabbitLogger.log(Logger::ERROR, "Cannot connect to rabbitmq")
34
- Airbrake.notify(e, params: { message: e.message, what_happened: "RabbitMQ unreachable!" }, environment_name: ENV['RAILS_ENV'])
34
+ Airbrake.notify(e, params: { message: e.message, what_happened: "RabbitMQ unreachable!" }, environment_name: environment)
35
35
  raise e
36
36
  end
37
37
 
@@ -44,28 +44,52 @@ module Banter
44
44
 
45
45
  @channel.basic_qos(@pool_size) if @pool_size != 0
46
46
 
47
+ @channel.on_uncaught_exception(&method(:notify_error))
48
+
47
49
  rabbit_queue = @channel.queue(@queue_name, durable: @durable, exclusive: false, arguments: queue_arguments)
48
- @listener = rabbit_queue.bind(@exchange, routing_key: @routing_key, exclusive: false)
50
+ @listener = rabbit_queue.bind(@exchange, routing_key: @routing_key, exclusive: false)
51
+
52
+ @callback_block = block
53
+ @consumer = @listener.subscribe({ consumer_tag: @queue_name, manual_ack: true, block: false}, &method(:process_message))
54
+ nil
55
+ end
49
56
 
50
- # Parameters for subscribe that might be useful:
51
- # :block=>true - Used for long running consumer applications. (backend servers?)
57
+ def process_message(delivery_info, properties, contents)
58
+ token = delivery_info.delivery_tag
59
+ begin
60
+ envelope = ::Banter::Message.new.parse(contents)
61
+ Banter::RabbitLogger.log(Logger::DEBUG, "Message delivery with contents: #{token}: #{delivery_info[:routing_key]}, #{contents}, #{contents.encoding}")
52
62
 
53
- @consumer = @listener.subscribe(consumer_tag: @queue_name, manual_ack: true, block: blocking) do |delivery_info, properties, contents|
54
- Banter::RabbitLogger.log(Logger::DEBUG, "Message delivery with contents: #{contents}")
55
63
  if delivery_info[:redelivered]
56
64
  e = StandardError.new("PubSub Message redelivery")
57
- Airbrake.notify(e, params: { info: delivery_info, props: properties, contents: contents }, environment_name: ENV['RAILS_ENV'], backtrace: caller)
65
+ Airbrake.notify(e, params: { info: delivery_info, props: properties, contents: contents }, backtrace: caller)
58
66
  end
59
- message = ::Banter::Message.new.parse(contents)
60
- Banter::RabbitLogger.log_receive(delivery_info[:routing_key], message)
61
- yield delivery_info, properties, message
62
- Banter::RabbitLogger.log_complete(delivery_info[:routing_key], message)
63
- Banter::RabbitLogger.log(Logger::DEBUG, "Message acknowledged with tag #{delivery_info.delivery_tag}")
67
+
68
+ Banter::RabbitLogger.log_receive(delivery_info[:routing_key], envelope)
69
+ @callback_block.call( delivery_info, properties, envelope)
70
+ Banter::RabbitLogger.log_complete(delivery_info[:routing_key], envelope)
71
+
64
72
  # Need to acknowledge the message for the next message to come down.
65
- @channel.ack(delivery_info.delivery_tag)
66
- true
73
+ Banter::RabbitLogger.log(Logger::DEBUG, "Message acknowledged with tag #{token}")
74
+ @channel.ack(token)
75
+ rescue => e
76
+ Banter::RabbitLogger.log(Logger::WARN, "Error in message: #{e}")
77
+ e.backtrace.each{|line| Banter::RabbitLogger.log(Logger::WARN, "-- #{line}") }
78
+ Banter::RabbitLogger.log(Logger::WARN, "contents: #{contents}")
79
+ Banter::RabbitLogger.log(Logger::WARN, { routing_key: delivery_info[:routing_key], delivery_info: delivery_info, properties: properties, envelope: envelope} )
80
+ Banter::RabbitLogger.log_subscriber_failed(delivery_info[:routing_key], delivery_info, properties, envelope)
81
+
82
+ # Does not get put back on the queue, and instead, will need to be processed either by the log parser
83
+ # later or by dead letter exchange
84
+ @channel.reject(token, false)
85
+ Airbrake.notify(e, params: { delivery_info: delivery_info, properties: properties, contents: contents, error: e.message }, environment_name: environment)
67
86
  end
68
- nil
87
+ end
88
+
89
+ def notify_error(error, consumer)
90
+ Banter::RabbitLogger.log(Logger::WARN, "Error with the message: #{error.message}: #{consumer}")
91
+ Banter::RabbitLogger.log(Logger::WARN, "#{caller.inspect}")
92
+ Airbrake.notify(error, params: { consumer: consumer, error: error.message }, environment_name: environment)
69
93
  end
70
94
 
71
95
  def teardown
@@ -73,9 +97,13 @@ module Banter
73
97
  @consumer.cancel if @consumer.present?
74
98
  @connection.close if @connection.present?
75
99
  rescue => e
76
- Airbrake.notify(e, params: { error: e.message }, environment_name: ENV['RAILS_ENV'], backtrace: caller)
100
+ Airbrake.notify(e, params: { error: e.message }, environment_name: environment)
77
101
  end
78
102
  end
103
+
104
+ def environment
105
+ ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
106
+ end
79
107
  end
80
108
  end
81
109
  end
@@ -1,3 +1,3 @@
1
1
  module Banter
2
- VERSION = "1.2.3"
2
+ VERSION = "1.2.4"
3
3
  end
@@ -5,10 +5,11 @@ describe Banter::Publisher do
5
5
  let(:routing_key) { "test/logger" }
6
6
  let(:publisher) { Banter::Publisher.new }
7
7
  let(:context) { {unique_id: "1234", orig_ip_address: "127.0.0.1"}}
8
+ let(:contents) { Message::new.serialize(context, routing_key, {"a"=>"b"})}
8
9
  let(:channel_mocker) {
9
10
  topic = double("::Bunny::Exchange", :publish=> true )
10
11
  if publish_fails
11
- topic.stub(:on_return).and_yield({routing_key: 'a.b.c'}, {some: 'property'}, '{"a":"b"}').and_return(false)
12
+ topic.stub(:on_return).and_yield({routing_key: 'a.b.c'}, {some: 'property'}, contents).and_return(false)
12
13
  else
13
14
  topic.stub(:on_return).and_return true
14
15
  end
@@ -1,98 +1,117 @@
1
+ #encoding: utf-8
1
2
  require 'spec_helper'
2
3
 
3
4
  describe Banter::RabbitLogger do
4
- let(:routing_key) { "test/logger" }
5
+ let(:routing_key) { "test.logger" }
5
6
  let(:subject) { Banter::RabbitLogger }
6
7
  let(:payload) { {"hit"=>"me"} }
7
8
  let(:message) { Banter::Message.new.serialize(context, routing_key, payload) }
8
9
  let(:config_buffer) { StringIO.new }
9
10
  let(:output_string) { config_buffer.string }
11
+ let(:delivery_info) { {empty: true}}
12
+ let(:properties) { {empty: true}}
10
13
  let(:context) { {unique_id: "1234", orig_ip_address: "127.0.0.1"}}
11
14
 
12
- before do
13
- allow(Banter.logger).to receive(:debug)
14
- allow(Banter.logger).to receive(:info)
15
- allow(Banter.logger).to receive(:warn)
16
- allow(Banter.logger).to receive(:error)
17
- allow(Banter.logger).to receive(:log)
18
- end
15
+ context "standard character set" do
16
+ before do
17
+ allow(Banter.logger).to receive(:debug)
18
+ allow(Banter.logger).to receive(:info)
19
+ allow(Banter.logger).to receive(:warn)
20
+ allow(Banter.logger).to receive(:error)
21
+ allow(Banter.logger).to receive(:log)
22
+ end
19
23
 
20
- describe "#log_publish" do
21
- let!(:result) { subject.log_publish(routing_key, message)}
24
+ describe "#log_publish" do
25
+ let!(:result) { subject.log_publish(routing_key, message)}
22
26
 
23
- it "should not fail" do
24
- expect{ result }.not_to raise_error
25
- end
27
+ it "should not fail" do
28
+ expect{ result }.not_to raise_error
29
+ end
30
+
31
+ it "should write a row to the log" do
32
+ expect(Banter.logger).to have_received(:warn).with(anything)
33
+ end
26
34
 
27
- it "should write a row to the log" do
28
- expect(Banter.logger).to have_received(:warn).with(anything)
29
35
  end
30
36
 
31
- end
37
+ describe "#failed_publish" do
38
+ let!(:result) { subject.failed_publish(routing_key, {}, message)}
32
39
 
33
- describe "#failed_publish" do
34
- let!(:result) { subject.failed_publish(routing_key, {}, message)}
40
+ context "warning log level" do
35
41
 
36
- context "warning log level" do
42
+ it "should not fail" do
43
+ expect{ result }.not_to raise_error()
44
+ end
45
+
46
+ it "should respect the log level of the file" do
47
+ expect(Banter.logger).to have_received(:error).with(anything)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#log_receive" do
53
+ let!(:result) { subject.log_receive(routing_key, message)}
37
54
 
38
55
  it "should not fail" do
39
56
  expect{ result }.not_to raise_error()
40
57
  end
41
58
 
42
59
  it "should respect the log level of the file" do
43
- expect(Banter.logger).to have_received(:error).with(anything)
60
+ expect(Banter.logger).to have_received(:warn).with(anything)
44
61
  end
45
62
  end
46
- end
47
63
 
48
- describe "#log_receive" do
49
- let!(:result) { subject.log_receive(routing_key, message)}
64
+ describe "#log_complete" do
65
+ let!(:result) { subject.log_complete(routing_key, message)}
50
66
 
51
- it "should not fail" do
52
- expect{ result }.not_to raise_error()
53
- end
67
+ it "should not fail" do
68
+ expect{ result }.not_to raise_error()
69
+ end
54
70
 
55
- it "should respect the log level of the file" do
56
- expect(Banter.logger).to have_received(:warn).with(anything)
71
+ it "should respect the log level of the file" do
72
+ expect(Banter.logger).to have_received(:warn).with(anything)
73
+ end
57
74
  end
58
- end
59
75
 
60
- describe "#log_complete" do
61
- let!(:result) { subject.log_complete(routing_key, message)}
76
+ describe "#log" do
77
+ let!(:result) { subject.log(::Logger::DEBUG, message)}
62
78
 
63
- it "should not fail" do
64
- expect{ result }.not_to raise_error()
65
- end
79
+ it "should not fail" do
80
+ expect{ result }.not_to raise_error()
81
+ end
66
82
 
67
- it "should respect the log level of the file" do
68
- expect(Banter.logger).to have_received(:warn).with(anything)
83
+ it "should respect the log level of the file" do
84
+ expect(Banter.logger).to have_received(:debug).with(anything)
85
+ end
69
86
  end
70
- end
71
87
 
72
- describe "#log" do
73
- let!(:result) { subject.log(::Logger::DEBUG, message)}
88
+ describe "#log_service" do
89
+ let(:service_name) { "test" }
90
+ let!(:result) { subject.log_service(service_name, message)}
74
91
 
75
- it "should not fail" do
76
- expect{ result }.not_to raise_error()
77
- end
92
+ context "debugger log" do
93
+ it "should not fail" do
94
+ expect{ result }.not_to raise_error()
95
+ end
78
96
 
79
- it "should respect the log level of the file" do
80
- expect(Banter.logger).to have_received(:debug).with(anything)
97
+ it "should respect the log level of the file" do
98
+ expect(Banter.logger).to have_received(:info).with(anything)
99
+ end
100
+ end
81
101
  end
82
- end
83
102
 
84
- describe "#log_service" do
85
- let(:service_name) { "test" }
86
- let!(:result) { subject.log_service(service_name, message)}
103
+ describe "#log_subscriber_failed" do
104
+ let!(:result) { subject.log_subscriber_failed(routing_key, delivery_info, properties, message)}
87
105
 
88
- context "debugger log" do
89
106
  it "should not fail" do
90
107
  expect{ result }.not_to raise_error()
91
108
  end
92
109
 
93
110
  it "should respect the log level of the file" do
94
- expect(Banter.logger).to have_received(:info).with(anything)
111
+ expect(Banter.logger).to have_received(:warn).with(anything)
95
112
  end
113
+
96
114
  end
97
115
  end
116
+
98
117
  end
@@ -1,5 +1,126 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Banter::Server::RabbitMQSubscriber do
4
- subject { Banter::Server::RabbitMQSubscriber.new( routing_key ) }
5
- end
4
+ let(:routing_key) { "fake.route"}
5
+ let(:subscriber){ Banter::Server::RabbitMQSubscriber.new( routing_key, "queue_name", 100 ) }
6
+ let(:delivery_info) { Hashie::Mash.new({ :delivery_tag=>1, :routing_key=> routing_key}) }
7
+ let(:properties) { {} }
8
+ let(:contents) { File.read( File.join(File.dirname(__FILE__), "../../fixtures/utf_message.json")) }
9
+
10
+
11
+ def do_nothing(delivery, props, content)
12
+ end
13
+
14
+ def raise_method(delivery, props, content)
15
+ raise StandardError.new("Bad Subscriber")
16
+ end
17
+
18
+ describe "#process_message" do
19
+ let(:result) {
20
+ subscriber.process_message(delivery_info, properties, contents)
21
+ }
22
+
23
+ before do
24
+ subscriber.instance_variable_set(:@channel, double("channel", :ack=>true, :reject=>true))
25
+ allow(subscriber.channel).to receive(:ack)
26
+ allow(subscriber.channel).to receive(:reject)
27
+ allow(Airbrake).to receive(:notify)
28
+ end
29
+
30
+
31
+ context "subscriber has an error" do
32
+ before do
33
+ subscriber.instance_variable_set(:@callback_block, method(:raise_method) )
34
+ end
35
+
36
+
37
+ it "should not raise any errors" do
38
+ expect{ result }.not_to raise_error
39
+ end
40
+
41
+ it "should have rejected the message" do
42
+ result
43
+ expect(subscriber.channel).to have_received(:reject).with(1, false)
44
+ end
45
+
46
+ it "should have generated an airbrake" do
47
+ result
48
+ expect(Airbrake).to have_received(:notify).exactly(1).times
49
+ end
50
+ end
51
+
52
+ context "subscriber works correctly" do
53
+ before do
54
+ subscriber.instance_variable_set(:@callback_block, method(:do_nothing) )
55
+ end
56
+
57
+ it "should not raise any errors" do
58
+ expect{ result }.not_to raise_error
59
+ end
60
+
61
+ it "should have acknowledged the message" do
62
+ result
63
+ expect(subscriber.channel).to have_received(:ack).with(1)
64
+ end
65
+
66
+ it "should not have generated an airbrake" do
67
+ result
68
+ expect(Airbrake).not_to have_received(:notify)
69
+ end
70
+ end
71
+
72
+ context "logger has problems" do
73
+
74
+ before do
75
+ subscriber.instance_variable_set(:@callback_block, method(:do_nothing) )
76
+ allow(Banter::RabbitLogger).to receive(:log_receive).and_raise("Cannot log")
77
+ allow(Banter::RabbitLogger).to receive(:log_complete)
78
+ end
79
+
80
+ it "should not raise any errors" do
81
+ expect{ result }.not_to raise_error
82
+ end
83
+
84
+ it "should have rejected the message" do
85
+ result
86
+ expect(subscriber.channel).to have_received(:reject).with(1, false)
87
+ end
88
+
89
+ it "should not have logged a complete message" do
90
+ result
91
+ expect(Banter::RabbitLogger).not_to have_received(:log_complete)
92
+ end
93
+
94
+
95
+ it "should have generated an airbrake" do
96
+ result
97
+ expect(Airbrake).to have_received(:notify).exactly(1).times
98
+ end
99
+
100
+
101
+ end
102
+
103
+ end
104
+
105
+ describe "#notify_error" do
106
+ let(:error) { StandardError.new("Error Message")}
107
+ let(:consumer) { Object.new}
108
+ let(:result) { subscriber.notify_error(error, consumer)}
109
+
110
+ before do
111
+ allow(Airbrake).to receive(:notify)
112
+ end
113
+
114
+ it "should not raise any errors" do
115
+ expect{result}.not_to raise_error
116
+ end
117
+
118
+ it "should inform airbrake of the problem" do
119
+ result
120
+ expect(Airbrake).to have_received(:notify).exactly(1).times
121
+ end
122
+
123
+ end
124
+
125
+
126
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "ts":123,
3
+ "pub":"order.state_changed.delivered",
4
+ "v":"1",
5
+ "payload": {
6
+ "jp" : "\xa1\xa1"
7
+ }
8
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: banter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Honest Company
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-09-23 00:00:00.000000000 Z
14
+ date: 2014-10-09 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -229,6 +229,7 @@ files:
229
229
  - spec/config.yml
230
230
  - spec/factories/banter_messages.rb
231
231
  - spec/factories/banter_workers.rb
232
+ - spec/fixtures/utf_message.json
232
233
  - spec/mongoid.yml
233
234
  - spec/spec_helper.rb
234
235
  - web/assets/javascripts/banter.js
@@ -294,5 +295,6 @@ test_files:
294
295
  - spec/config.yml
295
296
  - spec/factories/banter_messages.rb
296
297
  - spec/factories/banter_workers.rb
298
+ - spec/fixtures/utf_message.json
297
299
  - spec/mongoid.yml
298
300
  - spec/spec_helper.rb