Josh Ferrara
Josh Ferrara

Reputation: 767

Undefined class variable when defined in constructor

Just started using CoffeeScript and have hit a wall. In my code below I construct WebSocketServer with a number as the first argument and a function as the second argument. When the code is ran and the websocket receives a message, @msgHandler is now magically undefined. I tried to work around this by setting a variable handler to @msgHandler but that ended up being undefined as well. If anyone has any ideas I'd love to hear them, thanks!

main.coffee

#Used to communicate with browser interface
webSocketServ = new sio.WebSocketServer Sauce.Server.WebSocketPort, (data, socketId) ->
    try
        json = JSON.parse(data)
        msgType  = json.msgType
    catch error
        return

    if (this.isAuthed(socketId))
        switch msgType
            when "auth"

            else
                io.websocket 'TODO: ' + cmd        
    else
        if msgType is 'auth'
            token = json.token

socket.coffee

class WebSocketServer
    constructor: (@port, @msgHandler) ->
        @webSockets = []
        @handlers = {}
        @test = []
        @authedSockes = []
        @listen(@port);
        console.log @msgHandler #msgHandler is defined here as [ Function ]

    listen: (port) ->
        @wsServ = engine.listen port
        @wsServ.on 'connection', @onConnect
        io.socket "WebServer socket started on port #{port}"

    onConnect: (client) ->
        io.websocket 'New connection with id of ' + client.id

        handler = @msgHandler #@msgHandler is undefined here?

        client.on 'message', (data) ->
            handler data, client.id
            io.websocket '[' + this.id + '] ' + JSON.stringify(data)

        client.on 'close', ->
            io.websocket '[' + this.id + '] Disconnect'

        client.on 'error', (err) ->
            io.websocket "IO error: " + err

compiled socket.coffee

  WebSocketServer = (function() {
    function WebSocketServer(port, msgHandler) {
      this.port = port;
      this.msgHandler = msgHandler;
      this.webSockets = [];
      this.handlers = {};
      this.test = [];
      this.authedSockes = [];
      this.listen(this.port);
      console.log(this.msgHandler);
    }

    WebSocketServer.prototype.listen = function(port) {
      this.wsServ = engine.listen(port);
      this.wsServ.on('connection', this.onConnect);
      return io.socket("WebServer socket started on port " + port);
    };

    WebSocketServer.prototype.onConnect = function(client) {
      var handler;
      io.websocket('New connection with id of ' + client.id);
      handler = this.msgHandler;
      client.on('message', function(data) {
        handler(data, client.id);
        return io.websocket('[' + this.id + '] ' + JSON.stringify(data));
      });
      client.on('close', function() {
        return io.websocket('[' + this.id + '] Disconnect');
      });
      return client.on('error', function(err) {
        return io.websocket("IO error: " + err);
      });
    };

    WebSocketServer.prototype.isAuthed = function(socketId) {
      return __indexOf.call(this.authedSockets, user) >= 0;
    };

    WebSocketServer.prototype.authSocket = function(socketId) {
      this.authedSockets.push(socketId);
      return io.websocket('Authed socket with ID of ' + socketId);
    };

    WebSocketServer.prototype.deauthSocket = function(socketId) {
      return this.authedSockets = this.authedSockets.filter(function(word) {
        return word !== socketId;
      });
    };

    return WebSocketServer;

  })();

Upvotes: 0

Views: 216

Answers (1)

Bergi
Bergi

Reputation: 664317

It's not an undefined class variable, you mean that you cannot access an instance property. It's caused by the common this context issue in callbacks - you cannot just pass a method and expect it to know the instance.

Fortunately, this can be trivially fixed in CS using the fat arrow syntax for the method definition and other callbacks:

    onConnect: (client) =>
#                       ^^
        io.websocket 'New connection with id of ' + client.id

        handler = @msgHandler # @ is now the expected instance

        client.on 'message', (data) =>
#                                   ^^
            handler data, client.id
            io.websocket '[' + @id + '] ' + JSON.stringify(data)
#                              ^ or did you mean to use `client.` here?

        client.on 'close', =>
#                          ^^
            io.websocket '[' + @id + '] Disconnect'

        client.on 'error', (err) ->
            io.websocket "IO error: " + err

A different (and possibly better) approach might be to let your WebSocketServer class inherit from the engine's class, instead of building a wrapper around it. Since all callbacks are called on the server instance usually, you could access your properties directly without needing to bind the callbacks.

Upvotes: 3

Related Questions