@absolunet/ioc2.1.0

View on GitHub

http/repositories/ControllerRepository.js

//--------------------------------------------------------
//-- Node IoC - HTTP - Repositories - Controller Repository
//--------------------------------------------------------

import __ from '@absolunet/private-registry';


/**
 * Controller repository where controller registration and instantiation are done.
 *
 * @memberof http.repositories
 * @hideconstructor
 */
class ControllerRepository {

	/**
	 * Class dependencies: <code>['app']</code>.
	 *
	 * @type {Array<string>}
	 */
	static get dependencies() {
		return ['app'];
	}

	/**
	 * ControllerRepository constructor.
	 */
	constructor() {
		__(this).set('groups', []);
	}

	/**
	 * Add a controller binding.
	 *
	 * @param {string} name - The controller name.
	 * @param {http.controllers.Controller} controller - The controller class.
	 * @returns {http.repositories.ControllerRepository} The current controller repository instance.
	 */
	add(name, controller) {
		this.app.bind(this.buildName(name), controller);

		return this;
	}

	/**
	 * Get the controller instance by name.
	 * If it was not registered into the repository, it will attempt to find it in the application folder.
	 *
	 * @param {string} name - The controller name.
	 * @returns {http.controllers.Controller} The controller instance.
	 */
	get(name) {
		const fullName = this.getName(name);

		if (this.app.isBound(fullName)) {
			return this.app.make(this.getName(name));
		}

		return this.getFromPath(this.app.controllerPath(), name);
	}

	/**
	 * Check if the given controller was registered.
	 *
	 * @param {string} name - The controller name.
	 * @returns {boolean} Indicates that the controller name was already registered into the repository.
	 */
	has(name) {
		return this.app.isBound(this.getName(name));
	}

	/**
	 * Group controllers inside a given namespace.
	 *
	 * @param {string} name - The group namespace.
	 * @param {Function} group - The controller group closure.
	 * @returns {http.repositories.ControllerRepository} The current controller repository instance.
	 */
	group(name, group) {
		const groups = __(this).get('groups');
		const index = groups.push({ name }) - 1;
		group(this, this.app);
		groups.splice(index, 1);

		return this;
	}

	/**
	 * Make controller instance from base path and controller name.
	 * Mainly relies on path structure and file name.
	 *
	 * @param {string} controllerPath - The controller file path.
	 * @param {string} name - The controller name.
	 * @returns {http.controllers.Controller} The resolved controller instance.
	 */
	getFromPath(controllerPath, name) {
		const escape = '\\';
		const regex = new RegExp(`${escape}${this.namespaceSeparator}`, 'gu');
		const fileName = this.resolveName(name).replace(regex, '/');
		const filePath = this.app.formatPath(controllerPath, fileName);

		return this.app.make(filePath);
	}

	/**
	 * Resolve the name based on current groups and given name.
	 *
	 * @param {string} name - The base name.
	 * @returns {string} The resolved, fully qualified, name.
	 */
	buildName(name) {
		return this.getName([...__(this).get('groups'), { name }]
			.map(({ name: part }) => {
				return part;
			})
			.join(this.namespaceSeparator));
	}

	/**
	 * Get the name that is used for container binding.
	 *
	 * @param {string} name - The controller name.
	 * @param {boolean} [withAction=false] - Indicates that the returned value should contain the action.
	 * @returns {string} The controller name into toe container.
	 */
	getName(name, withAction = false) {
		const fullName = `controller.${name}`;

		return withAction ? fullName : this.resolveName(fullName);
	}

	/**
	 * Get controller name without container prefix or action.
	 *
	 * @param {string} name - The controller action.
	 * @returns {string} The controller name without the action method.
	 */
	resolveName(name) {
		return name.replace(this.actionPattern, '');
	}

	/**
	 * Get the action from the given qualified controller name.
	 *
	 * @example
	 * this.resolveAction('PostController@show'); // 'show'
	 *
	 * @param {string} name - The controller action.
	 * @returns {string} The controller method name, without the controller name.
	 */
	resolveAction(name) {
		return name.match(this.actionPattern).groups.action;
	}

	/**
	 * Action pattern for qualified action.
	 *
	 * @type {RegExp}
	 */
	get actionPattern() {
		return /@(?<action>.*)$/u;
	}

	/**
	 * Controller namespace separator.
	 *
	 * @type {string}
	 */
	get namespaceSeparator() {
		return '.';
	}

	/**
	 * The namespace for core controllers.
	 *
	 * @type {string}
	 */
	get coreNamespace() {
		return 'core';
	}

}


export default ControllerRepository;