Skip to content
This repository was archived by the owner on Aug 17, 2024. It is now read-only.

Cowsay coursework #189

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions docs/js-core-2/cowsay-project/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
id: readme
title: Cowsay
sidebar_label: Cowsay Project
---

## Cowsay

This coursework assumes you have [Node](https://nodejs.org/en/download/), a text editor, and a terminal or command line.

Cowsay is a fun script that prints an [ASCII](https://simple.wikipedia.org/wiki/ASCII) cow with your words in a speech bubble. It works like this: there is a library of pictures of cows made out of ASCII characters. You call the script in your terminal and you pass in arguments. There are lots of options, but one argument is required: a text string for the cow to say.

```
 ________
< Mooooo >
 --------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

```

Cowsay was originally written in [Perl](https://simple.wikipedia.org/wiki/Perl). In this coursework, we will run it with Node. How would we go about this? What things would be helpful? I think we could use:

* A node package that contains the cowsay library. I'll start you off by letting you know there is one, and it is called ```cowsay```
* A way to execute the script.

Before we start, what's the fastest way you could get the cow to talk? Well, you can execute some packages directly [in the command line with npx](https://nodejs.dev/learn/the-npx-nodejs-package-runner).

```
npx cowsay Moooo
```

We did it! But let's explore some more. How would you install cowsay as a dependency? Make a new folder called /cowsaying and initialise a new package.
```
mkdir /cowsaying
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing this highlights is that I don't think we have a consistent way we get our trainees to organise code on their laptops, which occasionally causes problems. Maybe we could introduce something like "All of your projects/git repos should be in ~/code" or similar early in the course, to help with this?

We definitely see problems with people accidentally running npm init in their homedir and it breaking stuff - having clear guidance here would help to ensure people don't get tripped up...

For now in this exercise, though, maybe ~/cowsaying would be handier than /cowsaying - I suspect the latter may have some permissions issues for some trainees :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we added some instructions to navigate to the trainee's CYF work folder, would mkdir cowsaying work? I feel like adding to ~ would not be helping with the folder mess.

cd /cowsaying
npm init
```
Press return through all the options and then do.
Comment on lines +40 to +42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I was about to suggest that we remove this, to avoid the interactive prompt. However I had forgotten that npm install X without a prior npm init will a) output a bunch of scary looking warnings and b) not create a package.json.

So instead, maybe we could switch this to npm init -y? This runs the same init command but answers yes to all the questions (which is fine since we don't expect to publish this).

```
npm install cowsay
```
### npm run

Let's find out how to execute cowsay from our package.json. Open /cowsaying in your text editor and look at the [package.json](https://nodejs.dev/learn/the-package-json-guide).

Add cowsay to the scripts object.
```
"scripts": {
    "cowsay": "cowsay Mooooo"
  },
```
Now cowsay is listed as a script, and it has an argument. In your terminal, run cowsay.
```
npm run cowsay
```
What are the downsides of this approach? I would say one is that we want to be able to pass in arguments, not just always say moo. How could we make the cow say Baaaa?

Remove the hardcoded "Mooooo". Can you pass a different string in the command line?

### node

Now let's execute cowsay in a different way. Open /index.js in your text editor. Import the cowsay package with require.
```
const cowsay = require("cowsay");
```
Comment on lines +67 to +69
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nitpick, you get nicer syntax highlighting if you mark the code block as Javascript by putting js just after the initial three backticks (I'd give an example but it's painful to add markdown examples in markdown)

And log a cow to the console that says "I require a package". Add some more arguments if you can.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it might be useful to link to the cowsay documentation here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... having poked around with it a bit, I think the docs are pretty confusing (and maybe plain wrong?). So I'm having second thoughts about it.

```
console.log(cowsay.say({
        text : "I require a package",
        e : "oO",
        T : "U "
Comment on lines +74 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found these options a bit confusing when I first saw them.

The docs seem to imply that it accepts eyes and tongue as options, but having looked in the source I don't think that's actually the case :/ Perhaps we could introduce them as a separate step with an explanation of what they mean?

    }));
```
Now run this with node:
```
node index.js
```
## Going deeper, your stretch goal

How is this cow being made and what else can we make? Open your node modules and look inside cowsay. There's a README file that tells you all about the package and a library of other animals you can make. Read it. You don't have to understand all the code to get an idea of what is going on.

How would you extend this library to add a new animal? What about [a duck](https://www.asciiart.eu/animals/birds-water)? Look at how the existing ```.cows``` are imported and try adapting this for your own .cow. There are no solutions provided for this challenge. Dig around for yourself.

## Project

Let's figure out how to make a cow say things in Node by ourselves. For this project we don't really need a package, a library or lots of options. Let's just get the cow printing out and saying whatever we write in the command line. What would be helpful? I think we need to:

* [Accept an argument](https://nodejs.dev/learn/nodejs-accept-arguments-from-the-command-line) from the command line.
* Output to the command line. You've already done this with console.log.
* Make an ASCII cow.
* Write a function that puts the string into the cow's speech bubble.

Write your solution in a file called solution.js and test it by running your program in the command line. How will you handle a null argument?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are two layers being discussed here - the sentence is about the command line (where you can't pass null), and the "null argument" question seems to be more about the function... It could be a little clearer to say something like "no argument being passed" so that both sentences are talking about the same layer of abstraction?


### Iterating

We could make our program more accessible by adding a command line interface that prompts us to write in the cow's words. What tools can we use? I think we could use:

* A command line interface. I'll start you off by letting you know that there is a built in CLI called [readline](https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think writing async callback functions is sadly a bit advanced to just link to for JS1/JS2 and expect the trainees to be able to follow - if we're going to use readline, we'll probably need a bit more scaffolding or a step-by-step to follow... Could be really interesting though, but should probably be marked pretty clearly as a very optional extension

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - I think with some scaffolding around how to accept command line argument it could be a really nice exercise

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree with this. What about splitting cowsay into two: Part one is just investigating installations and running things in terminal and then return to it in JS3 where we write our own cowsay and iterate on that. Returning to cowsay and being able to write your own by JS3 would create a satisfying experience of progress and mastery.

Copy link
Member Author

@SallyMcGrath SallyMcGrath Mar 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...That's with some more scaffolding around the MVP cowsay script and then this time using it to layer up on features - could maybe do readline, commander, and then a library of cows/ducks/unicorns?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of splitting up the project, however I would point out that we don't actually touch NPM at all until React in the current syllabus. I'm actually not quite sure at what point we introduce the terminal now? My feeling is that we should really focus on the basics of JS before introducing packages.

I was actually going to suggest that the first section of the project might be useful as a lesson/coursework in JS3 as an fun introduction to packages.

The second half would be useful as a challenge, especially if we gave some more step-by-step instructions or provided some scaffolding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, pushing the whole thing into JS3 sounds great!

* Our ASCII cow again.
* And our cow function.

Write your solution in a file called solution2.js and test it by running your program in the command line. Use a prompt to ask for your cow saying.

If you get stuck, I've included a couple of solutions. Yours might be different and that's ok. If you can print a cow and you can make it say different things, you solved it. Below I include a slightly simpler ASCII cow that might help if you hit formatting issues.

```
/
/
^__^ /
(oo)'_______
(__) )-~
||----w |
|| ||
```
42 changes: 42 additions & 0 deletions docs/js-core-2/cowsay-project/solutions/solution1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// =================
// Stripped down cowsayer CLI,
// no libraries
// https://nodejs.dev/learn/nodejs-accept-arguments-from-the-command-line
// =================

// 1. Accept arguments

const args = process.argv.slice(2);

// 2. Make supplies for our speech bubble

let topLine = '_';
let bottomLine = '-';
let saying = '';

// 3. Make a cow that takes a string

function cowsay(saying) {
if(saying) {
let cowsaying =
`
${topLine.repeat(saying.length)}
< ${saying} >
${bottomLine.repeat(saying.length)}
Comment on lines +21 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we stripped these lines out and turned them into an exercise, I think that would make it a lot more approachable for JS1/2 students

/
/
^__^ /
(oo)'_______
(__) )-~
||----w |
|| ||`;

return cowsaying;
} else {
// account for empty strings
return 'You must add an argument. Try again and add "some words in quotes".'
}
}

//4. Pipe argument into cowsay function and return a cow
console.log(cowsay(args[0]));
45 changes: 45 additions & 0 deletions docs/js-core-2/cowsay-project/solutions/solution2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// =================
// Stripped down cowsayer CLI,
// no libraries or arguments
// https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
// =================

// 1. Make a command line interface.

const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
})

// 2. Make supplies for our speech bubble

let topLine = '_';
let bottomLine = '-';
let saying = '';

// 3. Make a cow that takes a string

function cow(saying) {
let cowsay =
`
${topLine.repeat(saying.length)}
< ${saying} >
${bottomLine.repeat(saying.length)}
/
/
^__^ /
(oo)'_______
(__) )-~
||----w |
|| ||`;

return cowsay;
}

// 4. Use readline to get a string from the terminal
// (with a prompt so it's clearer what we want)

readline.question(`What does the cow say?`, saying => {
console.log(cow(saying))
readline.close()
})