File: src/lib/facade.coffee
require('es6-promise').polyfill()
copy = require('copy-class').copy
{ camelize, requireFile } = require './util'
getProto = Object.getPrototypeOf ? (obj) -> obj.__proto__
###*
Facade class of DDD pattern.
- create instance of factories
- create instance of repositories
@class Facade
@module base-domain
###
class Facade
###*
create instance of Facade
@method createInstance
@static
@param {Object} [options]
@return {Facade}
###
@createInstance: (options= {}) ->
Constructor = @
return new Constructor(options)
###*
constructor
@constructor
@param {String} [options]
@param {String} [options.dirname="."] path where domain definition files are included
###
constructor: (options) ->
@classes = {}
@dirname = options.dirname ? '.'
@init()
# for base-domainify. keep it empty
init: ->
###*
load master tables
@method loadMasterTables
@return {Promise}
###
loadMasterTables: (modelNames...) ->
Promise.all (@getRepository(modelName).load?() for modelName in modelNames)
###*
get a model class
@method getModel
@param {String} name
@return {Class}
###
getModel: (name) ->
return @require(name)
###*
get a factory class
ISSUE: user will never know load failure
@method getFactory
@param {String} name
@param {Boolean} [useAnonymousWhenFailed=false]
@return {Function}
###
getFactory: (name, useAnonymousWhenFailed = off) ->
try
return @require("#{name}-factory")
catch e
throw e if not useAnonymousWhenFailed
AnonymousFactory = Facade.BaseFactory.getAnonymousClass(name)
@addClass("#{name}-factory", AnonymousFactory, true)
###*
get a repository class
@method getRepository
@param {String} name
@return {Class}
###
getRepository: (name) ->
return @require("#{name}-repository")
###*
create a factory instance
@method createFactory
@param {String} name
@param {Boolean} [useAnonymousWhenFailed=false]
@return {BaseFactory}
###
createFactory: (name, useAnonymousWhenFailed = off) ->
FactoryClass = @getFactory(name, useAnonymousWhenFailed)
return new FactoryClass()
###*
create a repository instance
@method createRepository
@param {String} name
@param {Object} [options]
@return {BaseRepository}
###
createRepository: (name, options) ->
@create("#{name}-repository", options)
###*
read a file and returns class
@method require
@private
@param {String} name
@return {Function}
###
require: (name) ->
return @classes[name] if @classes[name]?
file = "#{@dirname}/#{name}"
klass = requireFile file
@addClass name, klass
###*
check existence of the class of the given name
@method hasClass
@param {String} name
@return {Function}
###
hasClass: (name) ->
try
@require(name)
return true
catch e
return false
###*
add copied class to facade.
the class is acquired by @require(name)
attaches getFacade() method (for both class and instance)
@method addClass
@private
@param {String} name
@param {Function} klass
@param {Boolean} skipNameValidation validate class name is compatible with the name to register
@return {Function}
###
addClass: (name, klass, skipNameValidation = false) ->
if skipNameValidation
camelCasedName = camelize name
else
if klass.getName() isnt name
throw @error """given class should be named '#{klass.getName()}',
but '#{name}' given."""
camelCasedName = klass.name
ParentClass = getProto(klass::).constructor
if @constructor.isBaseClass ParentClass
Class = copy(klass, camelCasedName)
else
CopiedParentClass = @require ParentClass.getName()
Class = copy(klass, camelCasedName, CopiedParentClass)
facade = @
Class.getFacade = -> facade
Class::getFacade = -> facade
@classes[name] = Class
###*
read a file and returns the instance of the file's class
@method create
@private
@param {String} name
@param {Object} [options]
@return {BaseFactory}
###
create: (name, options) ->
DomainClass = @require(name)
return new DomainClass(options)
###*
create instance of DomainError
@method error
@param {String} reason reason of the error
@param {String} [message]
@return {Error}
###
error: (reason, message) ->
DomainError = @constructor.DomainError
return new DomainError(reason, message)
###*
check if given object is instance of DomainError
@method isDomainError
@param {Error} e
@return {Boolean}
###
isDomainError: (e) ->
DomainError = @constructor.DomainError
return e instanceof DomainError
###*
insert fixture data
(Node.js only)
@method insertFixtures
@param {Object} [options]
@param {String} [options.dataDir='./data'] directory to have fixture data files
@param {String} [options.tsvDir='./tsv'] directory to have TSV files
@param {Array(String)} [options.models=null] model names to insert. default: all models
@return {Promise(Object)} dataPool inserted data
###
insertFixtures: (options = {}) ->
Fixture = require './fixture'
fixture = new Fixture(@, options)
fixture.insert(options.models).then ->
return fixture.dataPool
###*
check the given class is registered in facade
@method isBaseClass
@static
@param {Function} klass
@return {Boolean}
###
@isBaseClass: (klass) ->
(klass is @[klass.name]) or
(klass is @DomainError) or
(@[klass.name]?.toString() is klass.toString())
###*
registers the given class as a base class
@method registerBaseClass
@static
@param {Function} klass
###
@registerBaseClass: (klass) -> @[klass.name] = klass
@Base : require './base'
@BaseModel : require './base-model'
@Entity : require './entity'
@BaseList : require './base-list'
@BaseDict : require './base-dict'
@BaseFactory : require './base-factory'
@ListFactory : require './list-factory'
@DictFactory : require './dict-factory'
@BaseRepository : require './base-repository'
@MasterRepository : require './master-repository'
@DomainError : require './domain-error'
module.exports = Facade