npm package: @sasm/system
Description: Base System for SASM products.
Author: Spencer A. Lockhart
Latest version: 3.0.1

# SasmSystem

> Base system for SASM products.

[![NPM Version][npm-image]][npm-url]
[![Node Version][node-image]][node-url]
[![Express Version][express-image]][express-url]
[![Sasm Version][sasm-image]][sasm-url]

## Install

```bash
npm i @sasm/system
```

## Usage
Getting started with SASM is as simple as requiring the system and starting.
```JavaScript
const sasm = require('@sasm/system');
sasm.
	start().
    registerRoot(__dirname).
    listen();
```
All methods of system return the system, so can be chained as demonstrated above. The first thing you should do is invoke the start() method.

### Root Directory
It's important to give [![Sasm Version][sasm-image]][sasm-url] a root directory. The root may be registered at any point during the runtime cycle, but it's recommended you register the root directory  **before** registering any extensions.
```JavaScript
sasm.registerRoot(__dirname);
```

### Using Extensions
[![Sasm Version][sasm-image]][sasm-url] is extensible. You probably also want to use the router extension, [![Sasm Router][sasmRouter-image]][sasmRouter-url], so we'll use that as an example. It's also useful to create custom extensions, but we'll get into that later.

@Sasm Extensions must be installed just like the system.

```bash
npm i @sasm/router
```

Once installed, you may use extensions with [![Sasm Version][sasm-image]][sasm-url] simply by registering the name of the NPM module:
```JavaScript
sasm.registerExtension('@sasm/router');
```
Or by requiring the module, and then registering a reference to it:
```JavaScript
const router = require('@sasm/router');
sasm.registerExtension(router);
```
It's important to note that extensions should be registered **after** running `sasm.start()` and **before** running `sasm.listen()`.
You may use `sasm.extensions` to access the registered extensions, as in this example:
```JavaScript
const sasm = require('@sasm/system');
const router = require('@sasm/router');
sasm.
	start().
	registerRoot(__dirname).
	registerExtension(router).
	listen();
console.log("extensions:", sasm.extensions);

```
Possible Output: (connection address is subject to local machine preferences)
```bash
extensions: { SasmRouter: [Function: SasmRouter] }
Listening on: 6::::8080
```

### Running an HTTP server
> See the [![Sasm Router][sasmRouter-image]][sasmRouter-url] module for further documentation on setting up the routing end of the HTTP server.

To start the HTTP server, simply run `sasm.listen()`. 

You may pass a callback to `sasm.listen()`, but it is not necessary. 

### Configuration
It's possible to configure your HTTP and MongoDB connection preferences. The default configuration is:
```JSON
{
	"connection": {
		"hostname": "localhost",
		"ip4": "127.0.0.1",
		"ip6": null,
		"port": 8080
	},
	"database": {
		"username": null,
		"password": null,
		"address": [
			{
				"host": "localhost",
				"port": 27017
			}
		],
		"name": "sasm",
		"options": {}
	}
}
```
You can pass your own configuration to `sasm.start()` (not recommended), or modify the default configuration (recommended).
The default configuration is accessed using `sasm.prep.config`.

Example: Changing the HTTP port to 80:
```JavaScript
const sasm = require('@sasm/system');
sasm.prep.config.connection.port = 80;
sasm.start();
// ...
```
Example: Passing default configuration to `sasm.start()`
```JavaScript
const myConfig = {
	"connection": {
		"hostname": "localhost",
		"ip4": "127.0.0.1",
		"ip6": null,
		"port": 8080
	},
	"database": {
		"username": null,
		"password": null,
		"address": [
			{
				"host": "localhost",
				"port": 27017
			}
		],
		"name": "sasm",
		"options": {}
	}
};

const myRequirements = {
	"bcrypt": "bcrypt",
	"express": "express",
	"io": "socket.io",
	"mongojs": "mongojs"
};
const myPrep = {
    config: myConfig,
    requirements: myRequirements
}

sasm.start(myPrep);
```
As you can see, requirements are bundled into prep with configuration. You can add requirements if needed, and they will be required and assigned as expected...
```JSON
{
    "[name]": "[require]"
}
```

Requirements can be accessed directly from the sasm variable. For example, to get the socket.io reference, you would use `sasm.io`. 

**Note: you must invoke `sasm.start()` for requirements to be loaded.**

### Custom Extensions
Sasm extensions are simply JavaScript functions/classes whom take the sasm system as their sole argument. Extensions may add to, modify, and use anything branching off of sasm. With that said, one should be careful not to conflict with other extensions and anything on the namespace of sasm. It's common practice to create a sub-namespace for your extension, so not to conflict with other extensions.

Let's create a simple Hello World extension and register it.
```JavaScript
const sasm = require('@sasm/system');

class HelloWorld {
	constructor (sasm) {
		console.log("Hello, World!");
	}
}

sasm.
	start().
	registerExtension(HelloWorld);
console.log("extensions:", sasm.extensions);
```
Output:
```bash
Hello, World!
extensions: { HelloWorld: [Function: HelloWorld] }
```
#### Using SASM in Extensions
That's all easy enough, but let's think of a more useful example. Say we have some data for raw materials in our inventory, and we want to send it to a dynamic file accessible through a web browser. We'll call the module `requestMaterialData`.

The document layout for this example is as follows:
```
erp>
	server.js
    plugins>
    	requestMaterialData>
        	package.json
			index.js
    public>
    	index.html
```
`server.js` has the following code:
```JavaScript
const sasm = require('@sasm/system');
const requestMaterialData = require("./plugins/requestMaterialData");
sasm.
	start().
	registerRoot(__dirname).
	registerExtension("@sasm/router").
	registerExtension(requestMaterialData).
	listen();
console.log("extensions:", sasm.extensions);
```
The server requires the sasm system and our custom extension module. We simply start sasm, set the root, and register the `router` extension and our `requestMaterialData` extension. Then we set up the listener and log the extensions to make sure we created our extension correctly. 

For our module to work properly, we need to create the folder `plugins/requestMaterialData` and the file `plugins/requestMaterialData/index.js`. After that, we enter the folder in the terminal and run the command:
```bash
npm init
```
With that, we simply answer a few questions and we are given the following `package.json` (or something similar depending on what you answered):
`./plugins/requestMaterialData/package.json`
```JSON
{
  "name": "@sasm/requestmaterialdata",
  "version": "1.0.0",
  "description": "Supply material data on HTTP request.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Spencer A. Lockhart",
  "license": "MIT"
}

```
Once that's done, we can write some code for our new extension: 
`./plugins/requestMaterialData/index.js`
```JavaScript
const materials = [
	{
		"id": 0,
		"name": "case",
		"count": 900
	},
	{
		"id": 1,
		"name": "poly",
		"count": 1000
	},
	{
		"id": 2,
		"name": "tape",
		"count": 10000
	}
];

class RequestMaterialData {
	constructor (sasm) {
		const self = this;
		self.sasm = sasm;

		sasm.app.get('/requestMaterialData', function (req, res) {
			res.send(materials);
		});
	}
}

module.exports = RequestMaterialData;
```
First, we create a psuedo materials data structure- an array of objects, where each object is material data containing an id, a name, and a count. 

After the data is established, we create the class `RequestMaterialData`. We simply put a constructor in there that takes `sasm` as an argument, and we use `sasm.app` from [![Express Version][express-image]][express-url] to send the data when the url `/requestMaterialData` is requested. 

> 1. It's good practice to assign `this` to a variable, such as a constant named `self`, so that we do not confuse `this` in inner functions.
> 2. It's useful to assign `sasm` to `self.sasm` so that we can access it from other methods in our class.

When we run `server.js`, we should get the following output in our terminal:
```bash
extensions: { SasmRouter: [Function: SasmRouter],
  RequestMaterialData: [Function: RequestMaterialData] }
Listening on: 6::::8080
```
Also, when we visit `http://localhost:8080/requestMaterialData` in our web browser, we get our unformatted JSON data:
```JSON
[{"id":0,"name":"case","count":900},{"id":1,"name":"poly","count":1000},{"id":2,"name":"tape","count":10000}]
```

#### Adding to SASM
With this example so far, you learned how to use `sasm.app` from express in a custom module. But let's say we want to make the data available to other extensions on the server- not just HTTP requests. To do that, we have to add to `sasm`. To do that, we just add a single line to our constructor:

```JavaScript
class RequestMaterialData {
	constructor (sasm) {
		const self = this;
		self.sasm = sasm;
		sasm.requestMaterialData = materials;
		sasm.app.get('/requestMaterialData', function (req, res) {
			res.send(materials);
		});
	}
}
```
> Important Note: As of SasmSystem 3.0.0, a fatal error will be thrown if an extension adds anything to sasm that is not allowed.
> Allowed names are **one of**:
> 1. Extension class name. (example, SasmRouter)
> 2. Extension class name with the first letter lower-case. (example, sasmRouter)
> 3. Extension class name, minus "Sasm" (example, Router)
> 4. Extension class name, minus "Sasm", with first letter lower-case (example, router)

An attempt to assign something to sasm that is not an allowed name for that extension, and/or an attempt to assign more than one thing to sasm will result in a fatal error.

In a real system, we would use a database to store data for materials. But for this example, we're keeping it simple by using static JSON data. Communicating with a database would require quite a bit more code and a way to get/set data. But since we're just using plain JSON, one line makes it possible for other extensions and stuff on our server to read, write, and remove material data.

To test this, let's modify `server.js` to log our material data to the terminal:
```JavaScript
const sasm = require('@sasm/system');
const requestMaterialData = require("./plugins/requestMaterialData");
sasm.
	start().
	registerRoot(__dirname).
	registerExtension("@sasm/router").
	registerExtension(requestMaterialData).
	listen();
console.log(sasm.requestMaterialData);
```
Now, when we run our server, we get the following output:
```bash
[ { id: 0, name: 'case', count: 900 },
  { id: 1, name: 'poly', count: 1000 },
  { id: 2, name: 'tape', count: 10000 } ]
Listening on: 6::::8080
```
Now you've seen how to create an extension that uses Sasm and adds to Sasm. There's much to be done with this, but do **be careful not to pollute the Sasm namespace, and use best practices.** In future versions of sasm, there is no guarentee that anything not specifically allowed on sasm will be okay, and may result in a fatal error. Remember, every extension gets a single, clear, allowed property on sasm. 

[npm-image]: https://img.shields.io/badge/npm-5.8.0-green.svg
[npm-url]: https://www.npmjs.com/
[node-image]: https://img.shields.io/badge/NodeJS-9.10.1-green.svg
[node-url]: https://nodejs.org/
[sasm-image]: https://img.shields.io/badge/@sasm/system-latest-green.svg
[sasm-url]: https://www.npmjs.com/package/@sasm/system
[sasmRouter-image]: https://img.shields.io/badge/@sasm/router-latest-red.svg
[sasmRouter-url]: https://www.npmjs.com/package/@sasm/router
[express-image]: https://img.shields.io/badge/express-4.16.3-green.svg
[express-url]: https://www.npmjs.com/package/express