Class: DuoRuby::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/duoruby/client.rb

Overview

Represents a single connected WebSocket client.

Client wraps the low-level connection writer and acts as a typed key-value store for per-connection application state (e.g. current room, display name, authentication status).

Instances are created by Server#connect and passed as the first argument to every server event handler.

Examples:

Sending a message to a client

client.send(:snapshot, rooms: ["lobby"], users: ["Alice"])

Storing and reading application state

client[:name] = "Alice"
client[:name]  # => "Alice"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id:, writer: nil, metadata: {}) {|message| ... } ⇒ Client

Returns a new instance of Client.

Parameters:

  • id (String)

    a unique identifier for this connection

  • writer (Proc, nil) (defaults to: nil)

    callable that accepts a serialized message Hash; mutually exclusive with the block form

Yield Parameters:

  • message (Hash)

    the serialized message to deliver



39
40
41
42
43
44
45
46
47
48
# File 'lib/duoruby/client.rb', line 39

def initialize(id:, writer: nil, metadata: {}, &writer_block)
  @id = id
  @writer = writer || writer_block
  @metadata = 
  @attributes = {}
  @groups = {}
  @accepted = true
  @pending_calls = {}
  @next_call_id = 0
end

Instance Attribute Details

#attributesHash{Symbol => Object} (readonly)

Returns per-client application state.

Returns:

  • (Hash{Symbol => Object})

    per-client application state



28
29
30
# File 'lib/duoruby/client.rb', line 28

def attributes
  @attributes
end

#groupsHash{Symbol => Group} (readonly)

Returns groups this client currently belongs to.

Returns:

  • (Hash{Symbol => Group})

    groups this client currently belongs to



31
32
33
# File 'lib/duoruby/client.rb', line 31

def groups
  @groups
end

#idString (readonly)

Returns the unique connection identifier assigned by the server.

Returns:

  • (String)

    the unique connection identifier assigned by the server



25
26
27
# File 'lib/duoruby/client.rb', line 25

def id
  @id
end

#metadataObject (readonly)

Returns the value of attribute metadata.



33
34
35
# File 'lib/duoruby/client.rb', line 33

def 
  @metadata
end

Instance Method Details

#[](key) ⇒ Object?

Reads an application attribute by symbol key.

Parameters:

  • key (String, Symbol)

Returns:

  • (Object, nil)


53
54
55
# File 'lib/duoruby/client.rb', line 53

def [](key)
  attributes[key.to_sym]
end

#[]=(key, value) ⇒ Object

Writes an application attribute. Keys are always stored as symbols.

Parameters:

  • key (String, Symbol)
  • value (Object)


60
61
62
# File 'lib/duoruby/client.rb', line 60

def []=(key, value)
  attributes[key.to_sym] = value
end

#accepted?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'lib/duoruby/client.rb', line 107

def accepted?
  @accepted
end

#cancel_pending_calls(code: :disconnect, message: "connection closed", details: nil) ⇒ Object



92
93
94
95
96
97
# File 'lib/duoruby/client.rb', line 92

def cancel_pending_calls(code: :disconnect, message: "connection closed", details: nil)
  error = ReplyError.new(code: code, message: message, details: details)
  @pending_calls.each_value { |promise| promise.reject(error) }
  @pending_calls.clear
  self
end

#channel(name) ⇒ Object



78
79
80
# File 'lib/duoruby/client.rb', line 78

def channel(name)
  Channel::Namespace.new(self, name)
end

#deliver(message) ⇒ Object



74
75
76
# File 'lib/duoruby/client.rb', line 74

def deliver(message)
  @writer.call(Message.coerce(message).to_h)
end

#join(group) ⇒ Object



99
100
101
# File 'lib/duoruby/client.rb', line 99

def join(group)
  group.add(self)
end

#leave(group) ⇒ Object



103
104
105
# File 'lib/duoruby/client.rb', line 103

def leave(group)
  group.remove(self)
end

#reject(code: :unauthorized, message: "connection rejected", details: nil) ⇒ Object



111
112
113
114
115
116
# File 'lib/duoruby/client.rb', line 111

def reject(code: :unauthorized, message: "connection rejected", details: nil)
  @accepted = false
  cancel_pending_calls(code: code, message: message, details: details)
  deliver(Message.error(code: code, message: message, details: details))
  self
end

#reject_call(message) ⇒ Object



87
88
89
90
# File 'lib/duoruby/client.rb', line 87

def reject_call(message)
  promise = @pending_calls.delete(message.reply_to)
  promise&.reject(ReplyError.new(message.params))
end

#resolve_call(message) ⇒ Object



82
83
84
85
# File 'lib/duoruby/client.rb', line 82

def resolve_call(message)
  promise = @pending_calls.delete(message.reply_to)
  promise&.resolve(message.params[:result])
end

#send(event, **params) ⇒ Object

Sends a message to this client over the WebSocket connection.

Parameters:

  • event (String, Symbol)

    the event name

  • params

    keyword arguments that become the message params



68
69
70
71
72
# File 'lib/duoruby/client.rb', line 68

def send(event, **params)
  return send_question(event, **params) if question_event?(event)

  @writer.call(Message.new(event, **params).to_h)
end