Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
398 lines (332 sloc) 9.88 KB

Boneyard IoC Annotation Manifesto

Version: 1.0.0
Description: Initial document draft for Boneyard Annotation Instrumenter.
This document might be subjected to change.

Scope Types


  • Class
  • Constructor
  • Method
  • Field

Supported Annotations


@spec

  • Scope:
    • Class
  • Parameters:
    • id {String} required | Spec unique identifier
    • path {String} required | Spec Path location
    • include {Array} optional | List of parent spec ids that decorate the current spec
  • Examples:
/**
*	@spec({ id: 'configuration', path: 'specs/configuration' })
**/
class Configuration {
  ...
}
/**
*	@spec({ id: 'main', path: 'specs/main', include: ['header', 'footer', 'content'] })
**/
class ApplicationBootstrap extends Container {
  ...
}

@bone

  • Scope:
    • Class
  • Parameters:
    • id {String} required | Bone Identifier
    • specs {Array} required | Array of specs id in which the bone will belong to
    • singleton {Boolean} optional | Flags the module to create one and only one instance (Default is Non-singleton).
  • Examples:
/**
*	Declaration for registering a bone of type "Account" that extends "Backbone.Model".
*	Bone identifier set to "account".
*	Belongs to Spec "specs/main".
*	Note: Module path will be retrieved automatically based on the file location
*	if not specified manually.
*	@bone({ id: 'account', specs: ['main'] })
**/
class Account extends Backbone.Model {
  ...
}
/**
*	Declaration for registering a bone of type "UserService" that extends "Service".
*	Bone identifier set to "userService".
*	Belongs to Spec "user".
*	Bone path resolution will be automatically resolved based on the class definition of the file.
*	Flag class to resolve instanciates as a singleton (Multiple injects will reference same instance)
*
*	@bone({ id: 'userService', specs: ['user'], singleton: true })
**/
class UserService extends Service {
  ...
}

@component (review)

Similar as @bone with the only difference that it's mainly used to create bones that can NOT be annotated with the @bone annotation. Often, we want to declared a bone that it doesn't necessary need to be overriden by subclassing, the default implementation is enough to use it. A good example of this is: boneyard-ui package provides a lot of components out of the box and ready to use. In most of the cases the default implementation itself is enough to tell the DI to instanciate them.

In this case, notice that the parameter path is required to the IoC where to locate the component.

Best to be thought as a component that is required by the context of a class. Please see example.

  • Scope:
    • Class
  • Parameters:
    • id {String} required | Bone Identifier
    • specs {Array} required | Array of specs id in which the bone will belong to
    • path {String} required | Path to the file on which the class/object is declared and exported via amd.
    • singleton {Boolean} optional | Flags the module to create one and only one instance (Default is Non-singleton).
  • Examples:
import Form from 'ui/form/form';

/**
*  @bone({ id: 'accountForm', specs: ['account'] })
*  @class com.myproject.view.account.AccountForm
*  @extends com.boneyard.ui.form.Form
*
*  Notice that ui/misc/panel default implementation provided by the boneyard-ui is enough for this case.
*  @component({ id: 'basic', path: 'ui/misc/panel', specs: ['account'], params: [{ title: 'Basic Information' }] })
*  @component({ id: 'button', path: 'ui/form/controls/button', specs: ['account'], params: [{ text: 'Update' }] })
**/
class AccountForm extends Form {

  /**
  *  @wire({ id: 'basic', on: 'attrs.views' })
  *  @wire({ id: 'button', on: 'attrs.views' })
  **/
  constructor(attrs) {
	return super(attrs);
  }
  ...
}

@wire (Review)

  • Scope:
    • Constructor
    • Field
  • Parameters
    • id {String} required | Unique Bone identifier
    • on {String} optional | Not required on scope Field, but required on scope Constructor (dot notation).
  • Examples:

ES5:

/**
*  Scope: Constructor
*  @wire({ id: 'myconfig', on: 'config' })
**/
constructor: function(config, other) {
  this.config = config;
  ...
}
/**
*  Scope: Constructor (multiple bones to be injected in the same parameter attribute)
*  @wire({ id: 'header', on: 'attrs.views' })
*  @wire({ id: 'footer', on: 'attrs.views' })
*  @wire({ id: 'config', on: 'other' })
**/
constructor: function(attrs, other) {
  attrs.views[0]; // reference to an instance of 'header' bone
  attrs.views[1]; // reference to an instance of 'footer' bone
  other; // reference to an instance of 'config' bone
  return MyClass.apply(this, arguments);
}
/**
*  Sample output inside a spec of the example provided above.
*  Notice that `attrs` will map to the first constructor argument (index 0) as an object and `other` as second (index 1)
*  Also, notice when multiple wires has the same `on` path target set (`attrs.views`), this will be treated as an array.
*  At the moment of injection, this combination of multiple wires will be resolved as:
**/
...
mybone: {
  $module: 'path/to/module',
  $params: [{ views: ['$bone!header', '$bone!footer'] }, '$bone!config']
}
/**
*  Scope: Field
*  Will execute as a @Action annotation (Simple assignment)
*  Will automatically use the name of the field 'search' to resolve the 'on' attribute (One to One mapping).
*  @wire({ id: 'search' })
**/
search: null,

initialize: function(attrs) {
  this.search.render();
  ...
}

ES6:

/**
*  Scope: Constructor
*  @wire({ id: 'mymodel', on: 'attrs.model' })
**/
constructor(attrs) {
  this.model = attrs.model;
  ...
}
/**
*  Scope: Field (Setter)
*  Will execute as @Action (Assignment)
*  Will automatically use the name of the setter 'value' to resolve the 'on' attribute.
*  @wire({ id: 'mymodel' })
**/
set value(model) {
  this.property = model.get('value');
}

@action (Review)

  • Notes: Resolve multiple instances resolution in order to implement this
  • Scope:
    • Class
    • Method
  • Parameters:
    • bone {String} required | Bone Identifier
    • spec {String} required | Specifies on which spec the action should be part of.
    • method {String} optional | Required Only when the annotation is on the scope class.
    • params {Array} optional | Optional parameters to be passed to the method invocation.
  • Examples:
/**
*	Action Declaration to execute the method "fetch" on the bone "user" with no parameters
*	@bone({ id: 'user', specs: ['profile'] })
*	@action({ bone: 'user', spec: 'profile', method: 'fetch' })
**/
class User extends Backbone.Model {
  ...
}
/**
*  @bone({ id: 'userReportGrid', specs: ['userReport', 'userPerformanceReport'] })
**/
class UserReportGrid extends Container {
  ...

  /**
  *	Action Declaration to execute the method "render" on the bone "userReportGrid" component
  *	with params passed to the method "render".
  *	Notice that method attribute is omitted since the annotation is on scope method.
  *	Notice also, that the action will be executed when the bone is instanciated on the userReport spec only,
  *	even though the userReportGrid was targeted to get an instance on userPerformanceReport too.
  *	Important: @actions can specify one spec target and only one at a time.
  *	@action({ bone: 'userReportGrid', spec: 'userReport', params: [{ method: 'after', target: 'div.header' }] })
  **/
  render() {
    ...
  }

  ...
}

@listenTo (Review)

Specific use of annotation @Action to start listening for events on instances of a given class.

  • Scope:
    • Method
  • Parameters:
    • spec {String} required | Specifies on which spec the action should be part of.
    • events {String} required | list of events to start listen to (comma separated)
    • from {String} required | specifies which property of the actual instance the event should be listening from.

Examples:

class Grid extends Table {

  /**
  *	@wire({ id: 'elements', on: 'attrs.collection' })
  **/
  constructor(...attrs) {
    // Backbone automatically assign attrs.collection to this.collection (Backbone.View)
	super(...attrs);
    this.collection.reset();
  }

  /**
  * Update Grid when add or remove event from Elements Backbone.Collection is fired
  * @listenTo({ spec: 'grid', events: 'add,remove', from: 'collection' })
  **/
  update() {
    this.collection.each(function(element) {
      this.doSomething(element);
      ...
    });
    ...
  }

}

@json (Review)

Specific use of annotation @bone for objects that don't need to be instanciated.

  • Scope:
    • Class
  • Parameters:
    • id {String} required | Bone Identifier
    • specs {Array} required | Specifies on which specs the json bone should be part of.

Examples:

/**
*	Similar to @Bone annotation
*	@json({ id: 'myconfiguration', specs: ['config'] });
**/
var MyConfiguration = {
  key1: "value1",
  key2: "value2",
  ...
};

exports default MyConfiguation;

@plugin (Review)

  • Scope:
    • Class
  • Parameters:
    • name {String} required | Plugin name
    • specs {Array} required | Specifies on which specs the plugin should be part of.
    • config {Object} optional | Optional Plugin configuration

Examples:

/**
*	@plugin({ name: 'themes', specs: ['plugins'], config: '$bone!theme_config' })
*	@plugin({ name: 'html', specs: ['plugins'], config: { basePath: '$bone!html_basepath', mypackage: 'templates/mypackage' } })
**/
import Container from 'ui/container';
...

@ignore

  • Scope:
    • Class
  • Parameters: No parameters

Will ignore all annotations found in the current module.

Examples:

/**
*	@ignore()
**/
import {Container} from "ui";
...

Special Annotations for future releases

These set of featured annotation may affect boneyard-ioc core package.

Patterns:

@proxify

@factory

@decorate

Local Storage:

@persist

@cease