Просмотр исходного кода

chore: adds `getRegistered` and `getRegisteredService` method

e22m4u 3 месяцев назад
Родитель
Сommit
36f7b9691f

+ 18 - 13
README.md

@@ -56,14 +56,16 @@ const {Service} = require('@e22m4u/js-service');
 
 Методы:
 
-- `get(ctor, ...args)` получить существующий или новый экземпляр
-- `has(ctor)` проверить существование конструктора в контейнере
-- `add(ctor, ...args)` добавить конструктор в контейнер
-- `use(ctor, ...args)` добавить конструктор и создать экземпляр
-- `set(ctor, service)` добавить конструктор и его экземпляр
-
-- `getParent()` получить родительский сервис-контейнер
-- `hasParent()` проверить наличие родительского сервис-контейнера
+- `get(ctor, ...args)` получить существующий или новый экземпляр;
+- `getRegistered(ctor, ...args)` получить существующий или новый
+  экземпляр, только если указанный конструктор зарегистрирован
+  в контейнере, в противном случае выбрасывается ошибка;
+- `has(ctor)` проверить существование конструктора в контейнере;
+- `add(ctor, ...args)` добавить конструктор в контейнер;
+- `use(ctor, ...args)` добавить конструктор и создать экземпляр;
+- `set(ctor, service)` добавить конструктор и его экземпляр;
+- `getParent()` получить родительский сервис-контейнер;
+- `hasParent()` проверить наличие родительского сервис-контейнера;
 
 ### get
 
@@ -128,11 +130,14 @@ console.log(hasService); // true
 
 Методы:
 
-- `getService(ctor, ...args)` получить существующий или новый экземпляр
-- `hasService(ctor)` проверить существование конструктора в контейнере
-- `addService(ctor, ...args)` добавить конструктор в контейнер
-- `useService(ctor, ...args)` добавить конструктор и создать экземпляр
-- `setService(ctor, service)` добавить конструктор и его экземпляр
+- `getService(ctor, ...args)` получить существующий или новый экземпляр;
+- `getRegisteredService(ctor, ...args)` получить существующий или новый
+  экземпляр, только если указанный конструктор зарегистрирован
+  в контейнере, в противном случае выбрасывается ошибка;
+- `hasService(ctor)` проверить существование конструктора в контейнере;
+- `addService(ctor, ...args)` добавить конструктор в контейнер;
+- `useService(ctor, ...args)` добавить конструктор и создать экземпляр;
+- `setService(ctor, service)` добавить конструктор и его экземпляр;
 
 Сервисом может являться совершенно любой класс. Однако, если это
 наследник класса `Service`, то такой сервис позволяет инкапсулировать

+ 36 - 0
dist/cjs/index.cjs

@@ -122,6 +122,22 @@ var _ServiceContainer = class _ServiceContainer {
     }
     return service;
   }
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param {*} ctor
+   * @param {*} args
+   * @returns {*}
+   */
+  getRegistered(ctor, ...args) {
+    if (!this.has(ctor))
+      throw new InvalidArgumentError(
+        "The constructor %v is not registered.",
+        ctor
+      );
+    return this.get(ctor, ...args);
+  }
   /**
    * Проверить существование конструктора в контейнере.
    *
@@ -236,6 +252,17 @@ var _Service = class _Service {
   getService(ctor, ...args) {
     return this.container.get(ctor, ...args);
   }
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param {*} ctor
+   * @param {*} args
+   * @returns {*}
+   */
+  getRegisteredService(ctor, ...args) {
+    return this.container.getRegistered(ctor, ...args);
+  }
   /**
    * Проверка существования конструктора в контейнере.
    *
@@ -313,6 +340,15 @@ var _DebuggableService = class _DebuggableService extends import_js_debug.Debugg
   get getService() {
     return this._service.getService;
   }
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @type {Service['getRegisteredService']}
+   */
+  get getRegisteredService() {
+    return this._service.getRegisteredService;
+  }
   /**
    * Проверка существования конструктора в контейнере.
    *

+ 1 - 0
package.json

@@ -43,6 +43,7 @@
   "devDependencies": {
     "@commitlint/cli": "~19.8.1",
     "@commitlint/config-conventional": "~19.8.1",
+    "@e22m4u/js-spy": "~0.0.2",
     "@eslint/js": "~9.33.0",
     "c8": "~10.1.3",
     "chai": "~5.2.1",

+ 12 - 0
src/debuggable-service.d.ts

@@ -34,6 +34,18 @@ export class DebuggableService extends Debuggable implements Service {
     ctor: Constructor<T>,
     ...args: any[],
   ): T;
+  
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param ctor
+   * @param args
+   */
+  getRegisteredService<T extends object>(
+    ctor: Constructor<T>,
+    ...args: any[],
+  ): T;
 
   /**
    * Проверка существования конструктора в контейнере.

+ 10 - 0
src/debuggable-service.js

@@ -38,6 +38,16 @@ export class DebuggableService extends Debuggable {
     return this._service.getService;
   }
 
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @type {Service['getRegisteredService']}
+   */
+  get getRegisteredService() {
+    return this._service.getRegisteredService;
+  }
+
   /**
    * Проверка существования конструктора в контейнере.
    *

+ 12 - 0
src/service-container.d.ts

@@ -32,6 +32,18 @@ export declare class ServiceContainer {
     ...args: any[],
   ): T;
 
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param ctor
+   * @param args
+   */
+  getRegistered<T extends object>(
+    ctor: Constructor<T>,
+    ...args: any[],
+  ): T;
+
   /**
    * Проверить существование конструктора в контейнере.
    *

+ 17 - 0
src/service-container.js

@@ -123,6 +123,23 @@ export class ServiceContainer {
     return service;
   }
 
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param {*} ctor
+   * @param {*} args
+   * @returns {*}
+   */
+  getRegistered(ctor, ...args) {
+    if (!this.has(ctor))
+      throw new InvalidArgumentError(
+        'The constructor %v is not registered.',
+        ctor,
+      );
+    return this.get(ctor, ...args);
+  }
+
   /**
    * Проверить существование конструктора в контейнере.
    *

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

@@ -1,6 +1,7 @@
 import {expect} from 'chai';
 import {Service} from './service.js';
 import {format} from '@e22m4u/js-format';
+import {createSpy} from '@e22m4u/js-spy';
 import {ServiceContainer} from './service-container.js';
 import {SERVICE_CONTAINER_CLASS_NAME} from './service-container.js';
 
@@ -536,6 +537,28 @@ describe('ServiceContainer', function () {
     });
   });
 
+  describe('getRegistered', function () {
+    it('throws Error if the given constructor is not registered', function () {
+      const container = new ServiceContainer();
+      class MyService extends Service {}
+      const throwable = () => container.getRegistered(MyService);
+      expect(throwable).to.throw(
+        'The constructor MyService is not registered.',
+      );
+    });
+
+    it('should pass arguments to the "get" method and return a result', function () {
+      const container = new ServiceContainer();
+      class MyService extends Service {}
+      const spy = createSpy(container, 'get', () => 'result');
+      container.add(MyService);
+      const res = container.getRegistered(MyService, 'arg1', 'arg2');
+      expect(spy.callCount).to.be.eq(1);
+      expect(spy.getCall(0).args).to.be.eql([MyService, 'arg1', 'arg2']);
+      expect(res).to.be.eql('result');
+    });
+  });
+
   describe('has', function () {
     describe('Service', function () {
       it('returns true when a given constructor has its cached instance or false', function () {

+ 12 - 0
src/service.d.ts

@@ -38,6 +38,18 @@ export declare class Service {
     ...args: any[],
   ): T;
 
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param ctor
+   * @param args
+   */
+  getRegisteredService<T extends object>(
+    ctor: Constructor<T>,
+    ...args: any[],
+  ): T;
+
   /**
    * Проверка существования конструктора в контейнере.
    *

+ 12 - 0
src/service.js

@@ -48,6 +48,18 @@ export class Service {
     return this.container.get(ctor, ...args);
   }
 
+  /**
+   * Получить существующий или новый экземпляр,
+   * только если конструктор зарегистрирован.
+   *
+   * @param {*} ctor
+   * @param {*} args
+   * @returns {*}
+   */
+  getRegisteredService(ctor, ...args) {
+    return this.container.getRegistered(ctor, ...args);
+  }
+
   /**
    * Проверка существования конструктора в контейнере.
    *

+ 13 - 0
src/service.spec.js

@@ -46,6 +46,19 @@ describe('Service', function () {
     });
   });
 
+  describe('getRegisteredService', function () {
+    it('calls the container "getRegisteredService" method', function () {
+      const service = new Service();
+      service.container.getRegistered = function (ctor, ...args) {
+        expect(ctor).to.be.eq(Date);
+        expect(args).to.be.eql(['foo', 'bar', 'baz']);
+        return 'OK';
+      };
+      const res = service.getRegisteredService(Date, 'foo', 'bar', 'baz');
+      expect(res).to.be.eq('OK');
+    });
+  });
+
   describe('hasService', function () {
     it('calls the container "has" method', function () {
       const service = new Service();