Browse Source

chore: adds the "set" method to set a service constructor and its instance

e22m4u 1 year ago
parent
commit
ecf0902aea
7 changed files with 188 additions and 16 deletions
  1. 2 0
      README.md
  2. 15 4
      src/service-container.d.ts
  3. 28 4
      src/service-container.js
  4. 99 0
      src/service-container.spec.js
  5. 15 4
      src/service.d.ts
  6. 16 4
      src/service.js
  7. 13 0
      src/service.spec.js

+ 2 - 0
README.md

@@ -37,6 +37,7 @@ npm install @e22m4u/js-service
 - `has(ctor)` проверка существования конструктора в контейнере
 - `add(ctor, ...args)` добавить конструктор в контейнер
 - `use(ctor, ...args)` добавить конструктор и создать экземпляр
+- `set(ctor, service)` добавить конструктор и связанный экземпляр
 
 ### get
 
@@ -84,6 +85,7 @@ console.log(myDate3); // Sun May 05 2030 03:00:00
 - `hasService(ctor)` проверка существования конструктора в контейнере
 - `addService(ctor, ...args)` добавить конструктор в контейнер
 - `useService(ctor, ...args)` добавить конструктор и создать экземпляр
+- `setService(ctor, service)` добавить конструктор и связанный экземпляр
 
 Сервисом может являться совершенно любой класс. Однако, если это
 наследник класса `Service`, то такой сервис позволяет инкапсулировать

+ 15 - 4
src/service-container.d.ts

@@ -5,7 +5,7 @@ import {Constructor} from './types.js';
  */
 export declare class ServiceContainer {
   /**
-   * Get.
+   * Получить существующий или новый экземпляр.
    *
    * @param ctor
    * @param args
@@ -16,7 +16,7 @@ export declare class ServiceContainer {
   ): T;
 
   /**
-   * Has.
+   * Проверка существования конструктора в контейнере.
    *
    * @param ctor
    */
@@ -25,7 +25,7 @@ export declare class ServiceContainer {
   ): boolean;
 
   /**
-   * Add.
+   * Добавить конструктор в контейнер.
    *
    * @param ctor
    * @param args
@@ -36,7 +36,7 @@ export declare class ServiceContainer {
   ): this;
 
   /**
-   * Use.
+   * Добавить конструктор и создать экземпляр.
    *
    * @param ctor
    * @param args
@@ -45,4 +45,15 @@ export declare class ServiceContainer {
     ctor: Constructor<T>,
     ...args: any[],
   ): this;
+
+  /**
+   * Добавить конструктор и связанный экземпляр.
+   *
+   * @param ctor
+   * @param service
+   */
+  set<T extends object>(
+    ctor: Constructor<T>,
+    service: T,
+  ): this;
 }

+ 28 - 4
src/service-container.js

@@ -14,7 +14,7 @@ export class ServiceContainer {
   _services = new Map();
 
   /**
-   * Get.
+   * Получить существующий или новый экземпляр.
    *
    * @param {*} ctor
    * @param {*} args
@@ -44,7 +44,7 @@ export class ServiceContainer {
   }
 
   /**
-   * Has.
+   * Проверка существования конструктора в контейнере.
    *
    * @param {*} ctor
    * @return {boolean}
@@ -54,7 +54,7 @@ export class ServiceContainer {
   }
 
   /**
-   * Add.
+   * Добавить конструктор в контейнер.
    *
    * @param {*} ctor
    * @param {*} args
@@ -76,7 +76,7 @@ export class ServiceContainer {
   }
 
   /**
-   * Use.
+   * Добавить конструктор и создать экземпляр.
    *
    * @param {*} ctor
    * @param {*} args
@@ -96,4 +96,28 @@ export class ServiceContainer {
     this._services.set(ctor, service);
     return this;
   }
+
+  /**
+   * Добавить конструктор и связанный экземпляр.
+   *
+   * @param {*} ctor
+   * @param {*} service
+   * @return {this}
+   */
+  set(ctor, service) {
+    if (!ctor || typeof ctor !== 'function')
+      throw new InvalidArgumentError(
+        'The first argument of ServicesContainer.set must be ' +
+          'a class constructor, but %v given.',
+        ctor,
+      );
+    if (!service || typeof service !== 'object' || Array.isArray(service))
+      throw new InvalidArgumentError(
+        'The second argument of ServicesContainer.set must be ' +
+          'an Object, but %v given.',
+        service,
+      );
+    this._services.set(ctor, service);
+    return this;
+  }
 }

+ 99 - 0
src/service-container.spec.js

@@ -610,4 +610,103 @@ describe('ServiceContainer', function () {
       });
     });
   });
+
+  describe('set', function () {
+    it('requires the "ctor" argument to be a class constructor', function () {
+      const container = new ServiceContainer();
+      const throwable = v => () => container.set(v, {});
+      const error = v =>
+        format(
+          'The first argument of ServicesContainer.set must be ' +
+            'a class constructor, but %s given.',
+          v,
+        );
+      expect(throwable()).to.throw(error('undefined'));
+      expect(throwable('str')).to.throw(error('"str"'));
+      expect(throwable(10)).to.throw(error('10'));
+      expect(throwable(true)).to.throw(error('true'));
+      expect(throwable(false)).to.throw(error('false'));
+      expect(throwable(null)).to.throw(error('null'));
+      expect(throwable([])).to.throw(error('Array'));
+      expect(throwable({})).to.throw(error('Object'));
+      throwable(String)();
+    });
+
+    it('requires the "service" argument to be an Object', function () {
+      const container = new ServiceContainer();
+      const throwable = v => () => container.set(String, v);
+      const error = v =>
+        format(
+          'The second argument of ServicesContainer.set must be ' +
+            'an Object, but %s given.',
+          v,
+        );
+      expect(throwable()).to.throw(error('undefined'));
+      expect(throwable('str')).to.throw(error('"str"'));
+      expect(throwable(10)).to.throw(error('10'));
+      expect(throwable(true)).to.throw(error('true'));
+      expect(throwable(false)).to.throw(error('false'));
+      expect(throwable(null)).to.throw(error('null'));
+      expect(throwable([])).to.throw(error('Array'));
+      throwable({})();
+    });
+
+    describe('Service', function () {
+      it('returns itself', function () {
+        class MyService extends Service {}
+        const container = new ServiceContainer();
+        const res = container.set(MyService, {});
+        expect(res).to.be.eq(container);
+      });
+
+      it('sets the given service', function () {
+        class MyService extends Service {}
+        const container = new ServiceContainer();
+        const service = {};
+        container.set(MyService, service);
+        const res = container.get(MyService);
+        expect(res).to.be.eq(service);
+      });
+
+      it('overrides by the given service', function () {
+        class MyService extends Service {}
+        const container = new ServiceContainer();
+        const service1 = {foo: 'bar'};
+        const service2 = {bar: 'baz'};
+        container.set(MyService, service1);
+        container.set(MyService, service2);
+        const res = container.get(MyService);
+        expect(res).to.be.eq(service2);
+      });
+    });
+
+    describe('non-Service', function () {
+      it('returns itself', function () {
+        class MyService {}
+        const container = new ServiceContainer();
+        const res = container.set(MyService, {});
+        expect(res).to.be.eq(container);
+      });
+
+      it('sets the given service', function () {
+        class MyService {}
+        const container = new ServiceContainer();
+        const service = {};
+        container.set(MyService, service);
+        const res = container.get(MyService);
+        expect(res).to.be.eq(service);
+      });
+
+      it('overrides by the given service', function () {
+        class MyService {}
+        const container = new ServiceContainer();
+        const service1 = {foo: 'bar'};
+        const service2 = {bar: 'baz'};
+        container.set(MyService, service1);
+        container.set(MyService, service2);
+        const res = container.get(MyService);
+        expect(res).to.be.eq(service2);
+      });
+    });
+  });
 });

+ 15 - 4
src/service.d.ts

@@ -18,7 +18,7 @@ export declare class Service {
   constructor(container?: ServiceContainer);
 
   /**
-   * Get service.
+   * Получить существующий или новый экземпляр.
    *
    * @param ctor
    * @param args
@@ -29,7 +29,7 @@ export declare class Service {
   ): T;
 
   /**
-   * Has service.
+   * Проверка существования конструктора в контейнере.
    *
    * @param ctor
    */
@@ -38,7 +38,7 @@ export declare class Service {
   ): boolean;
 
   /**
-   * Add service.
+   * Добавить конструктор в контейнер.
    *
    * @param ctor
    * @param args
@@ -49,7 +49,7 @@ export declare class Service {
   ): this;
 
   /**
-   * Use service.
+   * Добавить конструктор и создать экземпляр.
    *
    * @param ctor
    * @param args
@@ -58,4 +58,15 @@ export declare class Service {
     ctor: Constructor<T>,
     ...args: any[],
   ): this;
+
+  /**
+   * Добавить конструктор и связанный экземпляр.
+   *
+   * @param ctor
+   * @param service
+   */
+  setService<T extends object>(
+    ctor: Constructor<T>,
+    service: T,
+  ): this;
 }

+ 16 - 4
src/service.js

@@ -24,7 +24,7 @@ export class Service {
   }
 
   /**
-   * Get service.
+   * Получить существующий или новый экземпляр.
    *
    * @param {*} ctor
    * @param {*} args
@@ -35,7 +35,7 @@ export class Service {
   }
 
   /**
-   * Has service.
+   * Проверка существования конструктора в контейнере.
    *
    * @param {*} ctor
    * @return {boolean}
@@ -45,7 +45,7 @@ export class Service {
   }
 
   /**
-   * Add service.
+   * Добавить конструктор в контейнер.
    *
    * @param {*} ctor
    * @param {*} args
@@ -57,7 +57,7 @@ export class Service {
   }
 
   /**
-   * Use service.
+   * Добавить конструктор и создать экземпляр.
    *
    * @param {*} ctor
    * @param {*} args
@@ -67,4 +67,16 @@ export class Service {
     this.container.use(ctor, ...args);
     return this;
   }
+
+  /**
+   * Добавить конструктор и связанный экземпляр.
+   *
+   * @param {*} ctor
+   * @param {*} service
+   * @return {this}
+   */
+  setService(ctor, service) {
+    this.container.set(ctor, service);
+    return this;
+  }
 }

+ 13 - 0
src/service.spec.js

@@ -66,4 +66,17 @@ describe('Service', function () {
       expect(res).to.be.eq(service);
     });
   });
+
+  describe('setService', function () {
+    it('calls the container "set" method', function () {
+      const service = new Service();
+      const date = new Date();
+      spy.on(service.container, 'set', (ctor, input) => {
+        expect(ctor).to.be.eq(Date);
+        expect(input).to.be.eq(date);
+      });
+      const res = service.addService(Date, date);
+      expect(res).to.be.eq(service);
+    });
+  });
 });