Chapter10 Command Line Rosetta

If you find this content useful, consider buying this book:

  • Amazon
  • Purchase all books bundle
  • Purchase from pragmatic ai labs
  • Subscribe all content monthly
  • If you enjoyed this book considering buying a copy

    Chapter 10: Command-line Rosetta Stone #

    This chapter serves as a guidebook for users coming from another language. The source code can be found here. The same style, hello world command-line tool is written in many languages.

    R Hello World #

    This step is an example of a hello world command-line tool in R. The source code is here.

    #!/usr/bin/env Rscript
    #Hello World R command-line tool
    #
    
    
    suppressPackageStartupMessages(library("optparse"))
    parser <- OptionParser()
    parser <- add_option(parser, c("-c", "--count"), type = "integer",
                         help = "Number of times to print phrase",
                         metavar = "number")
    parser <- add_option(parser, c("-p", "--phrase"),
                        help = "Phrase to print")
    
    args <- parse_args(parser)
    
    
    # Function to Generate Phrases
    phrasegen <- function(arguments){
        for (count in 1:arguments$count) {
            cat(paste(arguments$phrase, "\n"))
        }
    }
    
    #Run the program
    phrasegen(args)
    

    Depends on https://github.com/trevorld/optparse for R

    Usage #

    $  hello-world git:(master) $ ./hello-world.R --count 5 --phrase "hello world"
    hello world
    hello world
    hello world
    hello world
    hello world
    

    Bash Hello World #

    This step is a hello world Bash example. The source code is here.

    #!/bin/bash
    #output looks like this:
    #
    #  $hello-world git:(master) $ ./hello-world.sh --count 5 --phrase "hello world"
    #hello world
    #hello world
    #hello world
    #hello world
    #hello world
    
    #Generate phrase "N" times
    phrase_generator() {
        for ((i=0; i<$1;i++)); do
            echo "$2"
        done
    }
    
    #Parse Options
    while [[ $# -gt 1 ]]
    do
    key="$1"
    
    case $key in
        -c|--count)
        COUNT="$2"
        shift
        ;;
        -p|--phrase)
        PHRASE="$2"
        shift
        ;;
    esac
    shift
    done
    
    #Run program
    phrase_generator "${COUNT}" "${PHRASE}"
    

    To lint use make lint. And now run it:

    $  hello-world git:(master) $ ./hello-world.rb --count 5 --phrase "hello world"
    hello world
    hello world
    hello world
    hello world
    hello world
    

    Environment #

    You may need to also do:

    $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshenv
    $ echo 'eval "$(rbenv init -)"' >> ~/.zshenv
    $ echo 'source $HOME/.zshenv' >> ~/.zshrc
    $ exec $SHELL
    

    Go Hello World #

    The go source code is here.

    package main
    
    import (
        "fmt"
        "gopkg.in/urfave/cli.v1" // imports as package "cli"
        "os"
    )
    
    func main() {
        app := cli.NewApp()
        app.Flags = []cli.Flag{
            cli.StringFlag{
                Name:  "phrase",
                Usage: "Print phrase",
            },
            cli.Int64Flag{
                Name:  "count",
                Usage: "Count to print a phrase",
            },
        }
    
        app.Action = func(c *cli.Context) error {
            sum := 0
            for i := 0; i < c.Int("count"); i++ {
                sum -= i
                fmt.Println(c.String("phrase"))
            }
            return nil
        }
    
        app.Run(os.Args)
    }
    
    

    Running program #

    Run make all

    Then run program:

    $  hello-world git:(master) $ hello-world --phrase "hello world" --count 5
    hello world
    hello world
    hello world
    hello world
    hello world
    

    Environment #

    Setting these variables:

    export GOPATH="${HOME}/.go"
    export GOROOT="$(brew --prefix golang)/libexec"
    export PATH="$PATH:${GOPATH}/bin:${GOROOT}/bin"
    export GOBIN=$GOPATH/bin
    

    Node Hello World #

    The source code for the node examples is here. This project has several components. First, there are the .js file.

    #!/usr/bin/env node
    "use strict";
    
    /*
    
    Hello World Commandline Tool
    
    node index.js --phrase "hello world" --count 10
    
    hello world hello world hello world
    
    */
    
    const program = require('commander');
    program
      .version('0.0.1')
      .option('-p, --phrase [value]', 'phrase')
      .option('-c, --count <n>', 'Number of Times To Repeat Phrase', parseInt)
      .parse(process.argv);
    
    /**
     * Multiplies string with additional space.
     * @param {string} phrase The phrase.
     * @param {number} count The number of times to repeat
     * @returns {string} The multiplied string
     */
    function phraseGenerator (phrase, count) {
    
        return phrase.concat(" ").repeat(count);
    
    }
    
    // Check to see both options are used
    if (typeof program.phrase === 'undefined' ||
        typeof program.count === 'undefined') {
    
        console.error('ERROR! --phrase and --count options required');
        program.help();
        process.exit(1);
    
    }
    
    // Print Phrase To Standard Out
    console.log(phraseGenerator(
                    program.phrase,
                    program.count));
    

    Next there is a package.json file.

    {
      "name": "nodecli",
      "version": "1.0.0",
      "description": "nodecli",
      "main": "index.js",
      "dependencies": {
        "commander": "^2.10.0"
      },
      "devDependencies": {
        "eslint": "^4.1.1",
        "eslint-config-defaults": "^9.0.0"
      },
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "repository": {
        "type": "git",
        "url": "git+https://github.com/noahgift/nodecli.git"
      },
      "keywords": [
        "cli"
      ],
      "author": "Noah Gift",
      "license": "MIT",
      "bugs": {
        "url": "https://github.com/noahgift/nodecli/issues"
      },
      "homepage": "https://github.com/noahgift/nodecli#readme"
    }
    

    To run the example, you would do the following.

    Steps to run:

    npm install ./hello-world --phrase "hello world" --count 3

    The output should be:

    hello world hello world hello world

    Multi-paradigm Node.js #

    Getting Started #

    This application source code is here.

    To build this project, you need to:

    npm install

    To talk to the blockchain network, please run ./startFabric.sh. Then ./query-cli.js. Further information and background of this forked version can be found here

    Features #

    • Color Output
    • JSON Formatting
    • Async Network Operations
    • Blockchain Integration

    Screenshots #

    • Screenshot No Options: Output
    • Screenshot One Option: Output

    The source is below. You can see how a more sophisticated node tool integrates with both blockchain, and colored output works.

    #!/usr/bin/env node
    "use strict";
    
    /*
    
    Hyperledger Query Commandline Tool
    
    ./query-cli.js
    __dirname = path.resolve();
    */
    const Hfc = require('fabric-client'),
        path = require('path'),
        chalk = require('chalk'),
        prettyjson = require('prettyjson'),
        program = require('commander'),
        options = {
            walletPath: path.join(__dirname, './network/creds'),
            userId: 'PeerAdmin',
            channelId: 'mychannel',
            chaincodeId: 'fabcar',
            networkUrl: 'grpc://localhost:7051'
        };
    let channel = {},
        transactionId = null,
        client = null,
        jsonResult = null;
    program
        .version('0.0.1')
        .option('-c, --car [value]', 'car to query')
        .parse(process.argv);
    
    /**
     * Queries Blockchain
     * @param {string} chaincodeFunc The chaincode function to query
     * @param {string} car The individual car to query
     * @returns {string} nothing
     */
    function queryHelper (chaincodeFunc = 'queryAllCars', car = '') {
    
        Promise.resolve().then(() => {
    
            console.log(chalk.red("Create a client and set the wallet location"));
            client = new Hfc();
    
            return Hfc.newDefaultKeyValueStore({path: options.walletPath});
    
        })
    
        .then((wallet) => {
    
            console.log(chalk.red("Set wallet path, and associate user ",
                options.userId, " with application"));
            client.setStateStore(wallet);
    
            return client.getUserContext(options.userId, true);
    
        })
    
        .then((user) => {
    
            console.log(
            chalk.red(
                "Check user is enrolled, and set a query URL in the network"
                ));
            if (typeof user === "undefined" || user.isEnrolled() === false) {
    
                console.error("User not defined, or not enrolled - error");
    
            }
            channel = client.newChannel(options.channelId);
            channel.addPeer(client.newPeer(options.networkUrl));
    
        })
        .then(() => {
    
            console.log(chalk.red("Make query"));
            transactionId = client.newTransactionID();
            console.log(chalk.red("Assigning transaction_id: "),
                chalk.green(transactionId._transaction_id));
    
            // The queryCar - requires 1 argument, ex: args: ['CAR4'],
            // The queryAllCars - requires no arguments , ex: args: [''],
            const request = {
                chaincodeId: options.chaincodeId,
                txId: transactionId,
                fcn: chaincodeFunc,
                args: [car]
            };
    
            return channel.queryByChaincode(request);
    
        })
        .then((queryResponses) => {
    
            console.log(chalk.red("returned from query"));
            if (typeof queryResponses.length === 'undefined') {
    
                console.log("No payloads were returned from query");
    
            } else {
    
                console.log(
                    chalk.bgBlue("Query result count = ", queryResponses.length));
    
            }
            if (queryResponses[0] instanceof Error) {
    
                console.error("error from query = ", queryResponses[0]);
    
            }
            jsonResult = JSON.parse(queryResponses[0].toString());
            console.log(prettyjson.render(jsonResult, {
                keysColor: 'yellow',
                dashColor: 'blue',
                stringColor: 'white'
            }));
    
        })
        .catch((err) => {
    
            console.error("Caught Error", err);
    
        });
    
    }
    // Run The Command line Tool
    if (typeof program.car === 'undefined') {
    
        queryHelper('queryAllCars');
    
    } else {
    
        queryHelper('queryCar', program.car);
    
    }
    

    Python Hello World #

    Python 3.6.1

    Running #

    Steps to Run:

    Install packages:

    make install

    Activate Virtual Env:

    source ~/.hello-world-py-cli/bin/activate

    Run Tool:

    ./hello-world.py --phrase "hello world" --count 3

    The output should be:

    hello world hello world hello world

    The `Makefile looks like:

    install:
          mkdir -p ~/.hello-world-py-cli &&\
          python3 -m venv ~/.hello-world-py-cli &&\
          pip install -r requirements.txt
    
    
    source-cmd:
        echo "Virtualenv source command" 
        #source ~/.hello-world-py-cli/bin/activate
    
    lint:
        pylint hello-world.py
    

    The requirements.txt file looks like:

    click
    pylint
    

    Finally, the python code is as follows.

    #!/usr/bin/env python
    import click
    
    @click.version_option("0.1")
    @click.group()
    def cli():
        """Hello World"""
    
    @cli.command("hello")
    @click.option("--phrase", help="phrase to print")
    @click.option("--count", help="Number of times to repeat phrase", type=int)
    def hello(phrase, count):
        """Hello World Command-line tool"""
    
        while count:
            count -= 1
            click.echo(phrase)
    
    
    if __name__ == '__main__':
        cli()
    

    A full example of lint and run:

        (.hello-world-py-cli) $  hello-world git:(master) $ ./hello-world.py hello\
            --phrase "hello world" --count 3
        hello world
        hello world
        hello world
        (.hello-world-py-cli) $  hello-world git:(master) $ make lint
        pylint hello-world.py
    
        -------------------------------------------------------------------
        Your code has been rated at 10.00/10 (previous run: 7.50/10, +2.50)
    

    You can find the latest up to date examples in the Github Repo https://github.com/noahgift/cli-rosetta.