2012-03-27 14:22:24 +00:00
var fs = require ( 'fs' ) ;
var path = require ( 'path' ) ;
2012-10-28 23:21:51 +00:00
var Schema = require ( './schema' ) . Schema ;
2012-03-27 14:22:24 +00:00
2012-07-02 11:59:44 +00:00
var existsSync = fs . existsSync || path . existsSync ;
2012-10-28 23:21:51 +00:00
if ( global . railway ) {
railway . orm . _schemas = [ ] ;
}
2012-03-27 14:22:24 +00:00
2012-05-02 22:15:09 +00:00
module . exports = function init ( root ) {
2012-10-29 14:11:43 +00:00
var railway , app , models ;
2012-10-28 23:21:51 +00:00
2012-12-19 14:57:21 +00:00
if ( typeof root !== 'object' || root . constructor . name !== 'Compound' ) {
2012-10-28 23:21:51 +00:00
railway = global . railway ;
app = global . app ;
2012-10-29 14:11:43 +00:00
models = app . models ;
2012-10-28 23:21:51 +00:00
} else {
railway = root ;
app = railway . app ;
root = railway . root ;
2012-10-29 14:11:43 +00:00
models = railway . models ;
2012-10-28 23:21:51 +00:00
}
2012-05-02 22:15:09 +00:00
2012-10-28 23:21:51 +00:00
railway . orm . _schemas = [ ] ;
2012-03-27 19:48:23 +00:00
2012-10-28 23:21:51 +00:00
var confFile = ( root || app . root ) + '/config/database' ;
2012-12-15 12:05:32 +00:00
var config = { } ;
2012-10-28 23:21:51 +00:00
2012-12-21 14:26:21 +00:00
try {
config = require ( confFile ) [ app . set ( 'env' ) ] ;
} catch ( e ) {
console . log ( 'Could not load config/database.{js|json|yml}' ) ;
throw e ;
2012-10-28 23:21:51 +00:00
}
2012-12-21 14:26:21 +00:00
2012-10-28 23:21:51 +00:00
// when driver name started with point - look for driver in app root (relative path)
if ( config . driver && config . driver . match ( /^\./ ) ) {
config . driver = path . join ( app . root , config . driver ) ;
2012-03-27 19:48:23 +00:00
}
2012-03-27 14:22:24 +00:00
2012-10-28 23:21:51 +00:00
var schema = new Schema ( config && config . driver || 'memory' , config ) ;
schema . log = log ;
2012-03-27 14:22:24 +00:00
2012-12-21 14:26:21 +00:00
if ( ! schema . adapter ) throw new Error ( 'Adapter is not defined' ) ;
2012-12-16 13:08:51 +00:00
if ( schema . waitForConnect ) {
schema . on ( 'connected' , function ( ) {
loadSchema ( schema , railway , app , models ) ;
2012-12-10 13:48:22 +00:00
} ) ;
2012-12-16 13:08:51 +00:00
} else {
loadSchema ( schema , railway , app , models ) ;
}
2012-03-27 14:22:24 +00:00
2012-04-09 21:28:14 +00:00
// check validations and display warning
var displayWarning = false ;
2012-10-29 14:11:43 +00:00
Object . keys ( models ) . forEach ( function ( model ) {
var Model = models [ model ] ;
2012-04-09 21:28:14 +00:00
if ( Model . _validations ) {
displayWarning = true ;
}
} ) ;
if ( displayWarning ) {
var $ = railway . utils . stylize . $ ;
2012-11-10 05:28:07 +00:00
// require('util').puts($('WARNING:').bold.red + ' ' + $('I can see that you\'ve added validation to db/schema.js. However schema.js file is only used to describe database schema. Therefore validations configured in db/schema.js will be ignored.\nFor business logic (incl. validations) please create models as separate .js files here: app/models/*.js').yellow);
2012-04-09 21:28:14 +00:00
}
2012-12-16 13:08:51 +00:00
function loadSchema ( schema , railway , app , models ) {
railway . orm . _schemas . push ( schema ) ;
var context = prepareContext ( models , railway , app , schema ) ;
// run schema first
var schemaFile = ( root || app . root ) + '/db/schema.' ;
if ( existsSync ( schemaFile + 'js' ) ) {
schemaFile += 'js' ;
} else if ( existsSync ( schemaFile + 'coffee' ) ) {
schemaFile += 'coffee' ;
} else {
schemaFile = false ;
}
if ( schemaFile ) {
var code = fs . readFileSync ( schemaFile ) . toString ( ) ;
if ( schemaFile . match ( /\.coffee$/ ) ) {
code = require ( 'coffee-script' ) . compile ( code ) ;
}
var fn = new Function ( 'context' , 'require' , 'with(context){(function(){' + code + '})()}' ) ;
fn ( context , require ) ;
}
// and freeze schemas
railway . orm . _schemas . forEach ( function ( schema ) {
schema . freeze ( ) ;
} ) ;
}
2012-03-27 14:22:24 +00:00
function log ( str , startTime ) {
2012-04-07 13:43:15 +00:00
var $ = railway . utils . stylize . $ ;
2012-03-27 14:22:24 +00:00
var m = Date . now ( ) - startTime ;
2012-04-07 13:43:15 +00:00
railway . utils . debug ( str + $ ( ' [' + ( m < 10 ? m : $ ( m ) . red ) + ' ms]' ) . bold ) ;
2012-03-27 14:22:24 +00:00
app . emit ( 'app-event' , {
type : 'query' ,
param : str ,
time : m
} ) ;
}
2012-10-29 18:22:40 +00:00
function prepareContext ( models , railway , app , defSchema , done ) {
2012-03-27 14:22:24 +00:00
var ctx = { app : app } ,
2012-10-29 18:22:40 +00:00
_models = { } ,
2012-03-27 14:22:24 +00:00
settings = { } ,
cname ,
schema ,
wait = connected = 0 ,
nonJugglingSchema = false ;
done = done || function ( ) { } ;
/ * *
* Multiple schemas support
* example :
* schema ( 'redis' , { url : '...' } , function ( ) {
* describe models using redis connection
* ...
* } ) ;
* schema ( function ( ) {
* describe models stored in memory
* ...
* } ) ;
* /
ctx . schema = function ( ) {
var name = argument ( 'string' ) ;
var opts = argument ( 'object' ) || { } ;
var def = argument ( 'function' ) || function ( ) { } ;
schema = new Schema ( name || opts . driver || 'memory' , opts ) ;
railway . orm . _schemas . push ( schema ) ;
wait += 1 ;
ctx . gotSchema = true ;
schema . on ( 'log' , log ) ;
schema . on ( 'connected' , function ( ) {
if ( wait === ++ connected ) done ( ) ;
} ) ;
def ( ) ;
schema = false ;
} ;
/ * *
* Use custom schema driver
* /
ctx . customSchema = function ( ) {
var def = argument ( 'function' ) || function ( ) { } ;
nonJugglingSchema = true ;
def ( ) ;
Object . keys ( ctx . exports ) . forEach ( function ( m ) {
ctx . define ( m , ctx . exports [ m ] ) ;
} ) ;
nonJugglingSchema = false ;
} ;
ctx . exports = { } ;
ctx . module = { exports : ctx . exports } ;
/ * *
* Define a class in current schema
* /
ctx . describe = ctx . define = function ( className , callback ) {
var m ;
cname = className ;
2012-10-29 14:11:43 +00:00
_models [ cname ] = { } ;
2012-03-27 14:22:24 +00:00
settings [ cname ] = { } ;
if ( nonJugglingSchema ) {
m = callback ;
} else {
callback && callback ( ) ;
2012-10-29 14:11:43 +00:00
m = ( schema || defSchema ) . define ( className , _models [ cname ] , settings [ cname ] ) ;
2012-03-27 14:22:24 +00:00
}
2012-10-29 23:41:45 +00:00
if ( global . railway ) {
global [ cname ] = m ;
}
2012-10-29 14:11:43 +00:00
return models [ cname ] = ctx [ cname ] = m ;
2012-03-27 14:22:24 +00:00
} ;
/ * *
* Define a property in current class
* /
ctx . property = function ( name , type , params ) {
if ( ! params ) params = { } ;
if ( typeof type !== 'function' && typeof type === 'object' ) {
params = type ;
type = String ;
}
params . type = type || String ;
2012-10-29 14:11:43 +00:00
_models [ cname ] [ name ] = params ;
2012-03-27 14:22:24 +00:00
} ;
/ * *
* Set custom table name for current class
* @ param name - name of table
* /
ctx . setTableName = function ( name ) {
if ( cname ) settings [ cname ] . table = name ;
} ;
2012-12-16 13:29:53 +00:00
/ * *
* If the Schema has additional types , add them to the context
* e . g . MySQL has an additional Point type
* /
if ( Schema . types && Object . keys ( Schema . types ) . length ) {
for ( var typeName in Schema . types ) {
ctx [ typeName ] = Schema . types [ typeName ] ;
}
}
2012-03-27 14:22:24 +00:00
ctx . Text = Schema . Text ;
return ctx ;
function argument ( type ) {
var r ;
[ ] . forEach . call ( arguments . callee . caller . arguments , function ( a ) {
if ( ! r && typeof a === type ) r = a ;
} ) ;
return r ;
}
}
2012-05-02 22:15:09 +00:00
} ;