ably 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|