New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@robotlegsjs/macrobot

Package Overview
Dependencies
Maintainers
3
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@robotlegsjs/macrobot

TypeScript port of Robotlegs Utilities Macrobot

  • 0.2.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
91
decreased by-28.91%
Maintainers
3
Weekly downloads
 
Created
Source

RobotlegsJS Macrobot

GitHub license Gitter chat Build Status codebeat badge Maintainability Test Coverage npm version Greenkeeper badge styled with prettier

Macrobot is a macro command utility for RobotlegsJS which provides the ability to execute batches of commands in sequential or parallel fashion. It was originally implemented by Alessandro Bianco in AS3 and now is ported to TypeScript.

Introduction

While using RobotlegsJS and encapsulating your business logic inside commands, you may find yourself in a situation where you wish to batch commands, instead of relying on events to trigger every step.

Macrobot simplifies the process and provide two ways to group commands:

  • Sequence: The commands will be executed in order one after the other. A command will not be executed until the previous one is complete. The macro itself will not be complete until all its commands are complete.

  • Parallel: The commands will be executed as quickly as possible, with no regards to the order in which they were registered. The macro itself will not be complete until all its commands are complete.

Installation

You can get the latest release and the type definitions using NPM:

npm install @robotlegsjs/macrobot

Or using Yarn:

yarn add @robotlegsjs/macrobot

Usage

To create a macro command, extend one of the two classes Macrobot provides: SequenceMacro or ParallelMacro. Override the prepare() method and add sub commands by calling add() specifying the command class to use. At the appropriate time, the command will be created, instantiated by satisfying the injection points and then executed. This automated process of instantiation, injection, and execution is very similar to how commands are normally prepared and executed in RobotlegsJS.

You could use Guards and Hooks as you would normally use with regular commands to control the execution workflow. Additionally you could use the withPayloads() method to add some data that can be used to satisfy the injection points of the sub commands. The data provided will be available to the guards and hooks applied to the sub command as well.

Here's an example of a simple sequential macro:

import { injectable } from "@robotlegsjs/core";

import { SequenceMacro } from "@robotlegsjs/macrobot";

@injectable()
export class MyMacro extends SequenceMacro {
    public prepare(): void {
        this.add(CommandA);
        this.add(CommandB);
        this.add(CommandC);
    }
}

And here's an example of a simple parallel macro:

import { injectable } from "@robotlegsjs/core";

import { ParallelMacro } from "@robotlegsjs/macrobot";

@injectable()
export class MyMacro extends ParallelMacro {
    public prepare(): void {
        this.add(AwaitForCommand).withPayloads(25);
        this.add(AwaitForCommand).withPayloads(50);
        this.add(AwaitForCommand).withPayloads(75);
    }
}

Using Guards

Guards are used to approve or deny the execution of one of the subcommands.

import { injectable, IGuard } from "@robotlegsjs/core";

import { SequenceMacro } from "@robotlegsjs/macrobot";

@injectable()
export class DailyRoutine extends SequenceMacro {
    public prepare() {
        this.add(Work);
        this.add(Party).withGuards(IsFriday); // It will only party on fridays
        this.add(Sleep);
    }
}

@injectable()
class IsFriday implements IGuard {
    public approve():boolean {
        return (new Date()).getDay() === 5;
    }
}

Using Hooks

Hooks run before the subcommands. They are typically used to run custom actions based on environmental conditions. Hooks will run only if the applied Guards approve the execution of the command.

import { inject, injectable, IGuard, IHook } from "@robotlegsjs/core";

import { SequenceMacro } from "@robotlegsjs/macrobot";

@injectable()
export class DailyRoutine extends SequenceMacro {
    public prepare() {
        this.add(Work);
        this.add(Party).withGuards(IsFriday); // It will only party on fridays
        this.add(Sleep).withHook(GoToHome); // Try to avoid sleeping at the office or the pub
    }
}

@injectable()
class IsFriday implements IGuard {
    public approve():boolean {
        return (new Date()).getDay() === 5;
    }
}

@injectable()
class GoToHome implements IHook {
    @inject(Person)
    public me: Person;

    public hook():void {
        this.me.goHome();
    }
}

Using Payloads

Payloads are used to temporary inject some data, which would not be available otherwise, and make it available to the subcommand, it's guards and it's hooks.

You can pass the data to be injected directly to the withPayloads() method, for a normal injection.

import { inject, injectable, ICommand } from "@robotlegsjs/core";

import { SequenceMacro } from "@robotlegsjs/macrobot";

@injectable()
export class Macro extends SequenceMacro {
    public prepare() {
        const data: SomeModel = new SomeModel();

        this.add(Action).withPayloads(data);
    }
}

@injectable()
class Action implements ICommand {

    @inject(SomeModel)
    public data: SomeModel;

    public execute():void {
        this.data.property = "value";
    }
}

Or you can use the SubCommandPayload class to create a more complex injection.

import { inject, injectable, ICommand } from "@robotlegsjs/core";

import { SequenceMacro, SubCommandPayload } from "@robotlegsjs/macrobot";

@injectable()
export class Macro extends SequenceMacro {

    public prepare() {
        const data: SomeModel = new SomeModel();
        const payload: SubCommandPayload = new SubCommandPayload(data);

        payload.
            .withName("mydata")
            .ofType(IModel);

        this.add(Action).withPayloads(payload);
	}
}

@injectable()
class Action implements ICommand {
    @inject(IModel) @named("mydata")
    public data: IModel;

    public function execute():void {
        this.data.property = "value";
	}
}

Asynchronous commands

While Macrobot can work with synchronous commands, it is most effective when you have to deal with asynchronous ones. Your sub command may wait for a response from a server or for user interaction before being marked as complete. In this case you command can subclass Macrobot's AsyncCommand and call dispatchComplete() when it should be marked as complete. dispatchComplete() receives a single parameter which reports whether the subcommand completed successfully.

Here's an example of a simulated asynchronous sub command:

import { injectable, inject } from "@robotlegsjs/core";

import { AsyncCommand, SequenceMacro } from "@robotlegsjs/macrobot";

@injectable()
export class DelayCommand extends AsyncCommand {
    @inject(Number) protected _delay: number;

    public execute(): void {
        setTimeout(this.onTimeout.bind(this), this._delay);
    }

    protected onTimeout(): void {
        this.dispatchComplete(true);
    }
}

@injectable()
export class MyMacro extends SequenceMacro {

    public prepare():void {
        this.add(DelayCommand).withPayloads(50);
        this.add(DelayCommand).withPayloads(100);

        this.registerCompleteCallback(this.onComplete.bind(this));
    }

    protected onComplete(success): void {
        console.log("All commands have been executed!");
    }
}

Atomic execution

For sequential macros, when the atomic property is set to false (it is true by default) and one of the sub commands dispatches a failure (using dispatchComplete(false)), subsequent sub commands will not be executed and the macro itself will dispatch failure.

Here's an example of a non atomic sequence:

import { injectable, inject } from "@robotlegsjs/core";

import { SequenceMacro, AsyncCommand } from "@robotlegsjs/macrobot";

@injectable()
export class NonAtomicSequenceCommand extends SequenceMacro {
    public prepare(): void {
        this.atomic = false;

        this.add(DelayAsyncCommand).withPayloads(25, true);
        this.add(DelayAsyncCommand).withPayloads(50, false);
        this.add(DelayAsyncCommand).withPayloads(750, false);
        this.add(DelayAsyncCommand).withPayloads(100, false);
    }
}

@injectable()
class DelayAsyncCommand extends AsyncCommand {
    @inject(Number) protected _delay: number;

    @inject(Boolean) protected _succeed: boolean;

    public execute(): void {
        setTimeout(this.onTimeout.bind(this), this._delay);
    }

    protected onTimeout(): void {
        this.dispatchComplete(this._succeed);
    }
}

In the example above, the DelayAsyncCommand will be executed only two times, since the second execution will report a failure and all remaining mappings will be ignored.

This behaviour does not apply to parallel commands.

Contributing

If you want to contribute to the project refer to the contributing document for guidelines.

License

MIT

Keywords

FAQs

Package last updated on 02 Aug 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc