Skip to content

Commit

Permalink
events system working
Browse files Browse the repository at this point in the history
  • Loading branch information
dcrockwell committed Oct 12, 2016
1 parent 43c75cc commit 7a32165
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 33 deletions.
90 changes: 60 additions & 30 deletions documentation/developerGuide.md
Expand Up @@ -20,6 +20,14 @@ Conan is a generic system that helps break down complex tasks into managable `co
* Defines the interface for the deployer and contains all `components`, `properties`, and `steps` for the deployment.
* Portable. Can be used multiple times or stubbed into a new object that automatically `.use`

**Additional Terminology:**

* **pseudo.js**
* An optional file that serves only as a notepad to jot down API ideas for the plugin.
* None of the code in this file is ever intended to be executed.
* It's not required or used in any way other than as a guide for the developer.
* Feel free to skip making a `pseudo.js` if you don't think it's useful.

## Design The Plugin's API

The first step to making a great plugin is to design the API for it. This gives you an idea of what the finished product will look like, and which components / properties you'll need.
Expand All @@ -29,24 +37,25 @@ The first step to making a great plugin is to design the API for it. This gives
``` javascript
// pseudo.js

new Conan().use(MyPlugin)
const myDeployer = new Conan().use(MyPlugin);

myDeployer

.server("127.0.0.1")
.username("bob")
.password("12345")
.directory("~/my-server/")
.directory("~/my-server/") // deployment directory

.server("127.0.0.2")
.username("bob")
.password("12345")
.directory("~/my-server/")
.directory("~/my-server/") // deployment directory

.base("./some/dir/")
.base("./some/dir/") // local base directory, default to process.cwd()

.file("util/something.js")
.file("util/something.js") // single file in base directory

.files("bin/**/*")
.files("lib/**/*")
.files("bin/**/*") // multiple files by glob path

.deploy(error => {
if (error) { throw error; }
Expand All @@ -56,28 +65,49 @@ new Conan().use(MyPlugin)

**Note:** Use your imagination! This is just one way conan can be utilized to break down a complex task into a portable container!

## Defining Components

From the `pseudo.js` file, we see that a `server` and `file` component are needed, each with their respective `properties`. We also need a `fileCollection` component to find multiple `files` by glob string.

The idea is to ensure that every `component`, `property`, and `step` has a single responsibility so that they remain small and managable units of code. By that logic, we can break out each component in our example with it's properties in a list like this:

* **deployer**
* **base**: string
* **server**
* **hostname**: string
* **username**: string
* **password**: string
* **directory**: string
* **file**
* **path**: string
* **content**: string
* **name**: string
* **base**: string
* **fileCollection**
* **glob**: string
* **base**: string
* **files**: array of files
## Create The Plugin File

The plugin file's single responsibility is to define the top-level interface of the deployer.

Here's an example of a bare-bones **plugin.js**:

``` javascript
export default class MyPlugin {
constructor(deployer) {
// Here is where you customize the deployer's interface
// using `.properties` and `.component`
}
}
```

The next step is to define `properties` and `components` on the deployer.

## List Out Components & Properties

So, which `properties` and `components` get defined where? That's what we want to answer next.

From the `pseudo.js` file, we see that a `server` and `file` component are needed. We can also infer that we'll need a `fileCollection` component to find multiple `files` by glob string.

Each have with their respective `properties`, along with the deployer itself that has a single `base` property.

``` markdown
* deployer
* base: string
* server
* hostname: string
* username: string
* password: string
* directory: string
* file
* path: string
* content: string
* name: string
* base: string
* fileCollection
* glob: string
* base: string
* files: array of files
```

### Component Files

Expand All @@ -91,7 +121,7 @@ import { Component } from "conan";

export default class Server extends Component {
initialize() {

ss
}
}
```
11 changes: 11 additions & 0 deletions es5/lib/conan.js
Expand Up @@ -18,6 +18,10 @@ var _conanComponent = require("./conanComponent.js");

var _conanComponent2 = _interopRequireDefault(_conanComponent);

var _events = require("events");

var _events2 = _interopRequireDefault(_events);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
Expand Down Expand Up @@ -55,6 +59,13 @@ var Conan = function (_ConanComponent) {
_.staircase = new _staircase2.default(this);

this.stepGroups = _.staircase.stepGroups;
this.events = _.staircase.events;
}
}, {
key: "on",
value: function on(name, listener) {
this.events.on(name, listener);
return this;
}
}, {
key: "parallel",
Expand Down
9 changes: 9 additions & 0 deletions es5/spec/conan/conan.events.spec.js
@@ -0,0 +1,9 @@
import Conan from "../../lib/conan.js";
import privateData from "incognito";

describe("conan.events", () => {
it("should return staircase's event emitter", () => {
const conan = new Conan();
conan.events.should.eql(privateData(conan).staircase.events);
});
});
22 changes: 22 additions & 0 deletions es5/spec/conan/conan.on.spec.js
@@ -0,0 +1,22 @@
import Conan from "../../lib/conan.js";

describe("conan.on", () => {
let conan;

beforeEach(() => {
conan = new Conan();
});

it("should create a new event handler", done => {
conan.on("test", (one, two) => {
[one, two].should.eql([1, 2]);
done();
});

conan.events.emit("test", 1, 2);
});

it("should return this to enable chaining", () => {
conan.on("something", () => {}).should.eql(conan);
});
});
26 changes: 26 additions & 0 deletions es5/spec/conan/conan.on.step.start.spec.js
@@ -0,0 +1,26 @@
import Conan from "../../lib/conan.js";

describe("conan.events.on('step:start', handler)", () => {
it("should call the provided handler each time a step starts", done => {
const conan = new Conan();

const calledSteps = [];

conan
.on("step:start", (deployer, step) => {
calledSteps.push(step);
})
.step(function stepOne() {})
.series(function stepTwo() {}).apply(1, 2, 3);

conan
.deploy(() => {
calledSteps.should.eql([
{ name: "stepOne", arguments: [conan] },
{ name: "stepTwo", arguments: [conan, 1, 2, 3] }
]);

done();
});
});
});
2 changes: 1 addition & 1 deletion es5/spec/conan/conan.step.spec.js
Expand Up @@ -13,7 +13,7 @@ describe("conan.series(...steps)", () => {
returnValue = conan.step(step);
});

it("should return conan to enable chaining", () => {
it("should return this to enable chaining", () => {
returnValue.should.eql(conan);
});

Expand Down
8 changes: 8 additions & 0 deletions es6/lib/conan.js
Expand Up @@ -2,6 +2,8 @@ import Staircase from "staircase";
import privateData from "incognito";
import ConanComponent from "./conanComponent.js";

import EventEmitter from "events";

/**
* @class Conan
*/
Expand All @@ -20,6 +22,12 @@ export default class Conan extends ConanComponent {
_.staircase = new Staircase(this);

this.stepGroups = _.staircase.stepGroups;
this.events = _.staircase.events;
}

on(name, listener) {
this.events.on(name, listener);
return this;
}

parallel(...steps) {
Expand Down
9 changes: 9 additions & 0 deletions es6/spec/conan/conan.events.spec.js
@@ -0,0 +1,9 @@
import Conan from "../../lib/conan.js";
import privateData from "incognito";

describe("conan.events", () => {
it("should return staircase's event emitter", () => {
const conan = new Conan();
conan.events.should.eql(privateData(conan).staircase.events);
});
});
22 changes: 22 additions & 0 deletions es6/spec/conan/conan.on.spec.js
@@ -0,0 +1,22 @@
import Conan from "../../lib/conan.js";

describe("conan.on", () => {
let conan;

beforeEach(() => {
conan = new Conan();
});

it("should create a new event handler", done => {
conan.on("test", (one, two) => {
[one, two].should.eql([1, 2]);
done();
});

conan.events.emit("test", 1, 2);
});

it("should return this to enable chaining", () => {
conan.on("something", () => {}).should.eql(conan);
});
});
2 changes: 1 addition & 1 deletion es6/spec/conan/conan.step.spec.js
Expand Up @@ -13,7 +13,7 @@ describe("conan.series(...steps)", () => {
returnValue = conan.step(step);
});

it("should return conan to enable chaining", () => {
it("should return this to enable chaining", () => {
returnValue.should.eql(conan);
});

Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -52,7 +52,7 @@
"incognito": "^0.1.4",
"jargon": "^0.2.0",
"mrt": "^0.4.7",
"staircase": "^0.3.3"
"staircase": "^0.3.4"
},
"devDependencies": {
"babel": "^6.5.1",
Expand Down

0 comments on commit 7a32165

Please sign in to comment.