From 53fc0b6f10d1c83b51cde6d02f5a0573dd014a68 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Mon, 7 Oct 2019 19:41:37 +0100 Subject: [PATCH] updated lab sheet --- 06 Code Quality.md | 56 +++++++++------- exercises/06_code_quality/login/package.json | 58 ----------------- .../{ => shopping}/login/index.js | 0 .../shopping/login/modules/accounts.js | 28 ++++++++ .../login/modules/accounts.old.js} | 0 .../shopping/login/modules/user.js | 0 .../shopping/login/package.json | 57 ++++++++++++++++ .../login/public/avatars/avatar.png | Bin .../{ => shopping}/login/public/style.css | 0 .../login/views/error.handlebars | 0 .../login/views/index.handlebars | 0 .../login/views/login.handlebars | 0 .../login/views/register.handlebars | 0 exercises/06_code_quality/todo/index.js | 61 ++++++++++++++++++ .../06_code_quality/todo/modules/list.js | 27 ++++++++ exercises/06_code_quality/todo/package.json | 22 +++++++ .../06_code_quality/todo/views/empty.hbs | 19 ++++++ exercises/06_code_quality/todo/views/home.hbs | 33 ++++++++++ 18 files changed, 278 insertions(+), 83 deletions(-) delete mode 100644 exercises/06_code_quality/login/package.json rename exercises/06_code_quality/{ => shopping}/login/index.js (100%) create mode 100644 exercises/06_code_quality/shopping/login/modules/accounts.js rename exercises/06_code_quality/{login/modules/accounts.js => shopping/login/modules/accounts.old.js} (100%) create mode 100644 exercises/06_code_quality/shopping/login/modules/user.js create mode 100644 exercises/06_code_quality/shopping/login/package.json rename exercises/06_code_quality/{ => shopping}/login/public/avatars/avatar.png (100%) rename exercises/06_code_quality/{ => shopping}/login/public/style.css (100%) rename exercises/06_code_quality/{ => shopping}/login/views/error.handlebars (100%) rename exercises/06_code_quality/{ => shopping}/login/views/index.handlebars (100%) rename exercises/06_code_quality/{ => shopping}/login/views/login.handlebars (100%) rename exercises/06_code_quality/{ => shopping}/login/views/register.handlebars (100%) create mode 100644 exercises/06_code_quality/todo/index.js create mode 100644 exercises/06_code_quality/todo/modules/list.js create mode 100644 exercises/06_code_quality/todo/package.json create mode 100644 exercises/06_code_quality/todo/views/empty.hbs create mode 100644 exercises/06_code_quality/todo/views/home.hbs diff --git a/06 Code Quality.md b/06 Code Quality.md index 04066cd1..d816a195 100644 --- a/06 Code Quality.md +++ b/06 Code Quality.md @@ -10,43 +10,49 @@ In this worksheet you will be applying a range of techniques to improve the qual Before you start you need to pull any _upstream changes_. Detailed instructions can be found in the **Setup** lab. -## 1 Modularisation +## 1 The Package Manifest -The first step you will need to do is to split your code up to make it easier to understand. Take a look at the `06_code_quality/login` project. +Up until now you have needed to install each NodeJS package separately however most projects you will see from now onwards include a **package manifest** whick contains the project metadata. In a NodeJS project this file is named `package.json`. Locate the `06_code_quality/todo/` directory and open the `package.json` file it contains. This is a JSON file that contains a number of properties that are used by the application. -Notice the line where we import the module. +Locate the `dependencies` property, notice that this lists a number of packages that are needed by the application. Rather than install depndencies one by one we can tell the package manager to install all the packages listed here. -```javascript -const accounts = require('modules/accounts') +```bash +$ npm install ``` -This loads the module into a constant called `accounts`. +Notice that this has installed all these listed packages. The manifest also specifies which version of each package are installed. If you want to install additional packages you can get these added to the `package.json` file automatically. For example if we want to install the `http-status-codes` package this should be done like this: + +```bash +$ npm install --save http-status-codes +``` -Now locate the second `router.post('/login')` route (this is currently commented out). Comment out the one you have been using and uncomment this new shorter version. If you run your server and test it you will find the functionality is identical. Its time to understand how this new version works: +## 2 Modularisation -1. We start by storing the `request.body` data (the HTTP POST request body data) in an immutable variable called `body`. -2. Next we call the `checkCredentials()` function that we imported from the `accounts.js` module passing the username and password as parameters. -3. If this function does not throw an exception it means the credentials are valid so we set the cookie and redirect to the home page. -4. If either the username or password are invalid, the `checkCredentials()` function will throw an exception which will be handled by the `catch()` block. The error message will explain what us wrong so we pass this back to the login page. +Next you will need to do is to split your code up to make it easier to understand. Take a look at the `06_code_quality/todo/` project. If you run this you will see that it is a simple shopping list app where you can add multiple items and quantities. Currently all the functionality is contained in the `index.js` file. Locate the `modules/list.js` file. This declares a new Object Prototype called `List` which includes all the necessary functionality for the app. At the moment this is not being used by the app. -Now we need to look at the `accounts.js` module. This implements some very important concepts that you will need to understand and apply to your assignment. +Lets examine this module: -1. The `accounts` module contains two types of function: - 1. The [function declarations](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function) such as `runSQL()` have a private scope and are not visible outside the module. - 2. Any [function expressions](https://developer.mozilla.org/en-US/docs/web/JavaScript/Reference/Operators/function) stored as keys in the `module.exports` object such as `module.exports.checkCredentials` are available to any code that imports this module. -2. All the code in a function is wrapped in a [try-catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) block to handle any exceptions. -3. The catch block simple propagates the [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object to the calling code. -4. Any non-valid code flow is handled by throwing an Error object which forces the code flow to jump directly to the catch block. -5. This means that if the program flow reaches the end of the try block everything was successful and data can be returned. +1. The `module.exports` object is used to define what functionality will be made available to our `index.js` file. In this case we are exporting the Object Prototype. Notice we are using the new `Class` constructor. +2. Lines 6-8 are the constructor where we define our array that can be accessed by the object. +3. The rest of the file defines a series of functions that form part of the object prototype. -### 1.1 Test Your Understanding +Now look at the top of the `index.js` file. Lines 22-23 import our module into the `ToDo` variable. This can then be used to create a new object using our `ToDo` object prototype. This object is called `todo` and provides access to all the functionality defined in the object prototype. Foe example to add an item we can use: + +```javascript +todo.add('bread', 42) +``` + +This will call the `add()` function that is part of our `todo` object prototype. + +### 2.1 Test Your Understanding -To check you understand how to use modules you are now expected to move more of the functionality from the `index.js` file into this separate module. To help you with this you will find stub functions that only require the functionality to be added! You will be modifying the functions below the `/* --- STUB FUNCTIONS --- */` line. +The custom object prototype defined in the `list.js` module already contains the functionality needed by your app. -1. Implement the `checkNoDuplicateUsername(username)` function to comply with the JSDoc comments, it should throw an exception if a duplicate user is found or `true` if not. -2. Implement the `saveImage()` function. This should check that the image is of the correct type then save it to the `avatars/` directory. -3. Now implement the `addUser()` function. This should make use of the functions you created in the first two tasks. It should check for duplicates before saving the image then it should encrypting the password and then saving the new record. -4. The final step is to comment out the `router.post('register')` route in `index.js` then create a replacement that makes use of the functions you added to the `accounts.js` module. +1. Uncomment lines 22-23 to import the module and create a custom object. +2. In the `router.post('/')` function call replace lines 41-42 with a call to `todo.add()`, passing the item name and quantity as parameters. +3. Now modify the `router.get('/')` function callback by replacing lines 29-30 with a call to the `todo.getAll()` function. +4. To test the functionality so far, comment out the array declaration on line 20 and try starting the web server. You should be able to add items, the data is now stored in the custom object. +5. Finally replace line 53 with a call to the appropriate function in the custom object. Now much of the business logic has been moved to the separate module, are there any module imports in `index.js` that are no longer needed? Locate these and delete. diff --git a/exercises/06_code_quality/login/package.json b/exercises/06_code_quality/login/package.json deleted file mode 100644 index 9c9aec4c..00000000 --- a/exercises/06_code_quality/login/package.json +++ /dev/null @@ -1,58 +0,0 @@ - -{ - "name": "10_auth", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "acceptance": "jest --coverage --detectOpenHandles", - "jsdoc": "node_modules/.bin/jsdoc -c jsdoc.conf", - "linter": "node_modules/.bin/eslint .", - "test": "jest --coverage --detectOpenHandles", - "unit": "node_modules/.bin/jest --coverage --runInBand tests/unit/" - }, - "jest": { - "testEnvironment": "node", - "verbose": true, - "collectCoverage": true, - "coverageDirectory": "docs/coverage/", - "coverageThreshold": { - "global": { - "branches": 0, - "functions": 0, - "lines": 0, - "statements": 0 - } - } - }, - "author": "", - "license": "ISC", - "dependencies": { - "bcrypt": "^3.0.3", - "bcrypt-promise": "^2.0.0", - "fs-extra": "^7.0.1", - "handlebars": "^4.1.2", - "jimp": "^0.6.0", - "koa": "^2.6.2", - "koa-body": "^4.0.8", - "koa-bodyparser": "^4.2.1", - "koa-router": "^7.4.0", - "koa-session": "^5.10.1", - "koa-static": "^5.0.0", - "koa-views": "^6.1.5", - "mime-types": "^2.1.22", - "sqlite-async": "^1.0.11" - }, - "devDependencies": { - "eslint": "^5.15.2", - "handlebars-validate": "^0.1.2", - "html-validator-cli": "^6.0.0", - "http-status-codes": "^1.3.2", - "jest": "^24.1.0", - "jsdoc": "^3.5.5", - "jsdoc-route-plugin": "^0.1.0", - "puppeteer": "^1.12.2", - "site-validator-cli": "^1.0.1", - "supertest": "^4.0.2" - } - } \ No newline at end of file diff --git a/exercises/06_code_quality/login/index.js b/exercises/06_code_quality/shopping/login/index.js similarity index 100% rename from exercises/06_code_quality/login/index.js rename to exercises/06_code_quality/shopping/login/index.js diff --git a/exercises/06_code_quality/shopping/login/modules/accounts.js b/exercises/06_code_quality/shopping/login/modules/accounts.js new file mode 100644 index 00000000..206b176b --- /dev/null +++ b/exercises/06_code_quality/shopping/login/modules/accounts.js @@ -0,0 +1,28 @@ + +'use strict' + +module.exports = class List { + + constructor() { + this.items = [] + } + + add(item, qty) { + const data = {item: item, qty: qty} + this.items.push(data) + } + + getAll() { + return this.items + } + + delete(name) { + const index = this.items.indexOf(name) + if(index !== -1) this.items.splice(index, 1) + } + + count() { + return this.items.count + } + +} diff --git a/exercises/06_code_quality/login/modules/accounts.js b/exercises/06_code_quality/shopping/login/modules/accounts.old.js similarity index 100% rename from exercises/06_code_quality/login/modules/accounts.js rename to exercises/06_code_quality/shopping/login/modules/accounts.old.js diff --git a/exercises/06_code_quality/shopping/login/modules/user.js b/exercises/06_code_quality/shopping/login/modules/user.js new file mode 100644 index 00000000..e69de29b diff --git a/exercises/06_code_quality/shopping/login/package.json b/exercises/06_code_quality/shopping/login/package.json new file mode 100644 index 00000000..d1885e7b --- /dev/null +++ b/exercises/06_code_quality/shopping/login/package.json @@ -0,0 +1,57 @@ +{ + "name": "10_auth", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "acceptance": "jest --coverage --detectOpenHandles", + "jsdoc": "node_modules/.bin/jsdoc -c jsdoc.conf", + "linter": "node_modules/.bin/eslint .", + "test": "jest --coverage --detectOpenHandles", + "unit": "node_modules/.bin/jest --coverage --runInBand tests/unit/" + }, + "jest": { + "testEnvironment": "node", + "verbose": true, + "collectCoverage": true, + "coverageDirectory": "docs/coverage/", + "coverageThreshold": { + "global": { + "branches": 0, + "functions": 0, + "lines": 0, + "statements": 0 + } + } + }, + "author": "", + "license": "ISC", + "dependencies": { + "bcrypt": "^3.0.3", + "bcrypt-promise": "^2.0.0", + "fs-extra": "^7.0.1", + "handlebars": "^4.1.2", + "jimp": "^0.6.0", + "koa": "^2.6.2", + "koa-body": "^4.0.8", + "koa-bodyparser": "^4.2.1", + "koa-router": "^7.4.0", + "koa-session": "^5.10.1", + "koa-static": "^5.0.0", + "koa-views": "^6.1.5", + "mime-types": "^2.1.22", + "sqlite-async": "^1.0.11" + }, + "devDependencies": { + "eslint": "^5.15.2", + "handlebars-validate": "^0.1.2", + "html-validator-cli": "^6.0.0", + "http-status-codes": "^1.3.2", + "jest": "^24.1.0", + "jsdoc": "^3.5.5", + "jsdoc-route-plugin": "^0.1.0", + "puppeteer": "^1.12.2", + "site-validator-cli": "^1.0.1", + "supertest": "^4.0.2" + } +} diff --git a/exercises/06_code_quality/login/public/avatars/avatar.png b/exercises/06_code_quality/shopping/login/public/avatars/avatar.png similarity index 100% rename from exercises/06_code_quality/login/public/avatars/avatar.png rename to exercises/06_code_quality/shopping/login/public/avatars/avatar.png diff --git a/exercises/06_code_quality/login/public/style.css b/exercises/06_code_quality/shopping/login/public/style.css similarity index 100% rename from exercises/06_code_quality/login/public/style.css rename to exercises/06_code_quality/shopping/login/public/style.css diff --git a/exercises/06_code_quality/login/views/error.handlebars b/exercises/06_code_quality/shopping/login/views/error.handlebars similarity index 100% rename from exercises/06_code_quality/login/views/error.handlebars rename to exercises/06_code_quality/shopping/login/views/error.handlebars diff --git a/exercises/06_code_quality/login/views/index.handlebars b/exercises/06_code_quality/shopping/login/views/index.handlebars similarity index 100% rename from exercises/06_code_quality/login/views/index.handlebars rename to exercises/06_code_quality/shopping/login/views/index.handlebars diff --git a/exercises/06_code_quality/login/views/login.handlebars b/exercises/06_code_quality/shopping/login/views/login.handlebars similarity index 100% rename from exercises/06_code_quality/login/views/login.handlebars rename to exercises/06_code_quality/shopping/login/views/login.handlebars diff --git a/exercises/06_code_quality/login/views/register.handlebars b/exercises/06_code_quality/shopping/login/views/register.handlebars similarity index 100% rename from exercises/06_code_quality/login/views/register.handlebars rename to exercises/06_code_quality/shopping/login/views/register.handlebars diff --git a/exercises/06_code_quality/todo/index.js b/exercises/06_code_quality/todo/index.js new file mode 100644 index 00000000..c936822f --- /dev/null +++ b/exercises/06_code_quality/todo/index.js @@ -0,0 +1,61 @@ +#!/usr/bin/env node + +'use strict' + +const Koa = require('koa') +const Router = require('koa-router') +const stat = require('koa-static') +const bodyParser = require('koa-bodyparser') +const handlebars = require('koa-hbs-renderer') + +const app = new Koa() +const router = new Router() +app.use(stat('public')) +app.use(bodyParser()) +app.use(handlebars({ paths: { views: `${__dirname}/views` } })) +app.use(router.routes()) + +const port = 8080 + +const items = [] + +// const ToDo = require('./modules/todo') +// const todo = new ToDo() + +router.get('/', async ctx => { + try { + const data = {} + if(ctx.query.msg) data.msg = ctx.query.msg + data.items = items.map( (element, index) => ({key: index, item: element.item, qty: element.qty})) + console.log(data.items) + ctx.render('home', data) + } catch(err) { + console.log(err.message) + ctx.render('home', {msg: err.message}) + } +}) + +router.post('/', ctx => { + try { + const body = ctx.request.body + const data = {item: body.item, qty: body.qty} + items.push(data) + ctx.redirect('/') + } catch(err) { + console.log(err.message) + ctx.redirect(`/?msg=${err.message}`) + } +}) + +router.get('/delete/:key', ctx => { + try { + console.log(`key: ${ctx.params.key}`) + items.splice(ctx.params.key, 1) + ctx.redirect('/msg=item deleted') + } catch(err) { + console.log(err.message) + ctx.redirect(`/${err.message}`) + } +}) + +module.exports = app.listen(port, () => console.log(`listening on port ${port}`)) diff --git a/exercises/06_code_quality/todo/modules/list.js b/exercises/06_code_quality/todo/modules/list.js new file mode 100644 index 00000000..0849e064 --- /dev/null +++ b/exercises/06_code_quality/todo/modules/list.js @@ -0,0 +1,27 @@ + +'use strict' + +module.exports = class List { + + constructor() { + this.items = [] + } + + add(item, qty) { + const data = {item: item, qty: qty} + this.items.push(data) + } + + getAll() { + return this.items.map( (element, index) => ({key: index, item: element.item, qty: element.qty})) + } + + delete(id) { + this.items.splice(id, 1) + } + + count() { + return this.items.count + } + +} diff --git a/exercises/06_code_quality/todo/package.json b/exercises/06_code_quality/todo/package.json new file mode 100644 index 00000000..e3637641 --- /dev/null +++ b/exercises/06_code_quality/todo/package.json @@ -0,0 +1,22 @@ +{ + "name": "todo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "linter": "node_modules/.bin/eslint ." + }, + "author": "", + "license": "ISC", + "dependencies": { + "handlebars": "^4.2.0", + "koa": "^2.8.1", + "koa-bodyparser": "^4.2.1", + "koa-handlebars": "^1.0.0", + "koa-hbs-renderer": "^1.2.0", + "koa-router": "^7.4.0", + "koa-static": "^5.0.0", + "koa-views": "^6.2.1" + }, + "devDependencies": {} +} diff --git a/exercises/06_code_quality/todo/views/empty.hbs b/exercises/06_code_quality/todo/views/empty.hbs new file mode 100644 index 00000000..6be9593a --- /dev/null +++ b/exercises/06_code_quality/todo/views/empty.hbs @@ -0,0 +1,19 @@ + + + + + ToDo List + + + +

ToDo List

+

Your list is empty, lets add some items...

+
+ Item: + + Qty: + + +
+ + diff --git a/exercises/06_code_quality/todo/views/home.hbs b/exercises/06_code_quality/todo/views/home.hbs new file mode 100644 index 00000000..1a9495f0 --- /dev/null +++ b/exercises/06_code_quality/todo/views/home.hbs @@ -0,0 +1,33 @@ + + + + + ToDo List + + + +

ToDo List

+

My List

+ {{#if msg}} +

{{msg}}

+ {{/if}} + {{#if items.length}} + + + + {{#each items}} + + {{/each}} +
Shopping List
ItemQtyAction
{{this.item}}{{this.qty}}delete
+ {{else}} +

Your list is empty, lets add some items...

+ {{/if}} +
+ Item: + + Qty: + + +
+ +