Class: BrowserChannel::Session

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

Overview

A Session manages two arrays of responses. One is maintained in case the connection is dropped so that we don’t lose messages. This gets truncated as we receive ‘AID’ information from the client indicating the data was received. The other array collects outgoing data for the currently bound channel so it can be sent in a single chunk with other data in the next tick.

Constant Summary collapse

ID_CHARACTERS =
(('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id) ⇒ Session

Returns a new instance of Session.



126
127
128
129
130
131
# File 'lib/browser_channel.rb', line 126

def initialize id
  @id = id
  @array_id = -1
  @messages = []
  unbind(@channel = nil) # trick into init
end

Instance Attribute Details

#array_idObject

Returns the value of attribute array_id.



124
125
126
# File 'lib/browser_channel.rb', line 124

def array_id
  @array_id
end

#idObject

Returns the value of attribute id.



124
125
126
# File 'lib/browser_channel.rb', line 124

def id
  @id
end

#messagesObject

Returns the value of attribute messages.



124
125
126
# File 'lib/browser_channel.rb', line 124

def messages
  @messages
end

#unbound_atObject

Returns the value of attribute unbound_at.



124
125
126
# File 'lib/browser_channel.rb', line 124

def unbound_at
  @unbound_at
end

Class Method Details

.new(options, id, array_id) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/browser_channel.rb', line 92

def self.new options, id, array_id
  if Time.now > @@sessions_gc
    to_terminate = []
    @@sessions.each do |key, session|
      next unless session.unbound_at
      next if Time.now - session.unbound_at < options[:gc_max_age]
      to_terminate << key 
    end
    to_terminate.each do |key|
      @@sessions[key].terminate
    end
    @@sessions_gc = Time.now + options[:gc_frequency]
  end
  if id
    session = @@sessions[id]
    if session and array_id and !array_id.empty?
      array_id = array_id.to_i
      messages = session.messages
      messages.shift until messages.empty? or messages.first[0] > array_id
    end
    return session 
  end
  while !id or @@sessions.has_key? id
    id = (0..32).map{ID_CHARACTERS[rand(ID_CHARACTERS.size)]}.join 
  end
  allocate.instance_eval do
    @handler = options[:handler].new self
    initialize id
    @@sessions[id] = self
  end
end

Instance Method Details

#bind(channel) ⇒ Object



133
134
135
136
137
# File 'lib/browser_channel.rb', line 133

def bind channel
  @channel = channel
  @channel_queue = []
  @unbound_at = nil
end

#call(post_data) ⇒ Object

Data from the browser arrives here.



153
154
155
156
157
158
159
# File 'lib/browser_channel.rb', line 153

def call post_data
  session_bound = @channel ? 1 : 0
  pending_bytes = @messages.empty? ? 0 : @session.to_json.bytesize
  response = [session_bound, @array_id, pending_bytes]
  @handler.call post_data
  response
end

#push(array) ⇒ Object Also known as: <<

Adds a new message and schedules it to be sent. Magic [‘stop’] will tell the browser server is going down.



163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/browser_channel.rb', line 163

def push array
  message = [@array_id += 1, array]
  @messages << message
  return unless @channel
  if @channel_queue.empty?
    EventMachine.next_tick do
      if @channel
        @channel.send_data @channel_queue
        @channel_queue = []
      end
    end
  end
  @channel_queue << message
end

#terminateObject



147
148
149
150
# File 'lib/browser_channel.rb', line 147

def terminate
  @handler.terminate if @handler.respond_to? :terminate
  @@sessions.delete @id
end

#unbind(channel) ⇒ Object

A channel will request to unbind itself after the connection completes. It’s possible another connection has taken over so abort if it looks so.



141
142
143
144
145
# File 'lib/browser_channel.rb', line 141

def unbind channel
  return if @channel != channel
  @channel = channel
  @unbound_at = Time.now
end