krakow 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +16 -0
- data/CONTRIBUTING.md +25 -0
- data/LICENSE +13 -0
- data/README.md +62 -9
- data/krakow.gemspec +3 -1
- data/lib/krakow/command/cls.rb +3 -4
- data/lib/krakow/command/fin.rb +13 -4
- data/lib/krakow/command/identify.rb +22 -9
- data/lib/krakow/command/mpub.rb +14 -4
- data/lib/krakow/command/nop.rb +3 -4
- data/lib/krakow/command/pub.rb +15 -5
- data/lib/krakow/command/rdy.rb +13 -4
- data/lib/krakow/command/req.rb +14 -4
- data/lib/krakow/command/sub.rb +14 -4
- data/lib/krakow/command/touch.rb +13 -4
- data/lib/krakow/command.rb +25 -3
- data/lib/krakow/connection.rb +286 -60
- data/lib/krakow/connection_features/deflate.rb +26 -1
- data/lib/krakow/connection_features/snappy_frames.rb +34 -3
- data/lib/krakow/connection_features/ssl.rb +43 -1
- data/lib/krakow/connection_features.rb +1 -0
- data/lib/krakow/consumer.rb +162 -49
- data/lib/krakow/discovery.rb +17 -6
- data/lib/krakow/distribution/default.rb +61 -33
- data/lib/krakow/distribution.rb +107 -57
- data/lib/krakow/exceptions.rb +14 -0
- data/lib/krakow/frame_type/error.rb +13 -7
- data/lib/krakow/frame_type/message.rb +47 -4
- data/lib/krakow/frame_type/response.rb +14 -4
- data/lib/krakow/frame_type.rb +20 -8
- data/lib/krakow/producer/http.rb +95 -6
- data/lib/krakow/producer.rb +60 -17
- data/lib/krakow/utils/lazy.rb +99 -40
- data/lib/krakow/utils/logging.rb +11 -0
- data/lib/krakow/utils.rb +3 -0
- data/lib/krakow/version.rb +3 -1
- data/lib/krakow.rb +1 -0
- metadata +11 -11
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -34
- data/test/spec.rb +0 -81
- data/test/specs/consumer.rb +0 -49
- data/test/specs/http_producer.rb +0 -123
- data/test/specs/producer.rb +0 -20
data/lib/krakow/producer.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
+
require 'krakow'
|
2
|
+
|
1
3
|
module Krakow
|
4
|
+
|
5
|
+
# TCP based producer
|
2
6
|
class Producer
|
3
7
|
|
4
8
|
autoload :Http, 'krakow/producer/http'
|
5
9
|
|
6
10
|
include Utils::Lazy
|
11
|
+
# @!parse include Utils::Lazy::InstanceMethods
|
12
|
+
# @!parse extend Utils::Lazy::ClassMethods
|
13
|
+
|
7
14
|
include Celluloid
|
8
15
|
|
9
16
|
trap_exit :connection_failure
|
@@ -11,50 +18,79 @@ module Krakow
|
|
11
18
|
|
12
19
|
attr_reader :connection
|
13
20
|
|
21
|
+
# @!group Attributes
|
22
|
+
|
23
|
+
# @!macro [attach] attribute
|
24
|
+
# @!method $1
|
25
|
+
# @return [$2] the $1 $0
|
26
|
+
# @!method $1?
|
27
|
+
# @return [TrueClass, FalseClass] truthiness of the $1 $0
|
28
|
+
attribute :host, String, :required => true
|
29
|
+
attribute :port, [String, Integer], :required => true
|
30
|
+
attribute :topic, String, :required => true
|
31
|
+
attribute :reconnect_retries, Integer, :default => 10
|
32
|
+
attribute :reconnect_interval, Integer, :default => 5
|
33
|
+
attribute :connection_options, Hash, :default => ->{ Hash.new }
|
34
|
+
|
35
|
+
# @!endgroup
|
36
|
+
|
14
37
|
def initialize(args={})
|
15
38
|
super
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
arguments[:reconnect_interval] = 5
|
20
|
-
arguments[:connection_features] ||= {}
|
39
|
+
arguments[:connection_options] = {:features => {}, :config => {}}.merge(
|
40
|
+
arguments.fetch(:connection_options, {})
|
41
|
+
)
|
21
42
|
connect
|
22
43
|
end
|
23
44
|
|
24
45
|
# Establish connection to configured `host` and `port`
|
46
|
+
#
|
47
|
+
# @return nil
|
25
48
|
def connect
|
26
49
|
info "Establishing connection to: #{host}:#{port}"
|
27
50
|
begin
|
28
51
|
@connection = Connection.new(
|
29
52
|
:host => host,
|
30
53
|
:port => port,
|
31
|
-
:features =>
|
54
|
+
:features => connection_options[:features],
|
55
|
+
:features_args => connection_options[:config]
|
32
56
|
)
|
33
|
-
self.link connection
|
34
57
|
connection.init!
|
58
|
+
self.link connection
|
35
59
|
info "Connection established: #{connection}"
|
60
|
+
nil
|
36
61
|
rescue => e
|
37
62
|
abort e
|
38
63
|
end
|
39
64
|
end
|
40
65
|
|
66
|
+
# @return [String] stringify object
|
41
67
|
def to_s
|
42
68
|
"<#{self.class.name}:#{object_id} {#{host}:#{port}} T:#{topic}>"
|
43
69
|
end
|
44
70
|
|
45
|
-
#
|
71
|
+
# @return [TrueClass, FalseClass] currently connected to server
|
46
72
|
def connected?
|
47
|
-
connection && connection.alive?
|
73
|
+
!!(connection && connection.alive? && connection.connected?)
|
48
74
|
end
|
49
75
|
|
50
76
|
# Process connection failure and attempt reconnection
|
77
|
+
#
|
78
|
+
# @return [TrueClass]
|
51
79
|
def connection_failure(*args)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
80
|
+
@connection = nil
|
81
|
+
begin
|
82
|
+
warn "Connection failure detected for #{host}:#{port}"
|
83
|
+
connect
|
84
|
+
rescue => e
|
85
|
+
warn "Failed to establish connection to #{host}:#{port}. Pausing #{reconnect_interval} before retry"
|
86
|
+
sleep reconnect_interval
|
87
|
+
connect
|
88
|
+
end
|
89
|
+
true
|
56
90
|
end
|
57
91
|
|
92
|
+
# Instance destructor
|
93
|
+
# @return nil
|
58
94
|
def goodbye_my_love!
|
59
95
|
debug 'Tearing down producer'
|
60
96
|
if(connection && connection.alive?)
|
@@ -62,12 +98,19 @@ module Krakow
|
|
62
98
|
end
|
63
99
|
@connection = nil
|
64
100
|
info 'Producer torn down'
|
101
|
+
nil
|
65
102
|
end
|
66
103
|
|
67
|
-
# message
|
68
|
-
#
|
104
|
+
# Write message to server
|
105
|
+
#
|
106
|
+
# @param message [String] message to write
|
107
|
+
# @return [Krakow::FrameType::Error,nil]
|
108
|
+
# @raise [Krakow::Error::ConnectionUnavailable]
|
69
109
|
def write(*message)
|
70
|
-
if(
|
110
|
+
if(message.empty?)
|
111
|
+
abort ArgumentError.new 'Expecting one or more messages to send. None provided.'
|
112
|
+
end
|
113
|
+
if(connection && connection.alive?)
|
71
114
|
if(message.size > 1)
|
72
115
|
debug 'Multiple message publish'
|
73
116
|
connection.transmit(
|
@@ -86,7 +129,7 @@ module Krakow
|
|
86
129
|
)
|
87
130
|
end
|
88
131
|
else
|
89
|
-
abort Error.new 'Remote connection is unavailable!'
|
132
|
+
abort Error::ConnectionUnavailable.new 'Remote connection is unavailable!'
|
90
133
|
end
|
91
134
|
end
|
92
135
|
|
data/lib/krakow/utils/lazy.rb
CHANGED
@@ -1,64 +1,123 @@
|
|
1
|
+
require 'krakow'
|
2
|
+
|
1
3
|
module Krakow
|
2
4
|
module Utils
|
5
|
+
# Adds functionality to facilitate laziness
|
3
6
|
module Lazy
|
4
7
|
|
5
8
|
include Utils::Logging
|
6
9
|
|
7
|
-
|
10
|
+
# Instance methods for laziness
|
11
|
+
module InstanceMethods
|
12
|
+
|
13
|
+
# @return [Hash] argument hash
|
14
|
+
attr_reader :arguments
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
# Create new instance
|
17
|
+
#
|
18
|
+
# @param args [Hash]
|
19
|
+
# @return [Object]
|
20
|
+
def initialize(args={})
|
21
|
+
@arguments = {}.tap do |hash|
|
22
|
+
self.class.attributes.each do |name, options|
|
23
|
+
val = args[name]
|
24
|
+
if(options[:required] && !args.has_key?(name))
|
25
|
+
raise ArgumentError.new("Missing required option: `#{name}`")
|
26
|
+
end
|
27
|
+
if(val && options[:type] && !(valid = [options[:type]].flatten.compact).detect{|k| val.is_a?(k)})
|
28
|
+
raise TypeError.new("Invalid type for option `#{name}` (#{val} <#{val.class}>). Valid - #{valid.map(&:to_s).join(',')}")
|
29
|
+
end
|
30
|
+
if(val.nil? && options[:default] && !args.has_key?(name))
|
31
|
+
val = options[:default].respond_to?(:call) ? options[:default].call : options[:default]
|
32
|
+
end
|
33
|
+
hash[name] = val
|
34
|
+
end
|
13
35
|
end
|
14
36
|
end
|
37
|
+
alias_method :super_init, :initialize
|
38
|
+
|
39
|
+
# @return [String]
|
40
|
+
def to_s
|
41
|
+
"<#{self.class.name}:#{object_id}>"
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [String]
|
45
|
+
def inspect
|
46
|
+
"<#{self.class.name}:#{object_id} [#{arguments.inspect}]>"
|
47
|
+
end
|
48
|
+
|
15
49
|
end
|
16
50
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
51
|
+
# Class methods for laziness
|
52
|
+
module ClassMethods
|
53
|
+
|
54
|
+
# Add new attributes to class
|
55
|
+
#
|
56
|
+
# @param name [String]
|
57
|
+
# @param type [Class, Array<Class>]
|
58
|
+
# @param options [Hash]
|
59
|
+
# @option options [true, false] :required must be provided on initialization
|
60
|
+
# @option options [Object, Proc] :default default value
|
61
|
+
# @return [nil]
|
62
|
+
def attribute(name, type, options={})
|
63
|
+
name = name.to_sym
|
64
|
+
attributes[name] = {:type => type}.merge(options)
|
65
|
+
define_method(name) do
|
66
|
+
arguments[name.to_sym]
|
25
67
|
end
|
26
|
-
|
27
|
-
arguments[
|
68
|
+
define_method("#{name}?") do
|
69
|
+
!!arguments[name.to_sym]
|
28
70
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return attributes
|
75
|
+
#
|
76
|
+
# @param args [Symbol] :required or :optional
|
77
|
+
# @return [Array<Hash>]
|
78
|
+
def attributes(*args)
|
79
|
+
@attributes ||= {}
|
80
|
+
if(args.include?(:required))
|
81
|
+
Hash[@attributes.find_all{|k,v| v[:required]}]
|
82
|
+
elsif(args.include?(:optional))
|
83
|
+
Hash[@attributes.find_all{|k,v| !v[:required]}]
|
84
|
+
else
|
85
|
+
@attributes
|
33
86
|
end
|
34
87
|
end
|
88
|
+
|
89
|
+
# Directly set attribute hash
|
90
|
+
#
|
91
|
+
# @param attrs [Hash]
|
92
|
+
# @return [TrueClass]
|
93
|
+
# @todo need deep dup here
|
94
|
+
def set_attributes(attrs)
|
95
|
+
@attributes = attrs.dup
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
35
99
|
end
|
36
100
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
101
|
+
class << self
|
102
|
+
|
103
|
+
# Injects laziness into class
|
104
|
+
#
|
105
|
+
# @param klass [Class]
|
106
|
+
def included(klass)
|
107
|
+
klass.class_eval do
|
108
|
+
include InstanceMethods
|
109
|
+
extend ClassMethods
|
110
|
+
|
111
|
+
class << self
|
112
|
+
|
113
|
+
def inherited(klass)
|
114
|
+
klass.set_attributes(self.attributes)
|
115
|
+
end
|
116
|
+
|
51
117
|
end
|
52
118
|
end
|
53
119
|
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_s
|
57
|
-
"<#{self.class.name}:#{object_id}>"
|
58
|
-
end
|
59
120
|
|
60
|
-
def inspect
|
61
|
-
"<#{self.class.name}:#{object_id} [#{arguments.inspect}]>"
|
62
121
|
end
|
63
122
|
|
64
123
|
end
|
data/lib/krakow/utils/logging.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require 'krakow'
|
2
|
+
|
1
3
|
module Krakow
|
2
4
|
module Utils
|
5
|
+
# Logging helpers
|
3
6
|
module Logging
|
4
7
|
|
5
8
|
# Define base logging types
|
@@ -10,16 +13,24 @@ module Krakow
|
|
10
13
|
end
|
11
14
|
|
12
15
|
# Log message
|
16
|
+
#
|
17
|
+
# @param args [Array, nil]
|
18
|
+
# @return [Logger, nil]
|
13
19
|
def log(*args)
|
14
20
|
if(args.empty?)
|
15
21
|
Celluloid::Logger
|
16
22
|
else
|
17
23
|
severity, string = args
|
18
24
|
Celluloid::Logger.send(severity.to_sym, "#{self}: #{string}")
|
25
|
+
nil
|
19
26
|
end
|
20
27
|
end
|
21
28
|
|
22
29
|
class << self
|
30
|
+
# Set the logging output level
|
31
|
+
#
|
32
|
+
# @param level [Integer]
|
33
|
+
# @return [Integer, nil]
|
23
34
|
def level=(level)
|
24
35
|
if(Celluloid.logger.class == Logger)
|
25
36
|
Celluloid.logger.level = Logger.const_get(level.to_s.upcase.to_sym)
|
data/lib/krakow/utils.rb
CHANGED
data/lib/krakow/version.rb
CHANGED
data/lib/krakow.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: krakow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-05-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: celluloid-io
|
@@ -95,7 +95,10 @@ description: NSQ ruby library
|
|
95
95
|
email: code@chrisroberts.org
|
96
96
|
executables: []
|
97
97
|
extensions: []
|
98
|
-
extra_rdoc_files:
|
98
|
+
extra_rdoc_files:
|
99
|
+
- CHANGELOG.md
|
100
|
+
- CONTRIBUTING.md
|
101
|
+
- LICENSE
|
99
102
|
files:
|
100
103
|
- lib/krakow.rb
|
101
104
|
- lib/krakow/consumer.rb
|
@@ -129,17 +132,14 @@ files:
|
|
129
132
|
- lib/krakow/connection_features/ssl.rb
|
130
133
|
- lib/krakow/connection_features/snappy_frames.rb
|
131
134
|
- lib/krakow/connection_features/deflate.rb
|
132
|
-
- test/spec.rb
|
133
|
-
- test/specs/consumer.rb
|
134
|
-
- test/specs/producer.rb
|
135
|
-
- test/specs/http_producer.rb
|
136
|
-
- Gemfile
|
137
|
-
- README.md
|
138
135
|
- krakow.gemspec
|
136
|
+
- README.md
|
139
137
|
- CHANGELOG.md
|
140
|
-
-
|
138
|
+
- CONTRIBUTING.md
|
139
|
+
- LICENSE
|
141
140
|
homepage: http://github.com/chrisroberts/krakow
|
142
|
-
licenses:
|
141
|
+
licenses:
|
142
|
+
- Apache 2.0
|
143
143
|
post_install_message:
|
144
144
|
rdoc_options: []
|
145
145
|
require_paths:
|
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
krakow (0.1.3)
|
5
|
-
celluloid-io
|
6
|
-
digest-crc
|
7
|
-
http
|
8
|
-
multi_json
|
9
|
-
snappy
|
10
|
-
|
11
|
-
GEM
|
12
|
-
remote: https://rubygems.org/
|
13
|
-
specs:
|
14
|
-
celluloid (0.15.2)
|
15
|
-
timers (~> 1.1.0)
|
16
|
-
celluloid-io (0.15.0)
|
17
|
-
celluloid (>= 0.15.0)
|
18
|
-
nio4r (>= 0.5.0)
|
19
|
-
digest-crc (0.4.0)
|
20
|
-
http (0.5.0)
|
21
|
-
http_parser.rb
|
22
|
-
http_parser.rb (0.6.0)
|
23
|
-
minitest (5.0.8)
|
24
|
-
multi_json (1.8.4)
|
25
|
-
nio4r (1.0.0)
|
26
|
-
snappy (0.0.10)
|
27
|
-
timers (1.1.0)
|
28
|
-
|
29
|
-
PLATFORMS
|
30
|
-
ruby
|
31
|
-
|
32
|
-
DEPENDENCIES
|
33
|
-
krakow!
|
34
|
-
minitest
|
data/test/spec.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
require 'krakow'
|
2
|
-
require 'minitest/autorun'
|
3
|
-
|
4
|
-
module Krakow
|
5
|
-
class Test
|
6
|
-
class << self
|
7
|
-
|
8
|
-
def method_missing(*args)
|
9
|
-
name = method = args.first.to_s
|
10
|
-
if(name.end_with?('?'))
|
11
|
-
name = name.tr('?', '')
|
12
|
-
end
|
13
|
-
val = ENV[name.upcase] || ENV[name]
|
14
|
-
if(method.end_with?('?'))
|
15
|
-
!!val
|
16
|
-
else
|
17
|
-
val
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def _topic
|
22
|
-
'krakow-test'
|
23
|
-
end
|
24
|
-
|
25
|
-
def _scrub_topic!
|
26
|
-
_http_producer.delete_topic
|
27
|
-
end
|
28
|
-
|
29
|
-
def _http_producer
|
30
|
-
@http_producer ||= Krakow::Producer::Http.new(
|
31
|
-
:endpoint => 'http://127.0.0.1:4151',
|
32
|
-
:topic => _topic
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
def _producer(args={})
|
37
|
-
Krakow::Producer.new(
|
38
|
-
{
|
39
|
-
:host => Krakow::Test.nsq_producer_host || '127.0.0.1',
|
40
|
-
:port => Krakow::Test.nsq_producer_port || 4150,
|
41
|
-
:topic => 'krakow-test'
|
42
|
-
}.merge(args)
|
43
|
-
)
|
44
|
-
end
|
45
|
-
|
46
|
-
def _consumer(args={})
|
47
|
-
Krakow::Consumer.new(
|
48
|
-
{
|
49
|
-
:nslookupd => Krakow::Test.nsq_lookupd || 'http://127.0.0.1:4161',
|
50
|
-
:topic => 'krakow-test',
|
51
|
-
:channel => 'default',
|
52
|
-
:discovery_interval => 0.5,
|
53
|
-
:max_in_flight => 20
|
54
|
-
}.merge(args)
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
MiniTest::Spec.before do
|
63
|
-
Celluloid.boot
|
64
|
-
Krakow::Test._scrub_topic!
|
65
|
-
@consumer = Krakow::Test._consumer
|
66
|
-
@producer = Krakow::Test._producer
|
67
|
-
end
|
68
|
-
|
69
|
-
MiniTest::Spec.after do
|
70
|
-
@consumer.terminate if @consumer && @consumer.alive?
|
71
|
-
@producer.terminate if @producer && @producer.alive?
|
72
|
-
Krakow::Test._scrub_topic!
|
73
|
-
end
|
74
|
-
|
75
|
-
unless(Krakow::Test.debug?)
|
76
|
-
Celluloid.logger.level = 3
|
77
|
-
end
|
78
|
-
|
79
|
-
Dir.glob(File.join(File.dirname(__FILE__), 'specs', '*.rb')).each do |path|
|
80
|
-
require File.expand_path(path)
|
81
|
-
end
|
data/test/specs/consumer.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
describe Krakow do
|
2
|
-
|
3
|
-
describe Krakow::Consumer do
|
4
|
-
|
5
|
-
it 'should not have any connections' do
|
6
|
-
@consumer.connections.size.must_equal 0
|
7
|
-
end
|
8
|
-
it 'should have an empty queue' do
|
9
|
-
@consumer.queue.size.must_equal 0
|
10
|
-
end
|
11
|
-
|
12
|
-
describe 'with active producer' do
|
13
|
-
|
14
|
-
before do
|
15
|
-
@producer.write('msg1', 'msg2', 'msg3')
|
16
|
-
@inital_wait ||= sleep(0.8) # allow setup (topic creation, discovery, etc)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should have one connection' do
|
20
|
-
@consumer.connections.size.must_equal 1
|
21
|
-
end
|
22
|
-
it 'should have three messages queued' do
|
23
|
-
@consumer.queue.size.must_equal 3
|
24
|
-
end
|
25
|
-
it 'should properly confirm messages' do
|
26
|
-
3.times do
|
27
|
-
msg = @consumer.queue.pop
|
28
|
-
@consumer.confirm(msg).must_equal true
|
29
|
-
end
|
30
|
-
sleep(0.5) # pause to let everything settle
|
31
|
-
@consumer.queue.must_be :empty?
|
32
|
-
end
|
33
|
-
it 'should properly requeue messages' do
|
34
|
-
2.times do
|
35
|
-
@consumer.confirm(@consumer.queue.pop)
|
36
|
-
end
|
37
|
-
@consumer.queue.size.must_equal 1
|
38
|
-
original_msg = @consumer.queue.pop
|
39
|
-
@consumer.queue.must_be :empty?
|
40
|
-
@consumer.requeue(original_msg).must_equal true
|
41
|
-
sleep(0.2)
|
42
|
-
@consumer.queue.size.must_equal 1
|
43
|
-
req_msg = @consumer.queue.pop
|
44
|
-
req_msg.message_id.must_equal original_msg.message_id
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|