ably 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +9 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +8 -1
  6. data/Rakefile +10 -0
  7. data/ably.gemspec +18 -18
  8. data/lib/ably.rb +6 -5
  9. data/lib/ably/auth.rb +11 -14
  10. data/lib/ably/exceptions.rb +18 -15
  11. data/lib/ably/logger.rb +102 -0
  12. data/lib/ably/models/error_info.rb +1 -1
  13. data/lib/ably/models/message.rb +19 -5
  14. data/lib/ably/models/message_encoders/base.rb +107 -0
  15. data/lib/ably/models/message_encoders/base64.rb +39 -0
  16. data/lib/ably/models/message_encoders/cipher.rb +80 -0
  17. data/lib/ably/models/message_encoders/json.rb +33 -0
  18. data/lib/ably/models/message_encoders/utf8.rb +33 -0
  19. data/lib/ably/models/paginated_resource.rb +23 -6
  20. data/lib/ably/models/presence_message.rb +19 -7
  21. data/lib/ably/models/protocol_message.rb +5 -4
  22. data/lib/ably/models/token.rb +2 -2
  23. data/lib/ably/modules/channels_collection.rb +0 -3
  24. data/lib/ably/modules/conversions.rb +3 -3
  25. data/lib/ably/modules/encodeable.rb +68 -0
  26. data/lib/ably/modules/event_emitter.rb +10 -4
  27. data/lib/ably/modules/event_machine_helpers.rb +6 -4
  28. data/lib/ably/modules/http_helpers.rb +7 -2
  29. data/lib/ably/modules/model_common.rb +2 -0
  30. data/lib/ably/modules/state_emitter.rb +10 -1
  31. data/lib/ably/realtime.rb +19 -12
  32. data/lib/ably/realtime/channel.rb +26 -13
  33. data/lib/ably/realtime/client.rb +31 -7
  34. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -3
  35. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +13 -4
  36. data/lib/ably/realtime/connection.rb +152 -46
  37. data/lib/ably/realtime/connection/connection_manager.rb +168 -0
  38. data/lib/ably/realtime/connection/connection_state_machine.rb +56 -33
  39. data/lib/ably/realtime/connection/websocket_transport.rb +56 -29
  40. data/lib/ably/{models → realtime/models}/nil_channel.rb +1 -1
  41. data/lib/ably/realtime/presence.rb +38 -13
  42. data/lib/ably/rest.rb +7 -5
  43. data/lib/ably/rest/channel.rb +24 -3
  44. data/lib/ably/rest/client.rb +56 -17
  45. data/lib/ably/rest/middleware/encoder.rb +49 -0
  46. data/lib/ably/rest/middleware/exceptions.rb +3 -2
  47. data/lib/ably/rest/middleware/logger.rb +37 -0
  48. data/lib/ably/rest/presence.rb +10 -2
  49. data/lib/ably/util/crypto.rb +57 -29
  50. data/lib/ably/util/pub_sub.rb +11 -0
  51. data/lib/ably/version.rb +1 -1
  52. data/spec/acceptance/realtime/channel_spec.rb +65 -7
  53. data/spec/acceptance/realtime/connection_spec.rb +123 -27
  54. data/spec/acceptance/realtime/message_spec.rb +319 -34
  55. data/spec/acceptance/realtime/presence_history_spec.rb +58 -0
  56. data/spec/acceptance/realtime/presence_spec.rb +160 -18
  57. data/spec/acceptance/rest/auth_spec.rb +93 -49
  58. data/spec/acceptance/rest/base_spec.rb +10 -10
  59. data/spec/acceptance/rest/channel_spec.rb +35 -19
  60. data/spec/acceptance/rest/channels_spec.rb +8 -8
  61. data/spec/acceptance/rest/message_spec.rb +224 -0
  62. data/spec/acceptance/rest/presence_spec.rb +159 -23
  63. data/spec/acceptance/rest/stats_spec.rb +5 -5
  64. data/spec/acceptance/rest/time_spec.rb +4 -4
  65. data/spec/integration/rest/auth.rb +1 -1
  66. data/spec/resources/crypto-data-128.json +56 -0
  67. data/spec/resources/crypto-data-256.json +56 -0
  68. data/spec/rspec_config.rb +39 -0
  69. data/spec/spec_helper.rb +4 -42
  70. data/spec/support/api_helper.rb +1 -1
  71. data/spec/support/event_machine_helper.rb +0 -5
  72. data/spec/support/protocol_msgbus_helper.rb +3 -3
  73. data/spec/support/test_app.rb +3 -3
  74. data/spec/unit/logger_spec.rb +135 -0
  75. data/spec/unit/models/message_encoders/base64_spec.rb +181 -0
  76. data/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
  77. data/spec/unit/models/message_encoders/json_spec.rb +135 -0
  78. data/spec/unit/models/message_encoders/utf8_spec.rb +100 -0
  79. data/spec/unit/models/message_spec.rb +16 -1
  80. data/spec/unit/models/paginated_resource_spec.rb +46 -0
  81. data/spec/unit/models/presence_message_spec.rb +18 -5
  82. data/spec/unit/models/token_spec.rb +1 -1
  83. data/spec/unit/modules/event_emitter_spec.rb +24 -10
  84. data/spec/unit/realtime/channel_spec.rb +3 -3
  85. data/spec/unit/realtime/channels_spec.rb +1 -1
  86. data/spec/unit/realtime/client_spec.rb +44 -2
  87. data/spec/unit/realtime/connection_spec.rb +2 -2
  88. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +4 -4
  89. data/spec/unit/realtime/presence_spec.rb +1 -1
  90. data/spec/unit/realtime/realtime_spec.rb +3 -3
  91. data/spec/unit/realtime/websocket_transport_spec.rb +24 -0
  92. data/spec/unit/rest/channels_spec.rb +1 -1
  93. data/spec/unit/rest/client_spec.rb +45 -10
  94. data/spec/unit/util/crypto_spec.rb +82 -0
  95. data/spec/unit/{modules → util}/pub_sub_spec.rb +13 -1
  96. metadata +43 -12
  97. data/spec/acceptance/crypto.rb +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc321ac8e501652f72e08617651df2f90118a759
4
- data.tar.gz: 9f9b3f5abedeba6a4ac79841a7e1d5d09bf2bc4c
3
+ metadata.gz: d814eac0ae04dc623743bbe5973e80a58662056d
4
+ data.tar.gz: 95426e76baeaee6fbc297031b83cc128e5bed688
5
5
  SHA512:
6
- metadata.gz: cdd44c54e0015062c3ecd62bb39f2e03e2a5fe39118939bd7bd18d1bbddd3d774cd511634dac8273890d57b2fd2f2a2191df91016090a54e4e9ca8ffed3823c6
7
- data.tar.gz: 4ef9786cc786a7d4b6b824bf5bc137b17d92180c0b3d201ba48cb3d6aa9cf85a56fb8b3639170a4d0ce4f41179e1b6780569e8727df761f5a9f384df20f4535c
6
+ metadata.gz: b660702de87dec06d7f982ea110d59310ba1f9a130be320bd2ec7a40f777b2e5452cfa4f97f6a08a0cf751646326f53196e7cf3a39c023ce55795583309ad4db
7
+ data.tar.gz: 8088bb09de07b180385a4e817827ef2d867ccb2c6645f55c68888bfaf932df7f2a7c2ec7392b3aa1471a5045beda59d0f93fa5775171ca99b1a1083b3fca2037
data/.gitignore CHANGED
@@ -2,3 +2,5 @@ Gemfile.lock
2
2
  .yardoc
3
3
  doc/*
4
4
  pkg
5
+ .bundle
6
+ bin
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.1
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-2.4.1
9
+
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Lewis Marshall
1
+ Copyright (c) 2014 Ably
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # [Ably](https://ably.io)
2
2
 
3
+ [![Build Status](https://travis-ci.org/ably/ably-ruby.png)](https://travis-ci.org/ably/ably-ruby)
4
+ [![Gem Version](https://badge.fury.io/rb/ably.svg)](http://badge.fury.io/rb/ably)
5
+
3
6
  A Ruby client library for [ably.io](https://ably.io), the real-time messaging service.
4
7
 
5
8
  ## Installation
@@ -60,7 +63,7 @@ channel.publish("greeting", "Hello World!")
60
63
  ```ruby
61
64
  client = Ably::Realtime.new(api_key: "xxxxx")
62
65
  channel = client.channel("test")
63
- channel.presence.enter(client_data: 'john.doe') do |presence|
66
+ channel.presence.enter(data: 'john.doe') do |presence|
64
67
  presence.get #=> [Array of members present]
65
68
  end
66
69
  ```
@@ -106,6 +109,10 @@ client = Ably::Rest.new(api_key: "xxxxx")
106
109
  client.time #=> 2013-12-12 14:23:34 +0000
107
110
  ```
108
111
 
112
+ ## Dependencies
113
+
114
+ If you only need to use the REST features of this library and do not want EventMachine as a dependency, then you should use the [Ably Ruby REST gem](https://rubygems.org/gems/ably-rest).
115
+
109
116
  ## Contributing
110
117
 
111
118
  1. Fork it
data/Rakefile CHANGED
@@ -2,3 +2,13 @@ require "bundler/gem_tasks"
2
2
 
3
3
  require "yard"
4
4
  YARD::Rake::YardocTask.new
5
+
6
+ begin
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task :default => :spec
12
+ rescue LoadError
13
+ # no rspec available
14
+ end
data/ably.gemspec CHANGED
@@ -4,31 +4,31 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'ably/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "ably"
7
+ spec.name = 'ably'
8
8
  spec.version = Ably::VERSION
9
- spec.authors = ["Lewis Marshall", "Matthew O'Riordan"]
10
- spec.email = ["lewis@lmars.net", "matt@ably.io"]
9
+ spec.authors = ['Lewis Marshall', "Matthew O'Riordan"]
10
+ spec.email = ['lewis@lmars.net', 'matt@ably.io']
11
11
  spec.description = %q{A Ruby client library for ably.io, the real-time messaging service}
12
12
  spec.summary = %q{A Ruby client library for ably.io, the real-time messaging service}
13
- spec.homepage = "http://github.com/ably/ably-ruby"
14
- spec.license = "MIT"
13
+ spec.homepage = 'http://github.com/ably/ably-ruby'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_runtime_dependency "eventmachine", "~> 1.0"
22
- spec.add_runtime_dependency "statesman", "~> 1.0.0.beta2"
23
- spec.add_runtime_dependency "faraday", "~> 0.9"
24
- spec.add_runtime_dependency "json"
25
- spec.add_runtime_dependency "websocket-driver", "~> 0.3"
26
- spec.add_runtime_dependency "msgpack", "~> 0.5"
21
+ spec.add_runtime_dependency 'eventmachine', '~> 1.0'
22
+ spec.add_runtime_dependency 'statesman', '~> 1.0.0'
23
+ spec.add_runtime_dependency 'faraday', '~> 0.9'
24
+ spec.add_runtime_dependency 'json'
25
+ spec.add_runtime_dependency 'websocket-driver', '~> 0.3'
26
+ spec.add_runtime_dependency 'msgpack-ably', '~> 0.5.10'
27
27
 
28
- spec.add_development_dependency "bundler", "~> 1.3"
29
- spec.add_development_dependency "rake"
30
- spec.add_development_dependency "redcarpet"
31
- spec.add_development_dependency "rspec", "~> 3.0"
32
- spec.add_development_dependency "yard"
33
- spec.add_development_dependency "webmock"
28
+ spec.add_development_dependency 'bundler', '~> 1.3'
29
+ spec.add_development_dependency 'rake'
30
+ spec.add_development_dependency 'redcarpet'
31
+ spec.add_development_dependency 'rspec', '~> 3.0'
32
+ spec.add_development_dependency 'yard'
33
+ spec.add_development_dependency 'webmock'
34
34
  end
data/lib/ably.rb CHANGED
@@ -4,11 +4,12 @@
4
4
  end
5
5
  end
6
6
 
7
- require "ably/auth"
8
- require "ably/exceptions"
9
- require "ably/realtime"
10
- require "ably/rest"
11
- require "ably/version"
7
+ require 'ably/auth'
8
+ require 'ably/exceptions'
9
+ require 'ably/logger'
10
+ require 'ably/realtime'
11
+ require 'ably/rest'
12
+ require 'ably/version'
12
13
 
13
14
  # Ably is the base namespace for the Ably {Ably::Realtime Realtime} & {Ably::Rest Rest} client libraries.
14
15
  #
data/lib/ably/auth.rb CHANGED
@@ -2,7 +2,7 @@ require 'json'
2
2
  require 'faraday'
3
3
  require 'securerandom'
4
4
 
5
- require "ably/rest/middleware/external_exceptions"
5
+ require 'ably/rest/middleware/external_exceptions'
6
6
 
7
7
  module Ably
8
8
  # Auth is responsible for authentication with {https://ably.io Ably} using basic or token authentication
@@ -44,26 +44,26 @@ module Ably
44
44
  @auth_callback = auth_block if block_given?
45
45
 
46
46
  unless auth_options.kind_of?(Hash)
47
- raise ArgumentError, "Expected auth_options to be a Hash"
47
+ raise ArgumentError, 'Expected auth_options to be a Hash'
48
48
  end
49
49
 
50
50
  if auth_options[:api_key] && (auth_options[:key_secret] || auth_options[:key_id])
51
- raise ArgumentError, "api_key and key_id or key_secret are mutually exclusive. Provider either an api_key or key_id & key_secret"
51
+ raise ArgumentError, 'api_key and key_id or key_secret are mutually exclusive. Provider either an api_key or key_id & key_secret'
52
52
  end
53
53
 
54
54
  if auth_options[:api_key]
55
55
  api_key_parts = auth_options[:api_key].to_s.match(/(?<id>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
56
- raise ArgumentError, "api_key is invalid" unless api_key_parts
56
+ raise ArgumentError, 'api_key is invalid' unless api_key_parts
57
57
  auth_options[:key_id] = api_key_parts[:id]
58
58
  auth_options[:key_secret] = api_key_parts[:secret]
59
59
  end
60
60
 
61
61
  if using_basic_auth? && !api_key_present?
62
- raise ArgumentError, "api_key is missing. Either an API key, token, or token auth method must be provided"
62
+ raise ArgumentError, 'api_key is missing. Either an API key, token, or token auth method must be provided'
63
63
  end
64
64
 
65
65
  if has_client_id? && !api_key_present?
66
- raise ArgumentError, "client_id cannot be provided without a complete API key. Key ID & Secret is needed to authenticate with Ably and obtain a token"
66
+ raise ArgumentError, 'client_id cannot be provided without a complete API key. Key ID & Secret is needed to authenticate with Ably and obtain a token'
67
67
  end
68
68
 
69
69
  @options.freeze
@@ -162,7 +162,7 @@ module Ably
162
162
 
163
163
  token_request = IdiomaticRubyWrapper(token_request)
164
164
 
165
- response = client.post("/keys/#{token_request.fetch(:id)}/requestToken", token_request, send_auth_header: false)
165
+ response = client.post("/keys/#{token_request.fetch(:id)}/requestToken", token_request.hash, send_auth_header: false)
166
166
  body = IdiomaticRubyWrapper(response.body)
167
167
 
168
168
  Ably::Models::Token.new(body.fetch(:access_token))
@@ -199,7 +199,7 @@ module Ably
199
199
  request_key_id = token_options.delete(:key_id) || key_id
200
200
  request_key_secret = token_options.delete(:key_secret) || key_secret
201
201
 
202
- raise Ably::Exceptions::TokenRequestError, "Key ID and Key Secret are required to generate a new token request" unless request_key_id && request_key_secret
202
+ raise Ably::Exceptions::TokenRequestError, 'Key ID and Key Secret are required to generate a new token request' unless request_key_id && request_key_secret
203
203
 
204
204
  timestamp = if token_options[:query_time]
205
205
  client.time
@@ -294,7 +294,7 @@ module Ably
294
294
 
295
295
  # Basic Auth HTTP Authorization header value
296
296
  def basic_auth_header
297
- raise Ably::Exceptions::InsecureRequestError, "Cannot use Basic Auth over non-TLS connections" unless client.use_tls?
297
+ raise Ably::Exceptions::InsecureRequestError, 'Cannot use Basic Auth over non-TLS connections' unless client.use_tls?
298
298
  "Basic #{encode64("#{api_key}")}"
299
299
  end
300
300
 
@@ -313,7 +313,7 @@ module Ably
313
313
 
314
314
  # Basic Auth params to authenticate the Realtime connection
315
315
  def basic_auth_params
316
- raise Ably::Exceptions::InsecureRequestError, "Cannot use Basic Auth over non-TLS connections" unless client.use_tls?
316
+ raise Ably::Exceptions::InsecureRequestError, 'Cannot use Basic Auth over non-TLS connections' unless client.use_tls?
317
317
  # TODO: Change to key_secret when API is updated
318
318
  {
319
319
  key_id: key_id,
@@ -395,10 +395,7 @@ module Ably
395
395
  # Raise exceptions if response code is invalid
396
396
  builder.use Ably::Rest::Middleware::ExternalExceptions
397
397
 
398
- setup_incoming_middleware builder
399
-
400
- # Log HTTP requests if log level is DEBUG option set
401
- builder.response :logger if client.log_level == Logger::DEBUG
398
+ setup_incoming_middleware builder, client.logger
402
399
 
403
400
  # Set Faraday's HTTP adapter
404
401
  builder.adapter Faraday.default_adapter
@@ -9,7 +9,7 @@ module Ably
9
9
  # @return [String] HTTP status code of error
10
10
  # @!attribute [r] code
11
11
  # @return [String] Ably specific error code
12
- class Base < StandardError
12
+ class BaseAblyException < StandardError
13
13
  attr_reader :status, :code
14
14
  def initialize(message, status = nil, code = nil)
15
15
  super message
@@ -19,7 +19,23 @@ module Ably
19
19
  end
20
20
 
21
21
  # An invalid request was received by Ably
22
- class InvalidRequest < Base; end
22
+ class InvalidRequest < BaseAblyException; end
23
+
24
+ # The token is invalid
25
+ class InvalidToken < BaseAblyException; end
26
+
27
+ # Ably Protocol message received that is invalid
28
+ class ProtocolError < BaseAblyException; end
29
+
30
+ # Encryption or Decryption failure
31
+ class CipherError < BaseAblyException; end
32
+
33
+ # Encoding or decoding failure
34
+ class EncoderError < BaseAblyException; end
35
+
36
+ # A generic Ably exception taht supports a status & code.
37
+ # See https://github.com/ably/ably-common/blob/master/protocol/errors.json for a list of Ably errors
38
+ class Standard < BaseAblyException; end
23
39
 
24
40
  # The HTTP request has returned a 500 error
25
41
  class ServerError < StandardError; end
@@ -35,18 +51,5 @@ module Ably
35
51
 
36
52
  # The token request could not be created
37
53
  class TokenRequestError < StandardError; end
38
-
39
- # The token is invalid
40
- class InvalidToken < Base; end
41
-
42
- # Encryption or decryption related failures
43
- class EncryptionError < StandardError; end
44
-
45
- # Ably Protocol message received that is invalid
46
- class ProtocolError < Base; end
47
-
48
- # A generic Ably exception taht supports a status & code.
49
- # See https://github.com/ably/ably-common/blob/master/protocol/errors.json for a list of Ably errors
50
- class Standard < Base; end
51
54
  end
52
55
  end
@@ -0,0 +1,102 @@
1
+ module Ably
2
+ # Logger unifies logging for #debug, #info, #warn, #error, and #fatal messages.
3
+ # A new Ably client uses this Logger and sets the appropriate log level.
4
+ # A custom Logger can be configured when instantiating the client, refer to the {Ably::Rest::Client} and {Ably::Realtime::Client} documentation
5
+ #
6
+ class Logger
7
+ extend Forwardable
8
+
9
+ # @param client [Ably::Rest::Client,Ably::Realtime::Client] Rest or Realtime Ably client
10
+ # @param log_level [Integer] {http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html Ruby Logger} log level
11
+ # @param custom_logger [nil,Object] A custom logger can optionally be used instead of the,
12
+ # however it must provide a {http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html Ruby Logger} compatible interface.
13
+ #
14
+ def initialize(client, log_level, custom_logger = nil)
15
+ @client = client
16
+ @custom_logger = custom_logger
17
+ @logger = custom_logger || default_logger
18
+ @log_level = log_level
19
+
20
+ ensure_logger_interface_is_valid
21
+
22
+ @logger.level = log_level
23
+ end
24
+
25
+ # The logger used by this class, defaults to {http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html Ruby Logger}
26
+ # @return {Object,Logger}
27
+ attr_reader :logger
28
+
29
+ # If a custom logger is being used with this Logger, this property is not nil
30
+ # @return {nil,Object}
31
+ attr_reader :custom_logger
32
+
33
+ # The log level ranging from DEBUG to FATAL, refer to http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html
34
+ # @return {Integer}
35
+ attr_reader :log_level
36
+
37
+ def_delegators :logger, :fatal, :error, :warn, :info, :debug
38
+
39
+ private
40
+ attr_reader :client
41
+
42
+ def color(color_value, string)
43
+ "\033[#{color_value}m#{string}\033[0m"
44
+ end
45
+
46
+ def red(string)
47
+ color(31, string)
48
+ end
49
+
50
+ def magenta(string)
51
+ color(35, string)
52
+ end
53
+
54
+ def cyan(string)
55
+ color(36, string)
56
+ end
57
+
58
+ def connection_id
59
+ if realtime?
60
+ if client.connection.id
61
+ "[#{cyan(client.connection.id)}] "
62
+ else
63
+ "[ #{cyan('--')} ] "
64
+ end
65
+ end
66
+ end
67
+
68
+ def realtime?
69
+ client.respond_to?(:connection)
70
+ end
71
+
72
+ def default_logger
73
+ ::Logger.new(STDOUT).tap do |logger|
74
+ logger.formatter = proc do |severity, datetime, progname, msg|
75
+ severity = ::Logger::SEV_LABEL.index(severity) if severity.kind_of?(String)
76
+
77
+ formatted_date = if severity == ::Logger::DEBUG
78
+ datetime.strftime("%H:%M:%S.%L")
79
+ else
80
+ datetime.strftime("%Y-%m-%d %H:%M:%S.%L")
81
+ end
82
+
83
+ severity_label = if severity <= ::Logger::INFO
84
+ magenta(::Logger::SEV_LABEL[severity])
85
+ else
86
+ red(::Logger::SEV_LABEL[severity])
87
+ end
88
+
89
+ "#{formatted_date} #{severity_label} #{connection_id}#{msg}\n"
90
+ end
91
+ end
92
+ end
93
+
94
+ def ensure_logger_interface_is_valid
95
+ %w(fatal error warn info debug level level=).each do |method|
96
+ unless logger.respond_to?(method)
97
+ raise ArgumentError, "The custom Logger's interface does not provide the method '#{method}'"
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -31,7 +31,7 @@ module Ably::Models
31
31
  end
32
32
 
33
33
  def to_s
34
- "Error: #{message} (code: #{code}, status_code: #{status_code})"
34
+ "Error: #{message} (code: #{code}, http status: #{status})"
35
35
  end
36
36
  end
37
37
  end
@@ -25,6 +25,9 @@ module Ably::Models
25
25
  # @return [String] The id of the publisher of this message
26
26
  # @!attribute [r] data
27
27
  # @return [Object] The message payload. See the documentation for supported datatypes.
28
+ # @!attribute [r] encoding
29
+ # @return [Object] The encoding for the message data. Encoding and decoding of messages is handled automatically by the client library.
30
+ # Therefore, the `encoding` attribute should always be nil unless an Ably library decoding error has occurred.
28
31
  # @!attribute [r] timestamp
29
32
  # @return [Time] Timestamp when the message was received by the Ably the real-time service
30
33
  # @!attribute [r] id
@@ -34,6 +37,7 @@ module Ably::Models
34
37
  #
35
38
  class Message
36
39
  include Ably::Modules::ModelCommon
40
+ include Ably::Modules::Encodeable
37
41
  include EventMachine::Deferrable
38
42
 
39
43
  # {Message} initializer
@@ -44,10 +48,11 @@ module Ably::Models
44
48
  def initialize(hash_object, protocol_message = nil)
45
49
  @protocol_message = protocol_message
46
50
  @raw_hash_object = hash_object
47
- @hash_object = IdiomaticRubyWrapper(hash_object.clone.freeze, stop_at: [:data])
51
+
52
+ set_hash_object hash_object
48
53
  end
49
54
 
50
- %w( name client_id ).each do |attribute|
55
+ %w( name client_id encoding ).each do |attribute|
51
56
  define_method attribute do
52
57
  hash[attribute.to_sym]
53
58
  end
@@ -74,8 +79,11 @@ module Ably::Models
74
79
  end
75
80
 
76
81
  def as_json(*args)
77
- raise RuntimeError, ":name is missing, cannot generate a valid Hash for Message" unless name
78
- super
82
+ raise RuntimeError, ':name is missing, cannot generate a valid Hash for Message' unless name
83
+
84
+ hash.dup.tap do |message|
85
+ decode_binary_data_before_to_json message
86
+ end.as_json
79
87
  end
80
88
 
81
89
  # Assign this message to a ProtocolMessage before delivery to the Ably system
@@ -95,11 +103,13 @@ module Ably::Models
95
103
  # @return [Ably::Models::ProtocolMessage]
96
104
  # @api private
97
105
  def protocol_message
98
- raise RuntimeError, "Message is not yet published with a ProtocolMessage. ProtocolMessage is nil" if @protocol_message.nil?
106
+ raise RuntimeError, 'Message is not yet published with a ProtocolMessage. ProtocolMessage is nil' if @protocol_message.nil?
99
107
  @protocol_message
100
108
  end
101
109
 
102
110
  private
111
+ attr_reader :raw_hash_object
112
+
103
113
  def protocol_message_index
104
114
  protocol_message.messages.index(self)
105
115
  end
@@ -111,5 +121,9 @@ module Ably::Models
111
121
  def message_serial
112
122
  protocol_message.message_serial
113
123
  end
124
+
125
+ def set_hash_object(hash)
126
+ @hash_object = IdiomaticRubyWrapper(hash.clone.freeze, stop_at: [:data])
127
+ end
114
128
  end
115
129
  end