conf/routes
GET /websocket/room WebSocket.Room WS /websocket/room/socket WebSocket.RoomSocket
app/views/WebSocket/room.html
{{set . "title" "Chat room"}} {{template "header.html" .}} <h1>WebSocket - You are now chatting as {{.user}} <a href="/">Leave</a></h1> <div id="thread"> <script type="text/html" id="message_tmpl"> {{raw "<%"}} if(event.Type == 'message') { %> <div class="message <%= event.User == '{{.user}}' ? 'you' : '' %>"> <h2>{{raw "<%"}}= event.User %></h2> <p> {{raw "<%"}}= event.Text %> </p> </div> {{raw "<%"}} } %> {{raw "<%"}} if(event.Type == 'join') { %> <div class="message notice"> <h2></h2> <p> {{raw "<%"}}= event.User %> joined the room </p> </div> {{raw "<%"}} } %> {{raw "<%"}} if(event.Type == 'leave') { %> <div class="message notice"> <h2></h2> <p> {{raw "<%"}}= event.User %> left the room </p> </div> {{raw "<%"}} } %> {{raw "<%"}} if(event.Type == 'quit') { %> <div class="message important"> <h2></h2> <p> You are disconnected! </p> </div> {{raw "<%"}} } %> </script> </div> <div id="newMessage"> <input type="text" id="message" autocomplete="off" autofocus> <input type="submit" value="send" id="send"> </div> <script type="text/javascript"> var socket = new WebSocket('ws://'+window.location.host+'/websocket/room/socket?user={{.user}}') var display = function(event){ $('#thread').append(tmpl('message_tmpl', {event: event})); $('#thread').scrollTo('max') } socket.onmessage = function(event){ display(JSON.parse(event.data)) } $('#send').click(function(e){ var message = $('#message').val() $('#message').val('') socket.send(JSON.stringify(message)) }) $('#message').keypress(function(e){ if(e.charCode == 13 || e.keyCode == 13){ $('#send').click() e.preventDefault() } }) </script>
app/views/header.html
<script src="/public/js/jquery.scrollTo-min.js"></script> <script src="/public/js/templating.js"></script>
chatroom.go
package chatroom import ( "container/list" "time" ) type Event struct { Type string User string Timestamp int Text string } type Subscription struct { Archive []Event New <-chan Event } func (s Subscription) Cancel(){ unsubscribe <- s.New drain(s.New) } func newEvent(typ, user, msg string) Event { return Event{typ, user, int(time.Now().Unix()), msg} } func Subscribe() Subscription { resp := make(chan Subscription) subscribe <- resp return <- resp } func Join(user string){ publish <- newEvent("join", user, "") } func Say(user, message string){ publish <- newEvent("message", user, message) } func Leave(user string){ publish <- newEvent("leave", user, "") } const archiveSize = 10 var ( subscribe = make(chan (chan<- Subscription), 10) unsubscribe = make(chan (<-chan Event), 10) publish = make(chan Event, 10) ) func chatroom(){ archive := list.New() subscribers := list.New() for { select { case ch := <-subscribe: var events []Event for e := archive.Front(); e != nil; e = e.Next(){ events = append(events, e.Value.(Event)) } subscriber := make(chan Event, 10) subscribers.PushBack(subscriber) ch <- Subscription{events, subscriber} case event := <-publish: for ch := subscribers.Front(); ch != nil; ch = ch.Next(){ ch.Value.(chan Event) <- event } if archive.Len() >= archiveSize { archive.Remove(archive.Front()) } archive.PushBack(event) case unsub := <-unsubscribe: for ch := subscribers.Front(); ch != nil; ch = ch.Next(){ if ch.Value.(chan Event) == unsub { subscribers.Remove(ch) break } } } } } func init(){ go chatroom() } func drain(ch <-chan Event){ for { select { case _, ok := <-ch: if !ok { return } default: return } } }
websocket.go
package controllers import ( "github.com/revel/revel" "app/app/chatroom" ) type WebSocket struct { *revel.Controller } func (c WebSocket) Room(user string) revel.Result { user = "hpscript" return c.Render(user) } func (c WebSocket) RoomSocket(user string, ws revel.ServerWebSocket) revel.Result { if ws == nil { return nil } subscription := chatroom.Subscribe() defer subscription.Cancel() chatroom.Join(user) defer chatroom.Leave(user) for _, event := range subscription.Archive { if ws.MessageSendJSON(&event) != nil { return nil } } newMessages := make(chan string) go func(){ var msg string for { err := ws.MessageReceiveJSON(&msg) if err != nil { close(newMessages) return } newMessages <- msg } }() for { select { case event := <-subscription.New: if ws.MessageSendJSON(&event) != nil { return nil } case msg, ok := <-newMessages: if !ok { return nil } chatroom.Say(user, msg) } } return nil }
public/js/templating.js
{{set . "title" "Chat room"}} {{template "header.html" .}} <h1>WebSocket - You are now chatting as {{.user}} <a href="/">Leave</a></h1> <div id="thread"> <script type="text/html" id="message_tmpl"> {{raw "<%"}} if(event.Type == 'message') { %> <div class="message <%= event.User == '{{.user}}' ? 'you' : '' %>"> <h2>{{raw "<%"}}= event.User %></h2> <p> {{raw "<%"}}= event.Text %> </p> </div> {{raw "<%"}} } %> {{raw "<%"}} if(event.Type == 'join') { %> <div class="message notice"> <h2></h2> <p> {{raw "<%"}}= event.User %> joined the room </p> </div> {{raw "<%"}} } %> {{raw "<%"}} if(event.Type == 'leave') { %> <div class="message notice"> <h2></h2> <p> {{raw "<%"}}= event.User %> left the room </p> </div> {{raw "<%"}} } %> {{raw "<%"}} if(event.Type == 'quit') { %> <div class="message important"> <h2></h2> <p> You are disconnected! </p> </div> {{raw "<%"}} } %> </script> </div> <div id="newMessage"> <input type="text" id="message" autocomplete="off" autofocus> <input type="submit" value="send" id="send"> </div> <script type="text/javascript"> var socket = new WebSocket('ws://'+window.location.host+'/websocket/room/socket?user={{.user}}') var display = function(event){ $('#thread').append(tmpl('message_tmpl', {event: event})); $('#thread').scrollTo('max') } socket.onmessage = function(event){ display(JSON.parse(event.data)) } $('#send').click(function(e){ var message = $('#message').val() $('#message').val('') socket.send(JSON.stringify(message)) }) $('#message').keypress(function(e){ if(e.charCode == 13 || e.keyCode == 13){ $('#send').click() e.preventDefault() } }) </script>
<% ●● %>: 構文
<%= ■■ %>: 出力
これは凄し