[Go言語] revelのexamplesのchatを覗いてみる

examplesのgit: https://github.com/revel/examples

$ git clone https://github.com/revel/examples.git
$ revel run github.com/revel/examples/chat

templateのjs

  var socket = new WebSocket('ws://'+window.location.host+'/websocket/room/socket?user={{.user}}')

  // Display a message
  var display = function(event) {
    $('#thread').append(tmpl('message_tmpl', {event: event}));
    $('#thread').scrollTo('max')
  }

  // Message received on the socket
  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()
    }
  })

route

GET     /websocket/room                         WebSocket.Room
WS      /websocket/room/socket                  WebSocket.RoomSocket

top

<form action="{{url "Application.EnterDemo"}}">
      {{if .flash.error}}
        <p class="error">
          {{.flash.error}}
        </p>
      {{end}}
      <p>
        <label for="nick">Choose a nick name</label>
        <input type="text" name="user" id="user">
      </p>
      <p>
        <label for="nick">Demonstration</label>
        <select name="demo">
          <option></option>
          <option value="refresh">Ajax, active refresh</option>
          <option value="longpolling">Ajax, long polling</option>
          <option value="websocket">WebSocket</option>
        </select>
      </p>
      <p>
        <label></label>
        <input type="submit" id="enter" value="Enter the chat room">
      </p>
    </form>

controller

func (c Application) EnterDemo(user, demo string) revel.Result {
	c.Validation.Required(user)
	c.Validation.Required(demo)

	if c.Validation.HasErrors() {
		c.Flash.Error("Please choose a nick name and the demonstration type.")
		return c.Redirect(Application.Index)
	}

	switch demo {
	case "refresh":
		return c.Redirect("/refresh?user=%s", user)
	case "longpolling":
		return c.Redirect("/longpolling/room?user=%s", user)
	case "websocket":
		return c.Redirect("/websocket/room?user=%s", user)
	}
	return nil
}

controller

package controllers

import (
	"github.com/revel/revel"

	"github.com/revel/examples/chat/app/chatroom"
)

type WebSocket struct {
	*revel.Controller
}

func (c WebSocket) Room(user string) revel.Result {
	return c.Render(user)
}

func (c WebSocket) RoomSocket(user string, ws revel.ServerWebSocket) revel.Result {
	// Make sure the websocket is valid.
	if ws == nil {
		return nil
	}

	// Join the room.
	subscription := chatroom.Subscribe()
	defer subscription.Cancel()

	chatroom.Join(user)
	defer chatroom.Leave(user)

	// Send down the archive.
	for _, event := range subscription.Archive {
		if ws.MessageSendJSON(&event) != nil {
			// They disconnected
			return nil
		}
	}

	// In order to select between websocket messages and subscription events, we
	// need to stuff websocket events into a channel.
	newMessages := make(chan string)
	go func() {
		var msg string
		for {
			err := ws.MessageReceiveJSON(&msg)
			if err != nil {
				close(newMessages)
				return
			}
			newMessages <- msg
		}
	}()

	// Now listen for new events from either the websocket or the chatroom.
	for {
		select {
		case event := <-subscription.New:
			if ws.MessageSendJSON(&event) != nil {
				// They disconnected.
				return nil
			}
		case msg, ok := <-newMessages:
			// If the channel is closed, they disconnected.
			if !ok {
				return nil
			}

			// Otherwise, say something.
			chatroom.Say(user, msg)
		}
	}

	return nil
}

なるほど、なんとなく構成要素は分かったので、revelでやってみますか。