ably-pubsub-core 0.0.1.pre
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 +7 -0
- data/lib/ably/agent.rb +8 -0
- data/lib/ably/auth.rb +791 -0
- data/lib/ably/exceptions.rb +169 -0
- data/lib/ably/logger.rb +119 -0
- data/lib/ably/models/auth_details.rb +45 -0
- data/lib/ably/models/channel_details.rb +69 -0
- data/lib/ably/models/channel_metrics.rb +107 -0
- data/lib/ably/models/channel_occupancy.rb +50 -0
- data/lib/ably/models/channel_options.rb +120 -0
- data/lib/ably/models/channel_state_change.rb +84 -0
- data/lib/ably/models/channel_status.rb +63 -0
- data/lib/ably/models/cipher_params.rb +137 -0
- data/lib/ably/models/connection_details.rb +149 -0
- data/lib/ably/models/connection_state_change.rb +83 -0
- data/lib/ably/models/delta_extras.rb +28 -0
- data/lib/ably/models/device_details.rb +126 -0
- data/lib/ably/models/device_push_details.rb +94 -0
- data/lib/ably/models/error_info.rb +104 -0
- data/lib/ably/models/http_paginated_response.rb +112 -0
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +235 -0
- data/lib/ably/models/message.rb +223 -0
- data/lib/ably/models/message_encoders/base.rb +135 -0
- data/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/ably/models/message_encoders/cipher.rb +84 -0
- data/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_result.rb +201 -0
- data/lib/ably/models/presence_message.rb +244 -0
- data/lib/ably/models/protocol_message.rb +288 -0
- data/lib/ably/models/push_channel_subscription.rb +114 -0
- data/lib/ably/models/stats.rb +242 -0
- data/lib/ably/models/stats_types.rb +107 -0
- data/lib/ably/models/token_details.rb +148 -0
- data/lib/ably/models/token_request.rb +180 -0
- data/lib/ably/modules/ably.rb +26 -0
- data/lib/ably/modules/async_wrapper.rb +69 -0
- data/lib/ably/modules/channels_collection.rb +105 -0
- data/lib/ably/modules/conversions.rb +153 -0
- data/lib/ably/modules/encodeable.rb +111 -0
- data/lib/ably/modules/enum.rb +230 -0
- data/lib/ably/modules/event_emitter.rb +200 -0
- data/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/ably/modules/message_emitter.rb +78 -0
- data/lib/ably/modules/message_pack.rb +14 -0
- data/lib/ably/modules/model_common.rb +84 -0
- data/lib/ably/modules/safe_deferrable.rb +83 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +181 -0
- data/lib/ably/modules/state_machine.rb +59 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +111 -0
- data/lib/ably/realtime/auth.rb +283 -0
- data/lib/ably/realtime/channel/channel_manager.rb +272 -0
- data/lib/ably/realtime/channel/channel_properties.rb +40 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +104 -0
- data/lib/ably/realtime/channel/publisher.rb +82 -0
- data/lib/ably/realtime/channel/push_channel.rb +69 -0
- data/lib/ably/realtime/channel.rb +440 -0
- data/lib/ably/realtime/channels.rb +71 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +225 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +87 -0
- data/lib/ably/realtime/client.rb +363 -0
- data/lib/ably/realtime/connection/connection_manager.rb +594 -0
- data/lib/ably/realtime/connection/connection_state_machine.rb +144 -0
- data/lib/ably/realtime/connection/websocket_transport.rb +287 -0
- data/lib/ably/realtime/connection.rb +738 -0
- data/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/ably/realtime/presence/members_map.rb +395 -0
- data/lib/ably/realtime/presence/presence_manager.rb +78 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +57 -0
- data/lib/ably/realtime/presence.rb +502 -0
- data/lib/ably/realtime/push/admin.rb +75 -0
- data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/ably/realtime/push.rb +25 -0
- data/lib/ably/realtime/recovery_key_context.rb +36 -0
- data/lib/ably/realtime.rb +60 -0
- data/lib/ably/rest/channel/push_channel.rb +59 -0
- data/lib/ably/rest/channel.rb +194 -0
- data/lib/ably/rest/channels.rb +47 -0
- data/lib/ably/rest/client.rb +727 -0
- data/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/ably/rest/middleware/exceptions.rb +53 -0
- data/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +22 -0
- data/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/ably/rest/middleware/parse_message_pack.rb +43 -0
- data/lib/ably/rest/presence.rb +113 -0
- data/lib/ably/rest/push/admin.rb +58 -0
- data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/ably/rest/push/device_registrations.rb +114 -0
- data/lib/ably/rest/push.rb +25 -0
- data/lib/ably/rest.rb +40 -0
- data/lib/ably/util/ably_extensions.rb +29 -0
- data/lib/ably/util/crypto.rb +134 -0
- data/lib/ably/util/pub_sub.rb +45 -0
- data/lib/ably/util/safe_deferrable.rb +44 -0
- data/lib/ably/version.rb +7 -0
- data/lib/ably.rb +16 -0
- metadata +497 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Ably::Modules
|
|
2
|
+
module Conversions
|
|
3
|
+
private
|
|
4
|
+
# Convert device_push_details argument to a {Ably::Models::DevicePushDetails} object
|
|
5
|
+
#
|
|
6
|
+
# @param device_push_details [Ably::Models::DevicePushDetails,Hash,nil] A device push notification details object
|
|
7
|
+
#
|
|
8
|
+
# @return [Ably::Models::DevicePushDetails]
|
|
9
|
+
#
|
|
10
|
+
def DevicePushDetails(device_push_details)
|
|
11
|
+
case device_push_details
|
|
12
|
+
when Ably::Models::DevicePushDetails
|
|
13
|
+
device_push_details
|
|
14
|
+
else
|
|
15
|
+
Ably::Models::DevicePushDetails.new(device_push_details)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Ably::Models
|
|
22
|
+
# An object with the push notification details for {DeviceDetails} object
|
|
23
|
+
#
|
|
24
|
+
class DevicePushDetails < Ably::Exceptions::BaseAblyException
|
|
25
|
+
include Ably::Modules::ModelCommon
|
|
26
|
+
|
|
27
|
+
# @param hash_object [Hash,nil] Device push detail attributes
|
|
28
|
+
#
|
|
29
|
+
def initialize(hash_object = {})
|
|
30
|
+
@raw_hash_object = hash_object || {}
|
|
31
|
+
@hash_object = IdiomaticRubyWrapper(@raw_hash_object)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The current state of the push registration.
|
|
35
|
+
#
|
|
36
|
+
# @spec PCP4
|
|
37
|
+
#
|
|
38
|
+
# @return [Symbol]
|
|
39
|
+
#
|
|
40
|
+
def state
|
|
41
|
+
attributes[:state]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def state=(val)
|
|
45
|
+
unless val.nil? || val.kind_of?(String)
|
|
46
|
+
raise ArgumentError, "state must be nil or a string value"
|
|
47
|
+
end
|
|
48
|
+
attributes[:state] = val
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# A JSON object of key-value pairs that contains of the push transport and address.
|
|
52
|
+
#
|
|
53
|
+
# @spec PCP3
|
|
54
|
+
#
|
|
55
|
+
# @return [Hash, nil]
|
|
56
|
+
#
|
|
57
|
+
def recipient
|
|
58
|
+
attributes[:recipient] || {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def recipient=(val)
|
|
62
|
+
unless val.nil? || val.kind_of?(Hash)
|
|
63
|
+
raise ArgumentError, "recipient must be nil or a Hash value"
|
|
64
|
+
end
|
|
65
|
+
attributes[:recipient] = val
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# An {Ably::Models::ErrorInfo} object describing the most recent error when the state is Failing or Failed.
|
|
69
|
+
#
|
|
70
|
+
# @spec PCP2
|
|
71
|
+
#
|
|
72
|
+
# @return [Ably::Models::ErrorInfo]
|
|
73
|
+
#
|
|
74
|
+
def error_reason
|
|
75
|
+
attributes[:error_reason]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def error_reason=(val)
|
|
79
|
+
unless val.nil? || val.kind_of?(Hash) || val.kind_of?(Ably::Models::ErrorInfo)
|
|
80
|
+
raise ArgumentError, "error_reason must be nil, a Hash value or a ErrorInfo object"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
attributes[:error_reason] = if val.nil?
|
|
84
|
+
nil
|
|
85
|
+
else
|
|
86
|
+
ErrorInfo(val)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def attributes
|
|
91
|
+
@hash_object
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module Ably::Modules
|
|
2
|
+
module Conversions
|
|
3
|
+
private
|
|
4
|
+
# Convert error_details argument to a {ErrorInfo} object
|
|
5
|
+
#
|
|
6
|
+
# @param error_details [ErrorInfo,Hash] Error info attributes
|
|
7
|
+
#
|
|
8
|
+
# @return [ErrorInfo]
|
|
9
|
+
#
|
|
10
|
+
def ErrorInfo(error_details)
|
|
11
|
+
case error_details
|
|
12
|
+
when Ably::Models::ErrorInfo
|
|
13
|
+
error_details
|
|
14
|
+
else
|
|
15
|
+
Ably::Models::ErrorInfo.new(error_details)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Ably::Models
|
|
22
|
+
# An exception type encapsulating error information containing
|
|
23
|
+
# an Ably-specific error code and generic status code.
|
|
24
|
+
#
|
|
25
|
+
class ErrorInfo < Ably::Exceptions::BaseAblyException
|
|
26
|
+
include Ably::Modules::ModelCommon
|
|
27
|
+
|
|
28
|
+
def initialize(hash_object)
|
|
29
|
+
@raw_hash_object = hash_object
|
|
30
|
+
@hash_object = IdiomaticRubyWrapper(hash_object.clone.freeze)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Ably error code.
|
|
34
|
+
#
|
|
35
|
+
# @spec TI1
|
|
36
|
+
#
|
|
37
|
+
# @return [Integer]
|
|
38
|
+
#
|
|
39
|
+
def code
|
|
40
|
+
attributes[:code]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# This is included for REST responses to provide a URL for additional help on the error code.
|
|
44
|
+
#
|
|
45
|
+
# @spec TI4
|
|
46
|
+
#
|
|
47
|
+
# @return [String]
|
|
48
|
+
#
|
|
49
|
+
def href
|
|
50
|
+
attributes[:href]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Additional message information, where available.
|
|
54
|
+
#
|
|
55
|
+
# @spec TI1
|
|
56
|
+
#
|
|
57
|
+
# @return [String]
|
|
58
|
+
#
|
|
59
|
+
def message
|
|
60
|
+
attributes[:message]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Information pertaining to what caused the error where available.
|
|
64
|
+
#
|
|
65
|
+
# @spec TI1
|
|
66
|
+
#
|
|
67
|
+
# @return [Ably::Models::ErrorInfo]
|
|
68
|
+
#
|
|
69
|
+
def cause
|
|
70
|
+
attributes[:cause]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# HTTP Status Code corresponding to this error, where applicable.
|
|
74
|
+
#
|
|
75
|
+
# @spec TI1
|
|
76
|
+
#
|
|
77
|
+
# @return [Integer]
|
|
78
|
+
#
|
|
79
|
+
def status_code
|
|
80
|
+
attributes[:status_code]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# If a request fails, the request ID must be included in the ErrorInfo returned to the user.
|
|
84
|
+
#
|
|
85
|
+
# @spec RSC7c
|
|
86
|
+
#
|
|
87
|
+
# @return [String]
|
|
88
|
+
#
|
|
89
|
+
def request_id
|
|
90
|
+
attributes[:request_id]
|
|
91
|
+
end
|
|
92
|
+
alias_method :status, :status_code
|
|
93
|
+
|
|
94
|
+
def attributes
|
|
95
|
+
@hash_object
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def to_s
|
|
99
|
+
error_href = href || (code ? "https://help.ably.io/error/#{code}" : '')
|
|
100
|
+
see_msg = " -> see #{error_href} for help" unless message.to_s.include?(error_href.to_s)
|
|
101
|
+
"<Error: #{message} (code: #{code}, http status: #{status} request_id: #{request_id} cause: #{cause})>#{see_msg}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'ably/models/paginated_result'
|
|
2
|
+
|
|
3
|
+
module Ably::Models
|
|
4
|
+
# HTTP respones object from Rest#request object
|
|
5
|
+
# Wraps any Ably HTTP response that supports paging and provides methods to iterate through
|
|
6
|
+
# the pages using {#first}, {#next}, {#has_next?} and {#last?}
|
|
7
|
+
|
|
8
|
+
class HttpPaginatedResponse < PaginatedResult
|
|
9
|
+
# Retrieve the first page of results.
|
|
10
|
+
# When used as part of the {Ably::Realtime} library, it will return a {Ably::Util::SafeDeferrable} object,
|
|
11
|
+
# and allows an optional success callback block to be provided.
|
|
12
|
+
#
|
|
13
|
+
# @return [HttpPaginatedResponse,Ably::Util::SafeDeferrable]
|
|
14
|
+
#
|
|
15
|
+
def first(&success_callback)
|
|
16
|
+
async_wrap_if_realtime(success_callback) do
|
|
17
|
+
return nil unless supports_pagination?
|
|
18
|
+
HttpPaginatedResponse.new(client.get(pagination_url('first')), base_url, client, pagination_options, &each_block)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Retrieve the next page of results.
|
|
23
|
+
# When used as part of the {Ably::Realtime} library, it will return a {Ably::Util::SafeDeferrable} object,
|
|
24
|
+
# and allows an optional success callback block to be provided.
|
|
25
|
+
#
|
|
26
|
+
# @return [HttpPaginatedResponse,Ably::Util::SafeDeferrable]
|
|
27
|
+
#
|
|
28
|
+
def next(&success_callback)
|
|
29
|
+
async_wrap_if_realtime(success_callback) do
|
|
30
|
+
return nil unless has_next?
|
|
31
|
+
HttpPaginatedResponse.new(client.get(pagination_url('next')), base_url, client, pagination_options, &each_block)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The HTTP status code of the response.
|
|
36
|
+
#
|
|
37
|
+
# @spec HP4
|
|
38
|
+
#
|
|
39
|
+
# @return [Integer]
|
|
40
|
+
#
|
|
41
|
+
def status_code
|
|
42
|
+
http_response.status.to_i
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Whether statusCode indicates success. This is equivalent to 200 <= statusCode < 300.
|
|
46
|
+
#
|
|
47
|
+
# @spec HP5
|
|
48
|
+
#
|
|
49
|
+
# @return [Boolean]
|
|
50
|
+
#
|
|
51
|
+
def success?
|
|
52
|
+
(200..299).include?(http_response.status.to_i)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The error code if the X-Ably-Errorcode HTTP header is sent in the response.
|
|
56
|
+
#
|
|
57
|
+
# @spec HP6
|
|
58
|
+
#
|
|
59
|
+
# @return [Integer]
|
|
60
|
+
#
|
|
61
|
+
def error_code
|
|
62
|
+
if http_response.headers['X-Ably-Errorcode']
|
|
63
|
+
http_response.headers['X-Ably-Errorcode'].to_i
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The error message if the X-Ably-Errormessage HTTP header is sent in the response.
|
|
68
|
+
#
|
|
69
|
+
# @spec HP7
|
|
70
|
+
#
|
|
71
|
+
# @return [String]
|
|
72
|
+
#
|
|
73
|
+
def error_message
|
|
74
|
+
http_response.headers['X-Ably-Errormessage']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# The headers of the response.
|
|
78
|
+
#
|
|
79
|
+
# @spec HP8
|
|
80
|
+
#
|
|
81
|
+
# @return [Hash<String, String>]
|
|
82
|
+
#
|
|
83
|
+
def headers
|
|
84
|
+
http_response.headers || {}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Farady compatible response object used when an exception is raised
|
|
88
|
+
# @api private
|
|
89
|
+
class ErrorResponse
|
|
90
|
+
def initialize(status, error_code, error_message)
|
|
91
|
+
@status = status
|
|
92
|
+
@error_code = error_code
|
|
93
|
+
@error_message = error_message
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def status
|
|
97
|
+
@status
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def headers
|
|
101
|
+
{
|
|
102
|
+
'X-Ably-Errorcode' => @error_code,
|
|
103
|
+
'X-Ably-Errormessage' => @error_message
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def body
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Ably::Modules
|
|
4
|
+
module Conversions
|
|
5
|
+
private
|
|
6
|
+
# Creates or returns an {IdiomaticRubyWrapper} ensuring it never wraps itself
|
|
7
|
+
#
|
|
8
|
+
# @return [IdiomaticRubyWrapper]
|
|
9
|
+
#
|
|
10
|
+
def IdiomaticRubyWrapper(object, options = {})
|
|
11
|
+
case object
|
|
12
|
+
when Ably::Models::IdiomaticRubyWrapper
|
|
13
|
+
object
|
|
14
|
+
else
|
|
15
|
+
Ably::Models::IdiomaticRubyWrapper.new(object, options)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Ably::Models
|
|
22
|
+
# Wraps Hash objects returned by Ably service to appear as Idiomatic Ruby Hashes with symbol keys
|
|
23
|
+
# It recursively wraps containing Hashes, but will stop wrapping at arrays, any other non Hash object, or any key matching the `:stops_at` options
|
|
24
|
+
# It also provides methods matching the symbolic keys for convenience
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# ruby_hash = IdiomaticRubyWrapper.new({ 'keyValue' => 'true' })
|
|
28
|
+
# # or recommended to avoid wrapping wrapped objects
|
|
29
|
+
# ruby_hash = IdiomaticRubyWrapper({ 'keyValue' => 'true' })
|
|
30
|
+
#
|
|
31
|
+
# ruby_hash[:key_value] # => 'true'
|
|
32
|
+
# ruby_hash.key_value # => 'true'
|
|
33
|
+
# ruby_hash[:key_value] = 'new_value'
|
|
34
|
+
# ruby_hash.key_value # => 'new_value'
|
|
35
|
+
#
|
|
36
|
+
# ruby_hash[:none] # => nil
|
|
37
|
+
# ruby_hash.none # => nil
|
|
38
|
+
#
|
|
39
|
+
# @!attribute [r] stop_at
|
|
40
|
+
# @return [Array<Symbol,String>] array of keys that this wrapper should stop wrapping at to preserve the underlying Hash as is
|
|
41
|
+
#
|
|
42
|
+
class IdiomaticRubyWrapper
|
|
43
|
+
include Enumerable
|
|
44
|
+
include Ably::Modules::Conversions
|
|
45
|
+
include Ably::Modules::MessagePack
|
|
46
|
+
|
|
47
|
+
attr_reader :stop_at
|
|
48
|
+
|
|
49
|
+
# Creates an IdiomaticRubyWrapper around the mixed case Hash object
|
|
50
|
+
#
|
|
51
|
+
# @attribute [Hash] mixedCaseHashObject mixed case Hash object
|
|
52
|
+
# @attribute [Array<Symbol,String>] stop_at array of keys that this wrapper should stop wrapping at to preserve the underlying Hash as is
|
|
53
|
+
#
|
|
54
|
+
def initialize(mixedCaseHashObject, options = {})
|
|
55
|
+
stop_at = options.fetch(:stop_at, [])
|
|
56
|
+
|
|
57
|
+
if mixedCaseHashObject.kind_of?(IdiomaticRubyWrapper)
|
|
58
|
+
$stderr.puts "<IdiomaticRubyWrapper#initialize> WARNING: Wrapping a IdiomaticRubyWrapper with another IdiomaticRubyWrapper"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@attributes = mixedCaseHashObject
|
|
62
|
+
@stop_at = Array(stop_at).each_with_object({}) do |key, object|
|
|
63
|
+
object[convert_to_snake_case_symbol(key)] = true
|
|
64
|
+
end.freeze
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def [](key)
|
|
68
|
+
value = attributes[source_key_for(key)]
|
|
69
|
+
if stop_at?(key) || !value.kind_of?(Hash)
|
|
70
|
+
value
|
|
71
|
+
else
|
|
72
|
+
IdiomaticRubyWrapper.new(value, stop_at: stop_at)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def []=(key, value)
|
|
77
|
+
attributes[source_key_for(key)] = value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def fetch(key, default = nil)
|
|
81
|
+
if has_key?(key)
|
|
82
|
+
self[key]
|
|
83
|
+
else
|
|
84
|
+
if default
|
|
85
|
+
default
|
|
86
|
+
elsif block_given?
|
|
87
|
+
yield key
|
|
88
|
+
else
|
|
89
|
+
raise KeyError, "key not found: #{key}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def size
|
|
95
|
+
attributes.size
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def empty?
|
|
99
|
+
attributes.empty?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def keys
|
|
103
|
+
map { |key, value| key }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def values
|
|
107
|
+
map { |key, value| value }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def has_key?(key)
|
|
111
|
+
attributes.has_key?(source_key_for(key))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Method ensuring this {IdiomaticRubyWrapper} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
|
|
115
|
+
def each
|
|
116
|
+
return to_enum(:each) unless block_given?
|
|
117
|
+
|
|
118
|
+
attributes.each do |key, value|
|
|
119
|
+
key = convert_to_snake_case_symbol(key)
|
|
120
|
+
value = self[key]
|
|
121
|
+
yield key, value
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Compare object based on Hash equivalent
|
|
126
|
+
def ==(other)
|
|
127
|
+
return false unless other.kind_of?(self.class) || other.kind_of?(Hash)
|
|
128
|
+
|
|
129
|
+
other = other.to_hash if other.kind_of?(self.class)
|
|
130
|
+
to_hash == other
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def method_missing(method_sym, *arguments)
|
|
134
|
+
key = method_sym.to_s.gsub(%r{=$}, '')
|
|
135
|
+
return super if !has_key?(key)
|
|
136
|
+
|
|
137
|
+
if method_sym.to_s.match(%r{=$})
|
|
138
|
+
raise ArgumentError, "Cannot set #{method_sym} with more than one argument" unless arguments.length == 1
|
|
139
|
+
self[key] = arguments.first
|
|
140
|
+
else
|
|
141
|
+
raise ArgumentError, "Cannot pass an argument to #{method_sym} when retrieving its value" unless arguments.empty?
|
|
142
|
+
self[method_sym]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @!attribute [r] Hash
|
|
147
|
+
# @return [Hash] Access to the raw Hash object provided to the constructor of this wrapper
|
|
148
|
+
def attributes
|
|
149
|
+
@attributes
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Takes the underlying Hash object and returns it in as a JSON ready Hash object using camelCase for compability with the Ably service.
|
|
153
|
+
# Note name clashes are ignored and will result in loss of one or more values
|
|
154
|
+
# @example
|
|
155
|
+
# wrapper = IdiomaticRubyWrapper({ 'mixedCase': true, mixed_case: false, 'snake_case': 1 })
|
|
156
|
+
# wrapper.as_json => { 'mixedCase': true, 'snakeCase': 1 }
|
|
157
|
+
def as_json(*args)
|
|
158
|
+
attributes.each_with_object({}) do |key_val, new_hash|
|
|
159
|
+
key = key_val[0]
|
|
160
|
+
mixed_case_key = convert_to_mixed_case(key)
|
|
161
|
+
wrapped_val = self[key]
|
|
162
|
+
wrapped_val = wrapped_val.as_json(args) if wrapped_val.kind_of?(IdiomaticRubyWrapper)
|
|
163
|
+
|
|
164
|
+
new_hash[mixed_case_key] = wrapped_val
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Converts the current wrapped mixedCase object to JSON
|
|
169
|
+
# using snakedCase syntax as expected by the Realtime API
|
|
170
|
+
def to_json(*args)
|
|
171
|
+
as_json(args).to_json
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Generate a symbolized Hash object representing the underlying Hash in a Ruby friendly format.
|
|
175
|
+
# Note name clashes are ignored and will result in loss of one or more values
|
|
176
|
+
# @example
|
|
177
|
+
# wrapper = IdiomaticRubyWrapper({ 'mixedCase': true, mixed_case: false, 'snake_case': 1 })
|
|
178
|
+
# wrapper.to_hash => { mixed_case: true, snake_case: 1 }
|
|
179
|
+
def to_hash(*args)
|
|
180
|
+
each_with_object({}) do |key_val, object|
|
|
181
|
+
key, val = key_val
|
|
182
|
+
val = val.to_hash(args) if val.kind_of?(IdiomaticRubyWrapper)
|
|
183
|
+
object[key] = val
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Method to create a duplicate of the underlying Hash object
|
|
188
|
+
# Useful when underlying Hash is frozen
|
|
189
|
+
def dup
|
|
190
|
+
Ably::Models::IdiomaticRubyWrapper.new(attributes.dup, stop_at: stop_at.keys)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Freeze the underlying data
|
|
194
|
+
def freeze
|
|
195
|
+
attributes.freeze
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def to_s
|
|
199
|
+
attributes.to_s
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# @!attribute [r] hash
|
|
203
|
+
# @return [Integer] Compute a hash-code for this hash. Two hashes with the same content will have the same hash code
|
|
204
|
+
def hash
|
|
205
|
+
attributes.hash
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
private
|
|
209
|
+
def stop_at?(key)
|
|
210
|
+
@stop_at.has_key?(key)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# We assume by default all keys are interchangeable between :this_format and 'thisFormat'
|
|
214
|
+
# However, this method will find other fallback formats such as CamelCase or :symbols if a matching
|
|
215
|
+
# key is not found in mixedCase.
|
|
216
|
+
def source_key_for(symbolized_key)
|
|
217
|
+
format_preferences = [
|
|
218
|
+
lambda { |key_sym| convert_to_mixed_case(key_sym) },
|
|
219
|
+
lambda { |key_sym| key_sym.to_sym },
|
|
220
|
+
lambda { |key_sym| key_sym.to_s },
|
|
221
|
+
lambda { |key_sym| convert_to_mixed_case(key_sym).to_sym },
|
|
222
|
+
lambda { |key_sym| convert_to_lower_case(key_sym) },
|
|
223
|
+
lambda { |key_sym| convert_to_lower_case(key_sym).to_sym },
|
|
224
|
+
lambda { |key_sym| convert_to_mixed_case(key_sym, force_camel: true) },
|
|
225
|
+
lambda { |key_sym| convert_to_mixed_case(key_sym, force_camel: true).to_sym }
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
preferred_format = format_preferences.detect do |format|
|
|
229
|
+
attributes.has_key?(format.call(symbolized_key))
|
|
230
|
+
end || format_preferences.first
|
|
231
|
+
|
|
232
|
+
preferred_format.call(symbolized_key)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|