diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a61c7f7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.data +.data/** diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..df19346 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 + +[Makefile] +indent_style = tab +indent_size = 8 + +[{deps,tools,build}/**] +indent_style = ignore +indent_size = ignore +end_of_line = ignore +trim_trailing_whitespace = ignore +charset = ignore + +[{test/fixtures,deps,build,tools/eslint,tools/gyp,tools/icu,tools/msvs}/**] +insert_final_newline = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..158e0db --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +*.scss +*.d.ts +*.spec.ts +main.browser.ts +idex.js +node_modules/* +*.e2e.* diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..c540bbd --- /dev/null +++ b/.eslintrc @@ -0,0 +1,63 @@ +{ + "env": { + "browser": true, + "jest": true, + "es6": true + }, + "plugins": ["import"], + "extends": ["eslint:recommended", "prettier"], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "rules": { + "no-console": [ + "error", + { + "allow": ["debug", "error"] + } + ], + "no-eval": "error", + "import/first": "error", + "camelcase": [ + "error", + { + "ignoreImports": true, + "ignoreDestructuring": true + } + ], + "consistent-return": "warn", + "comma-dangle": ["warn", "always-multiline"], + "constructor-super": "error", + "curly": "error", + "eol-last": "warn", + "eqeqeq": ["error", "smart"], + "import/order": "always", + "new-parens": "error", + "no-debugger": "error", + "no-fallthrough": "off", + "max-len": [ + "warn", + { + "code": 100 + } + ], + "no-shadow": [ + "error", + { + "hoist": "all" + } + ], + "no-trailing-spaces": "warn", + "no-underscore-dangle": "error", + "no-unsafe-finally": "error", + "no-var": "error", + "object-shorthand": "error", + "one-var": ["error", "never"], + "prefer-arrow/prefer-arrow-functions": "off", + "prefer-const": "error", + "radix": "off", + "space-in-parens": ["off", "never"] + } + } + \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..baa0c1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +publish/ +build/Release +coverage +lib-cov + +# deps +node_modules/ +__pycache__/ + +# Logs +logs +*.log* + +# Runtime data +pids +*.pid +*.seed + +# Mac files +.DS_Store + +# randoms +*.pyc +dbdata +old +yarn-error.log +dist +*/test-results diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..a20e387 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,60 @@ +{ + "extends": [ + "stylelint-config-standard", + "stylelint-config-recommended-scss", + "stylelint-config-prettier" + ], + "rules": { + "font-family-name-quotes": "always-where-recommended", + "function-url-quotes": [ + "always", + { + "except": ["empty"] + } + ], + "declaration-empty-line-before":[ + "never", + ], + "comment-empty-line-before":[ + "always", + { + "except": ["first-nested"], + "ignore": ["stylelint-commands"], + "ignoreComments": ["/BEGIN/", "/END/", "/ignore/"] + } + ], + "selector-max-compound-selectors": 5, + "color-hex-length": "long", + "selector-pseudo-element-colon-notation": "single", + "selector-attribute-quotes": "always", + "string-quotes": "double", + "max-nesting-depth": 3, + "selector-max-specificity": "0,3,2", + "declaration-no-important": true, + "at-rule-no-vendor-prefix": true, + "media-feature-name-no-vendor-prefix": true, + "property-no-vendor-prefix": true, + "selector-no-vendor-prefix": true, + "value-no-vendor-prefix": true, + "no-empty-source": null, + "selector-class-pattern": "[a-z-]+", + "selector-id-pattern": "[a-z-]+", + "selector-max-id": 0, + "selector-max-universal": 2, + "selector-type-no-unknown": [ + true, + { + "ignore": ["custom-elements", "default-namespace"] + } + ], + "selector-pseudo-element-no-unknown": [ + true, + { + "ignorePseudoElements": ["ng-deep"] + } + ], + "unit-allowed-list": ["px", "%", "em", "rem", "vw", "vh", "deg", "s"], + "max-empty-lines": 2, + "max-line-length": 120 + } +} diff --git a/README.md b/README.md index 5eb6e1e..996f3f9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,64 @@ -# examples +# Examples + Various examples for various use-cases, mostly helping others :) + +## why in a repo? + +I like to be platform independent. Things like Github gist are nice but tie me to the platform. +Having these things in a repo gives me flexibility. + +It also consolidates all relevant examples in 1 place + +## table of contents + +```sh +. +├── bash +│ ├── parsing-command-line-params.sh +│ ├── remove-parentheses.sh # script to remove parentheses from all files in a directory (recursively) +│ └── whiptail.sh # tiny demo of how whiptail works + +├── c +│ └── ani2png.c # convery .ani files into a sequence of .pngs + +├── javascript +│ └── async # an example of how to use async / await in javascript +│ ├── async.js +│ ├── image.png +│ └── README.md + +├── js-ts-frameworks # JS-TS framework specific examples +│ ├── Angular +│ │ └── state +│ │ └── HttpWithStateSettingsService.ts # both a higher order component and a state settings service example for Angular + ngxs +│ └── React +│ └── tests +│ └── redux-promise-middleware.spec.js # single spec file to validate a lot of redux promise middleware reducers + +├── mongo +│ ├── add-field-with-default-value.js # add a field to an existing collection with a default value +│ └── collectionNames-and-count.js # return a list of collections in a database with the number of documents in them + +├── typescript +│ ├── datetime +│ │ └── getCustomIsoString.ts # function to return a valid ISO string from a given date object + +│ ├── geo +│ │ └── radiusToPolygon # example on how to convert from a radius to a GeoJSON polygon +│ │ ├── helpers.ts +│ │ ├── index.ts +│ │ └── point.ts + +│ ├── typing +│ │ └── omit.ts # utility type "omit" examples +│ └── inheritance-and-interfaces.ts # basic introduction to inheritance and interfaces for front-end devs + +├── LICENSE +└── README.md +``` + +## external examples + +Examples which are either too big to include or not made by me. + +- [Angular smart vs dumb components](https://github.com/Mastermindzh/angular-smart-dumb-component-example) diff --git a/bash/parsing-command-line-params.sh b/bash/parsing-command-line-params.sh new file mode 100644 index 0000000..907ee2d --- /dev/null +++ b/bash/parsing-command-line-params.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Allow params a b v +# : is used to disable verbosity +while getopts ":abv" opt; do + + # case to handle scenarias + case $opt in + a) + echo "-a passed, Parameter: $OPTARG" + ;; + b) + echo "-b passed, Parameter: $OPTARG" + ;; + v) + echo "-v passed, Parameter: $OPTARG" + ;; + # default case + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done diff --git a/bash/remove-parentheses.sh b/bash/remove-parentheses.sh new file mode 100644 index 0000000..e091930 --- /dev/null +++ b/bash/remove-parentheses.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# will recursively remove ( and ) from all files + +for d in /mnt/series/Smallville/*; do + if [ -d "$d" ]; then + echo "" + echo "=> moving to: $d" + cd "$d" + for f in *.*; do + echo "processing: $f" + des=$(echo "$f" | tr -d '()') + if [ "$f" != "$des" ]; then + mv "$f" "$des" + fi + done + fi +done diff --git a/bash/whiptail.sh b/bash/whiptail.sh new file mode 100644 index 0000000..47c168f --- /dev/null +++ b/bash/whiptail.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +Height=$(tput lines) +Width=$(tput cols) +Height=$((Height / 2)) +Width=$(((Width * 2) / 3)) + +whiptail --yesno --title "Hello!" "This is a whiptail window" $Height $Width + diff --git a/c/ani2png.c b/c/ani2png.c new file mode 100644 index 0000000..e767a9c --- /dev/null +++ b/c/ani2png.c @@ -0,0 +1,127 @@ +//build: gcc ani2png.c -o ani2png + +#include +#include +#include + +void ReadFile(char *name); +int teststring(char *buffer, int start); + +int main(int argc, char **argv) +{ + if (argc != 2) + { + fprintf(stderr, "Usage: ani2png /path.ani"); + return 0; + } + ReadFile(*(argv + 1)); + return 0; +} + +int teststring(char *buffer, int start) +{ + if (*(buffer + start) == 0x69) + { + if (*(buffer + start + 1) == 0x63) + { + if (*(buffer + start + 2) == 0x6f) + { + if (*(buffer + start + 3) == 0x6e) + { + return 1; + } + } + } + } + else + return 0; +} + +void ReadFile(char *name) +{ + FILE *file; + char *buffer, *fileName; + unsigned long fileLen; + + if (!strstr(name, ".ani")) + { + fprintf(stderr, "Usage: ani2png /path.ani"); + return; + } + + fileName = malloc(strlen(name) + 1); + strcpy(fileName, name); + *strstr(fileName, ".ani") = '\0'; + + file = fopen(name, "rb"); + + fseek(file, 0, SEEK_END); + fileLen = ftell(file); + fseek(file, 0, SEEK_SET); + + buffer = (char *)malloc(fileLen + 1); + + fread(buffer, fileLen, 1, file); + fclose(file); + + char *new_png_name = malloc(strlen(name) + 5); + + char png_counter_string[5]; + int i, j, png_counter = 1; + FILE *png_image; + + for (i = 0; i <= fileLen; i++) + { + if (png_counter == 9999) + { + free(fileName); + free(buffer); + free(new_png_name); + return; + } + + if ((i + 4 <= fileLen) && teststring(buffer, i) == 1) + { + sprintf(png_counter_string, "%d", png_counter); + strcpy(new_png_name, fileName); + strcat(new_png_name, png_counter_string); + strcat(new_png_name, ".png"); + png_counter++; + + png_image = fopen(new_png_name, "wb"); + if (!png_image) + { + fprintf(stderr, "Unable to open file %s.\n", new_png_name); + free(fileName); + free(buffer); + free(new_png_name); + return; + } + + j = 8; + while (i + j + 4 <= fileLen) + { + if (teststring(buffer, i + j + 1) == 1) + break; + if (j == 10) + putc(0x01, png_image); + else + putc(*(buffer + i + j), png_image); + j++; + } + if (i + j <= fileLen) + putc(*(buffer + i + j), png_image); + if (fileLen - i - j <= 3) + { + putc(*(buffer + i + j + 1), png_image); + putc(*(buffer + i + j + 2), png_image); + } + fclose(png_image); + i += j; + } + } + + free(new_png_name); + free(buffer); + free(fileName); +} diff --git a/javascript/async/README.md b/javascript/async/README.md new file mode 100644 index 0000000..7bd2a49 --- /dev/null +++ b/javascript/async/README.md @@ -0,0 +1,10 @@ +# Async + +Copy to a file (e.g test.js) and run with: +node test.js + +Result should be something like this: + +![image showing the result](./image.png) + +PS: To clarify. Both loops run at the same time, to test remove the Math.max bit of the second loop) but the log on line 69 is delayed because it is wrapped in a function which uses await to make sure that the loop has finished before (allowing for sync-like programming) diff --git a/javascript/async/async.js b/javascript/async/async.js new file mode 100644 index 0000000..6ae8a28 --- /dev/null +++ b/javascript/async/async.js @@ -0,0 +1,72 @@ +/** + * log a message with an iso datetime + * @param {*} msg msg to log + */ +const log = (msg) => { + console.log(msg, new Date().toISOString()); +}; + +/** + * sleep for a certain amount of ms + * @param {*} ms ms to sleep + */ +const sleep = (ms) => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; + +/** + * "asyncForEach" method on every array, allowing for async functionality on any array + */ +if (!Array.prototype.asyncForEach) { + Array.prototype.asyncForEach = async function (func) { + "use strict"; + + if (this == null) { + throw new TypeError( + "Array.prototype.asyncForEach called on null or undefined" + ); + } + + if (typeof func !== "function") { + throw new TypeError(); + } + + var t = Object(this); + + return await Promise.allSettled( + t.map(async (item) => { + return await func(item); + }) + ); + }; +} + +// array is not in order, this will be used to test +const items = [200, 1000, 400, 300]; + +// starting test +log("Starting async test"); + +// because of the sleep we expect the responses from small -> large +items.asyncForEach(async (item) => { + await sleep(item); + log(item); +}); + +// because the previous foreach is async we expect this to run immediately. +log("after async foreach (or before? because async..)"); + +async function testWithAwait() { + await items.asyncForEach(async (item) => { + // this will sleep for whatever the largest amount in items is + currentItem. + // this will make sure test items are displayed AFTER the first batch of items. + // this doesn't impact the result because the code IN the foreach will still run async (just with a delayed console.log) + await sleep(Math.max(...items) + item); + log(`Async with wait: ${item}`); + }); + + // because of the await on line 60 this will only run AFTER the async foreach has been handled + log("after testWithAwait"); +} + +testWithAwait(); diff --git a/javascript/async/image.png b/javascript/async/image.png new file mode 100644 index 0000000..f1905bf Binary files /dev/null and b/javascript/async/image.png differ diff --git a/js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts b/js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts new file mode 100644 index 0000000..fec8661 --- /dev/null +++ b/js-ts-frameworks/Angular/state/HttpWithStateSettingsService.ts @@ -0,0 +1,71 @@ +import { Injectable } from "@angular/core"; +import { Store } from "@ngxs/store"; +import { first } from "rxjs/operators"; + +import { Observable, Subject } from "rxjs"; +import { HttpClient, HttpResponse } from "@angular/common/http"; +import * as resolvePath from "object-resolve-path"; + +@Injectable({ + providedIn: "root", +}) +export abstract class HttpWithStateSettingsService { + constructor( + public httpWithStateSettingsServiceStore: Store, + public httpWithStateSettingsServiceHttp: HttpClient, + public settingsKey: string + ) {} + + /** + * Higher order component which will resolve the current backend settings from the state + * @param callback + */ + public withSettings( + callback: (settings) => Observable + ): Observable { + return this.withState((state) => { + return resolvePath(state, this.settingsKey); + }, callback); + } + + /** + * get http response with settings + * @param getUrl function to construct the url. fe: (settings) => { const { root, principals } = settings; return `${root}/${principals}`;} + */ + public getResponse( + getUrl: (settings: TSettings) => string + ): Observable> { + return this.withSettings((settings: TSettings) => { + return this.httpWithStateSettingsServiceHttp.get(getUrl(settings), { + observe: "response", + }); + }).pipe(first()); + } + + /** + * Higher order component which will resolve the current backend settings from the state + * @param callback + */ + private withState( + selector: (state) => any, + callback: (settings) => Observable + ): Observable { + const subject = new Subject(); + this.httpWithStateSettingsServiceStore + .select(selector) + .pipe(first()) + .subscribe((data) => { + callback(data).subscribe( + (result) => { + subject.next(result); + subject.complete(); + }, + (error) => { + subject.error(error); + } + ); + }); + + return subject.asObservable(); + } +} diff --git a/js-ts-frameworks/React/tests/redux-promise-middleware.spec.js b/js-ts-frameworks/React/tests/redux-promise-middleware.spec.js new file mode 100644 index 0000000..b8e38eb --- /dev/null +++ b/js-ts-frameworks/React/tests/redux-promise-middleware.spec.js @@ -0,0 +1,66 @@ +import { PENDING, FULFILLED, REJECTED } from "redux-promise-middleware"; + +export default function handleReduxPromiseMiddleware( + reducer, + actionType, + initialState, + statusKey, + meta = {} +) { + it(`should handle ${PENDING} (redux-promise-middleware)`, () => { + let taintedState = Object.assign({}, initialState); + taintedState[statusKey] = "TAINTED"; + + const pendingAction = { + type: `${actionType}_${PENDING}`, + payload: PENDING, + meta: meta, + }; + + expect(reducer(taintedState, pendingAction)[statusKey]).toEqual(PENDING); + }); + + it(`should handle ${REJECTED} (redux-promise-middleware)`, () => { + let taintedState = Object.assign({}, initialState); + taintedState[statusKey] = "TAINTED"; + + const rejectAction = { + type: `${actionType}_${REJECTED}`, + payload: REJECTED, + meta: meta, + }; + + expect(reducer(taintedState, rejectAction)[statusKey]).toEqual(REJECTED); + }); + + it(`should handle ${FULFILLED} (redux-promise-middleware)`, () => { + let taintedState = Object.assign({}, initialState); + taintedState[statusKey] = "TAINTED"; + + const fulfillAction = { + type: `${actionType}_${FULFILLED}`, + payload: FULFILLED, + meta: meta, + }; + + expect(reducer(taintedState, fulfillAction)[statusKey]).toEqual(FULFILLED); + }); +} + +/** +USAGE: +import { + handleReduxPromiseMiddleware +} from "redux-promise-middleware.spec.js"; +handleReduxPromiseMiddleware( + reducer, + "ACTION_KEY", + initialState, + "status", { + data: { + access_token: "", + token_type: "" + } + } +); +**/ diff --git a/typescript/geo/radiusToPolygon/helpers.ts b/typescript/geo/radiusToPolygon/helpers.ts new file mode 100644 index 0000000..e9088f6 --- /dev/null +++ b/typescript/geo/radiusToPolygon/helpers.ts @@ -0,0 +1,52 @@ +import { Point } from "./point"; + +/** + * Converts an angle in radians to an angle in degrees by multiplying with 180 and dividing by 180 + * @param angleInRadians + */ +export const radiansToDegrees = (angleInRadians: number) => { + return (angleInRadians * 180) / Math.PI; +}; + +/** + * Convert an angle in degrees to an angle in radians by multiplying by PI and dividing the result by 180 + * https://www.mathwarehouse.com/trigonometry/radians/convert-degee-to-radians.php + * @param angleInDegrees + */ +export const degreesToRadians = (angleInDegrees: number) => { + return (angleInDegrees * Math.PI) / 180; +}; + +/** + * Convert a point object to an array of longitude,latitude + * @param point + */ +export const pointToLongLatArray = (point: Point) => { + return [point.longitude, point.latitude]; +}; + +/** + * Get a new point given a point, distance and bearing + * https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/#:~:text=Here%20is%20the%20formula%20to,%E2%80%93%20sin%20la1%20*%20sin%20la2) + */ +export const getRelativePointByDistance = ( + point: Point, + distance: number, + bearing: number +): Point => { + const latitude = Math.asin( + Math.sin(point.latitude) * Math.cos(distance) + + Math.cos(point.latitude) * Math.sin(distance) * Math.cos(bearing) + ); + const longitude = + point.longitude + + Math.atan2( + Math.sin(bearing) * Math.sin(distance) * Math.cos(point.latitude), + Math.cos(distance) - Math.sin(point.latitude) * Math.sin(latitude) + ); + + return { + latitude: radiansToDegrees(latitude), + longitude: radiansToDegrees(longitude), + }; +}; diff --git a/typescript/geo/radiusToPolygon/index.ts b/typescript/geo/radiusToPolygon/index.ts new file mode 100644 index 0000000..142401b --- /dev/null +++ b/typescript/geo/radiusToPolygon/index.ts @@ -0,0 +1,55 @@ +import { + degreesToRadians, + getRelativePointByDistance, + pointToLongLatArray, +} from "./helpers"; +import { Point } from "./point"; + +/** + * + */ +const options = { + /** + * IERS Equatorial Radius of the earth + * https://en.wikipedia.org/wiki/Earth_ellipsoid + */ + equatorialRadiusInMeters: 6378136.6, + + /** + * Number of edges to create in the polygon + * Use 360 to get a true circle + */ + numberOfEdges: 64, +}; + +const input = { + center: { latitude: 51.7543453, longitude: 5.5526647 } as Point, + radius: 2000, +}; + +const coordinates = []; +for (var i = 0; i < options.numberOfEdges; ++i) { + const newPoint = getRelativePointByDistance( + { + latitude: degreesToRadians(input.center.latitude), + longitude: degreesToRadians(input.center.longitude), + }, + input.radius / options.equatorialRadiusInMeters, + // find bearing https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/ + (2 * Math.PI * -i) / options.numberOfEdges + ); + coordinates.push(pointToLongLatArray(newPoint)); +} +// make sure the circle is closed by pushing a copy of the first element +coordinates.push(coordinates[0]); + +console.log( + JSON.stringify( + { + type: "Polygon", + coordinates: [coordinates], + }, + null, + 2 + ) +); diff --git a/typescript/geo/radiusToPolygon/point.ts b/typescript/geo/radiusToPolygon/point.ts new file mode 100644 index 0000000..e6b7eb2 --- /dev/null +++ b/typescript/geo/radiusToPolygon/point.ts @@ -0,0 +1,4 @@ +export interface Point { + latitude: number; + longitude: number; +} diff --git a/typescript/inheritance-and-interfaces.ts b/typescript/inheritance-and-interfaces.ts new file mode 100644 index 0000000..65eb09f --- /dev/null +++ b/typescript/inheritance-and-interfaces.ts @@ -0,0 +1,213 @@ +/** + * interface which specifies all properties of a living being + */ +interface IlivingBeing { + name: string; +} + +/** + * Interface which specifies the bark method + */ +interface IBark { + bark(): string; // functie die een string returned +} + +// You can't instantiate an abstract class so this class is merely used as a base +abstract class animal implements IlivingBeing { + name: string; + age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + + makeNoise() { + return "No sound!"; + } +} + +/** + * The feline class extends the animal class, as such it gets all properties and methods of the animal class + */ +class feline extends animal { + // we receive a name and age and pass it to the super class (animal) + constructor(name: string, age: number) { + super(name, age); + } + + // override the makeNoise method for felines + makeNoise() { + return "Miaauw"; + } +} + +/** + * The canine class extends animal but aditionally implements the bark interface + * This means that we can put canine's in three groups now: + * - objects that implement IBark + * - Animals + * - Canines + * + * we can mark canine as abstract because we have subdivided the canines into + * dogs and wolves below. + */ +abstract class canine extends animal implements IBark { + constructor(name: string, age: number) { + super(name, age); + } + + makeNoise() { + return this.bark(); + } + + bark(): string { + return "Woof not implemented"; + } + + howl() { + return "Hooooowwwllll!"; + } +} + +/** + * The dog class simply extends canine and overrides the bark method + * Because dogs are happy creatures they can also wag their tails + */ +class dog extends canine { + constructor(name: string, age: number) { + super(name, age); + } + + wagTail() { + return "happy"; + } + + bark() { + return "wooof"; + } +} + +/** + * Simply extends from canine and implements a louder bark (more o's in wooof) + */ +class wolf extends canine { + constructor(name: string, age: number) { + super(name, age); + } + + bark() { + return "wooooof"; + } +} + +/** + * A rat is what we call those teeny tiny dogs (like chihuahuas) + */ +class rat extends canine { + size: string; + + constructor(name: string, age: number) { + super(name, age); + } + + howl() { + return "garble " + super.howl(); + } + + bark() { + return "woof"; + } +} + +// +// The classes below show that we can even group "animals" and "others" together +// if they all implement the same (ILivingBeing) interface +// + +class person implements IlivingBeing { + name: string; + + constructor(name: string) { + this.name = name; + } +} + +/** + * Aliens don't really have a name (they have a designation, military race and all) + * in order to class them as IlivingBeing we need to give them a name (because humans like to name everything) + * we do this in the constructor by simply using the aliens designation as its name. + */ +class alien implements IlivingBeing { + name: string; + designation: number; + + constructor(num: number) { + this.designation = num; + this.name = this.designation.toString(); + } +} + +// _______ _ +// |__ __| | | +// | | ___ ___| |_ _ __ _ _ _ __ ___ +// | |/ _ \/ __| __| | '__| | | | '_ \/ __| +// | | __/\__ \ |_ | | | |_| | | | \__ \ +// |_|\___||___/\__| |_| \__,_|_| |_|___/ + +// object declarations +let cat1 = new feline("cat1", 6); +let cat2 = new feline("cat2", 4); +let dog1 = new dog("dog1", 12); +let wolf1 = new wolf("wolf", 7); +let dog2 = new dog("dog2", 4); +let rat1 = new rat("rattekop", 6); +let person1 = new person("John"); +let alien1 = new alien(12351); + +// Arr declarations +let animals: animal[] = [cat1, cat2, dog1, wolf1, dog2, rat1]; +let dogs: dog[] = [dog1, dog2]; +let canines: canine[] = [dog1, dog2, wolf1, rat1]; +let barkers: IBark[] = [...canines]; +let felines: feline[] = [cat1]; +let livingBeings: IlivingBeing[] = [ + cat1, + cat2, + dog1, + wolf1, + dog2, + rat1, + person1, + alien1, +]; + +// for loops +console.log("Dog Wagtails"); +dogs.forEach((dog) => { + console.log(dog.wagTail()); +}); +console.log(" "); + +console.log("Canine Howls"); +canines.forEach((canine) => { + console.log(canine.howl()); +}); +console.log(" "); + +console.log("Animals makeNoise"); +animals.forEach((animal) => { + console.log(animal.makeNoise()); +}); +console.log(" "); + +console.log("Beings name"); +livingBeings.forEach((livingbeing) => { + console.log(livingbeing.name, livingbeing); +}); +console.log(" "); + +console.log("Barkers"); +barkers.forEach((barker) => { + console.log(barker.bark()); +});