krakow 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/CONTRIBUTING.md +25 -0
  3. data/LICENSE +13 -0
  4. data/README.md +62 -9
  5. data/krakow.gemspec +3 -1
  6. data/lib/krakow/command/cls.rb +3 -4
  7. data/lib/krakow/command/fin.rb +13 -4
  8. data/lib/krakow/command/identify.rb +22 -9
  9. data/lib/krakow/command/mpub.rb +14 -4
  10. data/lib/krakow/command/nop.rb +3 -4
  11. data/lib/krakow/command/pub.rb +15 -5
  12. data/lib/krakow/command/rdy.rb +13 -4
  13. data/lib/krakow/command/req.rb +14 -4
  14. data/lib/krakow/command/sub.rb +14 -4
  15. data/lib/krakow/command/touch.rb +13 -4
  16. data/lib/krakow/command.rb +25 -3
  17. data/lib/krakow/connection.rb +286 -60
  18. data/lib/krakow/connection_features/deflate.rb +26 -1
  19. data/lib/krakow/connection_features/snappy_frames.rb +34 -3
  20. data/lib/krakow/connection_features/ssl.rb +43 -1
  21. data/lib/krakow/connection_features.rb +1 -0
  22. data/lib/krakow/consumer.rb +162 -49
  23. data/lib/krakow/discovery.rb +17 -6
  24. data/lib/krakow/distribution/default.rb +61 -33
  25. data/lib/krakow/distribution.rb +107 -57
  26. data/lib/krakow/exceptions.rb +14 -0
  27. data/lib/krakow/frame_type/error.rb +13 -7
  28. data/lib/krakow/frame_type/message.rb +47 -4
  29. data/lib/krakow/frame_type/response.rb +14 -4
  30. data/lib/krakow/frame_type.rb +20 -8
  31. data/lib/krakow/producer/http.rb +95 -6
  32. data/lib/krakow/producer.rb +60 -17
  33. data/lib/krakow/utils/lazy.rb +99 -40
  34. data/lib/krakow/utils/logging.rb +11 -0
  35. data/lib/krakow/utils.rb +3 -0
  36. data/lib/krakow/version.rb +3 -1
  37. data/lib/krakow.rb +1 -0
  38. metadata +11 -11
  39. data/Gemfile +0 -5
  40. data/Gemfile.lock +0 -34
  41. data/test/spec.rb +0 -81
  42. data/test/specs/consumer.rb +0 -49
  43. data/test/specs/http_producer.rb +0 -123
  44. data/test/specs/producer.rb +0 -20
@@ -1,4 +1,8 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
4
+ # Message distribution
5
+ # @abstract
2
6
  class Distribution
3
7
 
4
8
  autoload :Default, 'krakow/distribution/default'
@@ -7,110 +11,147 @@ module Krakow
7
11
 
8
12
  include Celluloid
9
13
  include Utils::Lazy
14
+ # @!parse include Krakow::Utils::Lazy::InstanceMethods
15
+ # @!parse extend Krakow::Utils::Lazy::ClassMethods
16
+
17
+ attr_accessor :ideal, :flight_record, :registry
18
+
19
+ # @!group Attributes
10
20
 
11
- attr_accessor :max_in_flight, :ideal, :flight_record, :registry
21
+ # @!macro [attach] attribute
22
+ # @!method $1
23
+ # @return [$2] the $1 $0
24
+ # @!method $1?
25
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
26
+ attribute :consumer, Krakow::Consumer, :required => true
27
+ attribute :watch_dog_interval, Numeric, :default => 1.0
28
+ attribute :backoff_interval, Numeric
29
+ attribute :max_in_flight, Integer, :default => 1
30
+
31
+ # @!endgroup
12
32
 
13
33
  def initialize(args={})
14
34
  super
15
- optional :watch_dog_interval, :backoff_interval
16
- arguments[:watch_dog_interval] ||= 5
17
- @max_in_flight = arguments[:max_in_flight] || 1
18
35
  @ideal = 0
19
36
  @flight_record = {}
20
37
  @registry = {}
21
38
  end
22
39
 
23
- # Reset flight distributions
40
+ # [Abstract] Reset flight distributions
24
41
  def redistribute!
25
42
  raise NotImplementedError.new 'Custom `#redistrubute!` method must be provided!'
26
43
  end
27
44
 
28
- # connection:: Connection
29
- # Determine RDY value for given connection
30
- def calculate_ready!(connection)
45
+ # [Abstract] Determine RDY value for given connection
46
+ # @param connection_identifier [String]
47
+ # @return [Integer]
48
+ def calculate_ready!(connection_identifier)
31
49
  raise NotImplementedError.new 'Custom `#calculate_ready!` method must be provided!'
32
50
  end
33
51
 
34
- # message:: FrameType::Message or message ID string
35
- # Remove message metadata from registry. Should be used after
36
- # confirmations or requeue.
52
+ # Remove message metadata from registry
53
+ #
54
+ # @param message [Krakow::FrameType::Message, String] message or ID
55
+ # @return [Krakow::Connection]
37
56
  def unregister_message(message)
38
57
  msg_id = message.respond_to?(:message_id) ? message.message_id : message.to_s
39
- connection = flight_record[msg_id]
40
- registry_info = registry_lookup(connection)
58
+ connection = connection_lookup(flight_record[msg_id])
59
+ registry_info = registry_lookup(connection.identifier)
41
60
  flight_record.delete(msg_id)
42
61
  registry_info[:in_flight] -= 1
43
- calculate_ready!(connection)
62
+ calculate_ready!(connection.identifier)
44
63
  connection
45
64
  end
46
65
 
47
- # connection:: Connection
48
66
  # Return the currently configured RDY value for given connnection
49
- def ready_for(connection)
50
- registry_lookup(connection)[:ready]
67
+ #
68
+ # @param connection_identifier [String]
69
+ # @return [Integer]
70
+ def ready_for(connection_identifier)
71
+ registry_lookup(connection_identifier)[:ready]
51
72
  end
52
73
 
53
- # connection:: Connection
74
+
54
75
  # Send RDY for given connection
76
+ #
77
+ # @param connection [Krakow::Connection]
78
+ # @return [Krakow::FrameType::Error,nil]
55
79
  def set_ready_for(connection, *_)
56
80
  connection.transmit(
57
81
  Command::Rdy.new(
58
- :count => ready_for(connection)
82
+ :count => ready_for(connection.identifier)
59
83
  )
60
84
  )
61
85
  end
62
86
 
63
87
  # Initial ready value used for new connections
88
+ #
89
+ # @return [Integer]
64
90
  def initial_ready
65
91
  ideal > 0 ? 1 : 0
66
92
  end
67
93
 
68
- # message:: FrameType::Message
69
- # connection:: Connection
70
94
  # Registers message into registry and configures for distribution
71
- def register_message(message, connection)
72
- registry_info = registry_lookup(connection)
95
+ #
96
+ # @param message [FrameType::Message]
97
+ # @param connection_identifier [String]
98
+ # @return [Integer]
99
+ def register_message(message, connection_identifier)
100
+ registry_info = registry_lookup(connection_identifier)
73
101
  registry_info[:in_flight] += 1
74
- flight_record[message.message_id] = connection_key(connection)
75
- calculate_ready!(connection)
102
+ flight_record[message.message_id] = connection_identifier
103
+ calculate_ready!(connection_identifier)
76
104
  end
77
105
 
78
- # connection:: Connection
79
106
  # Add connection to make available for RDY distribution
107
+ #
108
+ # @param connection [Krakow::Connection]
109
+ # @return [TrueClass]
80
110
  def add_connection(connection)
81
- registry[connection_key(connection)] = {
82
- :ready => initial_ready,
83
- :in_flight => 0,
84
- :failures => 0,
85
- :backoff_until => 0
86
- }
111
+ unless(registry[connection.identifier])
112
+ registry[connection.identifier] = {
113
+ :ready => initial_ready,
114
+ :in_flight => 0,
115
+ :failures => 0,
116
+ :backoff_until => 0
117
+ }
118
+ end
87
119
  true
88
120
  end
89
121
 
90
- # connection:: Connection
91
122
  # Remove connection from RDY distribution
92
- def remove_connection(connection)
123
+ #
124
+ # @param connection_identifier [String]
125
+ # @return [TrueClass]
126
+ def remove_connection(connection_identifier, *args)
93
127
  # remove connection from registry
94
- registry.delete(connection_key(connection))
128
+ registry.delete(connection_identifier)
95
129
  # remove any in flight messages
96
130
  flight_record.delete_if do |k,v|
97
- v == connection_key(connection)
131
+ if(k == connection_identifier)
132
+ warn "Removing in flight reference due to failed connection: #{k}"
133
+ true
134
+ end
98
135
  end
99
136
  true
100
137
  end
101
138
 
102
- # connection:: Connection
103
- # Return lookup key (actor reference)
104
- def connection_key(connection)
105
- connection
139
+ # Return connection associated with given registry key
140
+ #
141
+ # @param identifier [String] connection identifier
142
+ # @return [Krakow::Connection, nil]
143
+ def connection_lookup(identifier)
144
+ consumer.connection(identifier)
106
145
  end
107
146
 
108
- # msg_id:: Message ID string
109
- # Return source connection of given `msg_id`. If block is
110
- # provided, the connection instance will be yielded to the block
111
- # and the result returned.
147
+ # Return source connection for given message ID
148
+ #
149
+ # @param msg_id [String]
150
+ # @yield execute with connection
151
+ # @yieldparam connection [Krakow::Connection]
152
+ # @return [Krakow::Connection, Object]
112
153
  def in_flight_lookup(msg_id)
113
- connection = flight_record[msg_id]
154
+ connection = connection_lookup(flight_record[msg_id])
114
155
  unless(connection)
115
156
  abort Krakow::Error::LookupFailed.new("Failed to locate in flight message (ID: #{msg_id})")
116
157
  end
@@ -121,34 +162,42 @@ module Krakow
121
162
  end
122
163
  end
123
164
 
124
- # connection:: Connection
125
165
  # Return registry information for given connection
126
- def registry_lookup(connection)
127
- registry[connection_key(connection)] ||
128
- abort(Krakow::Error::LookupFailed.new("Failed to locate connection information in registry (#{connection})"))
166
+ # @param connection_identifier [String]
167
+ # @return [Hash] registry information
168
+ # @raise [Krakow::Error::LookupFailed]
169
+ def registry_lookup(connection_identifier)
170
+ registry[connection_identifier] ||
171
+ abort(Krakow::Error::LookupFailed.new("Failed to locate connection information in registry (#{connection_identifier})"))
129
172
  end
130
173
 
131
- # Return list of all connections in registry
174
+ # @return [Array<Krakow::Connection>] connections in registry
132
175
  def connections
133
- registry.keys
176
+ registry.keys.map do |identifier|
177
+ connection_lookup(identifier)
178
+ end.compact
134
179
  end
135
180
 
136
- # connection:: Connection
137
181
  # Log failure of processed message
138
- def failure(connection)
182
+ #
183
+ # @param connection_identifier [String]
184
+ # @return [TrueClass]
185
+ def failure(connection_identifier)
139
186
  if(backoff_interval)
140
- registry_info = registry_lookup(connection)
187
+ registry_info = registry_lookup(connection_identifier)
141
188
  registry_info[:failures] += 1
142
189
  registry_info[:backoff_until] = Time.now.to_i + (registry_info[:failures] * backoff_interval)
143
190
  end
144
191
  true
145
192
  end
146
193
 
147
- # connection:: Connection
148
194
  # Log success of processed message
149
- def success(connection)
195
+ #
196
+ # @param connection_identifier [String]
197
+ # @return [TrueClass]
198
+ def success(connection_identifier)
150
199
  if(backoff_interval)
151
- registry_info = registry_lookup(connection)
200
+ registry_info = registry_lookup(connection_identifier)
152
201
  if(registry_info[:failures] > 1)
153
202
  registry_info[:failures] -= 1
154
203
  registry_info[:backoff_until] = Time.now.to_i + (registry_info[:failures] * backoff_interval)
@@ -156,6 +205,7 @@ module Krakow
156
205
  registry_info[:failures] = 0
157
206
  end
158
207
  end
208
+ true
159
209
  end
160
210
 
161
211
  end
@@ -1,13 +1,27 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
4
+ # Base error type
2
5
  class Error < StandardError
3
6
 
7
+ # Failed to enable required feature on connection
4
8
  class ConnectionFeatureFailure < Error; end
9
+ # Failed to perform lookup (not found)
5
10
  class LookupFailed < Error; end
11
+ # Connection has failed
6
12
  class ConnectionFailure < Error; end
13
+ # Configuration is not in valid state
7
14
  class ConfigurationError < Error; end
15
+ # Connection is temporarily unavailable
16
+ class ConnectionUnavailable < Error; end
17
+ # Consumer was not set
18
+ class OriginNotFound < Error; end
8
19
 
20
+ # Invalid response
9
21
  class BadResponse < Error
22
+ # @return [Response] error response
10
23
  attr_accessor :result
24
+ # No response received
11
25
  class NoResponse < BadResponse
12
26
  end
13
27
  end
@@ -1,16 +1,22 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class FrameType
5
+ # Error from server
3
6
  class Error < FrameType
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :error
8
- end
8
+ # @!group Attributes
9
9
 
10
- def error
11
- arguments[:error]
12
- end
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :error, String, :required => true
16
+
17
+ # @!endgroup
13
18
 
19
+ # @return [String] content of error
14
20
  def content
15
21
  error
16
22
  end
@@ -1,16 +1,59 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class FrameType
5
+ # Message received from server
3
6
  class Message < FrameType
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :attempts, :timestamp, :message_id, :message
8
- end
8
+ # @return [Krakow::Consumer]
9
+ attr_accessor :origin
10
+
11
+ # @!group Attributes
12
+
13
+ # @!macro [attach] attribute
14
+ # @!method $1
15
+ # @return [$2] the $1 $0
16
+ # @!method $1?
17
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
18
+ attribute :attempts, Integer, :required => true
19
+ attribute :timestamp, Integer, :required => true
20
+ attribute :message_id, String, :required => true
21
+ attribute :message, String, :required => true
9
22
 
23
+ # @!endgroup
24
+
25
+ # Message content
26
+ #
27
+ # @return [String]
10
28
  def content
11
29
  message
12
30
  end
13
31
 
32
+ # @return [Krakow::Consumer]
33
+ def origin
34
+ unless(@origin)
35
+ error 'No origin has been specified for this message'
36
+ abort Krakow::Error::OriginNotFound.new('No origin specified for this message')
37
+ end
38
+ @origin
39
+ end
40
+
41
+ # Proxy to [Krakow::Consumer#confirm]
42
+ def confirm(*args)
43
+ origin.confirm(*[self, *args].compact)
44
+ end
45
+ alias_method :finish, :confirm
46
+
47
+ # Proxy to [Krakow::Consumer#requeue]
48
+ def requeue(*args)
49
+ origin.requeue(*[self, *args].compact)
50
+ end
51
+
52
+ # Proxy to [Krakow::Consumer#touch]
53
+ def touch(*args)
54
+ origin.touch(*[self, *args].compact)
55
+ end
56
+
14
57
  end
15
58
  end
16
59
  end
@@ -1,12 +1,22 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class FrameType
5
+ # Response from server
3
6
  class Response < FrameType
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :response
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :response, String, :required => true
16
+
17
+ # @!endgroup
9
18
 
19
+ # @return [String] content of response
10
20
  def content
11
21
  response
12
22
  end
@@ -1,4 +1,8 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
4
+ # Received message
5
+ # @abstract
2
6
  class FrameType
3
7
 
4
8
  autoload :Error, 'krakow/frame_type/error'
@@ -6,25 +10,34 @@ module Krakow
6
10
  autoload :Response, 'krakow/frame_type/response'
7
11
 
8
12
  include Utils::Lazy
13
+ # @!parse include Krakow::Utils::Lazy::InstanceMethods
14
+ # @!parse extend Krakow::Utils::Lazy::ClassMethods
9
15
 
16
+ # Registered frame types
10
17
  FRAME_TYPE_MAP = [
11
18
  FrameType::Response,
12
19
  FrameType::Error,
13
20
  FrameType::Message
14
21
  ]
22
+ # Size bytes
15
23
  SIZE_BYTES = 4
16
24
 
17
25
  class << self
18
26
 
19
- # bytes:: 8 bytes
20
- # Return information about incoming frame
27
+ # Information about incoming frame
28
+ # @param bytes [String]
29
+ # @return [Hash]
21
30
  def decode(bytes)
22
31
  size, type = bytes.unpack('l>l>')
23
32
  {:size => size - SIZE_BYTES, :type => type}
24
33
  end
25
34
 
26
- # args:: arguments (:type, :data, :size)
27
35
  # Build proper FrameType instance based on args
36
+ # @param args [Hash]
37
+ # @option args [FrameType] :type class of frame
38
+ # @option args [String] :data
39
+ # @option args [Integer] :size
40
+ # @return [FrameType]
28
41
  def build(args={})
29
42
  klass = FRAME_TYPE_MAP[args[:type].to_i]
30
43
  if(klass == FrameType::Response)
@@ -42,12 +55,11 @@ module Krakow
42
55
  end
43
56
  end
44
57
 
45
- def initialize(args={})
46
- super
47
- end
48
-
58
+ # Content of message
59
+ #
60
+ # @return [String]
49
61
  def content
50
- raise NoMethodError.new 'Content method not properly defined!'
62
+ raise NotImplementedError.new 'Content method not properly defined!'
51
63
  end
52
64
 
53
65
  end
@@ -2,35 +2,85 @@ require 'http'
2
2
  require 'uri'
3
3
  require 'ostruct'
4
4
 
5
+ require 'cgi'
6
+ # NOTE: Prevents weird "first" run behavior
7
+ begin
8
+ require 'json'
9
+ rescue LoadError
10
+ # ignore (maybe log?)
11
+ end
12
+
13
+ require 'krakow'
14
+
5
15
  module Krakow
6
16
  class Producer
17
+
18
+ # HTTP based producer
7
19
  class Http
8
20
 
21
+ include Utils::Lazy
22
+ # @!parse include Krakow::Utils::Lazy::InstanceMethods
23
+ # @!parse extend Krakow::Utils::Lazy::ClassMethods
24
+
25
+ # Wrapper for HTTP response hash
9
26
  class Response < OpenStruct
10
27
  end
11
28
 
12
- include Utils::Lazy
13
-
14
29
  attr_reader :uri
15
30
 
31
+ # @!group Attributes
32
+
33
+ # @!macro [attach] attribute
34
+ # @!method $1
35
+ # @return [$2] the $1 $0
36
+ # @!method $1?
37
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
38
+ attribute :endpoint, String, :required => true
39
+ attribute :topic, String, :required => true
40
+ attribute :config, Hash, :default => ->{ Hash.new }
41
+ attribute :ssl_context, Hash
42
+
43
+ # @!endgroup
44
+
16
45
  def initialize(args={})
17
46
  super
18
- required! :endpoint, :topic
47
+ build_ssl_context if ssl_context
19
48
  @uri = URI.parse(endpoint)
20
49
  end
21
50
 
51
+ # Create a new SSL context
52
+ #
53
+ # @return [OpenSSL::SSL::SSLContext]
54
+ def build_ssl_context
55
+ require 'openssl'
56
+ context = OpenSSL::SSL::SSLContext.new
57
+ context.cert = OpenSSL::X509::Certificate.new(File.open(ssl_context[:certificate]))
58
+ context.key = OpenSSL::PKey::RSA.new(File.open(ssl_context[:key]))
59
+ config[:ssl_context] = context
60
+ end
61
+
62
+ # Send a message via HTTP
63
+ #
64
+ # @param method [String, Symbol] HTTP method to use (:get, :put, etc)
65
+ # @param path [String] URI path
66
+ # @param args [Hash] payload hash
67
+ # @return [Response]
22
68
  def send_message(method, path, args={})
23
69
  build = uri.dup
24
70
  build.path = "/#{path}"
25
- response = HTTP.send(method, build.to_s, args)
71
+ response = HTTP.send(method, build.to_s, args.merge(config))
26
72
  begin
27
- response = MultiJson.load(response.response.body)
73
+ response = MultiJson.load(response.body.to_s)
28
74
  rescue MultiJson::LoadError
29
- response = {'status_code' => response == 'OK' ? 200 : nil, 'status_txt' => response, 'data' => nil}
75
+ response = {'status_code' => response.code, 'status_txt' => response.body.to_s, 'data' => nil}
30
76
  end
31
77
  Response.new(response)
32
78
  end
33
79
 
80
+ # Send messages
81
+ #
82
+ # @param payload [String] message
83
+ # @return [Response]
34
84
  def write(*payload)
35
85
  if(payload.size == 1)
36
86
  payload = payload.first
@@ -46,18 +96,28 @@ module Krakow
46
96
  end
47
97
  end
48
98
 
99
+ # Create the topic
100
+ #
101
+ # @return [Response]
49
102
  def create_topic
50
103
  send_message(:post, :create_topic,
51
104
  :params => {:topic => topic}
52
105
  )
53
106
  end
54
107
 
108
+ # Delete the topic
109
+ #
110
+ # @return [Response]
55
111
  def delete_topic
56
112
  send_message(:post, :delete_topic,
57
113
  :params => {:topic => topic}
58
114
  )
59
115
  end
60
116
 
117
+ # Create channel on topic
118
+ #
119
+ # @param chan [String] channel name
120
+ # @return [Response]
61
121
  def create_channel(chan)
62
122
  send_message(:post, :create_channel,
63
123
  :params => {
@@ -67,6 +127,10 @@ module Krakow
67
127
  )
68
128
  end
69
129
 
130
+ # Delete channel on topic
131
+ #
132
+ # @param chan [String] channel name
133
+ # @return [Response]
70
134
  def delete_channel(chan)
71
135
  send_message(:post, :delete_channel,
72
136
  :params => {
@@ -76,12 +140,19 @@ module Krakow
76
140
  )
77
141
  end
78
142
 
143
+ # Remove all messages from topic
144
+ #
145
+ # @return [Response]
79
146
  def empty_topic
80
147
  send_message(:post, :empty_topic,
81
148
  :params => {:topic => topic}
82
149
  )
83
150
  end
84
151
 
152
+ # Remove all messages from given channel on topic
153
+ #
154
+ # @param chan [String] channel name
155
+ # @return [Response]
85
156
  def empty_channel(chan)
86
157
  send_message(:post, :empty_channel,
87
158
  :params => {
@@ -91,6 +162,10 @@ module Krakow
91
162
  )
92
163
  end
93
164
 
165
+ # Pause messages on given channel
166
+ #
167
+ # @param chan [String] channel name
168
+ # @return [Response]
94
169
  def pause_channel(chan)
95
170
  send_message(:post, :pause_channel,
96
171
  :params => {
@@ -100,6 +175,10 @@ module Krakow
100
175
  )
101
176
  end
102
177
 
178
+ # Resume messages on a given channel
179
+ #
180
+ # @param chan [String] channel name
181
+ # @return [Response]
103
182
  def unpause_channel(chan)
104
183
  send_message(:post, :unpause_channel,
105
184
  :params => {
@@ -109,6 +188,10 @@ module Krakow
109
188
  )
110
189
  end
111
190
 
191
+ # Server stats
192
+ #
193
+ # @param format [String] format of data
194
+ # @return [Response]
112
195
  def stats(format='json')
113
196
  send_message(:get, :stats,
114
197
  :params => {
@@ -117,10 +200,16 @@ module Krakow
117
200
  )
118
201
  end
119
202
 
203
+ # Ping the server
204
+ #
205
+ # @return [Response]
120
206
  def ping
121
207
  send_message(:get, :ping)
122
208
  end
123
209
 
210
+ # Server information
211
+ #
212
+ # @return [Response]
124
213
  def info
125
214
  send_message(:get, :info)
126
215
  end