Skip to content
This repository was archived by the owner on Jul 14, 2019. It is now read-only.

Getting Started

Alex Ford edited this page Sep 19, 2013 · 17 revisions

<< home

Installation

npm install shotgun

Basic Setup

To use shotgun you simply require it and create an instance of the shell.

var shotgun = require('shotgun');
var shell = new shotgun.Shell();

The shell optionally accepts an options object. One of the options available is cmdsDir which is a path to the directory containing your custom command modules (relative to the current working directory). If no directory is specified then 'shotgun_cmds' is used by default. Shotgun will automatically read in and require() all node modules in the specified directory and it will plug them into the framework as commands as long as they expose the required properties and functions.

Once you have an instance of the shotgun shell you are ready to get started building your application. You can build any UI application around shotgun that you wish. The simplest application is just a basic console app so that's what we'll setup here.

  1. First set up a basic app to continually get a value from the user.

     var readline = require('readline');
    
     // Create interface that reads from console and outputs to console.
     var rl = readline.createInterface(process.stdin, process.stdout);
     rl.setPrompt("> ");
    
     rl.on('line', function (cmdStr) {
         console.log("Echo: %s", cmdStr);
         rl.prompt();
     });
    
     rl.prompt();
    

    So far we haven't done anything with shotgun. We've just put together a small app that continually asks the user for input and then prints that input to the console.

    > test
    Echo: test

  2. Once you have a proper prompt application setup go ahead and install shotgun.

    npm install shotgun

  3. Require shotgun and instantiate a shell.

     var readline = require('readline'),
         shotgun = require('shotgun'),
         shell = new shotgun.Shell();
    
     ...
    
  4. Now that you have an instance of the shell you can begin to pass the user's value into the execute() function.

     var readline = require('readline'),
         shotgun = require('../index'),
         shell = new shotgun.Shell();
    
     // Create interface that reads from console and outputs to console.
     var rl = readline.createInterface(process.stdin, process.stdout);
    
     rl.on('line', function (cmdStr) {
         shell.execute(cmdStr);
         rl.prompt();
     });
    
     rl.prompt();
    
  5. So far all we've done is pass the user's input on to shotgun and setup an onData callback function that receives a data object, but we're not yet using it for anything. The data object passed to the callback from shotgun acts as a set of instructions. Depending on the command modules installed the data object could contain a wide variety of properties for you to consume in your application. There are a few default commands that come with shotgun: clear, exit, and help. 'clear' sets a property on the object called clearDisplay. 'exit' sets a property on the object called exit. 'help' prints out a bunch of lines showing the commands that are available. The line property on the data object represents a single line of text. This is how shotgun stays UI agnostic because the app using shotgun can iterate over these properties and take action based on the values provided. Let's write some code to handle each of these situations:

     var readline = require('readline'),
         shotgun = require('../index'),
         shell = new shotgun.Shell();
    
     // Create interface that reads from console and outputs to console.
     var rl = readline.createInterface(process.stdin, process.stdout);
    
    
     // Configure shotgun.
     shell.onData(function (data) {
         if (data.clearDisplay) console.log('\u001B[2J\u001B[0;0f');
         if (data.line) console[data.line.type](data.line.text);
         if (data.exit) {
             rl.close();
             process.exit();
         }
     });
    
     rl.on('line', function (cmdStr) {
         shell.execute(cmdStr);
         rl.prompt();
     });
    
     rl.prompt();
    

    In the above example we do several things with the result. First we check if clearDisplay is true. If it is then we clear the console display using ASCII control sequences. Next we check if exit is true and if it is then we close the readline interface and exit the current process. Lastly we see if there is a line property; if so, then we use the line object's properties to determine how to display the text. The line object has a type property, a text property, and an options property. Obviously text contains the text for that line; by default type contains either 'log', 'warn', 'error' as it's value, but it is possible to write lines with your own custom types. You can do whatever you choose with that value but in this example I decided to map that to the functions with the same name on console, passing in line.text to be displayed.

  6. We're almost done but there is one more piece we need to include. To maintain state across executions shotgun provides a context API. This context contains information that allows shotgun to know if it was prompting the user for a value among other things. You can even use this object yourself to maintain your own custom states for your users, such as authentication information.

By default shotgun maintains its own internal context. This works great for plain console applications where at most one user will usually be using the application at a time. However, if your application gets more complicated then you will likely want to store the context object yourself so you can maintain different context objects per user. For example, in a web application you would either store the context object in the user's session as a session variable or you would pass the object to the client to be stored in the browser and re-transmitted with the user's next input.

To configure a custom context object in our sample app we will create a contextStorage variable and use that as our context storage. Because objects in JavaScript are passed by reference, any changes shotgun makes to its context will be reflected on this object. Note: This is just an example. As I said earlier, in plain console applications you don't even have to do this because it will maintain its own internal context by default.

    var readline = require('readline'),
        shotgun = require('../index'),
        shell = new shotgun.Shell(),
        contextStorage = {}; // Declare empty context storage object.

    // Create interface that reads from console and outputs to console.
    var rl = readline.createInterface(process.stdin, process.stdout);

    // Configure shotgun.
    shell
        // Set the object that shotgun should use to maintain context information.
        .setContextStorage(contextStorage)
        // This callback is fired every time the context object is modified.
        .onContextChanged(function (context) {
            if (context.passive)
                // Set prompt text in console window to show context info to user.
                rl.setPrompt(context.passive.msg + " > ");
            else
                rl.setPrompt("> ");
        })
        // This callback is fired every time shotgun sends data back to your application.
        .onData(function (data) {
            if (data.clearDisplay) console.log('\u001B[2J\u001B[0;0f');
            if (data.line)
                console[data.line.type](data.line.text);
            if (data.exit) {
                rl.close();
                process.exit();
            }
        });

    rl.on('line', function (cmdStr) {
        shell.execute(cmdStr);
        rl.prompt();
    });

    rl.prompt();

Now our context object is available to us and we can store it however we wish. Before calling `shell.execute` just ensure you have called `shell.setContextStorage(object)` and passed in the correct context. Depending on your application, failure to do this will result in unpredictable behavior such as one user being prompted for a value and another user's input accidentally being used for that prompt's value.

That's it, you're done with your first little shotgun app!

<< home

Clone this wiki locally