Skip to content

Instantly share code, notes, and snippets.

@mpneuried

mpneuried/basic.coffee

Last active Aug 29, 2015
Embed
What would you like to do?
My Node.js base class
# import the external modules
_ = require('lodash')._
extend = require('extend')
colors = require('colors')
config = require('./config')
# # Basic Module
# ### extends [EventEmitter]
# Basic module to handle errors and initialize modules
module.exports = class Basic extends require('events').EventEmitter
# ## internals
# setting if errors are handled as rest errors. Then the error values are an array `"error-key":[ httpStatusCode, errorDetailMessage ]`
isRest: false
# make the deep extend availible for all modles
extend: extend
# **defaults** *Function* basic object to hold config defaults. Will be overwritten by the constructor options
defaults: =>
return {}
###
## constructor
`new Baisc( options )`
Basic constructor. Define the configuration by options and defaults, init logging and init the error handler
@param {Object} options Basic config object
###
constructor: ( options = {} )->
@on "_log", @_log
@getter "classname", ->
return @constructor.name.toLowerCase()
@config = extend( true, {}, @defaults(), config.get( @classname, true ), options )
# init errors
@_initErrors()
@initialize()
@debug "loaded"
return
# method to include other class methods to this class.
# useage:
# class Foo
# bar: ->
# console.log( 42 )
# return @
#
# class Lorem
# constructor: ->
# @mixin( Foo )
# return
# run: -> return 23
#
# new Lorem().bar().run() # -> log: 42 ; return 23
mixin: (mixins...)=>
if mixins?.length
for mxn in mixins
for _fnname, fn of mxn.prototype when _fnname isnt "constructor"
@[ _fnname ] = fn
return
###
## initialize
`basic.initialize()`
Overwritible Method to initialize the module
@api public
###
initialize: =>
return
###
## define
`basic.define( prop, fnGet [, fnSet] )`
Helper to define getter and setter methods fot a property
@param { String } prop Property name
@param { Function|Object } fnGet Get method or a object with `get` and `set`
@param { Function } [fnSet] Set method
@api public
###
define: ( prop, fnGet, fnSet, writable = true, enumerable = true )=>
_oGetSet =
enumerable: enumerable
writable: writable
if _.isFunction( fnGet )
# set the `defineProperty` object
_oGetSet =
get: fnGet
_oGetSet.set = fnSet if fnSet? and _.isFunction( fnSet )
else
_oGetSet.value = fnGet
# define by object
Object.defineProperty @, prop, _oGetSet
return
###
## getter
`basic.getter( prop, fnGet )`
Shortcut to define a getter
@param { String } prop Property name
@param { Function } fnGet Get method
@api public
###
getter: ( prop, _get, enumerable = true )=>
_obj =
enumerable: enumerable
#writable: false
if _.isFunction( _get )
_obj.get = _get
else
_obj.value = _get
Object.defineProperty @, prop, _obj
return
###
## setter
`basic.setter( prop, fnSet )`
Shortcut to define a setter
@param { String } prop Property name
@param { Function } fnSet Get method
@api public
###
setter: ( prop, fnGet, enumerable = true )=>
Object.defineProperty @, prop, set: fnGet, enumerable: enumerable, writable: true
return
# handle a error
###
## _handleError
`basic._handleError( cb, err [, data] )`
Baisc error handler. It creates a true error object and returns it to the callback, logs it or throws the error hard
@param { Function|String } cb Callback function or NAme to send it to the logger as error
@param { String|Error|Object } err Error type, Obejct or real error object
@api private
###
_handleError: ( cb, err, data = {}, errExnd )=>
# try to create a error Object with humanized message
if _.isString( err )
_err = new Error()
_err.name = err
if @isRest
_err.message = @_ERRORS?[ err ][ 1 ]?( data ) or "unkown"
else
_err.message = @_ERRORS?[ err ]?( data ) or "unkown"
_err.customError = true
else
_err = err
if errExnd?
_err.data = errExnd
for _k, _v of data
_err[ _k ] = _v
if _.isFunction( cb )
#@log "error", "", _err
cb( _err )
else if _.isString( cb )
@log "error", cb, _err
else
throw _err
return _err
###
## log
`base.log( severity, code [, content1, content2, ... ] )`
write a log to the console if the current severity matches the message severity
@param { String } severity Message severity
@param { String } code Simple code the describe/label the output
@param { Any } [contentN] Content to append to the log
@api public
###
log: ( severity, code, content... )=>
args = [ "_log", severity, code ]
@emit.apply( @, args.concat( content ) )
return
###
## _log
`base._log( severity, code [, content1, content2, ... ] )`
write a log to the console if the current severity matches the message severity
@param { String } severity Message severity
@param { String } code Simple code the describe/label the output
@param { Any } [contentN] Content to append to the log
@api private
###
_log: ( severity, code, content... )=>
# get the severity and throw a log event
if @_checkLogging( severity )
_tmpl = "%s %s - #{ new Date().toString()[4..23]} - %s "
args = [ _tmpl, severity.toUpperCase(), @_logname(), code ]
if content.length
args[ 0 ] += "\n"
for _c in content
args.push _c
switch severity
when "fatal"
args[ 0 ] = args[ 0 ].red.bold.inverse
console.error.apply( console, args )
console.trace()
when "error"
args[ 0 ] = args[ 0 ].red.bold
console.error.apply( console, args )
when "warning"
args[ 0 ] = args[ 0 ].yellow.bold
console.warn.apply( console, args )
when "info"
args[ 0 ] = args[ 0 ].blue.bold
console.info.apply( console, args )
when "debug"
args[ 0 ] = args[ 0 ].green.bold
console.log.apply( console, args )
else
return
_logname: =>
return @constructor.name
fatal: ( code, content... )=>
args = [ "_log", "fatal", code ]
@emit.apply( @, args.concat( content ) )
return
error: ( code, content... )=>
args = [ "_log", "error", code ]
@emit.apply( @, args.concat( content ) )
return
warning: ( code, content... )=>
args = ["_log", "warning", code ]
@emit.apply( @, args.concat( content ) )
return
info: ( code, content... )=>
args = [ "_log", "info", code ]
@emit.apply( @, args.concat( content ) )
return
debug: ( code, content... )=>
args = [ "_log", "debug", code ]
@emit.apply( @, args.concat( content ) )
return
###
## _checkLogging
`basic._checkLogging( severity )`
Helper to check if a log will be written to the console
@param { String } severity Message severity
@return { Boolean } Flag if the severity is allowed to write to the console
@api private
###
_checkLogging: ( severity )=>
if not @_logging_iseverity?
@_logging_iseverity = @config.logging.severitys.indexOf( @config.logging.severity )
iServ = @config.logging.severitys.indexOf( severity )
if @config.logging.severity? and iServ <= @_logging_iseverity
true
else
false
###
## _initErrors
`basic._initErrors( )`
convert error messages to underscore templates
@api private
###
_initErrors: =>
@_ERRORS = @ERRORS()
for key, msg of @_ERRORS
if @isRest
if not _.isFunction( msg[ 1 ] )
@_ERRORS[ key ][ 1 ] = _.template( msg[ 1 ] )
else
if not _.isFunction( msg )
@_ERRORS[ key ] = _.template( msg )
return
# error message mapping
ERRORS: =>
"ENOTIMPLEMENTED": "This function is planed but currently not implemented"
DEFAULT =
server:
port: 8401
host: "localhost"
listenHost: null
basepath: "/"
title: "Site title"
express:
logger: "dev"
tmpFolder: null
staticCacheTime: 1000 * 60 * 60 * 24 * 31
# The config module
extend = require( "extend" )
pckg = require( "../package.json" )
# load the local config if the file exists
try
_localconf = require( "../config.json" )
catch _err
if _err?.code is "MODULE_NOT_FOUND"
_localconf = {}
else
throw _err
class Config
constructor: ( @severity = "info" )->
return
init: ( input )=>
@config = extend( true, {}, DEFAULT, _localconf, input, { version: pckg.version } )
@_inited = true
return
all: ( logging = false )=>
if not @_inited
@init( {} )
_all = for _k, _v in @config
@get( _k, logging )
return _all
get: ( name, logging = false )=>
if not @_inited
@init( {} )
_cnf = @config?[ name ] or null
if logging
logging =
logging:
severity: process.env[ "severity_#{name}"] or @severity
severitys: "fatal,error,warning,info,debug".split( "," )
return extend( true, {}, logging, _cnf )
else
return _cnf
module.exports = new Config( process.env.severity )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment