ably-rest 0.8.6 → 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/SPEC.md +1049 -1001
  3. data/lib/submodules/ably-ruby/CHANGELOG.md +75 -3
  4. data/lib/submodules/ably-ruby/LICENSE +2 -2
  5. data/lib/submodules/ably-ruby/README.md +81 -20
  6. data/lib/submodules/ably-ruby/SPEC.md +1209 -693
  7. data/lib/submodules/ably-ruby/ably.gemspec +4 -4
  8. data/lib/submodules/ably-ruby/lib/ably/auth.rb +13 -4
  9. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +10 -1
  10. data/lib/submodules/ably-ruby/lib/ably/logger.rb +3 -1
  11. data/lib/submodules/ably-ruby/lib/ably/models/cipher_params.rb +114 -0
  12. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +10 -7
  13. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +3 -3
  14. data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +28 -21
  15. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +19 -17
  16. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +10 -9
  17. data/lib/submodules/ably-ruby/lib/ably/models/paginated_result.rb +27 -1
  18. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +20 -18
  19. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +26 -19
  20. data/lib/submodules/ably-ruby/lib/ably/models/{stat.rb → stats.rb} +21 -19
  21. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +14 -12
  22. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +16 -14
  23. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +2 -2
  24. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +11 -1
  25. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +10 -10
  26. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +18 -2
  27. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +3 -3
  28. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +13 -5
  29. data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +1 -1
  30. data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +2 -2
  31. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +8 -8
  32. data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +2 -2
  33. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +4 -2
  34. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
  35. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +6 -2
  36. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +7 -6
  37. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +7 -1
  38. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +7 -12
  39. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +9 -2
  40. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +7 -1
  41. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +19 -8
  42. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +16 -9
  43. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +12 -3
  44. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +35 -64
  45. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +23 -9
  46. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +9 -10
  47. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +1 -1
  48. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +16 -4
  49. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +7 -5
  50. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +50 -40
  51. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  52. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +4 -4
  53. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +2 -4
  54. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +46 -8
  55. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +20 -20
  56. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +7 -7
  57. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +114 -111
  58. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +9 -9
  59. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +5 -5
  60. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +1 -1
  61. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +1 -1
  62. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +4 -4
  63. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +15 -15
  64. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +4 -4
  65. data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +7 -7
  66. data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +4 -4
  67. data/lib/submodules/ably-ruby/spec/unit/models/cipher_params_spec.rb +140 -0
  68. data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +15 -8
  69. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +28 -22
  70. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +24 -0
  71. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +3 -3
  72. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +20 -18
  73. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +2 -2
  74. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +6 -6
  75. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +4 -4
  76. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +1 -1
  77. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +5 -5
  78. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +50 -17
  79. metadata +5 -3
@@ -8,10 +8,10 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Ably::VERSION
9
9
  spec.authors = ['Lewis Marshall', "Matthew O'Riordan"]
10
10
  spec.email = ['lewis@lmars.net', 'matt@ably.io']
11
- spec.description = %q{A Ruby client library for ably.io, the realtime messaging service}
12
- spec.summary = %q{A Ruby client library for ably.io, the realtime messaging service}
11
+ spec.description = %q{A Ruby client library for ably.io realtime messaging}
12
+ spec.summary = %q{A Ruby client library for ably.io realtime messaging implemented using EventMachine}
13
13
  spec.homepage = 'http://github.com/ably/ably-ruby'
14
- spec.license = 'MIT'
14
+ spec.license = 'Apache 2'
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'statesman', '~> 1.0.0'
24
24
  spec.add_runtime_dependency 'faraday', '~> 0.9'
25
25
  spec.add_runtime_dependency 'json'
26
- spec.add_runtime_dependency 'websocket-driver', '~> 0.3'
26
+ spec.add_runtime_dependency 'websocket-driver', '~> 0.6'
27
27
  spec.add_runtime_dependency 'msgpack', '>= 0.6.2'
28
28
  spec.add_runtime_dependency 'addressable', '>= 2.0.0'
29
29
 
@@ -54,6 +54,10 @@ module Ably
54
54
  raise ArgumentError, 'Expected token_params to be a Hash'
55
55
  end
56
56
 
57
+ # Ensure instance variables are defined
58
+ @client_id = nil
59
+ @client_id_validated = nil
60
+
57
61
  ensure_valid_auth_attributes auth_options
58
62
 
59
63
  @client = client
@@ -401,8 +405,13 @@ module Ably
401
405
  end
402
406
 
403
407
  private
404
- attr_reader :client
405
- attr_reader :token_option
408
+ def client
409
+ @client
410
+ end
411
+
412
+ def token_option
413
+ @token_option
414
+ end
406
415
 
407
416
  def ensure_valid_auth_attributes(attributes)
408
417
  if attributes[:timestamp]
@@ -453,7 +462,7 @@ module Ably
453
462
  end
454
463
 
455
464
  def split_api_key_into_key_and_secret!(options)
456
- api_key_parts = options[:key].to_s.match(/(?<name>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
465
+ api_key_parts = options[:key].to_s.match(/(?<name>[\w-]+\.[\w-]+):(?<secret>[\w-]+)/)
457
466
  raise ArgumentError, 'key is invalid' unless api_key_parts
458
467
 
459
468
  options[:key_name] = api_key_parts[:name].encode(Encoding::UTF_8)
@@ -576,7 +585,7 @@ module Ably
576
585
  token_request = Ably::Models::TokenRequest(token_request)
577
586
 
578
587
  response = client.post("/keys/#{token_request.key_name}/requestToken",
579
- token_request.hash, send_auth_header: false,
588
+ token_request.attributes, send_auth_header: false,
580
589
  disable_automatic_reauthorise: true)
581
590
 
582
591
  Ably::Models::TokenDetails.new(response.body)
@@ -32,6 +32,15 @@ module Ably
32
32
  # An invalid request was received by Ably
33
33
  class InvalidRequest < BaseAblyException; end
34
34
 
35
+ # Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided
36
+ class UnauthorizedRequest < BaseAblyException; end
37
+
38
+ # The request was a valid request, but Ably is refusing to respond to it
39
+ class ForbiddenRequest < BaseAblyException; end
40
+
41
+ # The requested resource could not be found but may be available again in the future
42
+ class ResourceMissing < BaseAblyException; end
43
+
35
44
  # Ably Protocol message received that is invalid
36
45
  class ProtocolError < BaseAblyException; end
37
46
 
@@ -92,7 +101,7 @@ module Ably
92
101
  # The token request could not be created
93
102
  class TokenRequestFailed < BaseAblyException; end
94
103
 
95
- # The token has expired
104
+ # The token has expired, 40140..40149
96
105
  class TokenExpired < BaseAblyException; end
97
106
 
98
107
  # The message could not be delivered to the server
@@ -37,7 +37,9 @@ module Ably
37
37
  def_delegators :logger, :fatal, :error, :warn, :info, :debug
38
38
 
39
39
  private
40
- attr_reader :client
40
+ def client
41
+ @client
42
+ end
41
43
 
42
44
  def color(color_value, string)
43
45
  "\033[#{color_value}m#{string}\033[0m"
@@ -0,0 +1,114 @@
1
+ require 'base64'
2
+ require 'ably/util/crypto'
3
+
4
+ module Ably::Models
5
+ # Convert cipher param attributes to a {CipherParams} object
6
+ #
7
+ # @param attributes (see #initialize)
8
+ #
9
+ # @return [CipherParams]
10
+ def self.CipherParams(attributes)
11
+ case attributes
12
+ when CipherParams
13
+ return attributes
14
+ else
15
+ CipherParams.new(attributes || {})
16
+ end
17
+ end
18
+
19
+ # CipherParams is used to configure a channel for encryption
20
+ #
21
+ class CipherParams
22
+ include Ably::Modules::ModelCommon
23
+
24
+ # @param params [Hash]
25
+ # @option params [String,Binary] :key Required private key must be either a binary (e.g. a ASCII_8BIT encoded string), or a base64-encoded string. If the key is a base64-encoded string, the it will be automatically converted into a binary
26
+ # @option params [String] :algorithm optional (default AES), specify the encryption algorithm supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
27
+ # @option params [String] :mode optional (default CBC), specify the cipher mode supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
28
+ # @option params [Integer] :key_length optional (default 128), specify the key length of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
29
+ # @option params [String] :combined optional (default AES-128-CBC), specify in one option the algorithm, key length and cipher of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
30
+ #
31
+ def initialize(params = {})
32
+ @attributes = IdiomaticRubyWrapper(params.clone)
33
+
34
+ raise Ably::Exceptions::CipherError, ':key param is required' unless attributes[:key]
35
+ raise Ably::Exceptions::CipherError, ':key param must be a base64-encoded string or byte array (ASCII_8BIT enocdede string)' unless key.kind_of?(String)
36
+ attributes[:key] = decode_key(key) if key.kind_of?(String) && key.encoding != Encoding::ASCII_8BIT
37
+
38
+ if attributes[:combined]
39
+ match = /(?<algorithm>\w+)-(?<key_length>\d+)-(?<mode>\w+)/.match(attributes[:combined])
40
+ raise Ably::Exceptions::CipherError, "Invalid :combined param, expecting format such as AES-256-CBC" unless match
41
+ attributes[:algorithm] = match[:algorithm]
42
+ attributes[:key_length] = match[:key_length].to_i
43
+ attributes[:mode] = match[:mode]
44
+ end
45
+
46
+ if attributes[:key_length] && (key_length != attributes[:key_length])
47
+ raise Ably::Exceptions::CipherError, "Incompatible :key length of #{key_length} and provided :key_length of #{attributes[:key_length]}"
48
+ end
49
+
50
+ if algorithm == 'aes' && mode == 'cbc'
51
+ unless [128, 256].include?(key_length)
52
+ raise Ably::Exceptions::CipherError, "Unsupported key length #{key_length} for aes-cbc encryption. Encryption key must be 128 or 256 bits (16 or 32 ASCII characters)"
53
+ end
54
+ end
55
+
56
+ attributes.freeze
57
+ end
58
+
59
+ # The Cipher algorithm string such as AES-128-CBC
60
+ # @param [Hash] params Hash containing :algorithm, :key_length and :mode key values
61
+ #
62
+ # @return [String]
63
+ def self.cipher_type(params)
64
+ "#{params[:algorithm]}-#{params[:key_length]}-#{params[:mode]}".to_s.upcase
65
+ end
66
+
67
+ # @!attribute [r] algorithm
68
+ # @return [String] The algorithm to use for encryption, currently only +AES+ is supported
69
+ def algorithm
70
+ attributes.fetch(:algorithm) do
71
+ Ably::Util::Crypto::DEFAULTS.fetch(:algorithm)
72
+ end.downcase
73
+ end
74
+
75
+ # @!attribute [r] key
76
+ # @return [Binary] Private key used to encrypt and decrypt payloads
77
+ def key
78
+ attributes[:key]
79
+ end
80
+
81
+ # @!attribute [r] key_length
82
+ # @return [Integer] The length in bits of the +key+
83
+ def key_length
84
+ key.unpack('b*').first.length
85
+ end
86
+
87
+ # @!attribute [r] mode
88
+ # @return [String] The cipher mode, currently only +CBC+ is supported
89
+ def mode
90
+ attributes.fetch(:mode) do
91
+ Ably::Util::Crypto::DEFAULTS.fetch(:mode)
92
+ end.downcase
93
+ end
94
+
95
+ # @!attribute [r] cipher_type
96
+ # @return [String] The complete Cipher algorithm string such as AES-128-CBC
97
+ def cipher_type
98
+ self.class.cipher_type(algorithm: algorithm, key_length: key_length, mode: mode)
99
+ end
100
+
101
+ # @!attribute [r] attributes
102
+ # @return [Hash] Access the token details Hash object ruby'fied to use symbolized keys
103
+ def attributes
104
+ @attributes
105
+ end
106
+
107
+ private
108
+
109
+ def decode_key(encoded_key)
110
+ normalised_key = encoded_key.gsub('_', '/').gsub('-', '+')
111
+ Base64.decode64(normalised_key)
112
+ end
113
+ end
114
+ end
@@ -28,26 +28,29 @@ module Ably::Models
28
28
  # @option attributes [Integer] :max_frame_size maximum size for a single frame of data sent to Ably. This restriction applies to a {Ably::Models::ProtocolMessage} sent over a realtime connection, or the total body size for a REST request
29
29
  # @option attributes [Integer] :max_inbound_rate maximum allowable number of requests per second from a client
30
30
  # @option attributes [Integer] :connection_state_ttl duration in seconds that Ably will persist the connection state when a Realtime client is abruptly disconnected
31
+ # @option attributes [String] :server_id unique identifier of the Ably server where the connection is established
31
32
  #
32
33
  def initialize(attributes = {})
33
34
  @hash_object = IdiomaticRubyWrapper(attributes.clone)
34
- hash[:connection_state_ttl] = (hash[:connection_state_ttl].to_f / 1000).round if hash[:connection_state_ttl]
35
- hash.freeze
35
+ if self.attributes[:connection_state_ttl]
36
+ self.attributes[:connection_state_ttl] = (self.attributes[:connection_state_ttl].to_f / 1000).round
37
+ end
38
+ self.attributes.freeze
36
39
  end
37
40
 
38
- %w(client_id connection_key max_message_size max_frame_size max_inbound_rate connection_state_ttl).each do |attribute|
41
+ %w(client_id connection_key max_message_size max_frame_size max_inbound_rate connection_state_ttl server_id).each do |attribute|
39
42
  define_method attribute do
40
- hash[attribute.to_sym]
43
+ attributes[attribute.to_sym]
41
44
  end
42
45
  end
43
46
 
44
47
  def has_client_id?
45
- hash.has_key?(:client_id)
48
+ attributes.has_key?(:client_id)
46
49
  end
47
50
 
48
- # @!attribute [r] hash
51
+ # @!attribute [r] attributes
49
52
  # @return [Hash] Access the token details Hash object ruby'fied to use symbolized keys
50
- def hash
53
+ def attributes
51
54
  @hash_object
52
55
  end
53
56
  end
@@ -8,7 +8,7 @@ module Ably::Models
8
8
  # @return [Integer] Ably error code (see ably-common/protocol/errors.json)
9
9
  # @!attribute [r] status
10
10
  # @return [Integer] HTTP Status Code corresponding to this error, where applicable
11
- # @!attribute [r] hash
11
+ # @!attribute [r] attributes
12
12
  # @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
13
13
  #
14
14
  class ErrorInfo
@@ -21,12 +21,12 @@ module Ably::Models
21
21
 
22
22
  %w(message code status_code).each do |attribute|
23
23
  define_method attribute do
24
- hash[attribute.to_sym]
24
+ attributes[attribute.to_sym]
25
25
  end
26
26
  end
27
27
  alias_method :status, :status_code
28
28
 
29
- def hash
29
+ def attributes
30
30
  @hash_object
31
31
  end
32
32
 
@@ -57,14 +57,14 @@ module Ably::Models
57
57
  $stderr.puts "<IdiomaticRubyWrapper#initialize> WARNING: Wrapping a IdiomaticRubyWrapper with another IdiomaticRubyWrapper"
58
58
  end
59
59
 
60
- @hash = mixedCaseHashObject
61
- @stop_at = Array(stop_at).each_with_object({}) do |key, hash|
62
- hash[convert_to_snake_case_symbol(key)] = true
60
+ @attributes = mixedCaseHashObject
61
+ @stop_at = Array(stop_at).each_with_object({}) do |key, object|
62
+ object[convert_to_snake_case_symbol(key)] = true
63
63
  end.freeze
64
64
  end
65
65
 
66
66
  def [](key)
67
- value = hash[source_key_for(key)]
67
+ value = attributes[source_key_for(key)]
68
68
  if stop_at?(key) || !value.kind_of?(Hash)
69
69
  value
70
70
  else
@@ -73,7 +73,7 @@ module Ably::Models
73
73
  end
74
74
 
75
75
  def []=(key, value)
76
- hash[source_key_for(key)] = value
76
+ attributes[source_key_for(key)] = value
77
77
  end
78
78
 
79
79
  def fetch(key, default = nil)
@@ -91,7 +91,7 @@ module Ably::Models
91
91
  end
92
92
 
93
93
  def size
94
- hash.size
94
+ attributes.size
95
95
  end
96
96
 
97
97
  def keys
@@ -103,14 +103,14 @@ module Ably::Models
103
103
  end
104
104
 
105
105
  def has_key?(key)
106
- hash.has_key?(source_key_for(key))
106
+ attributes.has_key?(source_key_for(key))
107
107
  end
108
108
 
109
109
  # Method ensuring this {IdiomaticRubyWrapper} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
110
110
  def each
111
111
  return to_enum(:each) unless block_given?
112
112
 
113
- hash.each do |key, value|
113
+ attributes.each do |key, value|
114
114
  key = convert_to_snake_case_symbol(key)
115
115
  value = self[key]
116
116
  yield key, value
@@ -138,9 +138,10 @@ module Ably::Models
138
138
  end
139
139
  end
140
140
 
141
- # Access to the raw Hash object provided to the constructer of this wrapper
142
- def hash
143
- @hash
141
+ # @!attribute [r] Hash
142
+ # @return [Hash] Access to the raw Hash object provided to the constructer of this wrapper
143
+ def attributes
144
+ @attributes
144
145
  end
145
146
 
146
147
  # Takes the underlying Hash object and returns it in as a JSON ready Hash object using snakeCase for compability with the Ably service.
@@ -149,8 +150,8 @@ module Ably::Models
149
150
  # wrapper = IdiomaticRubyWrapper({ 'mixedCase': true, mixed_case: false, 'snake_case': 1 })
150
151
  # wrapper.as_json({ 'mixedCase': true, 'snakeCase': 1 })
151
152
  def as_json(*args)
152
- hash.each_with_object({}) do |key_val, new_hash|
153
- key, val = key_val
153
+ attributes.each_with_object({}) do |key_val, new_hash|
154
+ key = key_val[0]
154
155
  mixed_case_key = convert_to_mixed_case(key)
155
156
  wrapped_val = self[key]
156
157
  wrapped_val = wrapped_val.as_json(args) if wrapped_val.kind_of?(IdiomaticRubyWrapper)
@@ -171,26 +172,32 @@ module Ably::Models
171
172
  # wrapper = IdiomaticRubyWrapper({ 'mixedCase': true, mixed_case: false, 'snake_case': 1 })
172
173
  # wrapper.to_hash({ mixed_case: true, snake_case: 1 })
173
174
  def to_hash(*args)
174
- each_with_object({}) do |key_val, hash|
175
- key, val = key_val
176
- val = val.to_hash(args) if val.kind_of?(IdiomaticRubyWrapper)
177
- hash[key] = val
175
+ each_with_object({}) do |key_val, object|
176
+ key, val = key_val
177
+ val = val.to_hash(args) if val.kind_of?(IdiomaticRubyWrapper)
178
+ object[key] = val
178
179
  end
179
180
  end
180
181
 
181
182
  # Method to create a duplicate of the underlying Hash object
182
183
  # Useful when underlying Hash is frozen
183
184
  def dup
184
- Ably::Models::IdiomaticRubyWrapper.new(hash.dup)
185
+ Ably::Models::IdiomaticRubyWrapper.new(attributes.dup, stop_at: stop_at.keys)
185
186
  end
186
187
 
187
188
  # Freeze the underlying data
188
189
  def freeze
189
- hash.freeze
190
+ attributes.freeze
190
191
  end
191
192
 
192
193
  def to_s
193
- hash.to_s
194
+ attributes.to_s
195
+ end
196
+
197
+ # @!attribute [r] hash
198
+ # @return [Integer] Compute a hash-code for this hash. Two hashes with the same content will have the same hash code
199
+ def hash
200
+ attributes.hash
194
201
  end
195
202
 
196
203
  private
@@ -214,7 +221,7 @@ module Ably::Models
214
221
  ]
215
222
 
216
223
  preferred_format = format_preferences.detect do |format|
217
- hash.has_key?(format.call(symbolized_key))
224
+ attributes.has_key?(format.call(symbolized_key))
218
225
  end || format_preferences.first
219
226
 
220
227
  preferred_format.call(symbolized_key)
@@ -34,7 +34,7 @@ module Ably::Models
34
34
  # @return [String] A globally unique message ID
35
35
  # @!attribute [r] connection_id
36
36
  # @return [String] The connection_id of the publisher of the message
37
- # @!attribute [r] hash
37
+ # @!attribute [r] attributes
38
38
  # @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
39
39
  #
40
40
  class Message
@@ -45,51 +45,51 @@ module Ably::Models
45
45
 
46
46
  # {Message} initializer
47
47
  #
48
- # @param hash_object [Hash] object with the underlying message details
48
+ # @param attributes [Hash] object with the underlying message detail key value attributes
49
49
  # @param [Hash] options an options Hash for this initializer
50
50
  # @option options [ProtocolMessage] :protocol_message An optional protocol message to assocate the presence message with
51
51
  # @option options [Logger] :logger An optional Logger to be used by {Ably::Modules::SafeDeferrable} if an exception is caught in a callback
52
52
  #
53
- def initialize(hash_object, options = {})
53
+ def initialize(attributes, options = {})
54
54
  @logger = options[:logger] # Logger expected for SafeDeferrable
55
55
  @protocol_message = options[:protocol_message]
56
- @raw_hash_object = hash_object
56
+ @raw_hash_object = attributes
57
57
 
58
- set_hash_object hash_object
58
+ set_attributes_object attributes
59
59
 
60
60
  ensure_utf_8 :name, name, allow_nil: true
61
61
  ensure_utf_8 :client_id, client_id, allow_nil: true
62
62
  ensure_utf_8 :encoding, encoding, allow_nil: true
63
63
  end
64
64
 
65
- %w( name client_id encoding connection_id ).each do |attribute|
65
+ %w( name client_id encoding ).each do |attribute|
66
66
  define_method attribute do
67
- hash[attribute.to_sym]
67
+ attributes[attribute.to_sym]
68
68
  end
69
69
  end
70
70
 
71
71
  def data
72
- @data ||= hash[:data].freeze
72
+ @data ||= attributes[:data].freeze
73
73
  end
74
74
 
75
75
  def id
76
- hash.fetch(:id) { "#{protocol_message.id!}:#{protocol_message_index}" }
76
+ attributes.fetch(:id) { "#{protocol_message.id!}:#{protocol_message_index}" }
77
77
  end
78
78
 
79
79
  def connection_id
80
- hash.fetch(:connection_id) { protocol_message.connection_id if assigned_to_protocol_message? }
80
+ attributes.fetch(:connection_id) { protocol_message.connection_id if assigned_to_protocol_message? }
81
81
  end
82
82
 
83
83
  def timestamp
84
- if hash[:timestamp]
85
- as_time_from_epoch(hash[:timestamp])
84
+ if attributes[:timestamp]
85
+ as_time_from_epoch(attributes[:timestamp])
86
86
  else
87
87
  protocol_message.timestamp
88
88
  end
89
89
  end
90
90
 
91
- def hash
92
- @hash_object
91
+ def attributes
92
+ @attributes
93
93
  end
94
94
 
95
95
  def to_json(*args)
@@ -120,14 +120,16 @@ module Ably::Models
120
120
  end
121
121
 
122
122
  private
123
- attr_reader :raw_hash_object
123
+ def raw_hash_object
124
+ @raw_hash_object
125
+ end
124
126
 
125
127
  def protocol_message_index
126
128
  protocol_message.messages.map(&:object_id).index(self.object_id)
127
129
  end
128
130
 
129
- def set_hash_object(hash)
130
- @hash_object = IdiomaticRubyWrapper(hash.clone.freeze, stop_at: [:data])
131
+ def set_attributes_object(new_attributes)
132
+ @attributes = IdiomaticRubyWrapper(new_attributes.clone.freeze, stop_at: [:data])
131
133
  end
132
134
 
133
135
  def logger