Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: alibby/ws-chat
base: 3ece261f3f
...
head fork: alibby/ws-chat
compare: 8771e898c9
Checking mergeability… Don't worry, you can still create the pull request.
  • 6 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
View
2  .gitignore
@@ -16,3 +16,5 @@ tmp
.yardoc
_yardoc
doc/
+.*.swp
+
View
7 Gemfile
@@ -0,0 +1,7 @@
+source 'http://rubygems.org'
+
+gem 'sinatra'
+gem 'em-websocket'
+gem 'thin'
+gem 'haml'
+gem 'json'
View
33 Gemfile.lock
@@ -0,0 +1,33 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ addressable (2.2.8)
+ daemons (1.1.8)
+ em-websocket (0.3.6)
+ addressable (>= 2.1.1)
+ eventmachine (>= 0.12.9)
+ eventmachine (0.12.10)
+ haml (3.1.4)
+ json (1.6.3)
+ rack (1.4.1)
+ rack-protection (1.2.0)
+ rack
+ sinatra (1.3.2)
+ rack (~> 1.3, >= 1.3.6)
+ rack-protection (~> 1.2)
+ tilt (~> 1.3, >= 1.3.3)
+ thin (1.3.1)
+ daemons (>= 1.0.9)
+ eventmachine (>= 0.12.6)
+ rack (>= 1.0.0)
+ tilt (1.3.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ em-websocket
+ haml
+ json
+ sinatra
+ thin
View
5 README.md
@@ -1,4 +1,7 @@
ws-chat
=======
-First crack at some web sockets code with Ruby/ Sinatra/ em-websockets
+
+Feel free to use this code according to the MIT license.
+
View
115 server.rb
@@ -0,0 +1,115 @@
+require 'rubygems'
+require 'em-websocket'
+require 'sinatra/base'
+require 'thin'
+require 'haml'
+require 'json'
+
+# Store sessions in memory - keyed by the object_id of
+# the underlying websocket connection. Since we always
+# get the same ws connection back with the same object_id
+# this seemed like a good safe hash key to use.
+class SessionStore
+ def initialize
+ @sessions = Hash.new
+ end
+
+ def <<(session)
+ session.session_store = self
+ @sessions[ session.connection.object_id ] = session
+ end
+
+ def delete_for_connection(ws)
+ @sessions.delete(ws.object_id)
+ end
+
+ def with_connection(ws,&blk)
+ yield @sessions[ ws.object_id ]
+ end
+
+ def each_peer(session, &blk)
+ @sessions.values.reject { |s| s == session }.each { |s| yield s }
+ end
+
+ def each(&blk)
+ @sessions.values.each { |s| yield s }
+ end
+end
+
+class ChatSession
+ attr_accessor :username, :connection, :session_store
+ def initialize(ws)
+ @session_store = nil
+ @connection = ws
+ @username = ''
+ end
+
+ def send(message)
+ puts "--> (%s) %s" % [ username, message.to_json ]
+ @connection.send( message.to_json )
+ end
+
+ def receive(message)
+ puts "<-- (%s) %s" % [ username, message.to_json ]
+ case message.type
+ when 'connect' then
+ self.username = message.content
+ session_store.each_peer(self) { |peer| peer.send message }
+ when 'chat' then
+ session_store.each { |s| s.send(message) }
+ end
+ end
+end
+
+class ChatMessage
+ attr_accessor :type, :content
+
+ def initialize(type, content)
+ @type = type
+ @content = content
+ end
+
+ def self.parse(packet)
+ tmp = JSON.parse(packet)
+ new tmp['type'], tmp['content']
+ end
+
+ def to_json
+ {
+ type: self.type,
+ content: self.content,
+ timestamp: DateTime.now.to_s
+ }.to_json
+ end
+end
+
+EventMachine.run do
+ class App < Sinatra::Base
+ get '/' do
+ haml :index
+ end
+ end
+
+ @sessions = SessionStore.new
+
+ EventMachine::WebSocket.start(:host => '0.0.0.0', :port => 3001) do |ws|
+
+ ws.onopen {
+ @sessions << ChatSession.new(ws)
+ }
+
+ ws.onmessage { |packet|
+ @sessions.with_connection(ws) do |session|
+ session.receive( ChatMessage.parse(packet) )
+ end
+ }
+
+ ws.onclose {
+ left = @sessions.delete_for_connection(ws)
+ @sessions.each { |s| s.send(ChatMessage.new("disconnect", left.username)) }
+ }
+ end
+
+ App.run!({:port => 3000})
+end
+
View
78 views/index.haml
@@ -0,0 +1,78 @@
+
+#identity_prmopt
+ %h1 Who are ya?
+ %form{ :id => 'name_form' }
+ %input{ :type => 'text', :name => :name }
+
+#chat_window.hidden
+ %h1 Chat
+ .status Warming
+ .history
+ %input{ :type => 'text' }
+
+:javascript
+ ChatterBox = function(name, chat_window) {
+ $this = this
+ $this.name = name
+ this.el = $(chat_window)
+ this.socket = new WebSocket('ws://#{request.host}:3001')
+
+ this.status = function(message) {
+ $this.el.find('.status').html(message)
+ }
+
+ this.show = function(msg) {
+ if(msg.type == 'chat')
+ $this.el.find('.history').append($("<p>").html(msg.content))
+ if(msg.type == 'connect')
+ $this.el.find('.history').append($("<p>").html(msg.content + " connected"))
+ if(msg.type == 'disconnect')
+ $this.el.find('.history').append($("<p>").html(msg.content + " disconnected"))
+
+ $this.el.find('.history').scrollTop(99999999)
+ }
+
+ this.send = function(message) {
+ this.socket.send(JSON.stringify(message))
+ }
+
+ this.send_chat = function(message) {
+ this.send({ type: 'chat', content: message })
+ }
+
+ this.send_connect = function(message) {
+ this.send({ type: 'connect', content: message })
+ }
+
+ this.socket.onopen = function(evt) {
+ $this.send_connect($this.name)
+ $this.status("Connected as " + $this.name )
+ }
+
+ this.socket.onmessage = function(evt) {
+ return $this.show(JSON.parse(evt.data))
+ }
+
+ this.socket.onclose = function(evt) {
+ $this.status('Disconnected')
+ }
+
+ this.el.find('input').on('keypress', function(event) {
+ if(event.keyCode != 13) return
+ $this.send_chat('[' + $this.name + '] ' + event.target.value)
+ event.target.value = ''
+ })
+ }
+
+ $(function() {
+ $('#name_form').on('submit', function(evt) {
+ $('#identity_prmopt').hide()
+ $('#chat_window').show()
+ cb = new ChatterBox($(evt.target).find('input').attr('value'), $('#chat_window'))
+ return false
+ })
+ $('form#name_form input').focus()
+ })
+
+
+
View
13 views/layout.haml
@@ -0,0 +1,13 @@
+!!!
+%html
+ %head
+ %title WebSockets Test
+ %script{ :type => 'text/javascript', :src => 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js' }
+ :css
+ h1 { font-size: 16px; }
+ .hidden { display: none; }
+ .history { font-size: 14px; width: 300px; height: 150px; border: 1px solid black; overflow-y: auto; }
+ .history p { padding: 1px; margin: 0; }
+
+ %body
+ = yield

No commit comments for this range

Something went wrong with that request. Please try again.