Takeaways

  • Practice writing JavaScript
  • Gain experience working with asynchronous code in Node.js
    • Working with delayed execution (with setTimeout)
    • Performing file operations
    • Issuing web requests
    • Communicating with child processes using standard input and standard output

Relevant Resources

Friend-inator

Your application perfectly addressed Dr. D’s needs. After a few days of cataloging, the temps got every -inator entered into the system. Impressed with the level of organizational organization, DEInc passed its safety evaluation. The only comment left by the inspector mentioned that the number banana peels found in the trash seemed strikingly high given the small number of employees in the company.

Our Story Continues

You arrive at work early to make coffee. Nothing starts a day of evil contracting quite like a cup of Joe brewed from fresh, ethically sourced beans1.

You notice Dr. Doofenshmirtz and Norm at the break room table. Dr. Doofenshmirtz looks very tired.

“SO THEN HE WOULD CRAWL AROUND AND GROWL LIKE A MONSTER,” Norm boomed. “Uh huh,” Doofenshmirtz agreed. “AND I WATCHED HIM TAKE GARBAGE AND PUT IT IN YOUR SHOES WHEN YOU WEREN’T LOOKING.” “That’s nice.” “I MISS HIM SO MUCH.” “Me too…. No… wait! NO I DON’T!”

Doofenshmirtz stood up in a huff and motioned you to follow him to his office. “I can’t take this anymore. Ever since Gug left, Norm keeps telling me about his… feelings… Yesterday he took over an hour to tell me about his favorite car wax. Apparently it’s Turtle Wax. Not because of the quality of the wax, though. Oh no. He likes it because the turtle is a snappy dresser.”

You start to wonder if you’ll ever get your coffee2.

“I need you to make a friend for Norm. Or a friend-inator. Something like that. I don’t have time to build one, and I DEFINITELY can’t just order another Norm to keep this Norm busy. One of him is already too much.” Norm bumbles through the door.

“DR. D! DID I TELL YOU ABOUT THE ONE TIME…”

As you turn to gauge Doofenshmirtz’s reaction, you find that he has fled the room.

Your Task

Your task is to develop a friend for Norm using the Hubot framework. You are to do the following:

  • Finish implementing the chat bot according to the specifications, ensuring that…
    • it passes the provided unit tests.
    • it responds to messages in a manner consistent with the sample output.

Refer to this assignment’s rubric for further description of point values for completed tasks.

Application Description

After necessary setup, the chat prompt can be started in a bash shell as follows:

$ ./hubot.sh
Hubot> hi Hubot
Shell: hey there

Now you can talk to your Hubot! It’s not terribly bright3, but it can do a few things. Say hubot help to get help. It will call you “Shell” because we’re using the shell adapter to develop it. You can change that by setting the HUBOT_SHELL_USER_NAME environment variable

You can also change the name of your Hubot, if you desire. If you do that, you’ll have to address it by its new name.

$ HUBOT_SHELL_USER_NAME="Norm" ./hubot.sh -n Friendo
Friendo> hi Friendo
Norm: hey there

Program Design

This program is built on Github’s Hubot framework. The shell of the program is already present in the starter code repository.

You will be editing files within the scripts/ directory. Handlers for the different Hubot commands are already provided for you, as well as detailed comments describing what each handler needs to do.

When complete, your Hubot should be able to handle the following commands:

  • For learning what your Hubot can do (This is a third-party feature. You don’t have to write any code for this one.):

    Hubot help - Displays all of the help commands that this bot knows about.
    Hubot help <query> - Displays all help commands that match <query>.
    
  • For saying hi to your Hubot (This script is for demonstration purposes. You don’t have to write any code for this one either.):

    Hubot hi - say hi
    
  • For working with to-do lists:

    Hubot add <item> to my todo list - Adds <item> to the list of todo items
    Hubot <itemID> is done - Removes the item with <itemID> from the todo list
    Hubot show my todo list - Lists todo items in no particular order
    
  • For checking the spelling of words:

    Hubot how do you spell <word> - Checks the spelling of a word
    
  • For requesting reminders:

    Hubot remind me to <task> in <sec> seconds - Send a reminder in <sec> secs
    
  • For hearing a joke:

    Hubot tell me a joke - Go fetch a random joke
    

Error Handling

Your code will need to handle a few different errors. The documentation for each script will detail how to handle them. In a nutshell, your Hubot will simply reply to the user (with msg.reply) whenever an error is encountered.

Working on the Program

For this assignment, your Hubot will be run using Node.js version v6.11.X. If you’re developing on campus Linux machines, we’ve included a script that will download and set up the correct version of Node.js just fine. If, however, you choose to use your own machine, you are welcome to download the necessary version of Node.js from https://nodejs.org/. Should you choose to work on your own machine, keep the following in mind:

  • You are on your own for setup and debugging the install. Make sure you’re using the right version of node. sudo apt-get install nodejs is probably going to install a really old release.
  • You should test your code on the CS213 machines (with the tools we’ve provided) before you submit it.

Setting up Node.JS and NPM

S&T’s CS213 machines have Node.js installed, which is nice, but it is an old version (v0.10.25). There’s a workaround, though!

Included in the starter code is a bash script that will do the following:

  1. Download and verify Node.js v6.11.X from https://nodejs.org/
  2. Unpack it to a hidden directory called .my_nodejs
  3. Create a symbolic link named bin, which allows for quick access to the freshly installed node and npm executables.

You can read more about setup.sh in the annotated source.

So how do you use it?

# First let's see what's in our directory here...
$ ls
eslint.sh              hubot.sh  package.json  scripts   test
external-scripts.json  mocha.sh  README.md     setup.sh

# Now let's set up Node.js
$ bash setup.sh

# ... there's a bunch of junk that gets output.

# Let's check again
$ ls
bin        external-scripts.json  mocha.sh      README.md  setup.sh
eslint.sh  hubot.sh               package.json  scripts    test

If there is a problem during setup, setup.sh will print a message and exit. It will only work on 64-bit Linux.

As the script downloads Node.js, you will see a progress bar appear. When it finishes, you’ll notice there’s a brand new item called bin. bin is a link to ./.my_nodejs/node-v6.11.4-linux-x64/bin/, which contains our node and npm executables. Now that Node.js is set up, let’s run it!

# We've already run setup.sh
$ ./bin/node
> console.log("Yay!");
Yay!
undefined
> .exit

We can type whatever we want into the Node.js REPL (the > prompt). Pretty cool!

In Summary

  • Run bash setup.sh to install Node.js locally.
  • Run bin/node to use the freshly setup Node.js.

Installing Prerequisite Packages

Remember pip and virtualenv in Python? Node.js has something similar called Node Package Manager (NPM). We’re using NPM to install a few things including (but not limited to)…

  • eslint - A JavaScript style checker and linter.
  • mocha - A JavaScript testing framework.
  • hubot - The chat framework.
  • uuid - A library for generating UUIDs
  • underscore - A super helpful utility library
  • underscore.string - A super helpful utility library for strings

You can install all required packages at once by running bin/npm install. npm will use package.json to determine which packages to install as well as the exact versions of those packages.

# Let's see what's in here already...
$ ls
bin        external-scripts.json  mocha.sh      README.md  setup.sh
eslint.sh  hubot.sh               package.json  scripts    test

# Assuming we've already run setup.sh
# We use the -d flag to tell NPM that we want to see some debug output.
# Otherwise, it's hard to tell if npm is frozen or broken...
$ bin/npm install -d

# ... a BUNCH of output. So colorful.

# Now let's have another look
$ ls
bin        external-scripts.json  mocha.sh      package.json  scripts   test
eslint.sh  hubot.sh               node_modules  README.md     setup.sh

See that node_modules directory? That’s where all our third-party packages were installed. Whenever we run bin/node in this directory, Node.js will automatically load up packages from node_modules, so that we can use them.

Pretty cool, huh?

In Summary

  • Run bin/npm install -d to install all the prerequisite node packages.

Running it

At this point, we’ve installed the right version of Node.js with setup.bash, and we’ve installed right versions of required packages with bin/npm install. It’s time to actually run our program.

# After running setup.sh and bin/npm install...
$ ./hubot.sh
Hubot>

If you make changes to the scripts and want to reload your Hubot, you can kill it with Ctrl-C and start it again.

Why do we start Hubot with a shell script? It does some sanity checking for you to make sure everything’s set up in the way we expect when we run it for grading.

Automated Tests

Automated tests have been provided for you.

You are welcome to add additional tests as you see fit. Additional tests you’ve written will not be evaluated as part of your final grade, but you may find them helpful in debugging your program.

Running the tests

We’ve included a helper script (mocha.sh) in the starter code to make it a little easier to invoke mocha. The script will perform some sanity checks to ensure that prerequisites are installed and that you’re using the right version of Node.js. If anything is fishy, mocha.sh will complain and exit. You can learn more about it by reading its annotated source.

# Assuming we've run setup.sh and bin/npm install...
$ ./mocha.sh

# Or if we just want to test the jokes script
$ ./mocha.sh test/jokes-test.js

Proper JavaScript Style

ESLint is a style checker and linter for JavaScript. You should use it to your advantage to check the style of your code.

We’ve included a helper script (eslint.sh) in the starter code to make it a little easier to invoke eslint. The script will perform some sanity checks to ensure that prerequisites are installed and that it’s using the right version of Node.js. If anything is fishy, eslint.sh will complain and exit. You can learn more about it by reading its annotated source.

# Assuming we've run setup.sh and bin/npm install...
$ ./eslint.sh scripts/*.js test/*.js

Further Details

Approaching this Assignment

You should tackle this assignment as follows - seriously do this so you’re not overwhelmed:

  • Read this entire posting and docs (you’ll likely do this more than once)
  • Clone the code
  • Get Node.js installed with bash setup.sh
  • Install prerequisite packages with bin/npm install
  • Try running ./hubot.sh as a sanity check
    • The hubot will respond to hubot help and hubot hi right away
  • Work on the scripts, following the descriptions in the posted documentation
    • Periodically talk to your Hubot to see if it’s responding properly
    • Periodically run the tests to see if your scripts are working
  • Compare output with the posted example conversation
  • Check that your code works on the campus Linux machines
  • Ensure your reflection.md has been added and committed
  • Ensure that your submission-ready code is indeed on GitLab using the web interface

Important Notes

  • Make certain that you are working in a subdirectory of linuxhome.
  • Do not install any extra packages. The packages pinned in package.json are sufficient. If there are any changes to package.json, your grade will suffer.
  • You are welcome to work on your own machine, but make sure your code works on the campus Linux boxes (rc__xcs213) machines, too.
  • The initial setup (with npm especially) may take a few minutes to run. Be patient. Once it’s setup, the run times aren’t as bad.

Point Value

This assignment is worth 100 points. It will be graded according to the following rubric:

Feature Points Possible Mostly or completely incorrect (0% of points possible) Needs improvement (50% of points possible) Adequate, but still some deficiencies (75% of points possible) Mostly or completely correct (100% of points possible)
Implementation          
Demonstrates understanding of fs.readdir to list directories asynchronously 5 -5 -3 -2 0
Demonstrates understanding of fs.readFile to read files asynchronously 10 -10 -5 -3 0
Demonstrates understanding of fs.writeFile to write files asynchronously 5 -5 -3 -2 0
Demonstrates understanding of fs.unlink to remove files asynchronously 5 -5 -3 -2 0
Demonstrates understanding of asynchronous web requests using “request” library 10 -10 -5 -3 0
Demonstrates understanding of setTimeout 10 -10 -5 -3 0
Demonstrates an understanding child process communication using stdin and stdout 10 -10 -5 -3 0
Functionality          
Program runs without runtime/syntax errors 10 -10 0
Hubot responses match expected responses from sample conversation 5 -5 -3 -2 0
Hubot responses match expected responses from instructor-generated conversations 10 -10 -5 -3 0
Style          
ESLint outputs no suggestions for improvement for any “.js” file 10 -10 0
Code Review (well-documented, implementation is straightforward, etc.) 10 -10 -5 -3 0
Points possible 100        

Although there’s no spot for unit tests in the rubric, your code will be run through the provided unit tests to assist with grading. We also have a few additional unit tests that we will run. Essentially the unit tests will help us understand whether or not you understood how to use the items in the Implementation section as well as whether your output adheres to expectations.

Each cell indicates how many points out of the available points will be awarded for that feature (row) and assessment level (column).

Cells marked -- cannot be achieved.

Submission

Deadline

  • 11:59:59 PM CDT – Thursday, October 26, 2017

Submission Procedure

Refer to the assignment submission page on the course website for details on submitting your code to GitLab.

Grading Procedure

When your assignment is graded, we will do the following (roughly).

# Setup node
$ bash setup.sh

# Install prerequisites
$ bin/npm install -d

# Check style for all source and test files
$ ./eslint.sh scripts/*.js test/*.js

# Run the tests
$ ./mocha.sh

# Then we're going to run your Hubot, and chat with it for a while.
$ ./hubot.sh

Hints and Tips

  • There’s a chance that the jokes test may fail if your Internet connection is slow, or if your network connection is unreliable. If these tests fail on occasion, it’s not a big deal. Just make sure your Hubot is capable of fetching jokes from the configured URL.
  • Be sure to check the sample conversation for output requirements. Don’t rely solely on the provided tests.
  • Don’t worry about varying newlines between Hubot’s responses. The sample conversation is formatted in an easy-to-read way, but the format of the output varies from machine to machine. Just make sure your Hubot says what it’s supposed to.
  • If you see an error similar to the following…

    Error: timeout of 4000ms exceeded. Ensure the done() callback is being called in this test.
    

    Simply adjust the this.timeout value in the troublesome test to a higher number. The problem is that Mocha gets impatient, and it’s killing your test early.

  • You can (and should) make sure that your Hubot can parse badly formatted joke data. Change your joke source using the JOKE_URL environment variable. Here are several different URLs to try:
    • http://cpl.mwisely.xyz/hw/5/jokes.json
      • Has a long JSON-encoded array of jokes.
      • This is the default.
    • http://cpl.mwisely.xyz/hw/5/test-joke.json
      • Just has one JSON-encoded joke
    • http://cpl.mwisely.xyz/hw/5/empty-jokes.json
      • Has a JSON-encoded empty array. No jokes.
    • http://cpl.mwisely.xyz/
      • Make sure your code handles the case where the page doesn’t contain JSON-encoded data.

FAQ

How long is this supposed to take to setup all the Node stuff?

I ran a practice run on a campus XCS machine ensuring that I was working in a subdirectory of linuxhome and got the following times:

  • setup.sh
    • A one-time setup
    • 17.042 seconds
  • bin/npm install -d
    • A one-time setup
    • 2 minutes, 50.274 seconds
  • ./mocha.sh
    • Running the entire test suite on completed code
    • 26.586 seconds
  • ./eslint.sh scripts/*.js test/*.js
    • Running the style checker on all relevant JS files
    • 4.878 seconds

Can eslint fix my formatting for me?

For the most part, yes it can!

Just pass the --fix flag to eslint and it’ll try to fix the style of the files you give it. Be careful if your files are open in your text editor. Some text editors get real grumpy if you change a file while it’s open. It’s like ripping the carpet out from under it. Not very nice.


Why are my jokes tests failing only sometimes?

The jokes script requires that we call out to a remove server to grab the JSON-encoded array of jokes. Occasionally, there may be network delays or server delays that cause your test to fail. This seems to happen a lot more on the campus machines than on others.

If you occasionally fail the jokes test, try turning up the timeout (via this.timeout()) for the test that’s causing trouble. If that doesn’t work, don’t sweat it. As long as you usually pass the test, it’ll very likely pass when your assignment is graded.

We’ve had to deal with this issue in the past, and it’s never been a problem during grading time. The machine we use to grade your stuff is much more reliable than the ones you develop on4.


Why do I periodically get an EADDRINUSE when I try to run my tests?

Hubot automatically starts a little web server (a Hubot feature that we’re not using) on port 8080 to send/receive HTTP requests/responses. As a result, we have the same situation we had in homework 4, where two people can’t use the same port on the same computer at the same time.

It’s really easy to avoid this situation, though. Just tell Hubot to use another port! Any ol’ port number will do. Let’s say we wanted to use port 5454

PORT=5454 ./hubot.sh

That should fix it. You can do the same thing for mocha, too, if it has the same problem.

PORT=5454 ./mocha.sh

Note that the port number doesn’t really matter. Just pick something random.

  1. You can really taste the morality. 

  2. Norm : Dr. Doofenshmirtz :: Dr. Doofenshmirtz : You 

  3. By design. 

  4. It’s a sad reality.