var cache = {}, layoutsCache = {}, fs = require('fs'),path = require('path'),// import railway utilsutils = require('./railway_utils'),safe_merge = utils.safe_merge,camelize = utils.camelize,classify = utils.classify,underscore = utils.underscore,singularize = utils.singularize,pluralize = utils.pluralize,$ = utils.stylize.$,log = utils.debug,runCode = utils.runCode;1var IS_NODE_04 = process.versions.node < '0.6';1var ctlParams = {};1var id = 0;1var CommonjsController = require('./commonjs_controller');1/*** Controller encapsulates http request handling layer. It allows to* render response, redirect, and tons of other related stuff.** Instance of controller is actual response handler, it's not a* like in RoR, you can not inherit controllers, just load, mix.** Inheritance in controllers is bad idea.** @param {String} name - name of controller*/function Controller(name, root) {var self = this;11this.id = ++id;11this._beforeFilters = [];11this._afterFilters = [];11this._actions = {};11this._layout = null;11this._buffer = {};11ctlParams[this.id] = {};11this.root = this.__dirname = root || app.root;11if (!layoutsCache[name]) {// TODO: what if view engine name differs from extension?layoutsCache[name] = railway.utils.existsSync(app.root + '/app/views/layouts/' + name + '_layout.' + app.settings['view engine']) ? name : 'application';6}ctlParams[this.id].baseLayout =ctlParams[this.id].layout = layoutsCache[name];11// allow to disable layout by default for all views// using app.settings['view options'].layout = falseif ((app.set('view options') || {}).layout === false) {ctlParams[this.id].baseLayout = false;}this.controllerName = name;11this.controllerFile = Controller.index[this.root][name];11if (!this.controllerFile) {throw new Error('Controller ' + name + ' is not defined');}// import outer contextvar outerContext = Controller.context[this.root][name];11if (outerContext) {Object.keys(outerContext).forEach(function (key) {self[key] = outerContext[key].bind(self);11});11}// fix object inheritance broken when using as contextObject.keys(Controller.prototype).forEach(function (method) {self[method] = Controller.prototype[method].bind(self);319});11this._filterParams = ['password'];11if (!IS_NODE_04) {this.__defineGetter__('response', function () { return this.ctx.res }.bind(this));11this.__defineGetter__('res', function () { return this.ctx.res }.bind(this));11this.__defineGetter__('request', function () { return this.ctx.req }.bind(this));11this.__defineGetter__('req', function () { return this.ctx.req }.bind(this));11this.__defineGetter__('session', function () { return this.ctx.req.session }.bind(this));11this.__defineGetter__('params', function () { return this.ctx.req.params }.bind(this));11this.__defineGetter__('body', function () { return this.ctx.req.body }.bind(this));11this.__defineGetter__('next', function () { return this.ctx.next }.bind(this));11this.__defineGetter__('actionName',function () { return this.ctx.action }.bind(this));11this.__defineGetter__('path_to', function () { return this.ctx.paths }.bind(this));11}this.t = T();11this.t.locale = app.settings.defaultLocale || 'en';11this.T = T;11if (global.__cov && !this.__cov) {this.__cov = global.__cov;11}}/*** Publish some object or function to use in another controller*/Controller.prototype.publish = function (name, obj) {if (typeof name === 'function') {obj = name;5this._buffer[obj.name] = obj;5} else if (typeof name === 'string') {this._buffer[name] = obj;5}};1/*** Get some object or function published in another controller*/Controller.prototype.use = function (name) {var what = this._buffer[name];2if (typeof what === 'undefined') throw new Error(name + ' is not defined');2return what;2};1/*** Configure which query params should be filtered from logging* @param {String} param 1 name* @param {String} param 2 name* @param ...* @param {String} param n name*/Controller.prototype.filterParameterLogging = function (args) {this._filterParams = this._filterParams.concat(Array.prototype.slice.call(arguments));5};1/*** Initialize controller (called on first request)* @private*/Controller.prototype._init = function initializeController() {// reset scope variablesthis._actions = {};11this._beforeFilters = [];11this._afterFilters = [];11this._buffer = {};11ctlParams[this.id].layout = ctlParams[this.id].baseLayout;11// publish modelsif (app.models) {Object.keys(app.models).forEach(function (className) {this[className] = app.models[className];}.bind(this));11}runCode(this.controllerFile, this);11};1/*** @param {String} name - name of filter to skip* @param {Array} or {String} only - choose actions to skip filter*/Controller.prototype.skipBeforeFilter = function (name, only) {this._beforeFilters.forEach(function (filter, i) {if (filter[0] && filter[0].customName && name === filter[0].customName) {skipFilter(this._beforeFilters, i, only ? only.only : null);4}}.bind(this));4};1/*** @param {String} name - name of filter to skip* @param {Array} or {String} only - choose actions to skip filter*/Controller.prototype.skipAfterFilter = function (name, only) {this._afterFilters.forEach(function (filter, i) {if (filter[0] && filter[0].customName && name === filter[0].customName) {skipFilter(this._afterFilters, i, only ? only.only : null);}}.bind(this));};1/*** @param {Array} filters collection* @param {Number} index of filter to skip* @param {Array} or {String} only - choose actions to skip filter* @private*/function skipFilter(filters, index, only) {if (!only) {delete filters[index];2} else if (filters[index][0]) {if (!filters[index][1]) {filters[index][1] = {except: []};2}if (!filters[index][1].except) {filters[index][1].except = [];} else if (typeof filters[index][1].except === 'string') {filters[index][1].except = [filters[index][1].except];}if (typeof only === 'string') {filters[index][1].except.push(only);} else if (only && only.constructor.name === 'Array') {only.forEach(function (name) {filters[index][1].except.push(name);2});2}}}/*** Define controller action** @param name String - optional (if missed, named function required as first param)* @param action Funcion - required, should be named function if first arg missed** @example* ```* action(function index() {* Post.all(function (err, posts) {* render({posts: posts});* });* });* ```**/Controller.prototype.action = function (name, action) {if (typeof name === 'function') {action = name;14name = action.name;14if (!name) {throw new Error('Named function required when `name` param omitted');}}action.isAction = true;16action.customName = name;16this._actions[name] = action;16};1/*** Internal request handler. Serves request using railway env:** - update context links: req, res, next and other req-sensitive stuff* - run before filters* - run action* - run after filters** `perform` method called by `ControllerBridge`** @param {String} actionName* @param {IncomingMessage} req - incoming http request* @param {ServerResponse} res - http server response* @private*/Controller.prototype.perform = function (actionName, req, res, nextRoute) {res.info = {controller: this.controllerName,action: actionName,startTime: Date.now()};12res.actionHistory = [];12if (!this.initialized) {this.initialized = true;5if (IS_NODE_04) {this.actionName = actionName;this.request = this.req = req;this.request.sandbox = {};this.response = this.res = res;this.params = req.params;this.session = res.session;this.body = req.body;this.next = next;this.path_to = Controller.getPathTo(actionName);}this._init();5}var ctl = this, timeStart = false, prevMethod;12// need to track uniqueness of filters by namevar queueIndex = {};12this.ctx = {req: req,res: res,next: next,action: actionName,paths: Controller.getPathTo(actionName)};12req.sandbox = {};12log('');12log($((new Date).toString()).yellow + ' ' + $(this.id).bold);12log($(req.method).bold, $(req.url).grey, 'controller:', $(this.controllerName).cyan, 'action:', $(this.actionName).blue);12if (req.query && Object.keys(req.query).length) {log($('Query: ').bold + JSON.stringify(req.query));}if (req.body && req.method !== 'GET') {var filteredBody = {};3Object.keys(req.body).forEach(function (param) {if (!ctl._filterParams.some(function (filter) {return param.search(filter) !== -1;})) {filteredBody[param] = req.body[param];1} else {filteredBody[param] = '[FILTERED]';1}});3log($('Body: ').bold + JSON.stringify(filteredBody));3}// build queue using before-, after- filters and actionvar queue = [];12enqueue(this._beforeFilters, queue);12queue.push(getCaller(this._actions[actionName]));12enqueue(this._afterFilters, queue);12if (app.disabled('model cache')) {// queue.push(getCaller(app.disconnectSchemas));}if (app.enabled('eval cache')) {queue.push(getCaller(function () {backToPool(ctl);}));}// start serving requestnext();12var logActions = app.enabled('log actions');12function next(err) {if (logActions && timeStart && prevMethod) {log('<<< ' + prevMethod.customName + ' [' + (Date.now() - timeStart) + ' ms]');17}if (err && err.constructor.name === 'Error') {return nextRoute(err);1}if (timeStart && prevMethod) {res.actionHistory.push({name: prevMethod.customName, time: Date.now() - timeStart});16}// run next method in queue (if any callable method)var method = queue.shift();29if (typeof method == 'function') {process.nextTick(function () {method.call(ctl.request.sandbox, next);27});27} else {res.info.appTime = Date.now() - res.info.startTime;2}}function getCaller(method) {if (!method) {throw new Error('Undefined action');}return function (next) {29req.inAction = method.isAction;27if (logActions && method.customName) {if (method.isAction) {log('>>> perform ' + $(method.customName).bold.cyan);10} else {log('>>> perform ' + $(method.customName).bold.grey);16}}timeStart = Date.now();27prevMethod = method;27method.call(this, next);27}}function enqueue(collection, queue) {collection.forEach(function (f) {var params = f[1];26if (!params) {enqueue();6} else if (params.only && params.only.indexOf(actionName) !== -1 && (!params.except || params.except.indexOf(actionName) === -1)) {enqueue();3} else if (params.except && params.except.indexOf(actionName) === -1) {enqueue();8}function enqueue() {if (f[2]) {if (queueIndex[f[2]]) return;16queueIndex[f[2]] = true;16}queue.push(getCaller(f[0]));17}});24}};1/*** Layout setter/getter** - when called without arguments, used as getter,* - when called with string, used as setter** When `layout` not called controller trying to get guess which layout to use.* First of all controller looking for layout with the same name as controller,* for example `users_controller` will choose `users_laout`, if there's no* layout with this name, controller using `application_layout`.** If you do not want to use any layout by default, you can just set it up:** app.set('view options', {layout: false});** this will prevent you from repeating `layout(false)` in each controller where* you do not want to use layout, for example in api controllers.** - choose** @param {String} layout - [optional] layout name*/Controller.prototype.layout = function layout(l) {if (typeof l !== 'undefined') {ctlParams[this.id].layout = l;3}return ctlParams[this.id].layout ? ctlParams[this.id].layout + '_layout' : null;5};1function filter(args) {if (typeof args[0] === 'string' && typeof args[1] === 'function') {// change orderargs[1].customName = args[0];return [args[1], args[2], args[0]];} else {// normal orderargs[0].customName = args[0].name;19return [args[0], args[1], args[0].name];19}}/*** Schedule before filter to the end of queue. This method can be called* with named function as single param, or with two params: name and* anonimous function** Examples:* ```* before('some named filter', function () {});* before(function namedMethod() {});* ```** This filters can be skipped using this names in future. Examples:* ```* skipBeforeFilter('some named filter');* skipBeforeFilter('namedMethod');* ```** Please note, that every named filter only can be scheduled once:* ```* before(function myMethod() {* ~~~C15~~~* });* before(function myMethod() {* ~~~C16~~~* });* ```* This will only schedule first method!** @alias beforeFilter* @param {Funcion} f* @param {Object} params*/Controller.prototype.before = function before(f, params) {this._beforeFilters.push(filter(arguments));19};1Controller.prototype.beforeFilter = Controller.prototype.before;1/*** Schedule before filter to the start of queue. This method can be called* with named function as single param, or with two params: name and* anonimous function.** @alias prependBeforeFilter* @param {Funcion} f* @param {Object} params*/Controller.prototype.prependBefore = function prependBefore(f, params) {this._beforeFilters.unshift(filter(arguments));};1Controller.prototype.prependBeforeFilter = Controller.prototype.prependBefore;1/*** @override default controller string representation*/Controller.prototype.toString = function toString() {return 'Controller ' + this.controllerName;};1/*** @param {String} name - name of action* @returns whether controller responds to action*/Controller.prototype.respondTo = function respondTo(name) {return typeof this._actions[name] == 'function';};1/*** Append after filter to the end of queue. This method can be called* with named function as single param, or with two params: name and* anonimous function.** @param {Function} f* @param {Object} params*/Controller.prototype.after = function after(f, params) {this._afterFilters.push(filter(arguments));};1Controller.prototype.afterFilter = Controller.prototype.after;1/*** Prepend after filter to the start of queue. This method can be called* with named function as single param, or with two params: name and* anonimous function.** @param {Function} f* @param {Object} params*/Controller.prototype.prependAfter = function prependAfter(f, params) {this._afterFilters.unshift(filter(arguments));};1Controller.prototype.prependAfterFilter = Controller.prototype.prependAfter;1/*** Set current locale. This setting will affect all views locale-specific helpers.** @param locale*/Controller.prototype.setLocale = function (locale) {this.t.locale = T.localeSupported(locale) ? locale : app.settings.defaultLocale;};1/*** Get current locale** @returns {String} locale name*/Controller.prototype.getLocale = function () {return this.t.locale;};1/*** Load another controller code in this context** @param {String} controller - name of controller (without _controller suffix)*/Controller.prototype.load = function (controller, alias) {var root = Controller.aliases[alias] || this.root;4var ctl = Controller.index[root][controller];4if (!ctl) {throw new Error('Controller ' + controller + ' is not defined. Please note that namespaced controllers names should include namespace when loading');}runCode(ctl, this);4};1/*** Send response, as described in ExpressJS guide:** This method is a high level response utility allowing you* to pass objects to respond with json, strings for html,* Buffer instances, or numbers representing the status code.* The following are all valid uses:* ```* send(); ~~~C17~~~* send(new Buffer('wahoo'));* send({ some: 'json' });* send('<p>some html</p>');* send('Sorry, cant find that', 404);* send('text', { 'Content-Type': 'text/plain' }, 201);* send(404);* ```**/Controller.prototype.send = function (x) {log('Send to client: ' + x);1this.response.send.apply(this.response, Array.prototype.slice.call(arguments));1if (this.request.inAction) this.next();1};1/*** Set or get response header** @param {String} key - name of header* @param {String} val - value of header (optional)** When second argument is omitted method acts as getter.** Example:* ```* header('Content-Length');* ~~~C18~~~** header('Content-Length', 123);* ~~~C19~~~** header('Content-Length');* ~~~C20~~~* ```*/Controller.prototype.header = function (key, val) {return this.response.header.call(this.response, key, val);};1/*** Redirect to `path`*/Controller.prototype.redirect = function (path) {log('Redirected to', $(path).grey);this.response.redirect(path.toString());if (this.request.inAction) this.next();};1/*** Render response.** @param {String} view name [optional]* @param {Object} locals - data passed to view as local variables [optional]** When first parameter is omitted action name used as view name:* ```* action(function index() {* render(); ~~~C21~~~* });* ```** Second argument is optional too, you can set local variables using `this` inside* action:* ```* action('new', function () {* this.title = 'Create new post';* this.post = new Post;* render();* });* ```* will result the same as* ```* action('new', function () {* render({* title: 'Create new post',* post: new Post* });* });* ```*/Controller.prototype.render = function (arg1, arg2, cb) {var view, params;2if (typeof arg1 == 'string') {view = arg1;params = arg2;} else {view = this.actionName;2params = arg1;2}params = params || {};2params.controllerName = params.controllerName || this.controllerName;2params.actionName = params.actionName || this.actionName;2params.path_to = this.path_to;2params.request = this.request;2params.params = this.request.params;2params.t = this.t;2var layout = this.layout(),file = this.controllerName + '/' + view;2if (this.response.renderCalled) {log('Rendering', $(file).grey, 'using layout', $(layout).grey, 'called twice.', $('render() can be called only once!').red);return;}var helper;2try {helper = require(this.root + '/app/helpers/' + this.controllerName + '_helper');} catch(e) {helper = {};2}var appHelper;2try {appHelper = require(this.root + '/app/helpers/application_helper');} catch(e) {appHelper = {};2}log('Rendering', $(file).grey, 'using layout', $(layout).grey);2var helpers = railway.helpers.personalize(this);2app.set('views', this.root + '/app/views');2this.response.renderCalled = true;2this.response.render(file, {locals: safe_merge(params, this.request.sandbox, this.path_to, helpers, helpers.__proto__, helper, appHelper),layout: layout ? 'layouts/' + layout : false,debug: false}, cb);2if (this.request.inAction) this.next();2};1/*** Add flash error to display in next request** @param {String} type* @param {String} message*/Controller.prototype.flash = function flash(type, message) {this.request.flash.apply(this.request, Array.prototype.slice.call(arguments));};1/*** Respond to .:format* @param {Function} block** Example (respond to json and html):** action(function index() {* var fruits = this.fruits = ['apple', 'banana', 'kiwi'];* respondTo(function (format) {* format.html(render);* format.json(function () {* send(fruits);* });* });* });*/Controller.prototype.respondTo = function respondTo(block) {var f = this.request.params.format || 'html';block({html: function (c) {if (f === 'html') {c();}},json: function (c) {if (f === 'json') {c();}}});};1var pool = {};1function backToPool(ctl) {pool[ctl.root][ctl.controllerName].push(ctl);}exports.load = function (name, root) {if (app.disabled('eval cache')) {return new Controller(name, root);11} else {if (!pool[root]) pool[root] = {};if (!pool[root][name]) pool[root][name] = [];var ctl = pool[root][name].shift();if (!ctl) {ctl = new Controller(name, root);}return ctl;}};1exports.loadNew = function (name, root) {if (app.disabled('eval cache')) {return CommonjsController.load(name, root);} else {if (!pool[root]) pool[root] = {};if (!pool[root][name]) pool[root][name] = [];var ctl = pool[root][name].shift();if (!ctl) {ctl = CommonjsController.load(name, root);}return ctl;}};1exports.loadAll = function (root) {Object.getOwnPropertyNames(Controller.index[root || app.root]).forEach(function(c) {exports.load(c, root)._init();6});1};1/*** @private*/Controller.getPathTo = function () {return railway.routeMapper.pathTo;12};1/*** Add custom base controller dir to railway pool. It allows you to build* extensions with your own controllers, and build app* breaken by modules** @param {String} basePath* @param {String} prefix* @param {Object} context - controller context tweaks, all members of* this object will be accesible in controller** @public railway.controller.addBasePath*/function addBasePath(basePath, prefix, context, root) {prefix = prefix || '';2if (!railway.utils.existsSync(basePath)) return;1root = root || app.root;1Controller.index[root] = Controller.index[root] || {};1Controller.context[root] = Controller.context[root] || {};1fs.readdirSync(basePath).forEach(addContoller);1function addContoller(file) {var stat = fs.statSync(path.join(basePath, file));6if (stat.isFile()) {var m = file.match(/(.*?)_?[cC]ontroller\.(js|coffee)$/);6if (m) {var ctl = prefix + m[1];6Controller.index[root][ctl] = Controller.index[root][ctl] || path.join(basePath, file);6Controller.context[root][ctl] = Controller.context[root][ctl] || context;6}} else if (stat.isDirectory()) {exports.addBasePath(path.join(basePath, file), prefix + file + '/', context, root);}}};1exports.addBasePath = addBasePath;1exports.Controller = Controller;1exports.init = function (root) {cache = {};1CommonjsController.index = Controller.index = Controller.index || {};1Controller.aliases = Controller.aliases || {};1Controller.context = Controller.context || {};1exports.addBasePath(root + '/app/controllers', null, null, root);1};1/*** Enables CSRF Protection** This filter will check `authenticity_token` param of POST request* and compare with token calculated by session token and app-wide secret** @param {String} secret* @param {String} paramName** @example `app/controllers/application_controller.js`* ```* before('protect from forgery', function () {* protectFromForgery('415858f8c3f63ba98546437f03b5a9a4ddea301f');* });* ```*/Controller.prototype.protectFromForgery = function protectFromForgery(secret, paramName) {var req = this.request;5if (!req.session) {return this.next();1}if (!req.session.csrfToken) {req.session.csrfToken = Math.random();1req.csrfParam = paramName || 'authenticity_token';1req.csrfToken = sign(req.session.csrfToken);1return this.next();1}// publish secure credentialsreq.csrfParam = paramName || 'authenticity_token';3req.csrfToken = sign(req.session.csrfToken);3if (req.originalMethod == 'POST') {var token = req.param('authenticity_token');2if (!token || token !== sign(req.session.csrfToken)) {railway.logger.write('Incorrect authenticity token');1this.send(403);1} else {this.next();1}} else {this.next();1}function sign(n) {return require('crypto').createHash('sha1').update(n.toString()).update(secret.toString()).digest('hex');5}};1Controller.prototype.protectedFromForgery = function () {return this.request.csrfToken && this.request.csrfParam;1};1
/*** Module dependencies*/var path = require('path');1var fs = require('fs');1var sys = require('util');1/*** Shortcut required railway utils*/var pluralize = railway.utils.pluralize;1var camelize = railway.utils.camelize;1var $ = railway.utils.stylize.$;1var exec = require('child_process').exec/*** Command line options* populated by parseOptions method** @api private*/var options = {};1/*** Generators collection hash** @type Hash* @api private*/var collection = {};1/*** Add generator to collection** @param name* @param callback* @param meta Object {description: String, examples: [String]}* @api public*/function addGenerator(name, callback, meta) {meta = meta || {};5callback.meta = meta;5collection[name] = callback;5if (meta.alias) {collection[meta.alias] = callback;1}}exports.addGenerator = addGenerator;1/*** Check whether generator exists* @param name - name of generator* @api public*/function exists(name) {return !!collection[name];}exports.exists = exists;1/*** Call generator method*/function perform(name, args) {collection[name](args);5}exports.perform = perform;1exports.list = function () {return Object.keys(collection).join(' ');};1/*** Add built-in generators*/(function () {addGenerator('init', initGenerator);1addGenerator('model', modelGenerator);1addGenerator('controller', controllerGenerator);1addGenerator('features', featuresGenerator);1addGenerator('crud', crudGenerator, {alias: 'scaffold'});1/*** Application initialization generator. Can be called with leading* appname param, or without it (init in current dir).** When destination* is not empty old files wouldn't be overwritten by this generator, it's safe* to call generator again and again to restore some non-existing files* initial state** @api public*/function initGenerator(args) {parseOptions('appname');1[ 'app/','app/models/','app/controllers/','app/observers/','app/helpers/','app/views/','app/views/layouts/','db/','db/seeds/','db/seeds/development/','log/','public/','public/images','public/stylesheets/','public/javascripts/','node_modules/','config/','config/locales/','config/initializers/','config/environments/'].forEach(createDir);1if (options.stylus) {createDir('app/assets/');1createDir('app/assets/styles/');1createFileByTemplate('app/assets/styles/example.styl', 'example.styl');1}var db = options.db;1createFileByTemplate('config/initializers/db-tools', 'db-tools');1createFileByTemplate('config/routes', 'config/routes');1// createFileByTemplate('config/requirements.json', 'requirements.json');var srv = createFileByTemplate('server', 'server');1var secret = require('crypto').createHash('sha1').update(Math.random().toString()).digest('hex');1if (options.coffee) {createFile('app/controllers/application_controller.coffee', 'before \'protect from forgery\', ->\n protectFromForgery \'' + secret + '\'\n\n');} else {createFile('app/controllers/application_controller.js', 'before(\'protect from forgery\', function () {\n protectFromForgery(\'' + secret + '\');\n});\n');1}createFileByTemplate('config/environment', 'config/environment', [ replaceViewEngine, replacePrependMiddleware ]);1createFileByTemplate('config/environments/test', 'config/environments/test');1createFileByTemplate('config/environments/development', 'config/environments/development');1createFileByTemplate('config/environments/production', 'config/environments/production');1createFileByTemplate('config/database.~', 'config/database_' + db + '.~', replaceAppname);1createFileByTemplate('db/schema', 'schema');1createViewByTemplate('app/views/layouts/application_layout', 'application_layout');1createFileByTemplate('public/index.html', 'index.html');1createFile('.gitignore', fs.readFileSync(path.join(__dirname, '/../templates/gitignore')));1// bootstrap filescreateFileByTemplate('public/stylesheets/bootstrap-responsive.css', 'bootstrap-responsive.css');1createFileByTemplate('public/stylesheets/bootstrap.css', 'bootstrap.css');1createFileByTemplate('public/images/glyphicons-halflings-white.png', 'glyphicons-halflings-white.png');1createFileByTemplate('public/images/glyphicons-halflings.png', 'glyphicons-halflings.png');1createFileByTemplate('public/javascripts/bootstrap.js', 'bootstrap.js');1createFileByTemplate('public/stylesheets/style.css', 'style.css');1createFileByTemplate('public/javascripts/rails.js', 'rails.js');1createFile('public/javascripts/application.js', '// place your application-wide javascripts here\n');1createFileByTemplate('npmfile', 'npmfile', replaceViewEngine);1createFileByTemplate('public/favicon.ico', 'favicon.ico');1var fileExtension = options.coffee ? '.coffee' : '.js';1var engine = options.coffee ? 'coffee' : 'node';1// this file is only needed for heroku deployments// maybe not produce it by defaultcreateFile('Procfile', 'web: ' + engine + ' server' + fileExtension);1createFileByTemplate('package.json', 'package.json', [replaceAppname, replaceViewEngine]);1fs.chmodSync(srv, 0755);1process.exit();1}/*** Generates model. Accepts list of params: first one is model name, then* fieldnames as name:type, by default type is string.** This generator accepts --coffee modifier to generate code in coffee** Example:** railway generate User email birthdate:Date isAdmin:Boolean** Affected files:** - db/schema.js* - app/models/ModelName.js** @api public*/function modelGenerator(args) {parseOptions('model');2var model = options.model, code = '';2if (!model) {sys.puts($('Model name required').red.bold);return;}if (model.match(/\//)) {model = model.match(/\/([^\/]+)$/)[1];}if (model.match(/\./)) {model = model.match(/\.([^\.]+)$/)[1];}var fileExtension = options.coffee ? '.coffee' : '.js';2var Model = model[0].toUpperCase() + model.slice(1);2var attrs = [], result = [];2options.forEach(function (arg) {var property = arg.split(':')[0],plainType = arg.split(':')[1],type = formatType(plainType);4if (options.coffee) {attrs.push(' property \'' + property + '\', ' + type);} else {attrs.push(' property(\'' + property + '\', ' + type + ');');4}result.push({name: property, type: type, plainType: plainType});4});2createDir('app/');2createDir('app/models/');2createFile('app/models/' + model + fileExtension, '');2if (options.coffee) {code = Model + ' = describe \'' + Model + '\', () ->\n' +attrs.join('\n') + '\n';} else {code = 'var ' + Model + ' = describe(\'' + Model + '\', function () {\n' +attrs.join('\n') + '\n});';2}railway.utils.appendToFile('db/schema' + fileExtension, code);2return result;2}/*** Generates single controller and related views. Accepts arguments:** - controller name* - action names* - `--coffee` modifier can be passed to generate code in coffee** Affected files:** - app/controllers/controller_name_controller.js* - app/views/controller_name/action1.ejs* - app/views/controller_name/action2.ejs** @api public*/function controllerGenerator(args) {parseOptions('controller');1var controller = options.controller;1if (!controller) {console.log('Usage example: railway g controller controllername actionName anotherActionName');console.log(' railway g controller controllername actionName anotherActionName --coffee');return;}var fileExtension;1var actions;1if (options.coffee) {fileExtension = '.coffee';actions = ['load \'application\''];options.forEach(function (action) {actions.push('action \'' + action + '\', () -> \n render\n title: "' + controller + '#' + action + '"');});} else {fileExtension = '.js';1actions = ['load(\'application\');'];1options.forEach(function (action) {actions.push('action(\'' + action + '\', function () {\n render({\n title: "' + controller + '#' + action + '"\n });\n});');6});1}var ns = controller.split('/');1ns.pop();1createDir('app/');1createDir('app/controllers/');1createParents(ns, 'app/controllers/');1// controllervar filename = 'app/controllers/' + controller + '_controller' + fileExtension;1createFile(filename, actions.join('\n\n'));1createDir('app/helpers/');1createParents(ns, 'app/helpers/');1// helperfilename = 'app/helpers/' + controller + '_helper.js';1createFile(filename, 'module.exports = {\n};');1// viewscreateDir('app/views/');1createParents(ns, 'app/views/');1createDir('app/views/' + controller + '/');1options.forEach(function (action) {createView('app/views/' + controller + '/' + action, 'default_action_view', [controller, action]);6});1}/*** Cucumis features generator* @deprecated* @api public*/function featuresGenerator() {createDir('features/');1createDir('features/step_definitions/');1createFileByTemplate('features/step_definitions/web_steps.js', 'features/step_definitions/web_steps.js');1createFileByTemplate('features/step_definitions/email_steps.js', 'features/step_definitions/email_steps.js');1createFileByTemplate('features/step_definitions/jquery.js', 'features/step_definitions/jquery.js');1try {require('cucumis');} catch(e) {sys.puts($('Cucumis is not installed').red + ' please run ' + $('npm install cucumis').yellow);1}}/*** Resource scaffolding generator. Accepts same arguments as model generator:* name of model (singilar) and list of model properties in* `property:type` format** It creates ready-to-use model, controller, controller tests (nodeunit),* routing rule, views and layout** @api public*/function crudGenerator(args) {var model = args[0].split('.').pop();1var parents = args[0].split('.').slice(0, -1);1var models = pluralize(model).toLowerCase();1if (!model) {console.log('Usage example: railway g crud post title:string content:string published:boolean');console.log(' railway g crud post title:string content:string published:boolean --coffee');return;}var ns = models.split('/');1ns.pop();1createDir('app/');1createDir('app/controllers/');1createParents(ns, 'app/controllers/');1var result = modelGenerator.apply(this, Array.prototype.slice.call(arguments));1var fileExtension = options.coffee ? '.coffee' : '.js';1createFile('app/controllers/' + models + '_controller' + fileExtension, controllerCode(args[0], result));1createDir('app/helpers/');1createParents(ns, 'app/helpers/');1function replaceModel(code) {return code1.replace(/new_model/g, addParents('new_', parents, model)).replace(/edit_model/g, addParents('edit_', parents, model, true)).replace(/path_to\.models/g, addParents('path_to.', parents, models)).replace(/path_to\.model/g, addParents('path_to.', parents, model, true)).replace(/(path_to\..*?\()model/g, addParents('$1', parents.map(function (p) { return 'params.' + p + '_id';}), model, true, ', ')).replace(/models/g, models).replace(/model/g, model.toLowerCase()).replace(/Model/g, camelize(model, true)).replace(/VALID_ATTRIBUTES/, result.map(function (attr) { return attr.name + ": ''" }).join(',\n '));}// helpercreateFile('app/helpers/' + models + '_helper.js', 'module.exports = {\n};');1// testscreateDir('test/');1createFileByTemplate('test/test_helper.js', 'test_helper.js');1createDir('test/controllers');1createParents(ns, 'test/controllers/');1createFileByTemplate('test/controllers/' + models + '_controller_test.js', 'crud_controller_test.js', replaceModel);1// views// _form partialcreateDir('app/views/');1createParents(ns, 'app/views/');1createParents(ns, 'app/views/layouts/');1// layoutcreateViewByTemplate('app/views/layouts/' + models + '_layout', 'scaffold_layout', replaceModel);1createDir('app/views/' + models + '/');1createView('app/views/' + models + '/_form', 'scaffold_form', result, replaceModel);1createView('app/views/' + models + '/show', 'scaffold_show', result, replaceModel);1['new', 'edit', 'index'].forEach(function (template) {createViewByTemplate('app/views/' + models + '/' + template, 'scaffold_' + template, replaceModel);3});1// routevar routesConfig = process.cwd() + '/config/routes.' + (options.coffee ? 'coffee' : 'js'),routes = fs.readFileSync(routesConfig, 'utf8').toString().replace(/\s+$/g, '').split('\n'),firstLine = routes.shift(),firstLineRegexp = options.coffee ? /^exports\.routes = \(map\)\->/ : /^exports\.routes = function \(map\) \{/,mapRouteResources = options.coffee ? ' map.resources \'' + models + '\'' : ' map.resources(\'' + models + '\');'if (firstLine.match() && !routes.some(containRoute)) {routes.unshift(mapRouteResources);1routes.unshift(firstLine);1fs.writeFileSync(routesConfig, routes.join('\n'));1sys.puts($('patch').bold.blue + ' ' + routesConfig);1}function containRoute(line) {var m = line.match(/^\s*map\.resources(\(|\s)'([^']+)'/);6return m && m[1] == models;6}}/*** Get controller code* @private*/function controllerCode(model, result) {var fileExtension = options.coffee ? '.coffee' : '.js';1var code;1var parents = model.split('.').slice(0, -1);1model = model.split('.').pop();1var models = pluralize(model).toLowerCase();1code = fs.readFileSync(__dirname + '/../templates/crud_controller' + fileExtension);1code = code.toString('utf8').replace(/new_model/g, addParents('new_', parents, model)).replace(/edit_model/g, addParents('edit_', parents, model, true)).replace(/path_to\.models/g, addParents('path_to.', parents, models)).replace(/path_to\.model/g, addParents('path_to.', parents, model, true)).replace(/(path_to\..*?\()this\.model/g, addParents('$1', parents.map(function (p) { return 'params.' + p + '_id';}), 'this.' + model, true, ', ')).replace(/models/g, models).replace(/model/g, model.toLowerCase()).replace(/Model/g, camelize(model, true)).replace(/FILTER_PROPERTIES/g, '[' + result.map(function (p) {return "'" + p.name + "'";2}).join(', ') + ']');1return code;1}function addParents(prefix, parents, base, skipParams, join) {var name;10if (!join) join = '_';10if (parents.length) {name = prefix + parents.join(join) + join + base;} else {name = prefix + base;10}if (skipParams) return name;4return name + '(' + parents.map(function (p) {4return 'params.' + p + '_id';}).join(', ') + ')';}/*** Parse command line options* @private*/function parseOptions(defaultKeyName) {options = [];4options.tpl = app.settings['view engine'] || 'ejs';4options.coffee = app.enabled('coffee');4options.db = 'memory';4options.stylus = false;4var key = defaultKeyName || false;4args.forEach(function (arg) {if (arg.slice(0, 2) == '--') {key = arg.slice(2);1options[key] = true;1} else if (arg[0] == '-') {key = arg.slice(1);options[key] = true;} else if (key) {options[key] = arg;3key = false;3} else {options.push(arg);10}});4if (options.nocoffee) {options.coffee = false;}}/*** Create directory* @private*/function createDir(dir) {var root = process.cwd();40if (options.appname && !createDir.rootCreated) {createDir.rootCreated = true;createDir('');}if (options.appname) {dir = path.join(options.appname, dir);}if (railway.utils.existsSync(path.join(root, dir))) {sys.puts($('exists').bold.grey + ' ' + dir);2} else {fs.mkdirSync(path.join(root, dir), 0755);38sys.puts($('create').bold.green + ' ' + dir);38}}/*** Append `contents` to a file** @param {String} filename* @param {String} contents* @private*/function appendToFile(filename, contents) {var root = process.cwd(),fd = fs.openSync(path.join(root, filename), 'a');fs.writeSync(fd, contents);fs.closeSync(fd);}railway.utils.appendToFile = appendToFile;1/*** Create file with given `contents`** @param {String} filename* @param {String} contents* @private*/function createFile(filename, contents) {var root = process.cwd();36if (options.appname) {filename = path.join(options.appname, filename);}var fullPath = root + '/' + filename;36if (railway.utils.existsSync(fullPath)) {sys.puts($('exists').bold.grey + ' ' + filename);2} else {fs.writeFileSync(fullPath, contents);34sys.puts($('create').bold.green + ' ' + filename);34}return fullPath;36}/*** Create file with name `filename` using contents of `template` file.* Run `prepare` function before returning result** @param {String} filename* @param {String} template* @param {Function} prepare - called with (text: {String}),* should return {String}** @private*/function createFileByTemplate(filename, template, prepare) {var configFilenameRegexp = /\.~$/;26if (template.match(configFilenameRegexp)) {var fileExtension = options.coffee ? '.yml' : '.json';1template = template.replace(configFilenameRegexp, fileExtension);1filename = filename.replace(configFilenameRegexp, fileExtension);1}else if (!template.match(/\..+$/)) {var fileExtension = options.coffee ? '.coffee' : '.js';9template += fileExtension;9filename += fileExtension;9}var text = fs.readFileSync(path.join(__dirname, '/../templates/', template));26if (prepare) {text = text.toString('utf8');5if (typeof prepare === 'function') {prepare = [prepare];3}prepare.forEach(function (p) {text = p(text);7});5}return createFile(filename, text);26}/*** Create view file with name `filename` using contents of `template` file.* Run `prepare` function before returning result. It look for -tpl option* and result file with proper extension and contents** @param {String} filename* @param {String} template* @param {Function} prepare - called with (text: {String}),* should return {String}** @private*/function createViewByTemplate(filename, template, prepare) {options.tpl = options.tpl || 'ejs';5var package = options.tpl + '-ext', tpl;5try {tpl = require(package);} catch(e) {sys.puts($('Templating engine ' + options.tpl + ' is not supported').red);5return;5}var text = fs.readFileSync(tpl.template(template));if (prepare) {text = prepare(text.toString('utf8'));}return createFile(filename + tpl.extension, text);}function createView(filename, template, data, fn) {options.tpl = options.tpl || 'ejs';8var package = options.tpl + '-ext';8try {var tpl = require(package);} catch(e) {sys.puts($('Templating engine ' + options.tpl + ' is not supported').red);8return;8}var text = tpl.templateText(template, data);if (typeof fn === 'function') {text = fn(text.toString());}return createFile(filename + tpl.extension, text);}function createParents(ns, d) {ns.forEach(function (dir) {d = path.join(d, dir);createDir(d);});8}function formatType(name) {name = (name || 'string').toLowerCase();4switch (name) {case 'text': return 'Text';case 'string': return 'String';case 'date': return 'Date';case 'bool':case 'boolean': return 'Boolean';case 'int':case 'real':case 'float':case 'decimal':case 'number': return 'Number';}return '"' + camelize(name, true) + '"';}function replaceAppname(template) {return template.replace(/APPNAME/g, options.appname || 'default');2}function replaceViewEngine(template) {return template.replace(/VIEWENGINE/g, options.tpl || 'ejs');3}function replacePrependMiddleware(template) {var mw = [];1if (options.stylus) {if (options.coffee) {mw.push("app.use require('stylus').middleware\n force: true, src: app.root + '/app/assets', dest: app.root + '/public', compress: true".replace(/, /g, ',\n '));} else {mw.push("app.use(require('stylus').middleware({\n force: true, src: app.root + '/app/assets', dest: app.root + '/public', compress: true\n }));".replace(/, /g, ',\n '));1}}return template.replace(/PREPEND_MIDDLEWARE/g, mw.join('\n '));1};1function loadConfig(filename) {var filenameExt = /\.([^\.]+)$/.exec(filename)[1];switch (filenameExt) {case '~':var config = null;['.json','.yml'].forEach(function(testExt){try {var testFilename = filename.replace(/\.~$/, testExt)var stats = fs.lstatSync(testFilename);if (stats.isFile())config = loadConfig(testFilename);}catch(e) {}});if (config != null) return config;throw new Error( sys.format('Can not find configuration file by path template "%s"', filename) );break;case 'json':return JSON.parse(fs.readFileSync(filename, 'utf8'));case 'yml':return require(filename).shift();default:throw new Error( sys.format('Unknown configuration file name extension "%s"', filenameExt) );}};1})();1
/*** Module dependencies*/var path = require('path'),fs = require('fs'),crypto = require('crypto'),exists = fs.exists || path.exists;1/*** Import utilities*/var htmlTagParams = require('./railway_utils').html_tag_params,safe_merge = require('./railway_utils').safe_merge,humanize = require('./railway_utils').humanize,undef;1/*** Config*/var regexps = {'cached': /^cache\//,'isHttp': /^https?:\/\/|\/\//},exts = {'css': '.css','js' : '.js'},paths = {'css': '/stylesheets/','js' : '/javascripts/'},merged = {stylesheets: {ext: exts.css},javascripts: {ext: exts.js}};1/*** Publish HelperSet*/module.exports = new HelperSet(null);1module.exports.HelperSet = HelperSet;1/*** Set of helper methods** @namespace* @param {Object} ctl Controller object*/function HelperSet(ctl) {var controller = ctl;3this.controller = ctl;3/*** CSRF Meta Tag generation** @returns {String} Meta tags against CSRF-attacks*/this.csrf_meta_tag = function () {return controller && controller.protectedFromForgery() ? ['<meta name="csrf-param" content="' + controller.request.csrfParam + '"/>','<meta name="csrf-token" content="' + controller.request.csrfToken + '"/>'].join('\n') : '';}}/*** Make helpers local to query** @param {Object} controller Controller Object* @returns {Object} containing all helpers*/module.exports.personalize = function (controller) {return new module.exports.HelperSet(controller);2};1/*** Return bunch of stylesheets link tags** Example in ejs:** <%- stylesheet_link_tag('bootstrap', 'style') %>** This returns:** <link media="screen" rel="stylesheet" type="text/css" href="/stylesheets/bootstrap.css" />* <link media="screen" rel="stylesheet" type="text/css" href="/stylesheets/style.css" />** @param {String} stylesheet filename* @returns {String} HTML code to the stylesheets in the parameters*/HelperSet.prototype.stylesheetLinkTag = function stylesheetLinkTag() {if (!paths.css || !paths.stylesheets) {paths.css = app.settings.cssDirectory || '/stylesheets/';1paths.stylesheets = paths.css;1}var args = Array.prototype.slice.call(arguments);4var options = {media: 'screen', rel: 'stylesheet', type: 'text/css'};4var links = [];4if (typeof args[args.length - 1] == 'object') {options = safe_merge(options, args.pop());}mergeFiles('stylesheets', args).forEach(function (file) {delete options.href;5// there should be an option to change the /stylesheets/ foldervar href = checkFile('css', file);5links.push(genericTagSelfclosing('link', options, { href: href }));5});4return links.join('\n ');4};1HelperSet.prototype.stylesheet_link_tag = HelperSet.prototype.stylesheetLinkTag;1/*** Generates set of javascript includes composed from arguments** Example in ejs:** <%- javascript_include_tag('rails', 'application') %>** This returns:** <script type="text/javascript" src="/javascripts/rails.js"></script>* <script type="text/javascript" src="/javascripts/application.js"></script>** @param {String} script filename* @returns {String} the generated <script> tags*/HelperSet.prototype.javascriptIncludeTag = function javascriptIncludeTag() {if (!paths.js || !paths.javascripts) {paths.js = app.settings.jsDirectory || '/javascripts/'paths.javascripts = paths.js;1}var args = Array.prototype.slice.call(arguments);4var options = {type: 'text/javascript'};4if (typeof args[args.length - 1] == 'object') {options = safe_merge(options, args.pop());}var scripts = [];4mergeFiles('javascripts', args).forEach(function (file) {// there should be an option to change the /javascripts/ foldervar href = checkFile('js', file);5delete options.src;5scripts.push(genericTag('script', '', options, {src: href}));5});4return scripts.join('\n ');4};1HelperSet.prototype.javascript_include_tag = HelperSet.prototype.javascriptIncludeTag;1/*** Merge files when caching enabled** You can enable merging manually with the following configuration:** app.set('merge javascripts')* app.set('merge stylesheets')** @param {String} scope Scope which is merged, e.g. javascripts or stylesheets* @param {Array} files Array of files which should be merged* @see https:~~~C4~~~* @returns {String} Pathname to the merged file*/function mergeFiles(scope, files) {// ensure that feature is enabledif (app.disabled('merge ' + scope)) {return files;8}var ext = merged[scope].ext,result = [],shasum = crypto.createHash('sha1'),minify = [],directory = merged[scope].directory = paths[scope].replace(/(\/+)/g, '');// only merge local filesfiles.forEach(function (file) {if (!regexps.isHttp.test(file)) {shasum.update(file);minify.push(file);} else {result.push(file);}});// calculate name of new script based on names of merged filesvar digest = shasum.digest('hex');// check cache state (undefined = not cached, false = cache in progress, String = cached)var cached = merged[scope][digest];if (cached) {// push resulted filename to resultresult.push(cached);} else {// we have no cache at the moment, just return filesresult = result.concat(minify);// if caching process is not started yetif (cached !== false) {// mark caching process as startedmerged[scope][digest] = false;// write resulted script as merged `minify` filesvar stream = fs.createWriteStream(path.join(app.root, 'public', directory, 'cache_' + digest + ext));var counter = 0;var fileContents = {};minify.forEach(function (file) {var filename = path.join(app.root, 'public', directory, file + ext);exists(filename, function (exists) {if (exists) {counter += 1;fs.readFile(filename, 'utf8', function (err, data) {fileContents[file] = data;done();});}})});function done() {if (--counter === 0) {minify.forEach(function (file) {data = fileContents[file];stream.write('~~~C52~~~ \n');stream.write(data + '\n');});stream.end();}}// save name of resulted file to the merge scope registrystream.on('close', function () {merged[scope][digest] = ['cache', digest].join('_');})}}return result;}/*** Link helper** Example in ejs:** <%- link_to("Home", '/') %>** This returns:** <a href="/">Home</a>** @param {String} text Text of the link* @param {String} url Url where the link points to* @param {Object} params Set of html params (class, style, etc..)* @returns {String} Generated html for link*/HelperSet.prototype.linkTo = function linkTo(text, url, params) {['remote', 'method', 'jsonp', 'confirm'].forEach(dataParam.bind(params));return genericTag('a', text, {href: url}, params);};1HelperSet.prototype.link_to = HelperSet.prototype.linkTo;1/*** Form tag helper** @methodOf HelperSet.prototype* @param {Object} params* @param {Function} block*/HelperSet.prototype.formTag = function (params, block) {var self = this;4var buf = arguments.callee.caller.buf;4// helper may be called with block onlyif (typeof params === 'function') {block = params;1params = {};1}if (typeof params === 'undefined') {params = {}}// default method is POSTif (!params.method) {params.method = 'POST';2}// hook up alternative methods (PUT, DELETE)var _method;4var method = _method = params.method.toUpperCase();4if (method != 'GET' && method != 'POST') {_method = method;2params.method = 'POST';2}// hook up data-params['remote', 'jsonp', 'confirm'].forEach(dataParam.bind(params));4// push outputbuf.push('<form' + htmlTagParams(params) + '>');4buf.push( this.csrf_tag() );4// alternative method?if (_method !== params.method) {buf.push(HelperSet.prototype.input_tag({type: "hidden", name: "_method", value: _method }));2}// function?if (typeof block === 'function') {block();2}buf.push('</form>');4};1HelperSet.prototype.form_tag = HelperSet.prototype.formTag;1/*** Prints error messages for the model instance** @methodOf HelperSet.prototype* @param {ModelInstance} resource* @returns {String} Error messages from the model instance*/HelperSet.prototype.errorMessagesFor = function errorMessagesFor(resource) {var out = '';var h = this;if (resource.errors) {out += genericTag('div', printErrors(), {class: 'alert alert-error'});}return out;function printErrors() {var out = '<p>';out += genericTag('strong', 'Validation failed. Fix following errors before you continue:');out += '</p>';for (var prop in resource.errors) {if (resource.errors.hasOwnProperty(prop)) {out += '<ul>';resource.errors[prop].forEach(function (msg) {out += genericTag('li', prop + ' ' + msg, {class: 'error-message'});});out += '</ul>';}}return out;}};1/*** Form fields for resource helper** @methodOf HelperSet.prototype* @param {ModelInstance} resource* @param {Function} block* @namespace*/HelperSet.prototype.fields_for = function (resource, block) {arguments.callee.buf = arguments.callee.caller.buf;resource = resource || {};var self = this;var resourceName = resource && resource.constructor && resource.constructor.modelName || false;var complexNames = (app.set('view options') || {}).complexNames;/*** Generates a name** @requires resourceName* @param {String} name Name of the element* @returns {String} returns the generated name*/function makeName(name) {return complexNames && resourceName ? (resourceName + '[' + name + ']') : name;}/*** Generates an id field** @requires resourceName* @param {String} name Name of the element* @returns {String} returns the generated id name*/function makeId(name) {return complexNames && resourceName ? (resourceName + '_' + name) : name;}block({/*** Input tag helper** Example in ejs:** <%- form.input("test") %>** This returns:** <input name="test"/>** @param {String} name Name of the element* @param {Object} params Additional parameters*/input: function (name, params) {params = params || {};if (params.value === undef) {params.value = resource.hasOwnProperty(name) ? resource[name] : '';}return HelperSet.prototype.input_tag({name: makeName(name),id: makeId(name)}, params);},checkbox: function (name, params) {params = params || {};if (params.value === undef) {params.value = resource[name] || 1;}if (params.checked === undef) {if(resource[name]) {params.checked = 'checked';}} else if (params.checked === false) {delete params.checked;}return HelperSet.prototype.input_tag({name: makeName(name),id: makeId(name),type: 'checkbox'}, params);},file: function (name, params) {return HelperSet.prototype.input_tag({name: makeName(name),id: makeId(name),type: 'file'}, params);},/** Label helper** Example in ejs:** <%- form.label("test", false, {class: "control-label"}) %>** This returns:** <label for="test" class="control-label">Test</label>** @param {String} name Name of the element* @param {String} caption Optional different caption of the elemt* @param {Object} params Additional parameters*/label: function (name, caption, params) {return HelperSet.prototype.label_tag(caption || self.controller.t('models.' + resource.constructor.modelName + '.fields.' + name, humanize(name)),{for: makeId(name) },params);},submit: function (name, params) {return genericTag('button', name || 'Commit', {type: 'submit'}, params);},textarea: function (name, params) {return genericTag('textarea', sanitizeHTML(resource[name] || ''), {name: makeName(name), id: makeId(name)}, params);},/** Provides a select tag** In ejs:** <%- form.select("state", states, {fieldname: 'name', fieldvalue: '_id'}) %>** Possible params:* * blank: {STRING} Blank value to be added at the beginning of the list* * fieldname: {STRING} Sets the name of the field in "options" field where the displayed values can be found. Default: "value"* * fieldvalue: {STRING} Sets the name of the field in "options" field where the submitted values can be found. Default = fieldname* * multiple: Can be set to false if size >1 to only select one value.* * select: Select a value. If fieldname and fieldvalue are different, the value is compared with fieldvalue otherwise with fieldname.* * size: Sets the displayed size of the select field** @author [Uli Wolf](https:~~~C22~~~* @param {String} name Name of the select tag* @param {Object} options Array of possible options* @param {Object} params Additional parameters*/select: function (name, options, params) {var options = options || '';var params = params || {};// optional: Holds the displayed fieldname where the data can be found in 'options'var optionFieldname = params.fieldname || 'value';delete params.fieldname;// optional: Holds the submittable fieldvalue where the data can be found in 'options'var optionFieldvalue = params.fieldvalue || optionFieldname;delete params.fieldvalue;// optional: Holds the number of entries that can be seen at once// If size > 1, multiple values can be selected (can be switched off via multiple:false)// If size = 1, only one value is selectable (Drop-Down)if (params.size === undef) {params.size = 1;} else {if (params.size > 1) {if (params.multiple === undef || params.multiple === true) {params.multiple = 'multiple';} else {delete params.multiple;}}}// optional: Preselect an entryif (params.select === undef) {params.select = resource[name] || '';}var optionSelected = params.select;delete params.select;// Render the optionsvar innerOptions = '';// optional: Add a blank field at the beginningif (params.blank !== undef) {innerOptions += HelperSet.prototype.option_tag(sanitizeHTML(params.blank), {value: ''})}for (var optionsNr in options) {var option = options[optionsNr];var optionParameters = new Array();// Is the value in a seperate field?if (option[optionFieldvalue] != option[optionFieldname]) {optionParameters.value = option[optionFieldvalue];}if(activeValue(option[optionFieldvalue], optionSelected)) {optionParameters.selected = 'selected';}// Generate the option TagsinnerOptions += HelperSet.prototype.option_tag(sanitizeHTML(option[optionFieldname]), optionParameters)}// Render the selectreturn HelperSet.prototype.select_tag(innerOptions, {name: makeName(name), id: makeId(name)}, params);}});};1/*** Form for resource helper** @methodOf HelperSet.prototype* @param {ModelInstance} resource* @param {Object} params* @param {Function} block*/HelperSet.prototype.formFor = function formFor(resource, params, block) {var self = this;1var buf = arguments.callee.buf = arguments.callee.caller.buf;1if (resource && resource.modelName) {if (typeof params !== 'object') {params = {};1}if (!params.method) {params.method = 'PUT';1}if (!params.action) {params.action = railway.routeMapper.pathTo[railway.utils.underscore(resource.modelName)](resource);1}}this.form_tag(params, function () {arguments.callee.buf = buf;1if (block) self.fields_for(resource, block);1});1};1HelperSet.prototype.form_for = HelperSet.prototype.formFor;1/*** Input tag helper** @methodOf HelperSet.prototype* @param {String} text - inner html* @param {Object} params - set of tag attributes* @param {Object} override - set params to override params in previous arg* @returns {String} Finalized input tag*/HelperSet.prototype.input_tag = function (params, override) {return '<input' + htmlTagParams(params, override) + ' />';2};1/*** Label tag helper** Result:** <label>text</label>** @methodOf HelperSet.prototype* @param {String} text - inner html* @param {Object} params - set of tag attributes* @param {Object} override - set params to override params in previous arg* @returns {String} Finalized label tag*/HelperSet.prototype.label_tag = function (text, params, override) {return genericTag('label', text, params, override);};1/*** Cross-site request forgery hidden inputs** @methodOf HelperSet.prototype* @returns {String} CSRF-Tag with parameters*/HelperSet.prototype.csrf_tag = function() {return '<input type="hidden" name="' + this.controller.request.csrfParam + '" value="' + this.controller.request.csrfToken + '" />';4}/*** Select tag helper** Result:** <select>innerOptions</select>** @methodOf HelperSet.prototype* @author [Uli Wolf](https:~~~C34~~~* @param {String} innerOptions Inner html of the select tag* @param {Object} params Set of tag attributes* @param {Object} override Set params to override params in previous arg* @returns {String} Finalized select tag*/HelperSet.prototype.select_tag = function (innerOptions, params, override) {return genericTag('select', innerOptions, params, override);};1/*** Option tag helper** Result:** <option>text</option>** @methodOf HelperSet.prototype* @author [Uli Wolf](https:~~~C35~~~* @param {String} text Inner html* @param {Object} params Set of tag attributes* @param {Object} override Set params to override params in previous arg* @returns {String} Finalized option tag*/HelperSet.prototype.option_tag = function (text, params, override) {return genericTag('option', text, params, override);};1/*** Private util methods*//*** Returns html code of one tag with contents** @param {String} name name of tag* @param {String} inner inner html* @param {Object} params set of tag attributes* @param {Object} override set params to override params in previous arg* @returns {String} Finalized generic tag*/function genericTag(name, inner, params, override) {return '<' + name + htmlTagParams(params, override) + '>' + inner + '</' + name + '>';5}/*** Returns html code of a selfclosing tag** @param {String} name name of tag* @param {Object} params set of tag attributes* @param {Object} override set params to override params in previous arg* @returns {String} Finalized generic selfclosing tag*/function genericTagSelfclosing(name, params, override) {return '<' + name + htmlTagParams(params, override) + ' />';5}/*** Prefixes key with 'data-'** @param {String} key name of key*/function dataParam(key) {if (this[key]) {this['data-' + key] = this[key];delete this[key];}}/*** Compares a string against a string or an array** @author [Uli Wolf](https:~~~C36~~~* @param {String} value Content of the String to be compared* @param {String|Array} selectvalue String or Array of possiblities to be equal to the first string* @returns {Boolean} True if the string matches the other string or array. False when not.*/function activeValue(value, selectvalue) {var returnBool = false;// If this is an Array (e.g. when multiple values should be selected), iterateif (Object.prototype.toString.call(selectvalue) === '[object Array]') {// This is an Array (e.g. when multiple values should be selected), iteratefor (var selectvalueNr in selectvalue) {// Cast to String as these might be objects.if (String(value) == String(selectvalue[selectvalueNr])) {returnBool = truecontinue;}}} else {// This is just one entry// Cast to String as these might be objects.if (String(value) == String(selectvalue)) {returnBool = true}}return returnBool;}/*** Escape &, < and > symbols** @param {String} html String with possible HTML-Elements* @returns {String} resulting string with escaped characters*/function sanitizeHTML(html) {return html.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<');}HelperSet.prototype.sanitize = sanitizeHTML;1/*** Checks if the environment is production** @returns {Boolean} True if production, otherwise false*/function checkProd() {return app.settings.env === 'production';10}/*** Provides the link to a file. Checks if a file needs to be suffixed with a timestamp** @param {String} type Type of the file, e.g. css or js* @param {String} file name (local file) or link (external) to the file* @returns {String} Final Link to the file*/function checkFile(type, file) {var isExternalFile = regexps.isHttp.test(file),isCached = file.match(regexps.cached),href = !isExternalFile ? paths[type] + file + exts[type] : file,isProd = checkProd();10if (!isCached && !isProd && !isExternalFile && !app.disabled('assets timestamps')) {href += '?' + Date.now()}return href;10}
module.exports = Controller;Controller.beforeFilters = [];1Controller.afterFilters = [];1Controller.actions = {};1Controller.layout = null;1Controller.buffer = {};1Controller.filterParams = [ 'password' ];1function Controller(req, res) {this.controllerName = this.constructor.controllerName;var root = this.constructor.root;this.root = root;this._layout = this.constructor.layout || 'application';try {this._helper = require(root + '/app/helpers/' + this.controllerName + '_helper');} catch(e) {this._helper = {};}try {this._appHelper = require(root + '/app/helpers/application_helper');} catch(e) {this._appHelper = {};}}/*** Configure which query params should be filtered from logging* @param {String} param 1 name* @param {String} param 2 name* @param ...* @param {String} param n name*/Controller.filterParameterLogging = function (args) {this.filterParams = this.filterParams.concat(Array.prototype.slice.call(arguments));};1Controller.load = function (name, root) {root = root || app.root;var file = Controller.index[root][name];var Ctl = require(file);Ctl.root = root;Ctl.controllerName = name;Ctl.controllerFile = file;return Ctl;};1/*** Define controller action** @param name String - optional (if missed, named function required as first param)* @param action Function - required, should be named function if first arg missed** @example* ```* BlogController.action(function index() {* Post.all(function (err, posts) {* this.render({posts: posts});* }.bind(this));* });* ```**/Controller.action = function (name, action) {if (typeof name === 'function') {action = name;name = action.name;if (!name) {throw new Error('Named function required when `name` param omitted');}}action.isAction = true;action.customName = name;this.actions[name] = action;};1/*** Layout setter/getter** - when called without arguments, used as getter,* - when called with string, used as setter** When `layout` not called controller trying to get guess which layout to use.* First of all controller looking for layout with the same name as controller,* for example `users_controller` will choose `users_laout`, if there's no* layout with this name, controller using `application_layout`.** If you do not want to use any layout by default, you can just set it up:** app.set('view options', {layout: false});** this will prevent you from repeating `layout(false)` in each controller where* you do not want to use layout, for example in api controllers.** - choose** @param {String} layout - [optional] layout name*/Controller.prototype.layout = function layout(l) {if (typeof l !== 'undefined') {this._layout = l;}return this._layout ? this._layout + '_layout' : null;};1/*** Render response.** @param {String} view name [optional]* @param {Object} locals - data passed to view as local variables [optional]** When first parameter is omitted action name used as view name:* ```* BlogController.action(function index() {* this.render(); ~~~C0~~~* });* ```** Second argument is optional too, you can set local variables using `this` inside* action:* ```* BlogController.action('new', function () {* this.title = 'Create new post';* this.post = new Post;* this.render();* });* ```* will result the same as* ```* BlogController.action('new', function () {* this.render({* title: 'Create new post',* post: new Post* });* });* ```*/Controller.prototype.render = function (arg1, arg2) {var view, params;var ctlName = this.controllerName;var root = this.root;if (typeof arg1 == 'string') {view = arg1;params = arg2;} else {view = this.actionName;params = arg1;}params = params || {};var layout = this.layout(),file = ctlName + '/' + view;if (this.res.renderCalled) {log('Rendering', $(file).grey, 'using layout', $(layout).grey, 'called more than once.', $('render() can be called only once!').red);return;}log('Rendering', $(file).grey, 'using layout', $(layout).grey);var helpers = railway.helpers.personalize(this);app.set('views', this.root + '/app/views');this.res.renderCalled = true;this.res.render(file, {locals: safe_merge(params, this, helpers, helpers.__proto__, this._helper, this._appHelper),layout: layout ? 'layouts/' + layout : false,debug: false});if (this.req.inAction) this.next();};1/*** Internal request handler. Serves request using railway env:** - update context links: req, res, next and other req-sensitive stuff* - run before filters* - run action* - run after filters** `perform` method called by `ControllerBridge`** @param {String} actionName* @param {IncomingMessage} req - incoming http request* @param {ServerResponse} res - http server response* @private*/Controller.prototype.perform = function perform(actionName, req, res, nextRoute) {var self = this;res.info = {controller: this.controllerName,action: actionName,startTime: Date.now()};this.request = this.req = req;this.response = this.res = res;this.nextRoute = this.next = nextRoute;res.actionHistory = [];if (!this.initialized) {this.initialized = true;this._init();}this.actionName = actionName;var ctl = this, timeStart = false, prevMethod;// need to track uniqueness of filters by namevar queueIndex = {};log('');log($((new Date).toString()).yellow + ' ' + $(this.id).bold);log($(req.method).bold, $(req.url).grey, 'controller:', $(this.controllerName).cyan, 'action:', $(this.actionName).blue);if (req.query && Object.keys(req.query).length) {log($('Query: ').bold + JSON.stringify(req.query));}if (req.body && req.method !== 'GET') {var filteredBody = {};Object.keys(req.body).forEach(function (param) {if (!ctl.constructor.filterParams.some(function (filter) {return param.search(filter) !== -1;})) {filteredBody[param] = req.body[param];} else {filteredBody[param] = '[FILTERED]';}});log($('Body: ').bold + JSON.stringify(filteredBody));}// build queue using before-, after- filters and actionvar queue = [];enqueue(this.constructor.beforeFilters, queue);queue.push(getCaller(this.constructor.actions[actionName]));enqueue(this.constructor.afterFilters, queue);if (app.enabled('eval cache')) {queue.push(getCaller(function () {backToPool(ctl);}));}// start serving requestnext();var logActions = app.enabled('log actions');function next(err) {if (logActions && timeStart && prevMethod) {log('<<< ' + prevMethod.customName + ' [' + (Date.now() - timeStart) + ' ms]');}if (err && err.constructor.name === 'Error') {return self.nextRoute(err);}if (timeStart && prevMethod) {res.actionHistory.push({name: prevMethod.customName, time: Date.now() - timeStart});}// run next method in queue (if any callable method)var method = queue.shift();if (typeof method == 'function') {process.nextTick(function () {method.call(ctl, next);});} else {res.info.appTime = Date.now() - res.info.startTime;}}function getCaller(method) {if (!method) {throw new Error('Undefined action');}return function (next) {req.inAction = method.isAction;if (logActions && method.customName) {if (method.isAction) {log('>>> perform ' + $(method.customName).bold.cyan);} else {log('>>> perform ' + $(method.customName).bold.grey);}}timeStart = Date.now();prevMethod = method;method.call(this, next);}}function enqueue(collection, queue) {collection.forEach(function (f) {var params = f[1];if (!params) {enqueue();} else if (params.only && params.only.indexOf(actionName) !== -1 && (!params.except || params.except.indexOf(actionName) === -1)) {enqueue();} else if (params.except && params.except.indexOf(actionName) === -1) {enqueue();}function enqueue() {if (f[2]) {if (queueIndex[f[2]]) return;queueIndex[f[2]] = true;}queue.push(getCaller(f[0]));}});}};1
var fs = require('fs');var path = require('path');1var singularize = require("../vendor/inflection").singularize;1var utils = require('./railway_utils');1var safe_merge = utils.safe_merge;1var Map = require('railway-routes').Map;1var existsSync = fs.existsSync || path.existsSync;1var exists = fs.exists || path.exists;1require('coffee-script');1/*** Global railway API singleton.* Available everywhere in project.** @member railway.locales - localization module* @member railway.utils - railway utilities (stylize, runCode, etc..)* @see railway_utils.html#* @member railway.controller* @see controller.html#* @member railway.extensions* @see extensions.html#* @member railway.generators* @see generators.html#* @member railway.tools* @see tools.html#* @member railway.logger* @see logger.html#* @member railway.helpers* @see helpers.html#* @member railway.models* @see model.html#*/function Railway() {if (global.hasOwnProperty('railway')) return railway;1global.railway = this;1this.locales = require('./locales');1this.utils = require('./railway_utils');1this.ControllerBridge = require('./controller_bridge');1this.controllerBridge = new this.ControllerBridge;1this.controller = require('./controller');1this.extensions = require('./extensions');1this.generators = require('./generators');1this.tools = require('./tools');1this.logger = require('./logger');1this.routeMapper = new Map(app, this.controllerBridge.uniCaller.bind(this.controllerBridge));1this.helpers = require('./helpers');1this.models = require('./models');1}try {if (process.versions.node < '0.6') {Railway.prototype.version = JSON.parse(fs.readFileSync(__dirname + '/../package.json')).version;} else {Railway.prototype.version = require('../package').version;1}} catch(e) {}/*** Initialize railway application:** - load modules* - run configurators (config/environment, config/environments/{env})* - init controllers* - init extensions (including ORM and db/schema)* - init models* - run initializers `config/initializers/*`* - add routes* - start http server* - locales* - loggers* - observers* - assets**/exports.init = function initRailway(app) {var isMainModule = !global.hasOwnProperty('app');1// globalize app objectif (isMainModule) {global.app = app;1app.root = process.cwd();1app.models = {};1}var root;1if (typeof app === 'string') {root = app;if (!isMainModule) {var cb = new railway.ControllerBridge(root);}} else {root = app.root;1}// create API publishing objectnew Railway();1// run environment.{js|coffee} and environments/{test|development|production}.{js|coffee}configureApp(root, isMainModule);1// controllers should be loaded before extensionsrailway.controller.init(root);1// extensions should be loaded before server startuprailway.extensions.init(root);1// init models in app/models/*railway.models.init(root);1// run config/initializers/*runInitializers(root);1if (existsSync(root + '/config') && (existsSync(root + '/config/routes.js') || existsSync(root + '/config/routes.coffee'))) {railway.routeMapper.addRoutes(root + '/config/routes', isMainModule ? null : cb.uniCaller.bind(cb));}// everything else can be done after starting serverprocess.nextTick(function () {railway.locales.init(root);1railway.logger.init(app.root);1app.reloadModels = railway.models.loadModels;1loadObservers();1if (global.app.enabled('merge javascripts')) {ensureDirClean(app.root + '/public' + app.set('jsDirectory'), 'cache');}if (global.app.enabled('merge stylesheets')) {ensureDirClean(app.root + '/public' + app.set('cssDirectory'), 'cache');}});1};1/*** Create http server object. Automatically hook up SSL keys stored in* app.root/config/tsl.{cert|key}** @param {Object} options: {key: 'path/to/tsl.key', cert: 'path/to/tsl.cert'}*/exports.createServer = function (options) {options = options || {};1var express = require('express');1var server;1if (express.createServer) {server = express.createServer;1} else {server = express;}var keys, app,key = options.key || process.cwd() + '/config/tsl.key',cert = options.cert || process.cwd() + '/config/tsl.cert';1if (existsSync(key) && existsSync(cert)) {keys = {key: fs.readFileSync(key).toString('utf8'),cert: fs.readFileSync(cert).toString('utf8')};}if (keys) {app = server(keys);} else {app = server();1}exports.init(app);1return app;1};1/*** Run app configutators in `config/environment` and `config/environments/{env}`.* Also try to monkey patch ejs and jade. **weird***/function configureApp(root, isMainModule) {var mainEnv = root + '/config/environment';1if (isMainModule) {app.set('views', root + '/app/views');1requireIfExists(mainEnv + '.js') || requireIfExists(mainEnv + '.coffee');1}var supportEnv = app.root + '/config/environments/' + app.settings.env;1requireIfExists(supportEnv + '.js') || requireIfExists(supportEnv + '.coffee');1if (!isMainModule) {return;}// TODO: remove monkey-patching from hereif (app.settings['view engine'] == 'ejs' && (!app.extensions || !app.extensions['ejs-ext'])) {// monkey patch ejstry {var ejs = require('ejs'), old_parse = ejs.parse;ejs.parse = function () {var str = old_parse.apply(this, Array.prototype.slice.call(arguments));return str.replace('var buf = [];', 'var buf = []; arguments.callee.buf = buf;');};} catch(e) {}}if (app.settings['view engine'] == 'jade' && (!app.extensions || !app.extensions['jade-ext'])) {// monkey patch jadetry {var jade = require('jade'), old_parse = jade.Compiler.prototype.compile;jade.Compiler.prototype.compile = function () {var str = old_parse.apply(this, Array.prototype.slice.call(arguments));// console.log(str);return 'arguments.callee.buf = buf;' + str;};} catch(e) {}}}/*** Require `module` if it exists** @param {String} module - path to file*/function requireIfExists(module) {if (railway.utils.existsSync(module)) {require(module);return true;} else {return false;4}}/*** Run initializers in sandbox mode*/function runInitializers(root) {var context = {global: {}};1for (var i in app.models) {context[i] = app.models[i];}var initializersPath = root + '/config/initializers/';1if (existsSync(initializersPath)) {fs.readdirSync(initializersPath).forEach(function (file) {if (file.match(/^\./)) return;var script_name = initializersPath + file;utils.runCode(script_name, context);});for (var i in context.global) {global[i] = context.global[i];}}}/*** Observer is a kind of controller, that listen for some event in* the system, for example: paypal, twitter or facebook observers* listens for callback from foreign service. Email observer may* listen some events related to emails.** If you need app.on('someEvent') you should place this code in* APPROOT/app/observers/NAME_observer.js*/function loadObservers() {var dir = app.root + '/app/observers';1exists(dir, function (exists) {if (exists) {fs.readdir(dir, function (err, files) {if (!err && files) {files.forEach(function (file) {if (file.match(/^[^\.]/)) {require(path.join(dir, file));}});}});}});1}/*** Cleanup or create dir*/function ensureDirClean(dir, prefix) {exists(dir, function (exists) {if (exists) {fs.readdir(dir, function (err, files) {files.filter(function (file) {return file.indexOf(prefix + '_') === 0;}).map(function (file) {return path.join(dir, file);}).forEach(fs.unlink);});} else {fs.mkdir(dir, 0755);}});}
var undef, sys = require('util'),path = require('path'),fs = require('fs'),Module = require('module'),vm = require('vm'),yaml = require('yaml-js');1exports.html_tag_params = function (params, override) {var maybe_params = '';16safe_merge(params, override);16for (var key in params) {if (params[key] != undef) {maybe_params += ' ' + key + '="' + params[key].toString().replace(/&/g, '&').replace(/"/g, '"') + '"';41}}return maybe_params;16};1var safe_merge = exports.safe_merge = function (merge_what) {merge_what = merge_what || {};18Array.prototype.slice.call(arguments).forEach(function (merge_with, i) {if (i == 0) return;28for (var key in merge_with) {if (!merge_with.hasOwnProperty(key) || key in merge_what) continue;50merge_what[key] = merge_with[key];50}});18return merge_what;18};1exports.humanize = function (underscored) {var res = underscored.replace(/_/g, ' ');1return res[0].toUpperCase() + res.substr(1);1};1exports.camelize = function (underscored, upcaseFirstLetter) {var res = '';5underscored.split('_').forEach(function (part) {res += part[0].toUpperCase() + part.substr(1);8});5return upcaseFirstLetter ? res : res[0].toLowerCase() + res.substr(1);5};1exports.classify = function (str) {return exports.camelize(exports.singularize(str));1};1exports.underscore = function (camelCaseStr) {var initialUnderscore = camelCaseStr.match(/^_/) ? '_' : '';4var str = camelCaseStr.replace(/^_([A-Z])/g, '$1').replace(/([A-Z])/g, '_$1').replace(/^_/, initialUnderscore);4return str.toLowerCase();4};1exports.singularize = require('../vendor/inflection.js').singularize;1exports.pluralize = require('../vendor/inflection.js').pluralize;1// Stylize a stringfunction stylize(str, style) {var styles = {'bold' : [1, 22],'italic' : [3, 23],'underline' : [4, 24],'cyan' : [96, 39],'blue' : [34, 39],'yellow' : [33, 39],'green' : [32, 39],'red' : [31, 39],'grey' : [90, 39],'green-hi' : [92, 32]};300var s = styles[style];300return '\033[' + s[0] + 'm' + str + '\033[' + s[1] + 'm';300};1var $ = function (str) {str = new(String)(str);497['bold', 'grey', 'yellow', 'red', 'green', 'cyan', 'blue', 'italic', 'underline'].forEach(function (style) {Object.defineProperty(str, style, {get: function () {return $(stylize(this, style));300}});4473});497return str;497};1stylize.$ = $;1exports.stylize = stylize;1exports.debug = function () {railway.logger.write(Array.prototype.join.call(arguments, ' '));85};1var addCoverage = exports.addCoverage = function (code, filename) {if (!global.__cov) return code;15return require('semicov').addCoverage(code, filename);15};1// cache for source codevar cache = {};1// cache for compiled scriptsvar scriptCache = {};1function runCode(filename, context) {var isCoffee = filename.match(/coffee$/);15context = context || {};15var dirname = path.dirname(filename);15// extend contextcontext.require = context.require || function (apath) {var isRelative = apath.match(/^\.\.?\//);return require(isRelative ? path.resolve(dirname, apath) : apath);};15context.app = app;15context.railway = railway;15context.console = console;15context.setTimeout = setTimeout;15context.setInterval = setInterval;15context.clearTimeout = clearTimeout;15context.clearInterval = clearInterval;15context.__filename = filename;15context.__dirname = dirname;15context.process = process;15context.t = context.t || t;15context.Buffer = Buffer;15// omit file reading and caching part if we have compiled scriptif (!scriptCache[filename]) {cache[filename] = cache[filename] || filename && exports.existsSync(filename) && require('fs').readFileSync(filename);15if (!cache[filename]) {return;}var code = cache[filename].toString();15if (isCoffee) {try {var cs = require('coffee-script');} catch(e) {throw new Error('Please install coffee-script npm package: `npm install coffee-script`');}try {code = require('coffee-script').compile(code);} catch(e) {console.log('Error in coffee code compilation in file ' + filename);throw e;}} else {code = addCoverage(code, filename);15}}try {var m;15if (scriptCache[filename]) {m = scriptCache[filename];} else {m = vm.createScript(code.toString('utf8'), filename);15scriptCache[filename] = m;15}m.runInNewContext(context);15} catch(e) {console.log('Error while executing ' + filename);throw e;}// disable caching in development modeif (app.disabled('eval cache')) {cache[filename] = null;15scriptCache[filename] = null;15}}exports.runCode = runCode;1function addSpaces(str, len, to_start) {var str_len = str.length;2for (var i = str_len; i < len; i += 1) {if (!to_start) {str += ' ';3} else {str = ' ' + str;3}}return str;2}exports.addSpaces = addSpaces;1function readYaml(file) {try {return require(file).shift();} catch(e) {console.log('Error in reading', file);console.log(e.message);console.log(e.stack);}}exports.readYaml = readYaml;1exports.existsSync = fs.existsSync || path.existsSync;1
var path = require('path');var fs = require('fs');1var readline = require('readline');1var childProcess = require('child_process');1var sys = require('util');1var util = require('util');1/*** Print routing map. Optionally accepts `filter` param, allowing to filter* output by method or helper name** @param filter** ```* $ railway routes* projects GET /projects projects#index* update ALL /:user/:repo/update doc#update* GET /:user/:repo doc#make* * GET /:user/:repo/* doc#make* ```* same but filtered to show GET routes only* ```* $ railway routes get* projects GET /projects projects#index* GET /:user/:repo doc#make* * GET /:user/:repo/* doc#make* ```* same but filtered to show routes with helper name contains `pro`* ```* $ railway routes get* projects GET /projects projects#indexw ```*/function routes() {var mapper = railway.routeMapper;var addSpaces = railway.utils.addSpaces;var dump = mapper.dump;var max_len = 0, helper_max_len = 0;var filter = (args.shift() || '').toUpperCase();var filtered = [];dump.forEach(function (data) {var method = data.method.toUpperCase();if (!filter || filter === method || data.helper.toUpperCase().search(filter) !== -1) {if (data.path.length > max_len) {max_len = data.path.length;}if (data.helper.length > helper_max_len) {helper_max_len = data.helper.length;}filtered.push(data);}});filtered.forEach(function (data) {var method = data.method.toUpperCase();console.log(addSpaces(data.helper, helper_max_len + 1, true) + ' ' +addSpaces(method, 7) +addSpaces(data.path, max_len + 1) +data.file + "#" + data.action);});return true;}exports.routes = routes;1routes.help = {shortcut: 'r',usage: 'routes [filter]',description: 'Display application routes'};1/*** Debug console.* node REPL console with railway bindings** Predefined helpers:** - `c` - callback, assigning it's arguments to _0 _1 .. _N variables* - `reload` - reload models* - `exit` - quit repl** Usage Example:* ```* $ railway console* railway> User.all(c)* undefined* railway> [ [ 'hgetall', 'User:68' ],* [ 'hgetall', 'User:69' ],* [ 'hgetall', 'User:70' ],* [ 'hgetall', 'User:71' ],* [ 'hgetall', 'User:72' ],* [ 'hgetall', 'User:73' ],* [ 'hgetall', 'User:74' ],* [ 'hgetall', 'User:75' ] ] '[2ms]'* Callback called with 2 arguments:* _0 = null* _1 = [object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]* railway> _1[0].toObject()* { id: '68',* githubId: null,* displayName: null,* username: null,* avatar: null }* ```**/function railwayConsole() {var ctx = require('repl').start('railway> ').context;ctx.reload = function () {global.models = {};ctx.app = app;app.reloadModels();for (var m in models) {ctx[m] = models[m];}};ctx.c = function () {var l = arguments.length,message = 'Callback called with ' + l +' argument' + (l === 1 ? '' : 's') + (l > 0 ? ':\n' : '');for (var i = 0; i < 10; i++) {if (i < arguments.length) {ctx['_' + i] = arguments[i];message += '_' + i + ' = ' + arguments[i] + '\n';} else {if (ctx.hasOwnProperty('_' + i)) {delete ctx['_' + i];}}}console.log(message);};ctx.exit = function () {process.exit(0);};process.nextTick(ctx.reload);return false;}exports.console = railwayConsole;1railwayConsole.help = {shortcut: 'c',usage: 'console',description: 'Debug console'};1exports.dbconsole = function dbconsole() {var db = childProcess.spawn('mongo');var rli = readline.createInterface(process.stdin, process.stdout, autocomplete);var data = '';db.stdout.on('data', function (chunk) {data += chunk;write();});function write() {if (write.to) {clearTimeout(write.to);}setTimeout(function () {process.stdout.write(data);rli.prompt();data = '';}, 50);}rli.on('SIGINT', rli.close.bind(rli));rli.addListener('close', process.exit);rli.setPrompt('mongo > ');rli.addListener('line', function (line) {db.stdin.write(line + '\n');});// console.log(db);function autocomplete(input) {return [['help', 'test'], input];}}/*** Railway server. Command optionally accept PORT argument** ```* railway server 8000 # run server on 8000 port* railway s 3000 # run server on 3000 port using shorter alias* PORT=80 railway s # run server on 80 port usin env var PORT* ```*/function server() {var port = process.env.PORT || args.shift() || 3000;app.listen(port);console.log("Railway server listening on port %d within %s environment", port, app.settings.env);return false;}exports.server = server;1server.help = {shortcut: 's',usage: 'server [port]',description: 'Run railway server'};1/*** Install railway extension*/function install() {var what = args.shift(), where = args.shift();if (!what || !what.match(/\.git$/)) {console.log('What do you want to install?');return true;}if (!where) {var m = what.match(/\/([^\/]*?)\.git/);where = m[1];}if (!railway.utils.existsSync(app.root + '/node_modules')) {fs.mkdirSync(app.root + '/node_modules');}var command = 'clone';if (railway.utils.existsSync(app.root + '/.git')) {command = 'submodule add';}console.log('Installing ' + where + ' extension from ' + what + ' repo to node_modules/' + where);var cp = require('child_process');cp.exec('git ' + command + ' ' + what + ' node_modules/' + where, function () {if (railway.utils.existsSync(app.root + '/npmfile.coffee')) {cp.exec('echo "require \'' + where + '\'" >> npmfile.coffee');console.log('Patched npmfile.coffee');} else {cp.exec('echo "require(\'' + where + '\');" >> npmfile.js');console.log('Patched npmfile.js');}var installScript = app.root + '/node_modules/' + where + '/install.js';if (railway.utils.existsSync(installScript)) {console.log('Running installation script');require(installScript);} else {process.exit(0);}});return false;}exports.install = install;1install.help = {shortcut: 'x',usage: 'install gitUrl [extName]',description: 'Install railway eXtension'};1
var fs = require('fs'),yaml = require('yaml-js'),coffee = require('coffee-script'),path = require('path'),localeData = {};1/*** Initialize localization module*/exports.init = function () {var dir = app.root + '/config/locales';1if (!railway.utils.existsSync(dir)) {return false;1}app.locales = app.locales || [];exports.load(dir);};1/*** Load localization files from `dir`. Locales can be in yaml, json or coffee* format** Example locale.yml file:** en:* key: 'Value'** @param {String} dir - absolute path to locales directory*/exports.load = function (dir) {fs.readdirSync(dir).forEach(function (file) {if (file.match(/^\./)) return;var filename = dir + '/' + file;var code = fs.readFileSync(filename, 'utf8').toString();var obj;try {if (file.match(/\.ya?ml$/)) {obj = require(filename).shift();} else if (file.match(/\.json/)) {obj = JSON.parse(code);} else if (file.match(/\.coffee/)) {obj = coffee.eval(code);} else {console.log('Unsupported extension of locale file ' + filename);}} catch(e) {console.log('Parsing file ' + filename);console.log(e);console.log(e.stack);}if (obj) {addTranslation(obj);}});};1/*** Add translation to `lang` to application locales collection*/function addTranslation(lang) {Object.keys(lang).forEach(function (localeName) {var translations = lang[localeName];if (app.locales.indexOf(localeName) === -1) {app.locales.push([localeName, translations.lang && translations.lang.name || localeName]);}localeData[localeName] = localeData[localeName] || {};Object.keys(translations).forEach(function (namespace) {localeData[localeName][namespace] = translations[namespace];});});}/*** Global translation helper** @param {Boolean} global* @public*/function T(global) {if (global) {// helper for global scope (models, initializers, etc)// requires two params (locale expected)return function t(path, locale, defaultValue) {1if (!locale) {throw new Error('Locale expected');}return translate(path, locale);};} else {// helper for local scope (controllers, views, helpers)// requires one paramreturn function t(path, defaultValue) {11return translate(path, t.locale, defaultValue);};}function translate(path, locale, defaultValue) {var translation = localeData[locale], substitute;function nextPathItem(token) {return (translation = translation[token]);}if (typeof path === 'string') {substitute = false;} else {substitute = path;path = substitute.shift();}if (!translation || !path.split('.').every(nextPathItem)) {translation = defaultValue || translationMissing(locale, path);}if (translation && substitute && substitute.length) {substitute.forEach(function (substitution) {translation = translation.replace(/%/, substitution.toString().replace(/%/g, ''));});}return translation;}function translationMissing(locale, path) {switch (app.settings.translationMissing) {case 'display':return 'translation missing for ' + locale + '.' + path;case 'default':case undefined:var defLocale = app.settings.defaultLocale;return !defLocale || locale === defLocale ? '' : translate(path, defLocale);}}};1T.localeSupported = function (localeName) {return !!localeData[localeName];};1global.t = T(true);1global.T = T;1
var utils = require('./railway_utils'),path = require('path');1app.extensions = {};1/*** Initialize extensions (using npmfile)*/exports.init = function (root) {app.extensions = {};1var ctx = getNPMFileContext(root);1var js = 'npmfile.js', coffee = 'npmfile.coffee',filename;1if (railway.utils.existsSync(path.join(root, js))) {filename = js;} else if (railway.utils.existsSync(path.join(root, coffee))) {filename = coffee;}if (filename) {utils.runCode(path.join(root, filename), ctx);}initBundledExtensions();1};1/*** Prepare context for executing npm file. Context has two additional features:* - group* - improved require (run init method of module)*/function getNPMFileContext(root) {var ctx = {};1ctx.require = function (package) {var ext = app.extensions[package] = require(package);if (ext && ext.init) {ext.init(root);}};1ctx.group = function (env, callback) {if (env == app.settings.env) {callback();}};1return ctx;1};1function initBundledExtensions() {envInfo();1}/*** Setup route /railway/environment.json to return information about environment*/function envInfo() {var jugglingdbVersion, npmVersion;1app.all('/railway/environment.json', function (req, res) {try {var jugglingdbVersion = require('jugglingdb').version;} catch(e) {}try {var npmVersion = require('npm').version;} catch(e) {}try {var viewEngineVersion = require(app.root + '/node_modules/' + app.set('view engine')).version;} catch(e) {viewEngineVersion = 'not installed';}if (app.disabled('env info')) return res.send({forbidden: true});res.send({settings: app.settings,versions: {core: process.versions,npm: npmVersion,railway: railway.version,jugglingdb: jugglingdbVersion,templating: {name: app.set('view engine'),version: viewEngineVersion}},application: {root: app.root,database: require(app.root + '/config/database')[app.set('env')].driver,middleware: app.stack.map(function (m) {return m.handle.name;})},env: process.env,});});1}
// Depsvar fs = require('fs'),path = require('path'),Module = require('module'),utils = require('./railway_utils');1/*** Initialize models*/exports.init = function (root) {// code coverage supportif (process.cov) context.__cov = __cov;1// quietly fallback to default schematry {if (!railway.orm) require('jugglingdb').init(root);1} catch(e) {}// then run modelsexports.loadModels(root + '/app/models/');1};1global.publish = function (name, model) {console.log('WARNING: `publish` call inside model files deprecated now, use module.exports = MyModel in case of declaring new model in app/models/*.js file, and not in db/schema.js');if (typeof name === 'function') {model = name;name = model.name;}app.models[name] = model;global[name] = model;};1exports.loadModels = function (modelsDir) {var ctx = {};1Object.keys(app.models).forEach(function (model) {ctx[model] = app.models[model];if (ctx[model]._validations) delete ctx[model]._validations;});1if (railway.utils.existsSync(modelsDir)) {fs.readdirSync(modelsDir).forEach(function (file) {if (file.match(/^[^\.].*?\.(js|coffee)$/)) {var filename = path.join(modelsDir, file);delete Module._cache[filename];var m = require(filename);if (m && (m.name || m.modelName)) {var name = m.modelName || m.name;app.models[name] = m;global[name] = m;}}});}};1app.disconnectSchemas = function disconnectSchemas() {if (_schemas.length) {_schemas.forEach(function (schema) {schema.disconnect();});_schemas = [];}}
function ControllerBrigde(root) {this.root = root || app.root;1};1ControllerBrigde.config = {subdomain: {tld: 2}};1ControllerBrigde.prototype.uniCaller = function (ns, controller, action, params) {return function (req, res, next) {var subdomain = req.headers.host.split('.').slice(0, -1 * ControllerBrigde.config.subdomain.tld)req.subdomain = subdomain.join('.');if (params && params.subdomain) {if (params.subdomain !== req.subdomain) {if (params.subdomain.match(/\*/)) {var matched = true;params.subdomain.split('.').forEach(function (part, i) {if (part === '*') return;if (part !== subdomain[i]) matched = false;});if (!matched) return next(); // next route} else return next();}}var ctl = this.loadController(ns + (controller || req.params.controller));if (app.disabled('model cache')) {// TODO: reloadModels should work without any params// it just should remember all paths// called previously withapp.reloadModels(this.root + '/app/models/');}ctl.perform(action || req.params.action, req, res, next);}.bind(this);};1ControllerBrigde.prototype.loadController = function (controllerFullName) {if (app.enabled('commonjs ctl')) {return railway.controller.loadNew(controllerFullName, this.root);} else {return railway.controller.load(controllerFullName, this.root);5}};1module.exports = ControllerBrigde;1
var fs = require('fs');var path = require('path');1exports.stream = null;1exports.init = function () {exports.stream = app.settings.quiet ?fs.createWriteStream(path.join(app.root, 'log', app.settings.env + '.log')) :process.stdout;1};1exports.write = function (str) {(exports.stream || process.stdout).write(str + '\n');86};1