UNPKG

@leansdk/leanrc

Version:

LeanRC is a MVC framework for creating graceful applications

675 lines (633 loc) 21.4 kB
# This file is part of LeanRC. # # LeanRC is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # LeanRC is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with LeanRC. If not, see <https://www.gnu.org/licenses/>. # example in use ### ```coffee Test.context.use Basis::SessionsUtil.middleware class Test::ApplicationRouter extends Module::Router @inheritProtected() @module Test @map -> @namespace 'version', module: '', prefix: ':v', -> @resource 'invitations', except: 'delete', -> @post 'confirm', at: 'collection' @member -> @post 'sendInvite' @resource 'descendants', only: 'list', -> @get 'count', at: 'collection' module.exports = Test::ApplicationRouter.initialize() ``` ### module.exports = (Module)-> { AnyT, PointerT FuncG, MaybeG, InterfaceG, EnumG, ListG, UnionG, SubsetG, SampleG RouterInterface ConfigurableMixin Class Utils: { _, inflect } } = Module:: class Router extends Module::Proxy @inheritProtected() @include ConfigurableMixin @implements RouterInterface @module Module ipsPath = PointerT @protected path: MaybeG(String), default: '/' ipsName = PointerT @protected name: MaybeG(String), default: '' ipsModule = PointerT @protected module: MaybeG String iplOnly = PointerT @protected only: MaybeG UnionG String, ListG String iplVia = PointerT @protected via: MaybeG UnionG String, ListG String iplExcept = PointerT @protected except: MaybeG UnionG String, ListG String ipoAbove = PointerT @protected above: MaybeG Object ipsAt = PointerT @protected at: MaybeG EnumG 'collection', 'member' ipsResource = PointerT @protected resource: MaybeG String ipsTag = PointerT @protected tag: MaybeG String ipsTemplates = PointerT @protected templates: MaybeG String ipsParam = PointerT @protected param: MaybeG String iplRouters = PointerT @protected routers: MaybeG ListG SubsetG Router iplPathes = PointerT @protected pathes: MaybeG ListG InterfaceG { method: String path: String resource: String action: String tag: String template: String keyName: MaybeG String entityName: String recordName: MaybeG String } iplResources = PointerT @protected resources: MaybeG ListG SampleG Router iplRoutes = PointerT @protected routes: MaybeG ListG InterfaceG { method: String path: String resource: String action: String tag: String template: String keyName: MaybeG String entityName: String recordName: MaybeG String } @public path: MaybeG(String), get: -> @[ipsPath] @public name: MaybeG(String), get: -> @[ipsResource] ? @[ipsName] @public above: MaybeG(Object), get: -> @[ipoAbove] @public tag: MaybeG(String), get: -> @[ipsTag] @public templates: MaybeG(String), get: -> @[ipsTemplates] @public param: MaybeG(String), get: -> @[ipsParam] @public defaultEntityName: FuncG([], String), default: -> [..., vsEntityName] = @[ipsName] .replace /\/$/, '' .split '/' inflect.singularize vsEntityName @public @static map: FuncG([MaybeG Function]), default: (lambda)-> lambda ?= -> @public map: Function, default: lambda return @public map: Function, default: -> return @public root: FuncG([InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String }]), default: ({to, at, resource, action})-> return @public defineMethod: FuncG([ MaybeG ListG InterfaceG { method: String path: String resource: String action: String tag: String template: String keyName: MaybeG String entityName: String recordName: MaybeG String } String String MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (container, method, path, {to, at, resource, action, tag:asTag, template, keyName, entityName, recordName}={})-> unless path? throw new Error 'path is required' path = path.replace /^[/]/, '' if to? unless /[#]/.test to throw new Error '`to` must be in format `<resource>#<action>`' [resource, action] = to.split '#' if not resource? and (vsResource = @[ipsResource]) isnt '' resource = vsResource if not resource? and (vsName = @[ipsName]) isnt '' resource = vsName unless resource? throw new Error 'options `to` or `resource` must be defined' unless action? action = path unless /[/]$/.test resource resource += '/' keyName ?= @[ipsParam]?.replace /^\:/, '' entityName ?= @defaultEntityName() unless _.isString(recordName) or _.isNull(recordName) recordName = @defaultEntityName() vsParentTag = if @[ipsTag]? and @[ipsTag] isnt '' @[ipsTag] else '' vsTag = if asTag? and asTag isnt '' "/#{asTag}" else '' tag = "#{vsParentTag}#{vsTag}" path = switch at ? @[ipsAt] when 'member' "#{@[ipsPath]}:#{inflect.singularize inflect.underscore resource.replace(/[/]/g, '_').replace /[_]$/g, ''}/#{path}" when 'collection' "#{@[ipsPath]}#{path}" else "#{@[ipsPath]}#{path}" template ?= resource + action container.push {method, path, resource, action, tag, template, keyName, entityName, recordName} return @public get: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'get', asPath, aoOpts return @public post: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'post', asPath, aoOpts return @public put: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'put', asPath, aoOpts return @public delete: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'delete', asPath, aoOpts return @public head: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'head', asPath, aoOpts return @public options: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'options', asPath, aoOpts return @public patch: FuncG([ String, MaybeG InterfaceG { to: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String action: MaybeG String tag: MaybeG String template: MaybeG String keyName: MaybeG String entityName: MaybeG String recordName: MaybeG String } ]), default: (asPath, aoOpts)-> # @[iplPathes] ?= [] @defineMethod @[iplPathes], 'patch', asPath, aoOpts return @public resource: FuncG([ String MaybeG UnionG(InterfaceG({ path: MaybeG String module: MaybeG String only: MaybeG UnionG String, ListG String via: MaybeG UnionG String, ListG String except: MaybeG UnionG String, ListG String tag: MaybeG String templates: MaybeG String param: MaybeG String at: MaybeG EnumG 'collection', 'member' resource: MaybeG String above: MaybeG Object }), Function) MaybeG Function ]), default: (asName, aoOpts = null, lambda = null)-> vcModule = @Module if _.isFunction aoOpts lambda = aoOpts aoOpts = {} aoOpts = {} unless aoOpts? { path, module:vsModule only, via, except tag:asTag, templates:alTemplates, param:asParam at, resource:asResource, above } = aoOpts path = path?.replace /^[/]/, '' vsPath = if path? and path isnt '' "#{path}/" else if path? and path is '' '' else "#{asName}/" vsFullPath = switch at ? @[ipsAt] when 'member' [..., previously, empty] = @[ipsPath].split '/' "#{@[ipsPath]}:#{inflect.singularize inflect.underscore previously}/#{vsPath}" when 'collection' "#{@[ipsPath]}#{vsPath}" else "#{@[ipsPath]}#{vsPath}" vsParentName = @[ipsName] vsParentTemplates = if @[ipsTemplates]? and @[ipsTemplates] isnt '' "#{@[ipsTemplates]}/" else '' vsParentTag = if @[ipsTag]? and @[ipsTag] isnt '' @[ipsTag] else '' vsName = if vsModule? and vsModule isnt '' "#{vsModule}/" else if vsModule? and vsModule is '' '' else "#{asName}/" vsTemplates = if alTemplates? and alTemplates isnt '' alTemplates else if alTemplates? and alTemplates is '' '' else if vsModule? and vsModule isnt '' vsModule else if vsModule? and vsModule is '' '' else asName vsTag = if asTag? and asTag isnt '' "/#{asTag}" else '' vsParam = if asParam? and asParam isnt '' asParam else ':' + inflect.singularize inflect.underscore (asResource ? "#{vsParentName}#{vsName}").replace(/[/]/g, '_').replace /[_]$/g, '' # @[iplRouters] ?= [] class ResourceRouter extends Router @inheritProtected() @module vcModule @protected path: String, default: vsFullPath @protected name: String, default: "#{vsParentName}#{vsName}" @protected module: String, default: vsModule @protected only: MaybeG(UnionG String, ListG String), default: only @protected via: MaybeG(UnionG String, ListG String), default: via @protected except: MaybeG(UnionG String, ListG String), default: except @protected above: MaybeG(Object), default: above @protected tag: String, default: "#{vsParentTag}#{vsTag}" @protected templates: String, default: "#{vsParentTemplates}#{vsTemplates}".replace /[\/][\/]/g, '/' @protected param: String, default: vsParam @protected resource: MaybeG(String), default: asResource @map lambda ResourceRouter.constructor = Class @[iplRouters].push ResourceRouter return @public namespace: FuncG([ MaybeG String UnionG(InterfaceG({ module: MaybeG String prefix: MaybeG String tag: MaybeG String templates: MaybeG String at: MaybeG EnumG 'collection', 'member' above: MaybeG Object }), Function) MaybeG Function ]), default: (asName, aoOpts = null, lambda = null)-> vcModule = @Module if aoOpts?.constructor is Function lambda = aoOpts aoOpts = {} aoOpts = {} unless aoOpts? { module:vsModule, prefix tag:asTag, templates:alTemplates at, above } = aoOpts vsParentPath = @[ipsPath] vsPath = if prefix? and prefix isnt '' "#{prefix}/" else if prefix? and prefix is '' '' else "#{asName}/" vsParentName = @[ipsName] vsParentTemplates = if @[ipsTemplates]? and @[ipsTemplates] isnt '' "#{@[ipsTemplates]}/" else '' vsParentTag = if @[ipsTag]? and @[ipsTag] isnt '' @[ipsTag] else '' vsName = if vsModule? and vsModule isnt '' "#{vsModule}/" else if vsModule? and vsModule is '' '' else "#{asName}/" vsTemplates = if alTemplates? and alTemplates isnt '' alTemplates else if alTemplates? and alTemplates is '' '' else if vsModule? and vsModule isnt '' vsModule else if vsModule? and vsModule is '' '' else asName vsTag = if asTag? and asTag isnt '' "/#{asTag}" else '' # @[iplRouters] ?= [] class NamespaceRouter extends Router @inheritProtected() @module vcModule @protected path: String, default: "#{vsParentPath}#{vsPath}" @protected name: String, default: "#{vsParentName}#{vsName}" @protected except: MaybeG(UnionG String, ListG String), default: ['all'] @protected tag: String, default: "#{vsParentTag}#{vsTag}" @protected templates: String, default: "#{vsParentTemplates}#{vsTemplates}".replace /[\/][\/]/g, '/' @protected at: MaybeG(EnumG 'collection', 'member'), default: at @protected above: MaybeG(Object), default: above @map lambda NamespaceRouter.constructor = Class @[iplRouters].push NamespaceRouter return @public member: FuncG(Function), default: (lambda)-> @namespace null, module: '', prefix: '', templates: '', at: 'member', lambda return @public collection: FuncG(Function), default: (lambda)-> @namespace null, module: '', prefix: '', templates: '', at: 'collection', lambda return @public resources: ListG(SampleG Router), get: -> return @[iplResources] @public routes: ListG(InterfaceG { method: String path: String resource: String action: String tag: String template: String keyName: MaybeG String entityName: String recordName: MaybeG String }), get: -> if @[iplRoutes]? and @[iplRoutes].length > 0 return @[iplRoutes] else vlRoutes = [] vlRoutes = vlRoutes.concat @[iplPathes] ? [] vlResources = [] @[iplRouters]?.forEach (ResourceRouter)=> resourceRouter = ResourceRouter.new() vlResources.push resourceRouter vlRoutes = vlRoutes.concat resourceRouter.routes ? [] vlResources = vlResources.concat resourceRouter.resources ? [] @[iplRoutes] = vlRoutes @[iplResources] = vlResources return @[iplRoutes] constructor: (args...)-> super args... @init args... @map() if _.isString @[iplOnly] @[iplOnly] = [@[iplOnly]] if _.isString @[iplVia] @[iplVia] = [@[iplVia]] if _.isString @[iplExcept] @[iplExcept] = [@[iplExcept]] voMethods = list: 'get' detail: 'get' create: 'post' update: 'put' delete: 'delete' voPaths = list: '' detail: null create: '' update: null delete: null # @[iplPathes] ?= [] if @[ipsName]? and @[ipsName] isnt '' vsKeyName = @[ipsParam]?.replace /^\:/, '' vsEntityName = @[ipoAbove]?.entityName vsEntityName ?= @defaultEntityName() vsRecordName = @[ipoAbove]?.recordName if _.isNil(vsRecordName) and not _.isNull vsRecordName vsRecordName = @defaultEntityName() if @[iplOnly]? @[iplOnly].forEach (asAction)=> vsPath = voPaths[asAction] vsPath ?= @[ipsParam] @defineMethod @[iplPathes], voMethods[asAction], vsPath, action: asAction resource: @[ipsResource] ? @[ipsName] template: @[ipsTemplates] + '/' + asAction keyName: vsKeyName entityName: vsEntityName recordName: vsRecordName else if @[iplExcept]? for own asAction, asMethod of voMethods do (asAction, asMethod)=> if not @[iplExcept].includes('all') and not @[iplExcept].includes asAction vsPath = voPaths[asAction] vsPath ?= @[ipsParam] @defineMethod @[iplPathes], asMethod, vsPath, action: asAction resource: @[ipsResource] ? @[ipsName] template: @[ipsTemplates] + '/' + asAction keyName: vsKeyName entityName: vsEntityName recordName: vsRecordName else if @[iplVia]? @[iplVia].forEach (asCustomAction)=> vsPath = voPaths[asCustomAction] vsPath ?= @[ipsParam] if asCustomAction is 'all' for own asAction, asMethod of voMethods do (asAction, asMethod)=> @defineMethod @[iplPathes], asMethod, vsPath, action: asAction resource: @[ipsResource] ? @[ipsName] template: @[ipsTemplates] + '/' + asAction keyName: vsKeyName entityName: vsEntityName recordName: vsRecordName else @defineMethod @[iplPathes], voMethods[asCustomAction], vsPath, action: asCustomAction resource: @[ipsResource] ? @[ipsName] template: @[ipsTemplates] + '/' + asAction keyName: vsKeyName entityName: vsEntityName recordName: vsRecordName else for own asAction, asMethod of voMethods do (asAction, asMethod)=> vsPath = voPaths[asAction] vsPath ?= @[ipsParam] @defineMethod @[iplPathes], asMethod, vsPath, action: asAction resource: @[ipsResource] ? @[ipsName] template: @[ipsTemplates] + '/' + asAction keyName: vsKeyName entityName: vsEntityName recordName: vsRecordName return @public init: FuncG([MaybeG(String), MaybeG AnyT]), default: (args...)-> @super args... @[iplRouters] = [] @[iplPathes] = [] return @initialize()