Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token naming refactor #29

Merged
merged 12 commits into from
Apr 23, 2015
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ end
All examples assume a client has been created as follows:

```ruby
# basic auth with an API key
client = Ably::Realtime.new(key: 'xxxxx')

# using token auth
client = Ably::Realtime.new(token: 'xxxxx')
```

### Connection
Expand Down Expand Up @@ -178,8 +182,10 @@ presence_page.next # retrieves the next page => #<Ably::Models::PaginatedResourc
### Generate Token and Token Request

```ruby
client.auth.request_token
# => #<Ably::Models::Token ...>
token_details = client.auth.request_token
# => #<Ably::Models::TokenDetails ...>
token_details.token # => "xVLyHw.CLchevH3hF....MDh9ZC_Q"
client = Ably::Rest.new(token: token_details.token)

token = client.auth.create_token_request
# => {"id"=>...,
Expand All @@ -189,8 +195,6 @@ token = client.auth.create_token_request
# "capability"=>"{\"*\":[\"*\"]}",
# "nonce"=>...,
# "mac"=>...}

client = Ably::Rest.new(token_id: token.id)
```

### Fetching your application's stats
Expand Down
331 changes: 186 additions & 145 deletions SPEC.md

Large diffs are not rendered by default.

234 changes: 146 additions & 88 deletions lib/ably/auth.rb

Large diffs are not rendered by default.

74 changes: 0 additions & 74 deletions lib/ably/models/token.rb

This file was deleted.

101 changes: 101 additions & 0 deletions lib/ably/models/token_details.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module Ably::Models
# Convert token details argument to a {TokenDetails} object
#
# @param attributes [TokenDetails,Hash] A {TokenDetails} object or Hash of token and meta data attributes
# @option attributes (see TokenDetails#initialize)
#
# @return [TokenDetails]
def self.TokenDetails(attributes)
case attributes
when TokenDetails
return attributes
else
TokenDetails.new(attributes)
end
end

# TokenDetails is a class providing details of the token string and the token's associated metadata,
# constructed from the response from Ably when request in a token via the REST API.
#
# Ruby {Time} objects are supported in place of Ably ms since epoch time fields. However, if a numeric is provided
# it must always be expressed in milliseconds as the Ably API always uses milliseconds for time fields.
#
class TokenDetails
include Ably::Modules::ModelCommon

# Buffer in seconds before a token is considered unusable
# For example, if buffer is 10s, the token can no longer be used for new requests 9s before it expires
TOKEN_EXPIRY_BUFFER = 5

def initialize(attributes)
@hash_object = IdiomaticRubyWrapper(attributes.clone.freeze)
end

# @param attributes
# @option attributes [String] :token token used to authenticate requests
# @option attributes [String] :key_name API key name used to create this token
# @option attributes [Time,Integer] :issued Time the token was issued as Time or Integer in milliseconds
# @option attributes [Time,Integer] :expires Time the token expires as Time or Integer in milliseconds
# @option attributes [String] :capability JSON stringified capabilities assigned to this token
# @option attributes [String] :client_id client ID assigned to this token
#
def initialize(attributes = {})
@hash_object = IdiomaticRubyWrapper(attributes.clone)

%w(issued expires).map(&:to_sym).each do |time_attribute|
hash[time_attribute] = (hash[time_attribute].to_f * 1000).round if hash[time_attribute].kind_of?(Time)
end

hash.freeze
end

# @!attribute [r] token
# @return [String] Token used to authenticate requests
def token
hash.fetch(:token)
end

# @!attribute [r] key_name
# @return [String] API key name used to create this token. An API key is made up of an API key name and secret delimited by a +:+
def key_name
hash.fetch(:key_name)
end

# @!attribute [r] issued
# @return [Time] Time the token was issued
def issued
as_time_from_epoch(hash.fetch(:issued), granularity: :ms)
end

# @!attribute [r] expires
# @return [Time] Time the token expires
def expires
as_time_from_epoch(hash.fetch(:expires), granularity: :ms)
end

# @!attribute [r] capability
# @return [Hash] Capabilities assigned to this token
def capability
JSON.parse(hash.fetch(:capability))
end

# @!attribute [r] client_id
# @return [String] Optional client ID assigned to this token
def client_id
hash[:client_id]
end

# Returns true if token is expired or about to expire
#
# @return [Boolean]
def expired?
expires < Time.now + TOKEN_EXPIRY_BUFFER
end

# @!attribute [r] hash
# @return [Hash] Access the token details Hash object ruby'fied to use symbolized keys
def hash
@hash_object
end
end
end
108 changes: 108 additions & 0 deletions lib/ably/models/token_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
module Ably::Models
# Convert token request argument to a {TokenRequest} object
#
# @param attributes [TokenRequest,Hash] A {TokenRequest} object or Hash of attributes to create a new token request
# @option attributes (see TokenRequest#initialize)
#
# @return [TokenRequest]
def self.TokenRequest(attributes)
case attributes
when TokenRequest
return attributes
else
TokenRequest.new(attributes)
end
end


# TokenRequest is a class that stores the attributes of a token request
#
# Ruby {Time} objects are supported in place of Ably ms since epoch time fields. However, if a numeric is provided
# it must always be expressed in milliseconds as the Ably API always uses milliseconds for time fields.
#
class TokenRequest
include Ably::Modules::ModelCommon

# @param attributes
# @option attributes [Integer] :ttl requested time to live for the token in milliseconds
# @option attributes [Time,Integer] :timestamp the timestamp of this request in milliseconds or as a {Time}
# @option attributes [String] :key_name API key name of the key against which this request is made
# @option attributes [String] :capability JSON stringified capability of the token
# @option attributes [String] :client_id client ID to associate with this token
# @option attributes [String] :nonce an opaque nonce string of at least 16 characters
# @option attributes [String] :mac the Message Authentication Code for this request
#
def initialize(attributes = {})
@hash_object = IdiomaticRubyWrapper(attributes.clone)
hash[:timestamp] = (hash[:timestamp].to_f * 1000).round if hash[:timestamp].kind_of?(Time)
hash.freeze
end

# @!attribute [r] key_name
# @return [String] API key name of the key against which this request is made. An API key is made up of an API key name and secret delimited by a +:+
def key_name
hash.fetch(:key_name)
end

# @!attribute [r] ttl
# @return [Integer] requested time to live for the token in seconds. If the token request is successful,
# the TTL of the returned token will be less than or equal to this value depending on application
# settings and the attributes of the issuing key.
# TTL when sent to Ably is in milliseconds.
def ttl
hash.fetch(:ttl) / 1000
end

# @!attribute [r] capability
# @return [Hash] capability of the token. If the token request is successful,
# the capability of the returned token will be the intersection of
# this capability with the capability of the issuing key.
def capability
JSON.parse(hash.fetch(:capability))
end

# @!attribute [r] client_id
# @return [String] the client ID to associate with this token. The generated token
# may be used to authenticate as this clientId.
def client_id
hash[:client_id]
end

# @!attribute [r] timestamp
# @return [Time] the timestamp of this request.
# Timestamps, in conjunction with the nonce, are used to prevent
# token requests from being replayed.
# Timestamp when sent to Ably is in milliseconds.
def timestamp
as_time_from_epoch(hash.fetch(:timestamp), granularity: :ms)
end

# @!attribute [r] nonce
# @return [String] an opaque nonce string of at least 16 characters to ensure
# uniqueness of this request. Any subsequent request using the
# same nonce will be rejected.
def nonce
hash.fetch(:nonce)
end

# @!attribute [r] mac
# @return [String] the Message Authentication Code for this request. See the
# {https://www.ably.io/documentation Ably Authentication documentation} for more details.
def mac
hash.fetch(:mac)
end

# Requests that the token is always persisted
# @api private
#
def persisted
hash.fetch(:persisted)
end

# @!attribute [r] hash
# @return [Hash] the token request Hash object ruby'fied to use symbolized keys
def hash
@hash_object
end
end
end
21 changes: 13 additions & 8 deletions lib/ably/realtime/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ def initialize(client, name, channel_options = {})
setup_presence
end

# Publish a message on the channel
# Publish a message on the channel.
#
# When publishing a message, if the channel is not attached, the channel is implicitly attached
#
# @param name [String] The event name of the message
# @param data [String,ByteArray] payload for the message
Expand All @@ -127,7 +129,9 @@ def publish(name, data, &success_block)
end
end

# Subscribe to messages matching providing event name, or all messages if event name not provided
# Subscribe to messages matching providing event name, or all messages if event name not provided.
#
# When subscribing to messages, if the channel is not attached, the channel is implicitly attached
#
# @param names [String] The event name of the message to subscribe to if provided. Defaults to all events.
# @yield [Ably::Models::Message] For each message received, the block is called
Expand Down Expand Up @@ -177,6 +181,8 @@ def detach(&success_block)
# presence on the channel and may also be used to obtain presence information
# and change events for other members of the channel.
#
# When accessing presence, if the channel is not attached, the channel is implicitly attached
#
# @return {Ably::Realtime::Presence}
#
def presence
Expand All @@ -186,22 +192,21 @@ def presence

# Return the message history of the channel
#
# Once attached to a channel, you can retrieve messages published on the channel before the
# channel was attached with the option <tt>until_attach: true</tt>. This is very useful for
# developers who wish to subscribe to new realtime messages yet also display historical messages with
# the guarantee that no messages have been missed.
# If the channel is attached, you can retrieve messages published on the channel before the
# channel was attached with the option <tt>until_attach: true</tt>. This is useful when a developer
# wishes to display historical messages with the guarantee that no messages have been missed since attach.
#
# @param (see Ably::Rest::Channel#history)
# @option options (see Ably::Rest::Channel#history)
# @option options [Boolean] :until_attach When true, request for history will be limited only to messages published before this channel was attached. Channel must be attached.
# @option options [Boolean] :until_attach When true, the history request will be limited only to messages published before this channel was attached. Channel must be attached
#
# @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] First {Ably::Models::PaginatedResource page} of {Ably::Models::Message} objects accessible with {Ably::Models::PaginatedResource#items #items}.
#
# @return [Ably::Util::SafeDeferrable]
#
def history(options = {}, &callback)
if options.delete(:until_attach)
raise ArgumentError, 'option :until_attach cannot be specified if the channel is not attached' unless attached?
raise ArgumentError, 'option :until_attach is invalid as the channel is not attached' unless attached?
options[:from_serial] = attached_serial
end

Expand Down
6 changes: 4 additions & 2 deletions lib/ably/realtime/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ module Realtime
# connected: 2
# disconnected: 3
# suspended: 4
# closed: 5
# failed: 6
# closing: 5
# closed: 6
# failed: 7
#
# Note that the states are available as Enum-like constants:
#
Expand All @@ -20,6 +21,7 @@ module Realtime
# Connection::STATE.Connected
# Connection::STATE.Disconnected
# Connection::STATE.Suspended
# Connection::STATE.Closing
# Connection::STATE.Closed
# Connection::STATE.Failed
#
Expand Down
Loading