API Docs for:
Show:

File: yui3-gallery/src/gallery-child-process/js/child-process.js

/**
 * This is a convenience wrapper around child processes in Node.js.
 * @module gallery-child-process
 */

(function (Y) {
    'use strict';
    
    var _spawn = require('child_process').spawn,
        _write,
    
        _class;
        
    /**
     * @class ChildProcess
     * @constructor
     * @extends Base
     * @param {Object} config Configuration object.
     */
    _class = function (config) {
        _class.superclass.constructor.call(this, config);
    };
    
    _class.ATTRS = {
        /**
         * Array of command line arguments.  Refer to Node.js
         * child_process.spawn documentation.
         * @attribute args
         * @default []
         * @initOnly
         * @type Array
         */
        args: {
            value: [],
            writeOnce: 'initOnly'
        },
        /**
         * The command to execute.  Refer to Node.js child_process.spawn
         * documentation.
         * @attribute command
         * @initOnly
         * @type String
         */
        command: {
            writeOnce: 'initOnly'
        },
        /**
         * Additional options.  Refer to Node.js child_process.spawn
         * documentation.
         * @attribute options
         * @default {}
         * @initOnly
         * @type Object
         */
        options: {
            value: {},
            writeOnce: 'initOnly'
        },
        /**
         * The PID of the child process.
         * @attribute pid
         * @readOnly
         * @type Number
         */
        pid: {
            readOnly: true
        },
        /**
         * This will be true when the stdin of the child process is writable and
         * its kernel buffer is not full.
         * @attribute ready
         * @default false
         * @readOnly
         * @type Boolean
         */
        ready: {
            readOnly: true,
            value: false
        },
        /**
         * Encoding may be one of 'ascii', 'base64', or 'utf8'.  If left
         * undefined, the stderr event will emit a Buffer instead of a string.
         * @attribute stderrEncoding
         * @initOnly
         * @type String
         */
        stderrEncoding: {
            writeOnce: 'initOnly'
        },
        /**
         * Encoding may be one of 'ascii', 'base64', or 'utf8'.  If left
         * undefined, the stdout event will emit a Buffer instead of a string.
         * @attribute stdoutEncoding
         * @initOnly
         * @type String
         */
        stdoutEncoding: {
            writeOnce: 'initOnly'
        }
    };
    
    _class.NAME = 'ChildProcess';
    
    Y.extend(_class, Y.Base, {
        initializer: function () {
            /**
             * Fires when the stdin of the child process is writable again after
             * having reported its kernel buffer was full.
             * @event drain
             * @preventable
             */
            this.publish('drain', {
                defaultFn: function () {
                    this._set('ready', true);
                }
            });
            
            /**
             * Fires when there is an error in any stream of the child process.
             * @event error
             * @param stderr Error reported from stderr.
             * @param stdin Error reported from stdin.
             * @param stdout Error reported from stdout.
             */
            this.publish('error');
            
            /**
             * Fires when the child process exits.
             * @event exit
             * @param {Number|null} code If the process terminated normally,
             * code is the final exit code of the process, otherwise null.
             * @param {String|null} signal If the process terminated due to
             * receipt of a signal, signal is the string name of the signal,
             * otherwise null.
             * @preventable destroy
             */
            this.publish('exit', {
                defaultFn: function () {
                    this.destroy();
                },
                fireOnce: true
            });
            
            /**
             * Fired when stderr receives data.
             * @event stderr
             * @param {Buffer|String} data
             */
            this.publish('stderr');
            
            /**
             * Fired when stdout receives data.
             * @event stdout
             * @param {Buffer|String} data
             */
            this.publish('stdout');
            
            var childProcess = _spawn(this.get('command'), this.get('args'), this.get('options')),
                me = this,
                stderr = childProcess.stderr,
                stderrEncoding = this.get('stderrEncoing'),
                stdin = childProcess.stdin,
                stdout = childProcess.stdout,
                stdoutEncoding = this.get('stdoutEncoding');
                
            if (stderrEncoding) {
                stderr.setEncoding(stderrEncoding);
            }
            
            stderr.on('data', function (data) {
                me.fire('stderr', {
                    data: data
                });
            });
            
            stderr.on('error', function (exception) {
                me.fire('error', {
                    stderr: exception
                });
            });
            
            stdin.on('drain', function () {
                me.fire('drain');
            });
            
            stdin.on('error', function (exception) {
                me.fire('error', {
                    stdin: exception
                });
            });
            
            if (stdoutEncoding) {
                stdout.setEncoding(stdoutEncoding);
            }
            
            stdout.on('data', function (data) {
                me.fire('stdout', {
                    data: data
                });
            });
            
            stdout.on('error', function (exception) {
                me.fire('error', {
                    stdout: exception
                });
            });
            
            childProcess.on('exit', function (code, signal) {
                me.fire('exit', {
                    code: code,
                    signal: signal
                });
            });
            
            /**
             * @property _childProcess
             * @protected
             */
            this._childProcess = childProcess;
            
            /**
             * @property _stderr
             * @protected
             */
            this._stderr = stderr;
            
            /**
             * @property _stdin
             * @protected
             */
            this._stdin = stdin;
            
            /**
             * @property _stdout
             * @protected
             */
            this._stdout = stdout;
            
            this._set('pid', childProcess.pid);
            this._set('ready', true);
        },
        /**
         * Sends a signal to the child process.  Refer to Node.js child.kill
         * documentation.  Note that while the method is called kill, the signal
         * delivered to the child process may not actually kill it.  kill really
         * just sends a signal to a process.
         * @method kill
         * @chainable
         * @param {String} signal (optional) Defaults to 'SIGTERM'.
         */
        kill: function (signal) {
            var childProcess = this._childProcess;
            
            if (childProcess) {
                childProcess.kill(signal || 'SIGTERM');
            }
            
            return this;
        },
        /**
         * Writes data to the stdin of the child process.  If the stdin of the
         * child process has reported its kernel buffer is full, the write will
         * be queued until the drain event.
         * @method write
         * @chainable
         * @param {Buffer|String} data The data to write.
         * @param {String} encoding (optional) The encoding to use when data is
         * a String value.  May be one of 'ascii', 'base64', or 'utf8'.  If
         * undefined, 'utf8' is assumed.
         */
        write: function (data, encoding) {
            if (this.get('ready')) {
                _write.call(this, data, encoding);
            } else {
                var eventHandle = this.after('readyChange', function (eventFacade) {
                    if (eventFacade.newVal) {
                        this.write(data, encoding);
                        eventHandle.detach();
                    }
                }, this);
            }
            
            return this;
        }
    });
    
    /**
     * @method _write
     * @param {Buffer|String} data
     * @param {String} encoding
     * @private
     */
    _write = function (data, encoding) {
        var stdin = this._stdin;
        
        if (!(stdin && stdin.writable && stdin.write(data, encoding))) {
            this._set('ready', false);
        }
    };
    
    Y.ChildProcess = _class;
}(Y));