ably 0.7.5 → 0.7.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.
Files changed (63) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +1 -0
  3. data/.gitmodules +3 -0
  4. data/README.md +46 -22
  5. data/SPEC.md +345 -240
  6. data/ably.gemspec +4 -2
  7. data/lib/ably/auth.rb +18 -14
  8. data/lib/ably/models/message.rb +1 -1
  9. data/lib/ably/models/paginated_resource.rb +31 -44
  10. data/lib/ably/models/presence_message.rb +1 -1
  11. data/lib/ably/models/stat.rb +67 -24
  12. data/lib/ably/models/stats_types.rb +131 -0
  13. data/lib/ably/modules/async_wrapper.rb +3 -2
  14. data/lib/ably/modules/message_emitter.rb +2 -2
  15. data/lib/ably/realtime.rb +1 -1
  16. data/lib/ably/realtime/channel.rb +24 -3
  17. data/lib/ably/realtime/channel/channel_manager.rb +1 -0
  18. data/lib/ably/realtime/client.rb +2 -2
  19. data/lib/ably/realtime/connection.rb +1 -1
  20. data/lib/ably/realtime/presence.rb +12 -1
  21. data/lib/ably/rest.rb +1 -1
  22. data/lib/ably/rest/channel.rb +4 -5
  23. data/lib/ably/rest/client.rb +5 -5
  24. data/lib/ably/rest/presence.rb +2 -2
  25. data/lib/ably/version.rb +1 -1
  26. data/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  27. data/spec/acceptance/realtime/channel_spec.rb +3 -3
  28. data/spec/acceptance/realtime/client_spec.rb +3 -3
  29. data/spec/acceptance/realtime/connection_failures_spec.rb +2 -2
  30. data/spec/acceptance/realtime/connection_spec.rb +4 -4
  31. data/spec/acceptance/realtime/message_spec.rb +5 -5
  32. data/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  33. data/spec/acceptance/realtime/presence_spec.rb +8 -8
  34. data/spec/acceptance/realtime/stats_spec.rb +1 -1
  35. data/spec/acceptance/realtime/time_spec.rb +1 -1
  36. data/spec/acceptance/rest/auth_spec.rb +31 -4
  37. data/spec/acceptance/rest/base_spec.rb +3 -3
  38. data/spec/acceptance/rest/channel_spec.rb +19 -19
  39. data/spec/acceptance/rest/channels_spec.rb +1 -1
  40. data/spec/acceptance/rest/client_spec.rb +9 -6
  41. data/spec/acceptance/rest/encoders_spec.rb +1 -1
  42. data/spec/acceptance/rest/message_spec.rb +10 -10
  43. data/spec/acceptance/rest/presence_spec.rb +81 -51
  44. data/spec/acceptance/rest/stats_spec.rb +46 -41
  45. data/spec/acceptance/rest/time_spec.rb +1 -1
  46. data/spec/shared/client_initializer_behaviour.rb +30 -19
  47. data/spec/spec_helper.rb +3 -0
  48. data/spec/support/markdown_spec_formatter.rb +1 -1
  49. data/spec/support/test_app.rb +11 -24
  50. data/spec/unit/auth_spec.rb +1 -1
  51. data/spec/unit/models/paginated_resource_spec.rb +81 -72
  52. data/spec/unit/models/stats_spec.rb +289 -0
  53. data/spec/unit/modules/async_wrapper_spec.rb +1 -1
  54. data/spec/unit/realtime/client_spec.rb +1 -1
  55. data/spec/unit/realtime/realtime_spec.rb +1 -1
  56. data/spec/unit/rest/channel_spec.rb +1 -1
  57. data/spec/unit/rest/client_spec.rb +8 -8
  58. data/spec/unit/rest/rest_spec.rb +1 -1
  59. data/spec/unit/util/crypto_spec.rb +1 -1
  60. metadata +55 -43
  61. data/spec/resources/crypto-data-128.json +0 -56
  62. data/spec/resources/crypto-data-256.json +0 -56
  63. data/spec/unit/models/stat_spec.rb +0 -113
@@ -8,8 +8,8 @@ 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 real-time messaging service}
12
- spec.summary = %q{A Ruby client library for ably.io, the real-time messaging service}
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}
13
13
  spec.homepage = 'http://github.com/ably/ably-ruby'
14
14
  spec.license = 'MIT'
15
15
 
@@ -33,4 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency 'rspec-retry'
34
34
  spec.add_development_dependency 'yard'
35
35
  spec.add_development_dependency 'webmock'
36
+
37
+ spec.add_development_dependency 'coveralls'
36
38
  end
@@ -15,7 +15,7 @@ module Ably
15
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
- # @!attribute [r] api_key
18
+ # @!attribute [r] key
19
19
  # @return [String] Complete API key containing both the key ID and key secret, if present
20
20
  # @!attribute [r] key_id
21
21
  # @return [String] Key ID (public part of the API key), if present
@@ -49,14 +49,16 @@ module Ably
49
49
  raise ArgumentError, 'Expected auth_options to be a Hash'
50
50
  end
51
51
 
52
- if auth_options[:api_key] && (auth_options[:key_secret] || auth_options[:key_id])
53
- raise ArgumentError, 'api_key and key_id or key_secret are mutually exclusive. Provider either an api_key or key_id & key_secret'
52
+ api_key = auth_options[:key] || auth_options[:api_key] # backwards support for previously used :api_key
53
+
54
+ if api_key && (auth_options[:key_secret] || auth_options[:key_id])
55
+ raise ArgumentError, 'key and key_id or key_secret are mutually exclusive. Provider either a key or key_id & key_secret'
54
56
  end
55
57
 
56
- split_api_key_into_key_and_secret! auth_options if auth_options[:api_key]
58
+ split_api_key_into_key_and_secret! auth_options if api_key
57
59
 
58
60
  if using_basic_auth? && !api_key_present?
59
- raise ArgumentError, 'api_key is missing. Either an API key, token, or token auth method must be provided'
61
+ raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided'
60
62
  end
61
63
 
62
64
  if has_client_id?
@@ -73,7 +75,7 @@ module Ably
73
75
  #
74
76
  # @param [Hash] options the options for the token request
75
77
  # @option options (see #request_token)
76
- # @option options [String] :api_key API key comprising the key ID and key secret in a single string
78
+ # @option options [String] :key API key comprising the key ID and key secret in a single string
77
79
  # @option options [Boolean] :force obtains a new token even if the current token is valid
78
80
  #
79
81
  # @yield (see #request_token)
@@ -84,7 +86,7 @@ module Ably
84
86
  #
85
87
  # @example
86
88
  # # will issue a simple token request using basic auth
87
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret')
89
+ # client = Ably::Rest::Client.new(key: 'key.id:secret')
88
90
  # token = client.auth.authorise
89
91
  #
90
92
  # # will use token request from block to authorise if not already authorised
@@ -99,7 +101,9 @@ module Ably
99
101
  end
100
102
 
101
103
  options = options.clone
102
- split_api_key_into_key_and_secret! options if options[:api_key]
104
+
105
+ api_key = options[:key] || options[:api_key] # backwards support for previously used :api_key
106
+ split_api_key_into_key_and_secret! options if api_key
103
107
 
104
108
  @options = @options.merge(options)
105
109
  @default_token_block = token_request_block if block_given?
@@ -131,7 +135,7 @@ module Ably
131
135
  #
132
136
  # @example
133
137
  # # simple token request using basic auth
134
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret')
138
+ # client = Ably::Rest::Client.new(key: 'key.id:secret')
135
139
  # token = client.auth.request_token
136
140
  #
137
141
  # # token request using auth block
@@ -179,7 +183,7 @@ module Ably
179
183
  # @return [Hash]
180
184
  #
181
185
  # @example
182
- # client.auth.create_request_token(id: 'asd.asd', ttl: 3600)
186
+ # client.auth.create_token_request(id: 'asd.asd', ttl: 3600)
183
187
  # # => {
184
188
  # # :id=>"asds.adsa",
185
189
  # # :client_id=>nil,
@@ -225,7 +229,7 @@ module Ably
225
229
  convert_to_mixed_case_hash(token_request)
226
230
  end
227
231
 
228
- def api_key
232
+ def key
229
233
  "#{key_id}:#{key_secret}" if api_key_present?
230
234
  end
231
235
 
@@ -308,12 +312,12 @@ module Ably
308
312
  # Basic Auth HTTP Authorization header value
309
313
  def basic_auth_header
310
314
  ensure_api_key_sent_over_secure_connection
311
- "Basic #{encode64("#{api_key}")}"
315
+ "Basic #{encode64("#{key}")}"
312
316
  end
313
317
 
314
318
  def split_api_key_into_key_and_secret!(options)
315
- api_key_parts = options[:api_key].to_s.match(/(?<id>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
316
- raise ArgumentError, 'api_key is invalid' unless api_key_parts
319
+ api_key_parts = (options[:key] || options[:api_key]).to_s.match(/(?<id>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
320
+ raise ArgumentError, 'key is invalid' unless api_key_parts
317
321
 
318
322
  options[:key_id] = api_key_parts[:id].encode(Encoding::UTF_8)
319
323
  options[:key_secret] = api_key_parts[:secret].encode(Encoding::UTF_8)
@@ -29,7 +29,7 @@ module Ably::Models
29
29
  # @return [Object] The encoding for the message data. Encoding and decoding of messages is handled automatically by the client library.
30
30
  # Therefore, the `encoding` attribute should always be nil unless an Ably library decoding error has occurred.
31
31
  # @!attribute [r] timestamp
32
- # @return [Time] Timestamp when the message was received by the Ably the real-time service
32
+ # @return [Time] Timestamp when the message was received by the Ably the realtime service
33
33
  # @!attribute [r] id
34
34
  # @return [String] A globally unique message ID
35
35
  # @!attribute [r] connection_id
@@ -1,12 +1,18 @@
1
1
  module Ably::Models
2
- # Wraps any Ably HTTP response that supports paging and automatically provides methods to iterate through
3
- # the array of resources using {#first_page}, {#next_page}, {#first_page?} and {#last_page?}
2
+ # Wraps any Ably HTTP response that supports paging and provides methods to iterate through
3
+ # the pages using {#first}, {#next}, {#first?}, {#has_next?} and {#last?}
4
+ #
5
+ # All items in the HTTP response are available in the Array returned from {#items}
4
6
  #
5
7
  # Paging information is provided by Ably in the LINK HTTP headers
8
+ #
6
9
  class PaginatedResource
7
- include Enumerable
8
10
  include Ably::Modules::AsyncWrapper if defined?(Ably::Realtime)
9
11
 
12
+ # The items contained within this {PaginatedResource}
13
+ # @return [Array]
14
+ attr_reader :items
15
+
10
16
  # @param [Faraday::Response] http_response Initial HTTP response from an Ably request to a paged resource
11
17
  # @param [String] base_url Base URL for request that generated the http_response so that subsequent paged requests can be made
12
18
  # @param [Client] client {Ably::Client} used to make the request to Ably
@@ -25,9 +31,9 @@ module Ably::Models
25
31
  @each_block = each_block
26
32
  @make_async = options.fetch(:async_blocking_operations, false)
27
33
 
28
- @body = http_response.body
29
- @body = coerce_items_into(body, @coerce_into) if @coerce_into
30
- @body = body.map { |item| yield item } if block_given?
34
+ @items = http_response.body
35
+ @items = coerce_items_into(items, @coerce_into) if @coerce_into
36
+ @items = items.map { |item| yield item } if block_given?
31
37
  end
32
38
 
33
39
  # Retrieve the first page of results.
@@ -35,8 +41,9 @@ module Ably::Models
35
41
  # and allows an optional success callback block to be provided.
36
42
  #
37
43
  # @return [PaginatedResource,Ably::Util::SafeDeferrable]
38
- def first_page(&success_callback)
44
+ def first(&success_callback)
39
45
  async_wrap_if_realtime(success_callback) do
46
+ return nil unless supports_pagination?
40
47
  PaginatedResource.new(client.get(pagination_url('first')), base_url, client, pagination_options, &each_block)
41
48
  end
42
49
  end
@@ -46,9 +53,9 @@ module Ably::Models
46
53
  # and allows an optional success callback block to be provided.
47
54
  #
48
55
  # @return [PaginatedResource,Ably::Util::SafeDeferrable]
49
- def next_page(&success_callback)
56
+ def next(&success_callback)
50
57
  async_wrap_if_realtime(success_callback) do
51
- raise Ably::Exceptions::InvalidPageError, 'There are no more pages' if supports_pagination? && last_page?
58
+ return nil unless has_next?
52
59
  PaginatedResource.new(client.get(pagination_url('next')), base_url, client, pagination_options, &each_block)
53
60
  end
54
61
  end
@@ -56,7 +63,7 @@ module Ably::Models
56
63
  # True if this is the last page in the paged resource set
57
64
  #
58
65
  # @return [Boolean]
59
- def last_page?
66
+ def last?
60
67
  !supports_pagination? ||
61
68
  pagination_header('next').nil?
62
69
  end
@@ -64,11 +71,18 @@ module Ably::Models
64
71
  # True if this is the first page in the paged resource set
65
72
  #
66
73
  # @return [Boolean]
67
- def first_page?
74
+ def first?
68
75
  !supports_pagination? ||
69
76
  pagination_header('first') == pagination_header('current')
70
77
  end
71
78
 
79
+ # True if there is a subsequent page in this paginated set available with {#next}
80
+ #
81
+ # @return [Boolean]
82
+ def has_next?
83
+ supports_pagination? && !last?
84
+ end
85
+
72
86
  # True if the HTTP response supports paging with the expected LINK HTTP headers
73
87
  #
74
88
  # @return [Boolean]
@@ -76,48 +90,21 @@ module Ably::Models
76
90
  !pagination_headers.empty?
77
91
  end
78
92
 
79
- # Standard Array accessor method
80
- def [](index)
81
- body[index]
82
- end
83
-
84
- # Returns number of items within this page, not the total number of items in the entire paged resource set
85
- def length
86
- body.length
87
- end
88
- alias_method :count, :length
89
- alias_method :size, :length
90
-
91
- # Method to allow {PaginatedResource} to be {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
92
- def each(&block)
93
- return to_enum(:each) unless block_given?
94
- body.each(&block)
95
- end
96
-
97
- # First item in this page
98
- def first
99
- body.first
100
- end
101
-
102
- # Last item in this page
103
- def last
104
- body.last
105
- end
106
-
107
93
  def inspect
108
94
  <<-EOF.gsub(/^ /, '')
109
95
  #<#{self.class.name}:#{self.object_id}
110
96
  @base_url="#{base_url}",
111
- @first_page?=#{!!first_page?},
112
- @last_page?=#{!!first_page?},
113
- @body=
114
- #{body.map { |item| item.inspect }.join(",\n ") }
97
+ @first?=#{!!first?},
98
+ @last?=#{!!first?},
99
+ @has_next?=#{!!has_next?},
100
+ @items=
101
+ #{items.map { |item| item.inspect }.join(",\n ") }
115
102
  >
116
103
  EOF
117
104
  end
118
105
 
119
106
  private
120
- attr_reader :body, :http_response, :base_url, :client, :coerce_into, :raw_body, :each_block, :make_async
107
+ attr_reader :http_response, :base_url, :client, :coerce_into, :raw_body, :each_block, :make_async
121
108
 
122
109
  def coerce_items_into(items, type_string)
123
110
  items.map do |item|
@@ -33,7 +33,7 @@ module Ably::Models
33
33
  # @return [Object] The encoding for the message data. Encoding and decoding of messages is handled automatically by the client library.
34
34
  # Therefore, the `encoding` attribute should always be nil unless an Ably library decoding error has occurred.
35
35
  # @!attribute [r] timestamp
36
- # @return [Time] Timestamp when the message was received by the Ably the real-time service
36
+ # @return [Time] Timestamp when the message was received by the Ably the realtime service
37
37
  # @!attribute [r] hash
38
38
  # @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
39
39
  #
@@ -1,38 +1,40 @@
1
+ require 'ably/models/stats_types'
2
+
1
3
  module Ably::Models
2
- # Convert stat argument to a {Stat} object
4
+ # Convert stat argument to a {Stats} object
3
5
  #
4
- # @param stat [Stat,Hash] A Stat object or Hash of stat properties
6
+ # @param stat [Stats,Hash] A Stats object or Hash of stat properties
5
7
  #
6
- # @return [Stat]
7
- def self.Stat(stat)
8
+ # @return [Stats]
9
+ def self.Stats(stat)
8
10
  case stat
9
- when Stat
11
+ when Stats
10
12
  stat
11
13
  else
12
- Stat.new(stat)
14
+ Stats.new(stat)
13
15
  end
14
16
  end
15
17
 
16
18
  # A class representing an individual statistic for a specified {#interval_id}
17
19
  #
18
20
  # @!attribute [r] all
19
- # @return [Hash] Breakdown of summary stats for all message types
21
+ # @return [Stats::MessageTypes] Breakdown of summary stats for all message types
20
22
  # @!attribute [r] inbound
21
- # @return [Hash] Breakdown of summary stats for traffic over various transport types for all inbound messages
23
+ # @return [Stats::MessageTraffic] Breakdown of summary stats for traffic over various transport types for all inbound messages
22
24
  # @!attribute [r] outbound
23
- # @return [Hash] Breakdown of summary stats for traffic over various transport types for all outbound messages
25
+ # @return [Stats::MessageTraffic] Breakdown of summary stats for traffic over various transport types for all outbound messages
24
26
  # @!attribute [r] persisted
25
- # @return [Hash] Breakdown of summary stats for all persisted messages
27
+ # @return [Stats::MessageTypes] Breakdown of summary stats for all persisted messages
26
28
  # @!attribute [r] connections
27
- # @return [Hash] A breakdown of summary stats data for different (TLS vs non-TLS) connection types.
29
+ # @return [Stats::ConnectionTypes] A breakdown of summary stats data for different (TLS vs non-TLS) connection types.
28
30
  # @!attribute [r] channels
29
- # @return [Hash] Aggregate data for usage of Channels
31
+ # @return [Stats::ResourceCount] Aggregate data for usage of Channels
30
32
  # @!attribute [r] api_requests
31
- # @return [Hash] Aggregate data for numbers of API requests
33
+ # @return [Stats::RequestCount] Aggregate data for numbers of API requests
32
34
  # @!attribute [r] token_requests
33
- # @return [Hash] Aggregate data for numbers of Token requests
35
+ # @return [Stats::RequestCount] Aggregate data for numbers of Token requests
34
36
  #
35
- class Stat
37
+ class Stats
36
38
  include Ably::Modules::ModelCommon
37
39
  extend Ably::Modules::Enum
38
40
 
@@ -53,7 +55,7 @@ module Ably::Models
53
55
  class << self
54
56
  # Convert a Time with the specified Granularity into an interval ID based on UTC 0 time
55
57
  # @example
56
- # Stat.to_interval_id(Time.now, :hour) # => '2015-01-01:10'
58
+ # Stats.to_interval_id(Time.now, :hour) # => '2015-01-01:10'
57
59
  #
58
60
  # @param time [Time] Time used to determine the interval
59
61
  # @param granularity [GRANULARITY] Granularity of the metrics such as :hour, :day
@@ -71,7 +73,7 @@ module Ably::Models
71
73
 
72
74
  # Returns the UTC 0 start Time of an interval_id
73
75
  # @example
74
- # Stat.from_interval_id('2015-01-01:10') # => 2015-01-01 10:00:00 +0000
76
+ # Stats.from_interval_id('2015-01-01:10') # => 2015-01-01 10:00:00 +0000
75
77
  #
76
78
  # @param interval_id [String]
77
79
  #
@@ -88,7 +90,7 @@ module Ably::Models
88
90
 
89
91
  # Returns the {GRANULARITY} determined from the interval_id
90
92
  # @example
91
- # Stat.granularity_from_interval_id('2015-01-01:10') # => :hour
93
+ # Stats.granularity_from_interval_id('2015-01-01:10') # => :hour
92
94
  #
93
95
  # @param interval_id [String]
94
96
  #
@@ -109,20 +111,61 @@ module Ably::Models
109
111
  end
110
112
  end
111
113
 
112
- # {Stat} initializer
114
+ # {Stats} initializer
113
115
  #
114
116
  # @param hash_object [Hash] object with the underlying stat details
115
117
  #
116
118
  def initialize(hash_object)
117
119
  @raw_hash_object = hash_object
118
-
119
120
  set_hash_object hash_object
120
121
  end
121
122
 
122
- %w( all inbound outbound persisted connections channels api_requests token_requests ).each do |attribute|
123
- define_method attribute do
124
- hash[attribute.to_sym]
125
- end
123
+ # Aggregates inbound and outbound messages
124
+ # return {@Stats::MessageTypes}
125
+ def all
126
+ @all ||= Stats::MessageTypes.new(hash[:all])
127
+ end
128
+
129
+ # All inbound messages i.e. received by Ably from clients
130
+ # @return {Stats::MessageTraffic}
131
+ def inbound
132
+ @inbound ||= Stats::MessageTraffic.new(hash[:inbound])
133
+ end
134
+
135
+ # All outbound messages i.e. sent from Ably to clients
136
+ # @return {Stats::MessageTraffic}
137
+ def outbound
138
+ @outbound ||= Stats::MessageTraffic.new(hash[:outbound])
139
+ end
140
+
141
+ # Messages persisted for later retrieval via the history API
142
+ # @return {Stats::MessageTypes}
143
+ def persisted
144
+ @persisted ||= Stats::MessageTypes.new(hash[:persisted])
145
+ end
146
+
147
+ # Breakdown of connection stats data for different (TLS vs non-TLS) connection types
148
+ # @return {Stats::ConnectionTypes}
149
+ def connections
150
+ @connections ||= Stats::ConnectionTypes.new(hash[:connections])
151
+ end
152
+
153
+ # Breakdown of channels stats
154
+ # @return {Stats::ResourceCount}
155
+ def channels
156
+ @channels ||= Stats::ResourceCount.new(hash[:channels])
157
+ end
158
+
159
+ # Breakdown of API requests received via the REST API
160
+ # @return {Stats::RequestCount}
161
+ def api_requests
162
+ @api_requests ||= Stats::RequestCount.new(hash[:api_requests])
163
+ end
164
+
165
+ # Breakdown of Token requests received via the REST API
166
+ # @return {Stats::RequestCount}
167
+ def token_requests
168
+ @token_requests ||= Stats::RequestCount.new(hash[:token_requests])
126
169
  end
127
170
 
128
171
  # @!attribute [r] interval_id
@@ -0,0 +1,131 @@
1
+ module Ably::Models
2
+ class Stats
3
+ # StatsStruct is a basic Struct like class that allows methods to be defined
4
+ # on the class that will be retuned co-erced objects from the underlying hash used to
5
+ # initialize the object.
6
+ #
7
+ # This class provides a concise way to create classes that have fixed attributes and types
8
+ #
9
+ # @example
10
+ # class MessageCount < StatsStruct
11
+ # coerce_attributes :count, :data, into: Integer
12
+ # end
13
+ #
14
+ # @api private
15
+ #
16
+ class StatsStruct
17
+ class << self
18
+ def coerce_attributes(*attributes)
19
+ options = attributes.pop
20
+ raise ArgumentError, 'Expected attribute into: within options hash' unless options.kind_of?(Hash) && options[:into]
21
+
22
+ @type_klass = options[:into]
23
+ setup_attribute_methods attributes
24
+ end
25
+
26
+ def type_klass
27
+ @type_klass
28
+ end
29
+
30
+ private
31
+ def setup_attribute_methods(attributes)
32
+ attributes.each do |attr|
33
+ define_method(attr) do
34
+ # Lazy load the co-erced value only when accessed
35
+ unless instance_variable_defined?("@#{attr}")
36
+ instance_variable_set "@#{attr}", self.class.type_klass.new(hash[attr.to_sym])
37
+ end
38
+ instance_variable_get("@#{attr}")
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ attr_reader :hash
45
+
46
+ def initialize(hash)
47
+ @hash = hash || {}
48
+ end
49
+ end
50
+
51
+ # IntegerDefaultZero will always return an Integer object and will default to value 0 unless truthy
52
+ #
53
+ # @api private
54
+ #
55
+ class IntegerDefaultZero
56
+ def self.new(value)
57
+ (value && value.to_i) || 0
58
+ end
59
+ end
60
+
61
+ # MessageCount contains aggregate counts for messages and data transferred
62
+ # @!attribute [r] count
63
+ # @return [Integer] count of all messages
64
+ # @!attribute [r] data
65
+ # @return [Integer] total data transferred for all messages in bytes
66
+ class MessageCount < StatsStruct
67
+ coerce_attributes :count, :data, into: IntegerDefaultZero
68
+ end
69
+
70
+ # RequestCount contains aggregate counts for requests made
71
+ # @!attribute [r] succeeded
72
+ # @return [Integer] requests succeeded
73
+ # @!attribute [r] failed
74
+ # @return [Integer] requests failed
75
+ # @!attribute [r] refused
76
+ # @return [Integer] requests refused typically as a result of permissions or a limit being exceeded
77
+ class RequestCount < StatsStruct
78
+ coerce_attributes :succeeded, :failed, :refused, into: IntegerDefaultZero
79
+ end
80
+
81
+ # ResourceCount contains aggregate data for usage of a resource in a specific scope
82
+ # @!attribute [r] opened
83
+ # @return [Integer] total resources of this type opened
84
+ # @!attribute [r] peak
85
+ # @return [Integer] peak resources of this type used for this period
86
+ # @!attribute [r] mean
87
+ # @return [Integer] average resources of this type used for this period
88
+ # @!attribute [r] min
89
+ # @return [Integer] minimum total resources of this type used for this period
90
+ # @!attribute [r] refused
91
+ # @return [Integer] resource requests refused within this period
92
+ class ResourceCount < StatsStruct
93
+ coerce_attributes :opened, :peak, :mean, :min, :refused, into: IntegerDefaultZero
94
+ end
95
+
96
+ # ConnectionTypes contains a breakdown of summary stats data for different (TLS vs non-TLS) connection types
97
+ # @!attribute [r] tls
98
+ # @return [ResourceCount] TLS connection count
99
+ # @!attribute [r] plain
100
+ # @return [ResourceCount] non-TLS connection count (unencrypted)
101
+ # @!attribute [r] all
102
+ # @return [ResourceCount] all connection count (includes both TLS & non-TLS connections)
103
+ class ConnectionTypes < StatsStruct
104
+ coerce_attributes :tls, :plain, :all, into: ResourceCount
105
+ end
106
+
107
+ # MessageTypes contains a breakdown of summary stats data for different (message vs presence) message types
108
+ # @!attribute [r] messages
109
+ # @return [MessageCount] count of channel messages
110
+ # @!attribute [r] presence
111
+ # @return [MessageCount] count of presence messages
112
+ # @!attribute [r] all
113
+ # @return [MessageCount] all messages count (includes both presence & messages)
114
+ class MessageTypes < StatsStruct
115
+ coerce_attributes :messages, :presence, :all, into: MessageCount
116
+ end
117
+
118
+ # MessageTraffic contains a breakdown of summary stats data for traffic over various transport types
119
+ # @!attribute [r] realtime
120
+ # @return [MessageTypes] count of messages transferred over a realtime transport such as WebSockets
121
+ # @!attribute [r] rest
122
+ # @return [MessageTypes] count of messages transferred using REST
123
+ # @!attribute [r] webhook
124
+ # @return [MessageTypes] count of messages delivered using WebHooks
125
+ # @!attribute [r] all
126
+ # @return [MessageTypes] all messages count (includes realtime, rest and webhook messages)
127
+ class MessageTraffic < StatsStruct
128
+ coerce_attributes :realtime, :rest, :webhook, :all, into: MessageTypes
129
+ end
130
+ end
131
+ end