@reformjs/girder

0.0.2-rc.11 • Public • Published

npm version

Girder

Girder is a framework built to compartmentalize common setup steps for any front end Javascript application. It is structured around the idea that any application is just a series of plugins, even the application itself. Each plugin in this context is called an Aspect, as that plugin is just an aspect of the application.

Table of contents

Contribution

These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. For instructions on how to use this system within another project please see the Installation section.

Prerequisites

This project requires NodeJS (version 16 or later), PNPM, and RushJS. Please follow their respective installation instructions. To verify if they are installed and available each has a "-v" command option for getting the installed version.

Setup

Start with cloning this repo on your local machine:

$ git clone https://github.com/CodeMedic42/reform.git
$ cd girder

To install dependencies run this Rush command

$ rush install

Project Commands

This project has these high level commands

  • build

    To build all project the command "rush build" will run the build command for each project which has changes from the last time this command was ran.

  • deploy

    To build and publish a new version the command "rush deploy" will perform a build and will deploy the latest version of the changes for those projects who have changes.

  • test

    To run the unit tests the command "rush test" will run the test system. (Note: There are no tests at this time)

  • lint

To run the linting rules the command "rush list" will run the linting system. (Note: There are is no command at this time)

Usage

These instructions will help you install this library to your project and provide assistance on how to use it. For information on how to contribute to this project please see the Contribution section

Installation

To install and set up the library, run:

  • npm

    $ npm install girder
  • yarn

    $ yarn add girder
  • pnpm

    $ pnpm install girder

Overview

There are only two primary parts to this system, the Client and the Aspect. Both are the building blocks to an application which is compartmentalized and flexible. The next two sections will provide information on both and how to take advantage of their functionality.

  1. Client

    The client is the root of your application it runs the show but needs very little. To get started with it all you need to do is import it and create an instance of it.

    import Client from '@reformjs/girder';
    
    const client = new Client();

    However the client it really nothing more than a startup workflow implementation. Its the Aspects which really do the work. We'll get to them in a minute. First we need to know how to add them to the client. Let's take a look at that.

    client.registerAspect(YOUR_ASPECT_HERE);

    You can register as many different aspects as you want. However as we will see, each Aspect has a unique id, and all registered Aspects must be unique.

    Once you have registered all your Aspects all that's needed is to start the client. This will start the process of setting up and executing the logic inside each Aspect.

    client.start();

    The start method will return a promise which when resolved will indicate that all Aspects have completely started.

    If however you want to stop the client all that you need to do is the following.

    client.stop();

    The stop method will return a promise which when resolved will indicate that all Aspects have completely stopped.

    That is all it takes, but we can shorten all this down like this.

    import Client from '@reformjs/girder';
    
    const client = new Client()
      .registerAspect(YOUR_ASPECT_HERE)
      .registerAspect(YOUR_ASPECT_HERE)
      .registerAspect(YOUR_ASPECT_HERE)
      .start();
  2. Aspect

    The Aspect is the brains behind the Client.

    An Aspect can be created via following

    import { Aspect } = "@reformjs/girder";
    
    const aspect = new Aspect('YOUR_ASPECT_ID');

    All an aspect requires is an ID as the first parameter to the constructor.

    However this Aspect is not going to do anything. We need to extend from it to take advantage of it functionality.

    import { Aspect } = "@reformjs/girder";
    
    class MyAspect extends Aspect {
      // ... overrides
    }

    However if you have logic you would like to run as part of the constructor be sure to either default the Aspect Id or allow whoever uses your Aspect to provide it.

    import { Aspect } = "@reformjs/girder";
    
    class MyAspect extends Aspect {
      constructor(aspectId) {
        super(aspectId);
      }
    
      // ... overrides
    }
    import { Aspect } = "@reformjs/girder";
    
    class MyAspect extends Aspect {
      constructor() {
        super('MY_STATIC_ID');
      }
    
      // ... overrides
    }

    Note: There are a series of methods which can be overridden which are called as part of the client startup and stopping workflows. We will go over these methods shortly. If extending directly from the Aspect you do not need to call the super instance of these methods. However if extending from a predefined Aspect from another library or a custom one then the super instance should be called. Further examples will illustrate this even though they are extending from the base Aspect. Failure to do so might prevent the system from fully starting up.

    - hooks

    The method, hooks, is synchronous, can be overridden, and is called as the client is starting up. The purpose of this method is to provide setup configuration to other Aspects as they are starting up. It takes no parameters and expects an object to er returned. We will get to how the Aspect accesses this information in a moment.

    For an example let's assume we have an Aspect like so...

    import { Aspect } = "@reformjs/girder";
    
    class MyAspect extends Aspect {
      constructor() {
        super('MY_STATIC_ID');
      }
    
      // ... overrides
    }

    Let's assume the above Aspect has configuration information it uses.

    To pass this information in from another Aspect we would do this.

    import { Aspect } = "@reformjs/girder";
    
    class MyOtherAspect extends Aspect {
      constructor() {
        super('MY_OTHER_STATIC_ID');
      }
    
      hooks() {
        return {
          ...super.hooks(),
          MY_OTHER_STATIC_ID: {} // Here is the configuration information.
        };
      }
    
      // ... overrides
    }

    The hooks method expects an object to be returned where each property key is the id of the Aspect you want to pass information to. You are not obligated to override or use this method or even pass information to all aspects. Only provide information to those Aspects you want to. The information passed can be any data type.

    - onInitialize

    The method, onInitialize, is asynchronous, can be overridden, and is called as the client is starting up. The purpose of this method is to setup and run any prelaunch logic.

    • Return Value

      Before we get to the parameters provided to this method we first need to talk about what can be returned from it and how that information is used.

      The return value can be generally be anything. If it is a promise then the Client will wait for this and any other Aspects to complete their initialization before starting the system fully. What's important is what happens to what you return directly or through a promise. Inside the client when it is started as each onInitialize method is called, their return values, if not nil, are place in an object called the clientContext under a key matching the id of the Aspect. As an example given the MyAspect example above, if it were to return something like this...

      import { Aspect } = "@reformjs/girder";
      
      class MyAspect extends Aspect {
        constructor() {
          super('MY_STATIC_ID');
        }
      
        onInitialize(config) {
          return super.onInitialize(config)
            .then((superControls) => {
              return {
                ...superControls,
                startInterval: (cb, delay, ...args) => {
                  this.interval = setInterval(cb, delay, ...args);
                },
                clearInterval: () => {
                  clearInterval(this.interval);
                }
              };
            });
        }
      
        // ... overrides
      }

      Then the clientContext would result in something like this.

      {
        MY_STATIC_ID: {
          startInterval,
          clearInterval,
        }
      }

      For the rest of this document the return value from this method will be referred to as the Aspect control.

    • Parameters

      The onInitialize method is passed one object called the configuration. The configuration provides two properties.

      • hooks

        This will be an array of all hooks gathered from all the registered Aspects who specified a hook for that AspectId. It will be up to the running Aspect to combine that data together. An Aspect should not require any order to the hooks as no guarantee can be given on the order of how Aspects are registered.

        import { Aspect } = "@reformjs/girder";
        
        class MyOtherAspect extends Aspect {
          constructor() {
            super('MY_OTHER_STATIC_ID');
          }
        
          onInitialize(config) {
            const { hooks } = config;
        
            // Do what you will with the provided hooks.
          }
        
          // ... overrides
        }
      • getContext

        This is a function which will, when called, return the clientContext providing access to all the Aspect controls. Be aware that the clientContext is not considered complete until the client has fully started up and will throw an error if called too soon. This method is meant to be used inside any functions returned as part of the Aspect control.

      • stopClient

        This is a function which will, when called, start the process of stopping the client. It is the same as calling the stop method off the client object itself.

    - onStart

    The method, onStart, is synchronous, can be overridden, and is called as the client is starting up. It is called after the onInitialize method is called for all Aspects and all promises returned from them have successfully resolved. This method is called to signal that everything is loaded and ready to be used. Any logic which requires the use of another aspect can safely be done here. This is where the main part of your application will probably start from.

    • Return Value

      There is no return value for this method

    • Parameters

      Only one parameter is provided to this method and that is the clientContext. This can be used as part of an Aspect final startup routine and all Aspect controls can safely be used at this point. Also any calls from getContext from within the Aspect controls will also work.

    - onStop

    The method, onStop, is asynchronous, can be overridden, and is called as the client is stopping. This method can be used for any teardown needed within an Aspect. A promise can be return

    • Return Value

      A promise can be returned from here and will delay the stopping of the client until it resolves. The client will not be able to be restarted until it has completely stopped.

    • Parameters

      There are no parameters provided to this method.

Examples

A working example on how to setup girder and create some basic Aspects has been provided.

Versioning

We use SemVer for versioning.

Authors

Contributors

See also the list of contributors who participated in this project.

License

MIT License © Christian Micle

Readme

Keywords

none

Package Sidebar

Install

npm i @reformjs/girder

Weekly Downloads

0

Version

0.0.2-rc.11

License

ISC

Unpacked Size

27 kB

Total Files

14

Last publish

Collaborators

  • codemedic42