UNPKG

neft

Version:

Universal Platform

320 lines (249 loc) 9.22 kB
# App 'use strict' utils = require 'src/utils' log = require 'src/log' signal = require 'src/signal' db = require 'src/db' assert = require 'src/assert' Schema = require 'src/schema' Networking = require 'src/networking' Document = require 'src/document' Renderer = require 'src/renderer' Styles = require 'src/styles' Resources = require 'src/resources' Dict = require 'src/dict' AppRoute = require './route' if utils.isNode bootstrapRoute = require './bootstrap/route.node' `//<development>` pkg = require 'package.json' `//</development>` BASE_FILE_NAME_RE = /(.+)\.(?:node|server|client|browser|ios|android|native)/ DEFAULT_CONFIG = title: 'Neft.io Application' protocol: 'http' port: 3000 host: 'localhost' language: 'en' type: 'app' exports = module.exports = (opts = {}, extraOpts = {}) -> # Welcome log also for release mode log.ok "Welcome! Neft.io v#{pkg.version}; Feedback appreciated" `//<development>` log.warn "Use this bundle only in development; type --release when it's ready" `//</development>` config = utils.clone DEFAULT_CONFIG config = utils.mergeAll config, opts.config, extraOpts app = new Dict ## *Object* app.config = `{}` Config object from the *package.json* file. Can be overriden in the *init.js* file. ### type The `app` type (the default one) uses renderer on the client side. The `game` type uses special renderer (if exists) focused on more performance goals. The `text` type always return HTML document with no renderer on the client side. It's used for the crawlers (e.g. GoogleBot) or browsers with no javascript support. ```javascript // package.json { "name": "neft.io app", "version": "0.1.0", "config": { "title": "My first application!", "protocol": "http", "port": 3000, "host": "localhost", "language": "en", "type": "app" } } // init.js module.exports = function(NeftApp) { var app = NeftApp({ title: "Overridden title" }); console.log(app.config); // {title: "My first application!", protocol: "http", port: ....} }; ``` app.config = config ## *Networking* app.networking Standard Networking instance used to communicate with the server and to create local requests. All routes created by the *App.Route* uses this networking. HTTP protocol is used by default with the data specified in the *package.json*. app.networking = new Networking type: Networking.HTTP protocol: config.protocol port: parseInt(config.port, 10) host: config.host url: config.url language: config.language ## *Object* app.models = `{}` Files from the *models* folder with objects returned by their exported functions. ```javascript // models/user/permission.js module.exports = function(app) { return { getPermission: function(id){} }; }; // controllers/user.js module.exports = function(app) { return { get: function(req, res, callback) { var data = app.models['user/permission'].getPermission(req.params.userId); callback(null, data); } } }; ``` app.models = {} ## *Object* app.routes = `{}` Files from the *routes* folder with objects returned by their exported functions. app.routes = {} ## *Object* app.styles = `{}` Files from the *styles* folder as *Function*s ready to create new *Item*s. app.styles = {} ## *Object* app.views = `{}` Files from the *views* folder as the *Document* instances. app.views = {} ## *Resources* app.resources app.resources = do -> if opts.resources Resources.fromJSON(opts.resources) else new Resources ## *Signal* app.onReady() Called when all modules, views, styled etc. have been loaded. signal.create app, 'onReady' # config.type config.type ?= 'app' assert.ok utils.has(['app', 'game', 'text'], config.type), "Unexpected app.config.type value. Accepted app/game/text, but '#{config.type}' got." app.Route = AppRoute app ## *Dict* app.cookies On the client side, this object refers to the last received cookies from the networking request. On the server side, this cookies object are added into the each networking response. By default, client has *clientId* and *sessionId* hashes. ```javascript app.cookies.onChange(function(key){ console.log('cookie changed', key, this[key]); }); ``` ```xml <h1>Your clientId</h1> <em>${context.app.cookies.clientId}</em> ``` # cookies COOKIES_KEY = '__neft_cookies' app.cookies = null onCookiesReady = (dict) -> app.cookies = dict if utils.isClient dict.set 'sessionId', utils.uid(16) db.get COOKIES_KEY, db.OBSERVABLE, (err, dict) -> if dict onCookiesReady dict else if utils.isClient cookies = {clientId: utils.uid(16)} else cookies = {} db.set COOKIES_KEY, cookies, (err) -> db.get COOKIES_KEY, db.OBSERVABLE, (err, dict) -> onCookiesReady dict app.networking.onRequest (req, res) -> if utils.isClient utils.merge req.cookies, app.cookies else utils.merge res.cookies, app.cookies req.onLoadEnd.listeners.unshift -> if utils.isClient for key, val of res.cookies unless utils.isEqual(app.cookies[key], val) app.cookies.set key, val return , null return # propagate data Renderer.resources = app.resources Renderer.serverUrl = app.networking.url Renderer.onLinkUri (uri) -> app.networking.createLocalRequest method: Networking.Request.GET type: Networking.Request.HTML_TYPE uri: uri Document.Scripts.scripts = utils.arrayToObject opts.scripts, (index, elem) -> elem.name, (index, elem) -> elem.file # set styles window item if opts.styles? for style in opts.styles if style.name in ['view', '__view__'] style.file._init app: app, view: null windowStyle = style.file._main.getComponent() break windowStyleItem = windowStyle?.item assert.ok windowStyleItem, '__view__ style must be defined' Renderer.window = windowStyleItem if opts.styles? stylesInitObject = app: app view: windowStyleItem # initialize styles for style in opts.styles when style.name? if style.name isnt 'view' style.file._init stylesInitObject app.styles[style.name] = style.file # load styles Styles windowStyle: windowStyle styles: app.styles queries: opts.styleQueries resources: app.resources # load bootstrap if utils.isNode bootstrapRoute app # loading files helper init = (files, target) -> for file in files when file.name? if typeof file.file isnt 'function' continue fileObj = file.file app target[file.name] = fileObj if baseNameMatch = BASE_FILE_NAME_RE.exec(file.name) [_, baseName] = baseNameMatch if target[baseName]? if utils.isPlainObject(target[baseName]) and utils.isPlainObject(fileObj) fileObj = utils.merge Object.create(target[baseName]), fileObj target[baseName] = fileObj return # load app extensions if utils.isObject(opts.extensions) for ext in opts.extensions ext app # exports app classes exports.app = Route: app.Route # load views for view in opts.views when view.name? app.views[view.name] = Document.fromJSON view.file # load files init opts.models, app.models init opts.routes, app.routes for path, obj of app.routes r = {} if utils.isObject(obj) and not (obj instanceof app.Route) for method, opts of obj if utils.isObject(opts) route = new app.Route method, opts r[route.name] = route else r[method] = opts app.routes[path] = r # provide default index route unless app.routes.index app.routes.index = new app.Route 'get /', {} # emit ready signal app.onReady.emit() app