ably 0.1.5 → 0.1.6
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 +4 -4
- data/README.md +11 -1
- data/ably.gemspec +4 -3
- data/lib/ably.rb +6 -2
- data/lib/ably/auth.rb +24 -16
- data/lib/ably/exceptions.rb +16 -5
- data/lib/ably/{realtime/models → models}/error_info.rb +9 -11
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +57 -26
- data/lib/ably/{realtime/models → models}/message.rb +45 -38
- data/lib/ably/{realtime/models → models}/nil_channel.rb +4 -4
- data/lib/ably/{rest/models/paged_resource.rb → models/paginated_resource.rb} +21 -10
- data/lib/ably/models/presence_message.rb +126 -0
- data/lib/ably/{realtime/models → models}/protocol_message.rb +76 -38
- data/lib/ably/models/token.rb +74 -0
- data/lib/ably/modules/channels_collection.rb +49 -0
- data/lib/ably/modules/conversions.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +43 -8
- data/lib/ably/modules/event_machine_helpers.rb +1 -0
- data/lib/ably/modules/http_helpers.rb +9 -2
- data/lib/ably/modules/message_pack.rb +14 -0
- data/lib/ably/modules/model_common.rb +29 -0
- data/lib/ably/modules/{state.rb → state_emitter.rb} +8 -7
- data/lib/ably/realtime.rb +37 -7
- data/lib/ably/realtime/channel.rb +154 -31
- data/lib/ably/realtime/channels.rb +47 -0
- data/lib/ably/realtime/client.rb +39 -33
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +50 -21
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +9 -11
- data/lib/ably/realtime/connection.rb +148 -79
- data/lib/ably/realtime/connection/connection_state_machine.rb +111 -0
- data/lib/ably/realtime/connection/websocket_transport.rb +161 -0
- data/lib/ably/realtime/presence.rb +270 -0
- data/lib/ably/rest.rb +14 -3
- data/lib/ably/rest/channel.rb +3 -3
- data/lib/ably/rest/channels.rb +26 -12
- data/lib/ably/rest/client.rb +42 -25
- data/lib/ably/rest/middleware/exceptions.rb +21 -23
- data/lib/ably/rest/middleware/external_exceptions.rb +8 -10
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
- data/lib/ably/rest/middleware/parse_json.rb +9 -2
- data/lib/ably/rest/middleware/parse_message_pack.rb +6 -2
- data/lib/ably/rest/presence.rb +4 -4
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +125 -0
- data/spec/acceptance/realtime/channel_spec.rb +135 -63
- data/spec/acceptance/realtime/connection_spec.rb +86 -0
- data/spec/acceptance/realtime/message_spec.rb +116 -94
- data/spec/acceptance/realtime/presence_history_spec.rb +0 -0
- data/spec/acceptance/realtime/presence_spec.rb +277 -0
- data/spec/acceptance/rest/auth_spec.rb +351 -347
- data/spec/acceptance/rest/base_spec.rb +43 -26
- data/spec/acceptance/rest/channel_spec.rb +88 -83
- data/spec/acceptance/rest/channels_spec.rb +32 -28
- data/spec/acceptance/rest/presence_spec.rb +83 -63
- data/spec/acceptance/rest/stats_spec.rb +38 -37
- data/spec/acceptance/rest/time_spec.rb +10 -6
- data/spec/integration/modules/{state_spec.rb → state_emitter_spec.rb} +16 -2
- data/spec/spec_helper.rb +14 -0
- data/spec/support/api_helper.rb +4 -0
- data/spec/support/model_helper.rb +28 -9
- data/spec/support/protocol_msgbus_helper.rb +8 -1
- data/spec/support/test_app.rb +24 -14
- data/spec/unit/{realtime → models}/error_info_spec.rb +4 -4
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +46 -9
- data/spec/unit/models/message_spec.rb +229 -0
- data/spec/unit/{rest/paged_resource_spec.rb → models/paginated_resource_spec.rb} +19 -11
- data/spec/unit/models/presence_message_spec.rb +230 -0
- data/spec/unit/models/protocol_message_spec.rb +280 -0
- data/spec/unit/{token_spec.rb → models/token_spec.rb} +18 -22
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/event_emitter_spec.rb +36 -4
- data/spec/unit/realtime/channel_spec.rb +76 -2
- data/spec/unit/realtime/channels_spec.rb +50 -0
- data/spec/unit/realtime/client_spec.rb +31 -1
- data/spec/unit/realtime/connection_spec.rb +8 -15
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +6 -6
- data/spec/unit/realtime/presence_spec.rb +100 -0
- data/spec/unit/rest/channels_spec.rb +48 -0
- metadata +72 -38
- data/lib/ably/realtime/models/shared.rb +0 -17
- data/lib/ably/rest/models/message.rb +0 -64
- data/lib/ably/rest/models/presence_message.rb +0 -21
- data/lib/ably/token.rb +0 -80
- data/spec/unit/realtime/message_spec.rb +0 -117
- data/spec/unit/realtime/protocol_message_spec.rb +0 -172
- data/spec/unit/rest/message_spec.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc321ac8e501652f72e08617651df2f90118a759
|
4
|
+
data.tar.gz: 9f9b3f5abedeba6a4ac79841a7e1d5d09bf2bc4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdd44c54e0015062c3ecd62bb39f2e03e2a5fe39118939bd7bd18d1bbddd3d774cd511634dac8273890d57b2fd2f2a2191df91016090a54e4e9ca8ffed3823c6
|
7
|
+
data.tar.gz: 4ef9786cc786a7d4b6b824bf5bc137b17d92180c0b3d201ba48cb3d6aa9cf85a56fb8b3639170a4d0ce4f41179e1b6780569e8727df761f5a9f384df20f4535c
|
data/README.md
CHANGED
@@ -55,6 +55,16 @@ channel = client.channel("test")
|
|
55
55
|
channel.publish("greeting", "Hello World!")
|
56
56
|
```
|
57
57
|
|
58
|
+
### Presence on a channel
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
client = Ably::Realtime.new(api_key: "xxxxx")
|
62
|
+
channel = client.channel("test")
|
63
|
+
channel.presence.enter(client_data: 'john.doe') do |presence|
|
64
|
+
presence.get #=> [Array of members present]
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
58
68
|
## Using the REST API
|
59
69
|
|
60
70
|
### Publishing a message to a channel
|
@@ -78,7 +88,7 @@ channel.history #=> [{:name=>"test", :data=>"payload"}]
|
|
78
88
|
```ruby
|
79
89
|
client = Ably::Rest.new(api_key: "xxxxx")
|
80
90
|
client.auth.authorise # creates a token and will use token authentication moving forwards
|
81
|
-
client.auth.current_token #=> #<Ably::Token>
|
91
|
+
client.auth.current_token #=> #<Ably::Models::Token>
|
82
92
|
channel.publish("myEvent", "Hello!") #=> true, sent using token authentication
|
83
93
|
```
|
84
94
|
|
data/ably.gemspec
CHANGED
@@ -18,11 +18,12 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "eventmachine"
|
21
|
+
spec.add_runtime_dependency "eventmachine", "~> 1.0"
|
22
|
+
spec.add_runtime_dependency "statesman", "~> 1.0.0.beta2"
|
22
23
|
spec.add_runtime_dependency "faraday", "~> 0.9"
|
23
24
|
spec.add_runtime_dependency "json"
|
24
|
-
spec.add_runtime_dependency "websocket-driver"
|
25
|
-
spec.add_runtime_dependency "msgpack"
|
25
|
+
spec.add_runtime_dependency "websocket-driver", "~> 0.3"
|
26
|
+
spec.add_runtime_dependency "msgpack", "~> 0.5"
|
26
27
|
|
27
28
|
spec.add_development_dependency "bundler", "~> 1.3"
|
28
29
|
spec.add_development_dependency "rake"
|
data/lib/ably.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
%w(modules
|
1
|
+
%w(modules util).each do |namespace|
|
2
2
|
Dir.glob(File.expand_path("ably/#{namespace}/*.rb", File.dirname(__FILE__))).each do |file|
|
3
3
|
require file
|
4
4
|
end
|
@@ -8,8 +8,12 @@ require "ably/auth"
|
|
8
8
|
require "ably/exceptions"
|
9
9
|
require "ably/realtime"
|
10
10
|
require "ably/rest"
|
11
|
-
require "ably/token"
|
12
11
|
require "ably/version"
|
13
12
|
|
13
|
+
# Ably is the base namespace for the Ably {Ably::Realtime Realtime} & {Ably::Rest Rest} client libraries.
|
14
|
+
#
|
15
|
+
# Please refer to the {file:README.md Readme} on getting started.
|
16
|
+
#
|
17
|
+
# @see file:README.md README
|
14
18
|
module Ably
|
15
19
|
end
|
data/lib/ably/auth.rb
CHANGED
@@ -12,7 +12,7 @@ module Ably
|
|
12
12
|
# @!attribute [r] client_id
|
13
13
|
# @return [String] The provided client ID, used for identifying this client for presence purposes
|
14
14
|
# @!attribute [r] current_token
|
15
|
-
# @return [Ably::Token] Current {Ably::Token} issued by this library or one of the provided callbacks used to authenticate requests
|
15
|
+
# @return [Ably::Models::Token] Current {Ably::Models::Token} issued by this library or one of the provided callbacks used to authenticate requests
|
16
16
|
# @!attribute [r] token_id
|
17
17
|
# @return [String] Token ID provided to the {Ably::Client} constructor that is used to authenticate all requests
|
18
18
|
# @!attribute [r] api_key
|
@@ -37,7 +37,7 @@ module Ably
|
|
37
37
|
# @param [Hash] auth_options see {Ably::Rest::Client#initialize}
|
38
38
|
# @yield [auth_options] see {Ably::Rest::Client#initialize}
|
39
39
|
def initialize(client, auth_options, &auth_block)
|
40
|
-
auth_options = auth_options.
|
40
|
+
auth_options = auth_options.dup
|
41
41
|
|
42
42
|
@client = client
|
43
43
|
@options = auth_options
|
@@ -81,7 +81,7 @@ module Ably
|
|
81
81
|
# @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
|
82
82
|
# @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
|
83
83
|
# @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
|
84
|
-
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
84
|
+
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
85
85
|
# @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
|
86
86
|
# @option options [Boolean] :query_time when true will query the {https://ably.io Ably} system for the current time instead of using the local time
|
87
87
|
# @option options [Time] :timestamp the time of the of the request
|
@@ -90,9 +90,9 @@ module Ably
|
|
90
90
|
#
|
91
91
|
# @yield [options] (optional) if an auth block is passed to this method, then this block will be called to create a new token request object
|
92
92
|
# @yieldparam [Hash] options options passed to request_token will be in turn sent to the block in this argument
|
93
|
-
# @yieldreturn [Hash] valid token request object, see {#create_token_request}
|
93
|
+
# @yieldreturn [Hash] valid token request object, see {Auth#create_token_request}
|
94
94
|
#
|
95
|
-
# @return [Ably::Token]
|
95
|
+
# @return [Ably::Models::Token]
|
96
96
|
#
|
97
97
|
# @example
|
98
98
|
# # will issue a simple token request using basic auth
|
@@ -113,7 +113,7 @@ module Ably
|
|
113
113
|
@current_token = request_token(options, &block)
|
114
114
|
end
|
115
115
|
|
116
|
-
# Request a {Ably::Token} which can be used to make authenticated token based requests
|
116
|
+
# Request a {Ably::Models::Token} which can be used to make authenticated token based requests
|
117
117
|
#
|
118
118
|
# @param [Hash] options the options for the token request
|
119
119
|
# @option options [String] :key_id key ID for the designated application (defaults to client key_id)
|
@@ -123,7 +123,7 @@ module Ably
|
|
123
123
|
# @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
|
124
124
|
# @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
|
125
125
|
# @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
|
126
|
-
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
126
|
+
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
127
127
|
# @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
|
128
128
|
# @option options [Boolean] :query_time when true will query the {https://ably.io Ably} system for the current time instead of using the local time
|
129
129
|
# @option options [Time] :timestamp the time of the of the request
|
@@ -131,9 +131,9 @@ module Ably
|
|
131
131
|
#
|
132
132
|
# @yield [options] (optional) if an auth block is passed to this method, then this block will be called to create a new token request object
|
133
133
|
# @yieldparam [Hash] options options passed to request_token will be in turn sent to the block in this argument
|
134
|
-
# @yieldreturn [Hash] valid token request object, see {#create_token_request}
|
134
|
+
# @yieldreturn [Hash] valid token request object, see {Auth#create_token_request}
|
135
135
|
#
|
136
|
-
# @return [Ably::Token]
|
136
|
+
# @return [Ably::Models::Token]
|
137
137
|
#
|
138
138
|
# @example
|
139
139
|
# # simple token request using basic auth
|
@@ -165,7 +165,7 @@ module Ably
|
|
165
165
|
response = client.post("/keys/#{token_request.fetch(:id)}/requestToken", token_request, send_auth_header: false)
|
166
166
|
body = IdiomaticRubyWrapper(response.body)
|
167
167
|
|
168
|
-
Ably::Token.new(body.fetch(:access_token))
|
168
|
+
Ably::Models::Token.new(body.fetch(:access_token))
|
169
169
|
end
|
170
170
|
|
171
171
|
# Creates and signs a token request that can then subsequently be used by any client to request a token
|
@@ -174,7 +174,7 @@ module Ably
|
|
174
174
|
# @option options [String] :key_id key ID for the designated application
|
175
175
|
# @option options [String] :key_secret key secret for the designated application used to sign token requests (defaults to client key_secret)
|
176
176
|
# @option options [String] :client_id client ID identifying this connection to other clients
|
177
|
-
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
177
|
+
# @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::Token}. Limits may apply, see {http://docs.ably.io/other/authentication/}
|
178
178
|
# @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
|
179
179
|
# @option options [Boolean] :query_time when true will query the {https://ably.io Ably} system for the current time instead of using the local time
|
180
180
|
# @option options [Time] :timestamp the time of the of the request
|
@@ -210,9 +210,9 @@ module Ably
|
|
210
210
|
token_request = {
|
211
211
|
id: request_key_id,
|
212
212
|
client_id: client_id,
|
213
|
-
ttl: Token::DEFAULTS[:ttl],
|
213
|
+
ttl: Ably::Models::Token::DEFAULTS[:ttl],
|
214
214
|
timestamp: timestamp,
|
215
|
-
capability: Token::DEFAULTS[:capability],
|
215
|
+
capability: Ably::Models::Token::DEFAULTS[:capability],
|
216
216
|
nonce: SecureRandom.hex
|
217
217
|
}.merge(token_options.select { |key, val| token_attributes.include?(key.to_s) })
|
218
218
|
|
@@ -354,11 +354,18 @@ module Ably
|
|
354
354
|
connection = Faraday.new("#{uri.scheme}://#{uri.host}", connection_options)
|
355
355
|
method = options[:auth_method] || :get
|
356
356
|
|
357
|
-
connection.send(method) do |request|
|
357
|
+
response = connection.send(method) do |request|
|
358
358
|
request.url uri.path
|
359
359
|
request.params = options[:auth_params] || {}
|
360
360
|
request.headers = options[:auth_headers] || {}
|
361
|
-
end
|
361
|
+
end
|
362
|
+
|
363
|
+
unless response.body.kind_of?(Hash)
|
364
|
+
raise Ably::Exceptions::InvalidResponseBody,
|
365
|
+
"Content Type #{response.headers['Content-Type']} is not supported by this client library"
|
366
|
+
end
|
367
|
+
|
368
|
+
response.body
|
362
369
|
end
|
363
370
|
|
364
371
|
# Return a Hash of connection options to initiate the Faraday::Connection with
|
@@ -383,11 +390,12 @@ module Ably
|
|
383
390
|
# @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
|
384
391
|
def middleware
|
385
392
|
@middleware ||= Faraday::RackBuilder.new do |builder|
|
386
|
-
|
393
|
+
setup_outgoing_middleware builder
|
387
394
|
|
388
395
|
# Raise exceptions if response code is invalid
|
389
396
|
builder.use Ably::Rest::Middleware::ExternalExceptions
|
390
397
|
|
398
|
+
setup_incoming_middleware builder
|
391
399
|
|
392
400
|
# Log HTTP requests if log level is DEBUG option set
|
393
401
|
builder.response :logger if client.log_level == Logger::DEBUG
|
data/lib/ably/exceptions.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Ably
|
2
2
|
module Exceptions
|
3
|
-
#
|
3
|
+
# Base Ably exception class that contains status and code values used by Ably
|
4
|
+
# Refer to https://github.com/ably/ably-common/blob/master/protocol/errors.json
|
4
5
|
#
|
5
6
|
# @!attribute [r] message
|
6
7
|
# @return [String] Error message from Ably
|
@@ -8,19 +9,22 @@ module Ably
|
|
8
9
|
# @return [String] HTTP status code of error
|
9
10
|
# @!attribute [r] code
|
10
11
|
# @return [String] Ably specific error code
|
11
|
-
class
|
12
|
+
class Base < StandardError
|
12
13
|
attr_reader :status, :code
|
13
|
-
def initialize(message, status
|
14
|
+
def initialize(message, status = nil, code = nil)
|
14
15
|
super message
|
15
16
|
@status = status
|
16
17
|
@code = code
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
21
|
+
# An invalid request was received by Ably
|
22
|
+
class InvalidRequest < Base; end
|
23
|
+
|
20
24
|
# The HTTP request has returned a 500 error
|
21
25
|
class ServerError < StandardError; end
|
22
26
|
|
23
|
-
#
|
27
|
+
# PaginatedResource cannot retrieve the page
|
24
28
|
class InvalidPageError < StandardError; end
|
25
29
|
|
26
30
|
# The expected response from the server was invalid
|
@@ -33,9 +37,16 @@ module Ably
|
|
33
37
|
class TokenRequestError < StandardError; end
|
34
38
|
|
35
39
|
# The token is invalid
|
36
|
-
class InvalidToken <
|
40
|
+
class InvalidToken < Base; end
|
37
41
|
|
38
42
|
# Encryption or decryption related failures
|
39
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
|
40
51
|
end
|
41
52
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Ably::
|
1
|
+
module Ably::Models
|
2
2
|
# An exception type encapsulating error information containing
|
3
3
|
# an Ably-specific error code and generic status code.
|
4
4
|
#
|
@@ -8,29 +8,27 @@ module Ably::Realtime::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]
|
11
|
+
# @!attribute [r] hash
|
12
12
|
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
13
13
|
#
|
14
14
|
class ErrorInfo
|
15
|
-
include
|
16
|
-
include Ably::Modules::Conversions
|
15
|
+
include Ably::Modules::ModelCommon
|
17
16
|
|
18
|
-
def initialize(
|
19
|
-
@
|
20
|
-
@
|
17
|
+
def initialize(hash_object)
|
18
|
+
@raw_hash_object = hash_object
|
19
|
+
@hash_object = IdiomaticRubyWrapper(hash_object.clone.freeze)
|
21
20
|
end
|
22
21
|
|
23
22
|
%w( message code status_code ).each do |attribute|
|
24
23
|
define_method attribute do
|
25
|
-
|
24
|
+
hash[attribute.to_sym]
|
26
25
|
end
|
27
26
|
end
|
28
27
|
alias_method :status, :status_code
|
29
28
|
|
30
|
-
def
|
31
|
-
@
|
29
|
+
def hash
|
30
|
+
@hash_object
|
32
31
|
end
|
33
|
-
alias_method :to_json, :json
|
34
32
|
|
35
33
|
def to_s
|
36
34
|
"Error: #{message} (code: #{code}, status_code: #{status_code})"
|
@@ -18,7 +18,7 @@ module Ably::Modules
|
|
18
18
|
end
|
19
19
|
|
20
20
|
module Ably::Models
|
21
|
-
# Wraps
|
21
|
+
# Wraps Hash objects returned by Ably service to appear as Idiomatic Ruby Hashes with symbol keys
|
22
22
|
# It recursively wraps containing Hashes, but will stop wrapping at arrays, any other non Hash object, or any key matching the `:stops_at` options
|
23
23
|
# It also provides methods matching the symbolic keys for convenience
|
24
24
|
#
|
@@ -36,32 +36,33 @@ module Ably::Models
|
|
36
36
|
# ruby_hash.none # => nil
|
37
37
|
#
|
38
38
|
# @!attribute [r] stop_at
|
39
|
-
# @return [Array<Symbol,String>] array of keys that this wrapper should stop wrapping at to preserve the underlying
|
39
|
+
# @return [Array<Symbol,String>] array of keys that this wrapper should stop wrapping at to preserve the underlying Hash as is
|
40
40
|
#
|
41
41
|
class IdiomaticRubyWrapper
|
42
42
|
include Enumerable
|
43
43
|
include Ably::Modules::Conversions
|
44
|
+
include Ably::Modules::MessagePack
|
44
45
|
|
45
46
|
attr_reader :stop_at
|
46
47
|
|
47
|
-
# Creates an IdiomaticRubyWrapper around the mixed case
|
48
|
+
# Creates an IdiomaticRubyWrapper around the mixed case Hash object
|
48
49
|
#
|
49
|
-
# @attribute [Hash]
|
50
|
-
# @attribute [Array<Symbol,String>] stop_at array of keys that this wrapper should stop wrapping at to preserve the underlying
|
50
|
+
# @attribute [Hash] mixedCaseHashObject mixed case Hash object
|
51
|
+
# @attribute [Array<Symbol,String>] stop_at array of keys that this wrapper should stop wrapping at to preserve the underlying Hash as is
|
51
52
|
#
|
52
|
-
def initialize(
|
53
|
-
if
|
53
|
+
def initialize(mixedCaseHashObject, stop_at: [])
|
54
|
+
if mixedCaseHashObject.kind_of?(IdiomaticRubyWrapper)
|
54
55
|
$stderr.puts "<IdiomaticRubyWrapper#initialize> WARNING: Wrapping a IdiomaticRubyWrapper with another IdiomaticRubyWrapper"
|
55
56
|
end
|
56
57
|
|
57
|
-
@
|
58
|
+
@hash = mixedCaseHashObject
|
58
59
|
@stop_at = Array(stop_at).each_with_object({}) do |key, hash|
|
59
60
|
hash[convert_to_snake_case_symbol(key)] = true
|
60
61
|
end.freeze
|
61
62
|
end
|
62
63
|
|
63
64
|
def [](key)
|
64
|
-
value =
|
65
|
+
value = hash[source_key_for(key)]
|
65
66
|
if stop_at?(key) || !value.kind_of?(Hash)
|
66
67
|
value
|
67
68
|
else
|
@@ -70,7 +71,7 @@ module Ably::Models
|
|
70
71
|
end
|
71
72
|
|
72
73
|
def []=(key, value)
|
73
|
-
|
74
|
+
hash[source_key_for(key)] = value
|
74
75
|
end
|
75
76
|
|
76
77
|
def fetch(key, default = nil, &missing_block)
|
@@ -88,7 +89,7 @@ module Ably::Models
|
|
88
89
|
end
|
89
90
|
|
90
91
|
def size
|
91
|
-
|
92
|
+
hash.size
|
92
93
|
end
|
93
94
|
|
94
95
|
def keys
|
@@ -100,12 +101,12 @@ module Ably::Models
|
|
100
101
|
end
|
101
102
|
|
102
103
|
def has_key?(key)
|
103
|
-
|
104
|
+
hash.has_key?(source_key_for(key))
|
104
105
|
end
|
105
106
|
|
106
107
|
# Method ensuring this {IdiomaticRubyWrapper} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
|
107
108
|
def each(&block)
|
108
|
-
|
109
|
+
hash.each do |key, value|
|
109
110
|
key = convert_to_snake_case_symbol(key)
|
110
111
|
value = self[key]
|
111
112
|
if block_given?
|
@@ -137,29 +138,59 @@ module Ably::Models
|
|
137
138
|
end
|
138
139
|
end
|
139
140
|
|
140
|
-
# Access to the raw
|
141
|
-
def
|
142
|
-
@
|
141
|
+
# Access to the raw Hash object provided to the constructer of this wrapper
|
142
|
+
def hash
|
143
|
+
@hash
|
143
144
|
end
|
144
145
|
|
145
|
-
#
|
146
|
-
#
|
146
|
+
# Takes the underlying Hash object and returns it in as a JSON ready Hash object using snakeCase for compability with the Ably service.
|
147
|
+
# Note name clashes are ignored and will result in loss of one or more values
|
148
|
+
# @example
|
149
|
+
# wrapper = IdiomaticRubyWrapper({ 'mixedCase': true, mixed_case: false, 'snake_case': 1 })
|
150
|
+
# wrapper.as_json({ 'mixedCase': true, 'snakeCase': 1 })
|
151
|
+
def as_json(*args)
|
152
|
+
hash.each_with_object({}) do |key_val, new_hash|
|
153
|
+
key, val = key_val
|
154
|
+
mixed_case_key = convert_to_mixed_case(key)
|
155
|
+
wrapped_val = self[key]
|
156
|
+
wrapped_val = wrapped_val.as_json(args) if wrapped_val.kind_of?(IdiomaticRubyWrapper)
|
157
|
+
|
158
|
+
new_hash[mixed_case_key] = wrapped_val
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Converts the current wrapped mixedCase object to JSON
|
163
|
+
# using mixedCase syntax as expected by the Realtime API
|
147
164
|
def to_json(*args)
|
148
|
-
|
165
|
+
as_json(args).to_json
|
149
166
|
end
|
150
167
|
|
151
|
-
# Generate a symbolized Hash object representing the underlying
|
152
|
-
|
168
|
+
# Generate a symbolized Hash object representing the underlying Hash in a Ruby friendly format.
|
169
|
+
# Note name clashes are ignored and will result in loss of one or more values
|
170
|
+
# @example
|
171
|
+
# wrapper = IdiomaticRubyWrapper({ 'mixedCase': true, mixed_case: false, 'snake_case': 1 })
|
172
|
+
# wrapper.to_hash({ mixed_case: true, snake_case: 1 })
|
173
|
+
def to_hash(*args)
|
153
174
|
each_with_object({}) do |key_val, hash|
|
154
|
-
key, val
|
175
|
+
key, val = key_val
|
176
|
+
val = val.to_hash(args) if val.kind_of?(IdiomaticRubyWrapper)
|
155
177
|
hash[key] = val
|
156
178
|
end
|
157
179
|
end
|
158
180
|
|
159
|
-
# Method to create a duplicate of the underlying
|
160
|
-
# Useful when underlying
|
181
|
+
# Method to create a duplicate of the underlying Hash object
|
182
|
+
# Useful when underlying Hash is frozen
|
161
183
|
def dup
|
162
|
-
Ably::Models::IdiomaticRubyWrapper.new(
|
184
|
+
Ably::Models::IdiomaticRubyWrapper.new(hash.dup)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Freeze the underlying data
|
188
|
+
def freeze
|
189
|
+
hash.freeze
|
190
|
+
end
|
191
|
+
|
192
|
+
def to_s
|
193
|
+
hash.to_s
|
163
194
|
end
|
164
195
|
|
165
196
|
private
|
@@ -183,7 +214,7 @@ module Ably::Models
|
|
183
214
|
]
|
184
215
|
|
185
216
|
preferred_format = format_preferences.detect do |format|
|
186
|
-
|
217
|
+
hash.has_key?(format.call(symbolized_key))
|
187
218
|
end || format_preferences.first
|
188
219
|
|
189
220
|
preferred_format.call(symbolized_key)
|