ably 0.7.5 → 0.7.6

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