Skip to content

Commit

Permalink
[wip] Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
filipediasf committed Jan 3, 2013
2 parents c003792 + 5e578fc commit 6f6276c
Show file tree
Hide file tree
Showing 10 changed files with 575 additions and 85 deletions.
6 changes: 2 additions & 4 deletions .gitignore
Expand Up @@ -41,7 +41,7 @@ Session.vim

# Sublime TextEditor #
######################
/*.sublime-project
*.sublime-project

# Eclipse #
######################
Expand All @@ -55,6 +55,4 @@ npm-debug.*

# Project specific #
######################
components
vendor
test/tmp
/test/tmp
57 changes: 56 additions & 1 deletion .npmignore
@@ -1,3 +1,58 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so

# OS generated files #
######################
.DS_Store*
ehthumbs.db
Icon?
Thumbs.db
Desktop.ini
.Spotlight-V100
.Trashes
._*

# NetBeans #
############
nbsettings/
nbproject/
nbbuild/
nbdist/
nbactions.xml
nb-configuration.xml

# Textmate #
############
*.tmproj
*.tmproject
tmtags

# VIM #
#######
.*.sw[a-z]
*.un~
Session.vim

# Sublime TextEditor #
######################
*.sublime-project

# Eclipse #
######################
/.project
/.settings

# Node #
######################
node_modules
npm-debug.*

# Project specific #
######################
test
/test
78 changes: 49 additions & 29 deletions README.md
Expand Up @@ -27,22 +27,23 @@ An automaton task is a simple object, describing what the task will do.

For illustration purposes, here's a simple `autofile` that just creates a folder and copies a file into it:

```javascript
```js
var myTask = {
tasks: [
{
task: 'mkdir',
description: 'create the project root folder',
description: 'Create the project root folder',
options: {
dir: 'some_dir'
dirs: ['some_dir']
}
},
{
task: 'cp',
description: 'copy some file',
description: 'Copy some file',
options: {
src: 'some_file',
dst: 'some_dir/dest_file'
files: {
'some_file': 'some_dir/dest_file'
}
}
}
]
Expand All @@ -53,31 +54,37 @@ module.exports = myTask;

To illustrate most of the capabilities of Automaton, here's a complete `autofile` with comments along the file:

```javascript
```js
var task = {
// This id is not mandatory but,
// if you want to use this task in other tasks,
// must be provided and unique
// must be provided and should be unique.
id: 'example_task',

// A user friendly name,
// just for reference, not mandatory
// just for reference, not mandatory.
name: 'Example task',

// also not mandatory
// Also not mandatory
author: 'Indigo United',

// Description is not mandatory,
// but can be used to give a base description for the task.
// Can be a string or a function that receives the passed options
// and returns a string.
description: 'My example task',

// Filter is not mandatory,
// but can be used to perform some operation
// on the options before running the subtasks
// on the options before running the subtasks.
filter: function (options, next) {
// You can change existing options
// You can change existing options.
options.dir2 = options.dir2 + '_indigo';

// and even define additional options.
// In this case we're defining
// a `dir3` option,
// which will be used by one of the subtasks
// which will be used by one of the subtasks.
options.dir3 = 'united';

next();
Expand All @@ -86,7 +93,8 @@ var task = {
// This is also optional,
// but useful if you want the automaton
// to automatically check for required options,
// and some additional features, check below
// and some additional features
// Check below for more info.
options: {
dir1: {
// Option description is not mandatory
Expand All @@ -108,16 +116,16 @@ var task = {
},

// A list of subtasks that will run
// when the example_task runs
// when the example_task runs.
tasks: [
{
task: 'mkdir',
description: 'create the root and second folder',
description: 'Create the root and second folder',
options: {
// the option below
// will have its placeholders replaced by
// the value that it receives
dir: '{{dir1}}/{{dir2}}'
// the value that it receives.
dirs: ['{{dir1}}/{{dir2}}']
}
},
{
Expand All @@ -128,32 +136,37 @@ var task = {
// In this case, we even used a placeholder,
// allowing us to skip this subtask depending
// on the run_all option. Of course, you have
// just setted it to something like `false`
// just setted it to something like `false`.
on: '{{run_all}}',
// Description messages can be generated according to the options
// by using a function instead of a static description
// by using a function instead of a static description.
description: function (opt) {
return 'Creating ' + opt.dir1 + '/' + opt.dir2 + '/' + opt.dir3
},
options: {
dir: '{{dir1}}/{{dir2}}/{{dir3}}'
dirs: ['{{dir1}}/{{dir2}}/{{dir3}}']
}
},
{
// if you find yourself looking
// If you find yourself looking
// for something a bit more custom,
// you can just provide a function as the task
// you can just provide a function as the task.
task : function (opt, next) {
// opt is a list of the options
// provided to the task
// provided to the task.

console.log('I can do whatever I want', opt);

// when the task is done,
// When the task is done,
// you just call next(),
// not like the MTV show, though…
// (- -')
next();
},
// The 'on' attribute can also be a function
// for more complex cases.
on: function (opt) {
return !!opt.run_all;
}
}
]
Expand All @@ -175,6 +188,7 @@ Please note that placeholders can be escaped with backslashes:

- **chmod:** Change mode of files
- **cp:** Copy files and directories
- **mv:** Move files and directories
- **mkdir:** Make directories recursively
- **rm:** Remove several files or directories
- **symlink:** Create symlink
Expand Down Expand Up @@ -213,18 +227,24 @@ In order to run an `autofile`, you simply run `automaton`. This will look for `a

### Node.js

Automaton can also be used as a node module. Here's a quick example of its usage:
Automaton can also be used programatically as a node module. Here's a quick example of its usage:

```javascript
var automaton = require('automaton');
var automaton = require('automaton').create();

// Since autofiles are node modules themselves,
// you can just require them
// Note that you could have instead declared
// the module inline, in JSON
// the module inline, in JSON.
var myTask = require('my_autofile');

automaton.run(myTask, { 'some_option': 'that is handy' });
automaton.run(myTask, { 'some_option': 'that is handy' }, function (err) {
if (err) {
console.log('Something went wrong: ' + err.message);
} else {
console.log('All done!');
}
});
```

## Acknowledgements
Expand Down
37 changes: 25 additions & 12 deletions index.js
Expand Up @@ -142,6 +142,10 @@ var Automaton = d.Class.declare({

// function to handle the completion of the task
handle = function (err) {
if (utils.lang.isString(err)) {
err = new Error(err);
}

if (err) {
this._logger.errorln(err.message);
}
Expand All @@ -160,7 +164,8 @@ var Automaton = d.Class.declare({
try {
batch = this._batchTask({
task: task,
options: $options
options: $options,
depth: 1
});
} catch (e) {
return handle(e);
Expand Down Expand Up @@ -192,12 +197,13 @@ var Automaton = d.Class.declare({
if (utils.lang.isString(def.task)) {
this._assertTaskLoaded(def.task, true);
def.task = this._tasks[def.task];
} else if (def.depth === 1) {
this._validateTask(def.task);
}

def.options = def.options || {};
def.parentOptions = def.parentOptions || {};
def.description = def.description || def.task.description;
def.depth = def.depth || 1;

// fill in the options with default values where the option was not provided
for (option in def.task.options) {
Expand Down Expand Up @@ -246,10 +252,8 @@ var Automaton = d.Class.declare({
this._reportNextTask(this._createTaskDefinition(currentSubtask, def));
currentSubtask.task.call(this._context, def.options, next);
}.$bind(this));
// it's not a function, then it must be another task, check if it is loaded, and batch it
} else if (utils.lang.isString(currentSubtask.task)) {
this._assertTaskLoaded(currentSubtask.task);

// it's not a function, then it must be another task
} else {
subtaskBatch = this._batchTask(this._createTaskDefinition(currentSubtask, def));
batch.push(function (subtask, subtaskBatch, next) {
// skip task if disabled
Expand Down Expand Up @@ -376,17 +380,22 @@ var Automaton = d.Class.declare({
* @param {Object} task The task
*/
_validateTask: function (task) {
var taskId = task.id || 'unknown',
var taskId,
x,
curr;
curr,
length;

this._assertIsObject(task, 'Expected task to be an object', true);
if (task.id !== undefined) {
this._assertIsString(task.id, 'Expected id to be a string', true);
if (!task.id) {
this._throwError('Task id cannot be empty.', true);
}
taskId = task.id;
} else {
taskId = 'unknown';
}

if (task.name !== undefined) {
this._assertIsString(task.name, 'Expected name to be a string in \'' + taskId + '\' task', true);
}
Expand All @@ -404,24 +413,28 @@ var Automaton = d.Class.declare({
for (x in task.options) {
curr = task.options[x];
this._assertIsObject(curr, 'Expected options definition to be an object in \'' + taskId + '\' task', true);
if (curr.description !== undefined) {
this._assertIsString(curr.description, 'Expected \'' + x + '\' option description to be a string in \'' + taskId + '\' task', true);
}
}
} else {
task.options = {};
}

this._assertIsArray(task.tasks, 'Expected subtasks to be an array in \'' + taskId + '\' task', true);

for (x = 0; x < task.tasks; ++x) {
length = task.tasks.length;
for (x = 0; x < length; ++x) {
curr = task.tasks[x];

this._assertIsObject('Expected subtask at index \'' + x + '\' to be an object', true);
this._assertIsObject(curr, 'Expected subtask at index \'' + x + '\' to be an object', true);
if (utils.lang.isObject(curr.task)) {
this._assertIsValidTask(curr.task);
this._validateTask(curr.task);
} else {
if (!utils.lang.isString(curr.task) && !utils.lang.isFunction(curr.task)) {
this._throwError('Expected subtask at index \'' + x + '\' to be a string, a function or a task object in \'' + taskId + '\' task', true);
}
if (curr.description !== undefined && !utils.lang.isString(task.description) && !utils.lang.isFunction(task.description)) {
if (curr.description !== undefined && !utils.lang.isString(curr.description) && !utils.lang.isFunction(curr.description)) {
this._throwError('Expected subtask description at index \'' + x + '\' to be a string or a function in \'' + taskId + '\' task', true);
}
if (curr.options !== undefined) {
Expand Down

0 comments on commit 6f6276c

Please sign in to comment.