1

First I should point out that I'm new at both JS and node.js. I'm trying to write a node.js chat bot, currently working at commands.

My problem is with calling a method of a method .. if that's even possible. This is the relevant part :

    var text; // received message from user
    var userId; // Id of the user, number.
    if (cmd.hasOwnProperty(text.split(' ')[0])) { // Message will be a string, it should be ignore if the first word is not a command name.
        console.log("[Inc]" + userId + ": " + text)
        if (text.split(' ').length === 1) { // If the command name is not followed by any arguments then the only argument passed should be the userID(used to send replies to the user).
            cmd[text.split(' ')[0]](userId)
        } else {
            var args = [] // Placing all args in  an array because the commands will take different number of arguments, a calculator command for example could take a lot of args.
            for (var i = 1; i < text.split(' ').length; i++) {
                args.push(text.split(' ')[i])
            }
            console.log(args)
            cmd[text.split(' ')[0]](userId, args)
        }   
    } else {
        console.log("Command not found, try again");    
    }
    var cmd = {
        help : function () {
            cookies : function () { // 
                console.log('Cookies recipe')
            }
            if (arguments.length === 1) {
            console.log('General help text');
            } else if (help.hasOwnProperty(arguments[1])) { 
                this[arguments[0]](); // Seems my problem was here, this was creating the reference error.
            } else {
                console.log('Requested topic not found')
            }   
        },
        invite : function(id) { 
            send_PRIVGRP_INVITE(id)
        }   

    }

Any ideas how I could make this work, or is there some better way, cleaner way to do this. Also I should mention I chose to use commands as objects and methods because some commands will be more complex and I was planing to giving them their on file.js and exporting it to the main.js file and it would be a lot easier to add commands w/o editing the main file all the time.

Keep in mind I'm quite new at this, a detailed explanation would go a long way, thank you.

Trax
  • 956
  • 4
  • 15
  • 28
  • 4
    The line commented with "My first issue is here..." doesn't do what you think it does. You can't combine function and object definitions like that. (It doesn't throw a syntax error only because through sheer coincidence that's valid syntax but for something completely different than what you intended it to be.) – JJJ Apr 23 '15 at 17:12
  • Well it does what I intended it to do just not when, that's my problem :) – Trax Apr 23 '15 at 17:20
  • 2
    @Trax Pretty sure it doesn't; you have a label with a block. You're not "calling" anything. – Dave Newton Apr 23 '15 at 17:21
  • Right -- `cookies` is a label, but you act like you expect it to be in a JSON style notation there. You're in the middle of a function. How is `cookies` supposed to be called? – ruffin Apr 23 '15 at 17:26
  • If you want to call something conditionally then wrap it in a conditional, like you've done with the code immediately following your labeled code block. – Dave Newton Apr 23 '15 at 17:26
  • That is, `help : function () { cookies : {...` doesn't make sense. `help : { cookies : function () {...` does. – ruffin Apr 23 '15 at 17:27
  • Yes ruffin that's what I was trying to do, changed the code quite a few times and somehow I missed that. Anyway if I modify it when I try to run the code I get cookies : function () { ^ SyntaxError: Unexpected token ( – Trax Apr 23 '15 at 17:38
  • Is `cmd` supposed to be a function? Do you want to call `cmd()` and, if there are no args, have it execute its own help function? – ruffin Apr 23 '15 at 17:43
  • Cmd is supposed to be an object that contains the actual commands, like cmd.help, cmd.invite, etc. – Trax Apr 23 '15 at 17:46
  • Okay, that makes it a little easier. Fwiw, it might be useful to [check out this answer](http://stackoverflow.com/a/20734003/1028230). – ruffin Apr 23 '15 at 17:52

1 Answers1

1

I think the code below gives the behavior you're looking for, but it's pretty antipattern. It's sort of object oriented looking, but it's really not, since it's defining the cookies function this same way inside each call of the containing function. You probably want cookies to live on cmd, but that's not what your question asks.

I think you eventually are going to inch into doing some real object oriented stuff, where you'll have a constructor set up all the properties for your functions within functions. That is, you'll want to leave JSON notation and run this as "real code" inside of an object constructor returning instances of cmd that you might initialize in different ways (maybe with JSON notation!).

If this isn't what you wanted, drop a comment, and I'll revise. Again, I wouldn't actually use this code in production. Antipattern.

var cmd = {
    help: function () {
        var context = this.help;
        context.cookies = function () {
            console.log('Cookies recipe');
        };

        if (arguments.length === 0) {
            console.log('General help text');
        } else if (context.hasOwnProperty(arguments[0])) {
            context[arguments[0]]();
        } else {
            console.log('Requested topic not found');
        }
    },

    invite: function(id) {
        //send_PRIVGRP_INVITE(id);
        console.log("send_PRIVGRP_INVITE(" + id + ");");
    }
};

cmd.help();
cmd.help("cookies");
cmd.help("milk");
cmd.invite("wack");
cmd.help("invite");

That will produce this output:

General help text
Cookies recipe
Requested topic not found
send_PRIVGRP_INVITE(wack);
Requested topic not found

EDIT: There's some good information on how to use this here:

The quick take-home is that this refers to the function's execution context, as the SO answer quotes...

The ECMAScript Standard defines this as a keyword that "evaluates to the value of the ThisBinding of the current execution context" (§11.1.1).

So if you don't have something attached to an object, window (or whatever your global context is; in a browser, it's window) is this. Unless you use strict mode... But otherwise this is the calling object. foo.bar() should have foo in this when bar is called, for instance.

You can use the functions call and apply to explicitly set the context that's in this when calling a function. But that's all explained in detail at those links.

One OO solution

For how to use OO, I'd probably do something like...

function Cmd(helpInfo, executableFunctions) {
    var functionName;

    // Note that we're no longer redefining the cookies function
    // with every call of cmd.help, for instance.
    this.help = function (helpTopic) {
        if (undefined === helpTopic) {
            console.log('General help text');
        } else if (helpInfo.hasOwnProperty(helpTopic)) {
            console.log(helpInfo[helpTopic]);
        } else {
            console.log('Requested topic not found');
        }
    };

    for (functionName in executableFunctions)
    {
        if (executableFunctions.hasOwnProperty(functionName))
        {
            this[functionName] = executableFunctions[functionName];
        }
    }
}

// Set up initialization info for your new command object.
var helpInfoCooking = {
    cookies: "Cookies recipe",
    brownies: "Brownies recipe",
    cake: "Cake receipe"
};

var executableFunctions = {
    invite: function(id) {
        //send_PRIVGRP_INVITE(id);
        console.log("send_PRIVGRP_INVITE(" + id + ");");
    },
    say: function(message) {
        console.log("you'd probably say \"" + message + "\" somehow");
    }
};

// Create an instance of Cmd.
var cmd = new Cmd(helpInfoCooking, executableFunctions);

cmd.help();
cmd.help("cookies");
cmd.help("milk");
cmd.help("cake");
cmd.invite("wack");
cmd.help("invite");

cmd.say("This is a message");

Results:

General help text
Cookies recipe
Requested topic not found
Cake receipe
send_PRIVGRP_INVITE(wack);
Requested topic not found
you'd probably say "This is a message" somehow

YMMV, etc. Maybe overkill if you're doing a singleton setup, but it wouldn't be that hard to rearrange into a real singleton setup either.

Community
  • 1
  • 1
ruffin
  • 13,513
  • 8
  • 72
  • 118
  • There is a slight problem as far as I can tell 'this' is referring to Cmd not to the help function as I thought and it's making help command malfunction if its called with an argument of an existing method like 'invite. Upon calling help invite instead of replying 'Requested topic not found' it's actually calling invite(). – Trax Apr 23 '15 at 19:50
  • Sorry, that was stupid. Fixed. Though I really do think you'll want to go with a real OO setup (or even [an abbreviated one](http://stackoverflow.com/a/1963370/1028230)) for this use case. I'll try to suggest something later on. Then the `this` and closure issues will make more sense. – ruffin Apr 23 '15 at 19:58
  • Yes that works, I just assumed that 'this' will always refer to whatever block it's contained within not the main object. When you say real object oriented do you mean I should do something like : var Cmd = new Object(); Cmd.invite = function(id) { send_PRIVGRP_INVITE(id) } instead of writing all the 'commands' directly within the cmd object? – Trax Apr 24 '15 at 08:30
  • Okay, very quickly tried to address `this` question (it's all about "execution context") and one quick cut at doing this with OO. – ruffin Apr 24 '15 at 13:42
  • So let me see if I understand what you did here, you created 2 objects, 1 contains the help file while the other one the executable functions and you made a object constructor called Cmd that takes up to 2 arguments and it has a method called help which takes 1 optional argument which would be the topic name. If the optional argument is passed to it then it checks if the first argument passed to Cmd, for example foo has a property called whatever the helpTopic variable contains like foo.hasOwnProperty(bar) and prints the help info to console. – Trax Apr 24 '15 at 15:38
  • Then using 'for key in object' which dynamically creates the methods from executableFunctions inside Cmd. Interesting approach that I don't fully understand yet but that;s what google is for. I did try implementing it into my code but I keep getting 'Command not found' which means I need to also adjust the first part of my script which checks if the command exists before trying to execute it. And thank you for taking the time to help me. – Trax Apr 24 '15 at 15:39