Skip to content

Echopraxium/mixin-interface

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mixin-interface

This extension of 'mixin-interface-api' provided a (now deprecated) implementation of the Log feature (e.g. MxI.$System.log()). Please note that this package is now obsolete (event though if is still usable). It is strongly advised to use 'mixin-interface-api' instead.

Release 4.9.3 changelog

Documentation fixes

Release 4.9.0 changelog

This release deprecates the previous Log feature implementation (MxI.$System). This release moves the implementation of Log feature in mixin-interface-api. It is much better and modern thanks to the sink metaphor.

This idea is neither new nor mine but I thought that it would be very nice to have. You're welcome to read this article and take a look at the Serilog library.

Now the Log client sends a trace request (MxI.$Log.write()), then the trace message is eventually processed by being sent to a specific target (e.g. Console, File, Server, Database, etc...). The sink(s) must be explicitly declared (MxI.$Log.addSink()) else the trace request is not processed.

Notice that sink classes must implement MxI.$ILogSink but they are no more singletons.

  • Major refactoring of Log API: step 1/2 - move some classes from mixin-interface to mixin-interface-api
    • MxI.$ILogger interface moved and rename to MxI.$ILogSink.
    • MxI.$DefaultLogger implementation moved and renamed to MxI.$ConsoleLogSink.
    • Implementation of Log feature moved from MxI.$System class to MxI.$Log class. Please notice that the previous API (e.g. MxI.$System.log()) is still supported but is now deprecated.

* Major refactoring of _Log API_: step 2/2 - New implementation classes in `mixin-interface-api` * `MxI.$Log` is the new implementation of the _Log feature_ in which _trace requests_ are processed by _sink(s)_. A _sink_ redirects traces (`MxI.$Log.write()` calls) to specific target (e.g. `$ConsoleLogSink` redirects to the console). * `MxI.$FileLogSink` is a _sink_ which redirects traces (`MxI.$Log.write()` calls) to a file (e.g. `log.txt`) ___ ## Release 4.7.5 changelog * Documentation upgrade 1/2: UML model diagram for the implementation sample * Documentation upgrade 2/2: Paragraphs reordering ( _Sample UML Model_, _Core API Reference_ and _Extended API Reference_ now before _Installation and Usage_ and _How to run the Unit Test_)

Sample UML Model

alt text

Howto: FlyingFish implementation class

Here is an example of how to subclass an implementation class (see ./src/test_classes/flying_fish.js). Please find below how to subclass Animal and implement the IBird and IFish interface classes as well.

  • Subclass Animal (from mixin-interface-api) by means of the MxI.Implementation().$with() idiom (after extends to define both a subclass and the interfaces that it implements).
  • Provide implementation of the service defined by IBird (fly()) and IFish (swim()). If a service from the parent interface(s) is not provided then it may be inherited from the parent implementation class.

Notice this is the case in the following sample: for run() an live(), as they are disabled by the __ prefix then it is the implementation from the parent class which is inherited instead.

  • Add the MxI.$setClass(Cat).$asImplementationOf(IBird, IFish) idiom just after the class definition.

This is required so that MxI.$isInstanceOf() works properly to identify an object both as being an instance of an implementation class (and its superclass(es)) as well being an instance of an interface class (and its superclass(es)).

const MxI     = require('../mixin_interface.js').MxI;
const Animal  = require('mixin-interface-api/src/test_classes/animal.js').Animal;
const IAnimal = require('mixin-interface-api/src/test_classes/i_animal.js').IAnimal;
const IBird   = require('./i_bird.js').IBird;
const IFish   = require('./i_fish.js').IFish;

class FlyingFish extends MxI.$Implementation(Animal).$with(IBird, IFish) {
  constructor() {
    super();
  } // 'FlyingFish' constructor

  fly() {
    MxI.$Log.write('--> FlyingFish.fly');
  } // IBird.fly()

  swim() {
    MxI.$Log.write('--> FlyingFish.swim');
  } // IFish.swim()

  __run() {
    MxI.$Log.write('--> FlyingFish.run');
  } // IAnimal.run()

  __live() {
    MxI.$Log.write('--> FlyingFish.live');
  } // ILifeForm.live()
} // 'FlyingFish' class
MxI.$setClass(FlyingFish).$asImplementationOf(IBird, IFish);
exports.FlyingFish = FlyingFish;

Notice that IAnimal.run() and ILifeForm.live() services are not provided, so they are inherited from the parent implementation class (Animal).

API Reference - Foreword

Please note the following keywords and their meaning:

API service: function provided by 'mixin-interface' (e.g. Mxi.$isInstanceOf())
MxI: namespace for all the mixin-interface API services
object: for _instance of an implementation class
service: for function defined by an interface class (e.g. IAnimal.run())
type: for either an implementation class (e.g. Animal) or an interface class (e.g. IAnimal)
interface: for interface class
super_interface: for superclass of the interface class
implementation: for implementation class
super_implementation: for superclass of the implementation class
...interfaces: list of implemented interfaces. The list is provided as interface class(es) separated by a comma (e.g. ILifeForm and IAnimal, ILifeForm are valid ...interfaces arguments)

Core API reference (mixin-interface-api)

For these services please refer to (mixin-interface-api) for their documentation

  • MxI.$isInstanceOf(): replacement for javascript instanceof operator
  • MxI.$isInterface(): checks if a type is an interface class or not
  • MxI.$implements(): checks if a type implements an interface class or not
  • MxI.$getSuperclass(): get the superclass of a a _type
  • MxI.$Interface(): defines an interface class and its super_interface
  • MxI.$setAsInterface().$asChildOf(): defines that a class is an interface class and its super_implementation

This is syntactically redundant but nevertheless required in order that MxI.$isInstanceOf() works correctly.

  • MxI.$Implementation().$with(): defines an implementation class and its superclass (Mxi.$Object if no other class applies)

  • MxI.$setClass().$asImplementationOf(): defines the interface class(es) implemented by an implementation class

  • MxI.$raiseNotImplementedError(): error handling when a service (defined by of an interface class) is not implemented

  • MxI.$Object().init(): Delayed Initialization feature

  • MxI.$Object().isInitialized(): checks if an object has been initialized

  • MxI.$ISingleton: interface class for the Singleton (i.e. Unique instance) design pattern (see design-patterns-api)

  • MxI.$Singleton: Default implementation for MxI.$ISingleton interface

  • MxI.$isSingleton(): Checks if an object is a Singleton

  • MxI.$setAsSingleton(): Required to define that an implementation is a Singleton

  • MxI.$INullObject: interface class for the Null Object design pattern (see design-patterns-api

  • MxI.$NullObject: Default implementation for MxI.$INullObject interface

  • MxI.$Null: Singleton of MxI.$NullObject

  • MxI.$isNull(): Returns true in 2 cases. The first is when the input value is an object which is both a Null Object an a Singleton (typically the 'default Null Object' which is MxI.$Null). The second case is when the input value is undefined

  • Log Feature

This feature was previously implemented by MxI.$System (in mixin-interface package). MxI.$System still supports the previous implementation but is now deprecated.

  • MxI.$ILogSink: interface class for a sink (implementation of the Log feature).
  • MxI.$Log.write(arg_msg, ...arg_values): new implementation of trace requests.
  • MxI.$Log.banner(): outputs arg_msg within a banner.
  • MxI.$Log.addSink(): declares a sink object (which must implement $ILogSink).
  • MxI.$Log.getSinkCount(): returns the number of sinks.
  • MxI.$Log.clearSinks(): deletes all the sinks.
  • MxI.$ConsoleLogSink: default sink implementation class (sends trace messages to the console).
  • MxI.$FileLogSink: predefined sink implementation class (sends trace messages to a file - e.g. ./log.txt).

Extended API Reference (mixin-interface)

  • MxI.$System.log(): (deprecated, replaced by MxI.$Log.write) Log feature, more effective and flexible than console.log()
  • MxI.$System.banner(): (deprecated, replaced by MxI.$Log.banner) a variant of MxI.$System.log() which allows "decorated logs" with banners
  • MxI.$DefaultLogger: (deprecated) Default implementation of MxI.$ILogSink.
  • MxI.$System.setLogger(): (deprecated) Changes the Logger by providing a instance of a class which implements MxI.$ILogSink
  • MxI.$System.getLogger(): (deprecated) get the current LogSink (an instance of a class which implements MxI.$ILogSink)
  • MxI.$System.resetLogger(): (deprecated) Restores the Default LogSink (MxI.$DefaultLogger)

Log Feature Services

Notice that these are deprecated services.

MxI.$DefaultLogger
MxI.$System.log(arg_msg, ...arg_values)
MxI.$System.banner(arg_msg, arg_single_line_banner, arg_separator_char, arg_separator_length)
MxI.$System.setLogger(log_sink)
MxI.$System.resetLogger()
  • MxI.$System.log(): (deprecated, replaced by MxI.$Log.write) It is more effective and flexible than console.log(), like enabling/disabling traces, redirectog to a File or a Stream, define trace levels and categories etc... To use this feature just replace calls to console.log() by MxI.$Log.write().

A custom Log sink must implement MxI.$ILogSink interface, MxI.$DefaultLogger is provided as the default implementation of this interface (NB: the implementation class should be a Singleton)

  • MxI.$System.setLogger(log_sink): sets the current Log sink.

log_sink must be an instance of a class which implements MxI.$ILogSink

const $StarPrefixLogger = require('./src/test_classes/star_prefix_logger.js').$StarPrefixLogger;
MxI.$System.setLogger( new $StarPrefixLogger() );
  • MxI.$System.resetLogger(): restore the default logger (MxI.$DefaultLogger):
MxI.$System.resetLogger();
  • MxI.$System.banner(): (deprecated, replaced by MxI.$Log.banner) generates nicer logs by surrounding the message in a banner. Optional arguments (after arg_msg) allow to change
    • the number of lines (3 by default, one if arg_single_line_banner is set to true)
    • the separator ('=' by default, another if arg_separator_char is set)
    • the banner size (60 by default, another if arg_separator_length is set)

Example 1:

MxI.$Log.banner("Unit Test for 'mixin-interface' package");

will generate this output:

============================================================
========== Unit Test for 'mixin-interface' package =========
============================================================

Example 2:

MxI.$Log.banner("End of Unit Test", true);

will generate this output:

===================== End of Unit Test =====================

Here is the source code of StarPrefixLogger (see ./src/test_classes/star_prefix_logger.js). Once it is set as the current Log sink (with MxI.$System.setLogger()), it will add '* ' prefix on each output of MxI.$System.log() call (see ./test.js).

const MxI = require('../mixin_interface.js').MxI;
//============ 'StarPrefixLogger' implementation class ============
class StarPrefixLogger extends MxI.$Implementation(MxI.$DefaultLogger).$with(MxI.$ILogSink) {
  constructor(...args) {
	  super();
      this._$prefix = "* ";
  } // 'StarPrefixLogger' constructor
} // 'StarPrefixLogger' class
MxI.$setClass(StarPrefixLogger).$asImplementationOf(MxI.$ILogSink);
exports.StarPrefixLogger = StarPrefixLogger;

Installation and Usage

npm install mixin-interface -S

How to run the Unit Test

Step 1: Install Prerequisite Tools

Install NodeJS and Git

Step 2: Clone the 'mixin-interface' repository locally

Open a command shell then enter the following commands:

git clone git://github.com/Echopraxium/mixin-interface
cd mixin-interface
npm update

Step 3: Run the Unit Test

Now enter the following command:

node test.js

You should get this kind of output (please find here the full output):

============================================================
========== Unit Test for 'mixin-interface' package =========
============================================================
1.Instance of 'Animal' created: animal_0
'animal_0' is a 'Animal' ?           true
'animal_0' is a 'IAnimal' ?          true
--> Animal.run
--> Animal.live
----------------------------------
2. Instance of 'Cat' created: cat_0
'cat_0' is a 'Animal' ?      true
'cat_0' is a 'Cat' ?         true
'cat_0' is a 'IAnimal' ?     true
'cat_0' is a 'IMammal' ?     true
--> Animal.run
--> Cat.suckle
--> Animal.live
...
===================== End of Unit Test =====================

Please notice in the previous output that an implementation class may inherit functions (i.e implementation of services from interface classes) from its parent class (e.g. FlyingFish inherits IAnimal.run() and IAnimal.live() from Animal) but it is also possible to override these default implementations them as well.