提交 d7dc6f09f1d57fb734b70e48adf58b27cce1f392

作者 fanwh
0 个父辈

init

  1 +
  2 +# ------------------------------------------------------------------------------
  3 +# Common Node files and folders
  4 +# https://github.com/github/gitignore/blob/master/Node.gitignore
  5 +# ------------------------------------------------------------------------------
  6 +
  7 +lib-cov
  8 +*.seed
  9 +*.log
  10 +*.csv
  11 +*.dat
  12 +*.out
  13 +*.pid
  14 +*.gz
  15 +
  16 +pids
  17 +logs
  18 +results
  19 +
  20 +node_modules
  21 +npm-debug.log
  22 +
  23 +
  24 +# ------------------------------------------------------------------------------
  25 +# Nodemon
  26 +# ------------------------------------------------------------------------------
  27 +
  28 +.monitor
  29 +
  30 +
  31 +# ------------------------------------------------------------------------------
  32 +# JSHint
  33 +# ------------------------------------------------------------------------------
  34 +
  35 +jshint
  36 +
  37 +
  38 +# ------------------------------------------------------------------------------
  39 +# Numerous always-ignore extensions
  40 +# ------------------------------------------------------------------------------
  41 +
  42 +*.diff
  43 +*.err
  44 +*.orig
  45 +*.rej
  46 +*.swo
  47 +*.swp
  48 +*.vi
  49 +*~
  50 +*.sass-cache
  51 +
  52 +
  53 +# ------------------------------------------------------------------------------
  54 +# Files from other repository control systems
  55 +# ------------------------------------------------------------------------------
  56 +
  57 +.hg
  58 +.svn
  59 +.CVS
  60 +
  61 +
  62 +# ------------------------------------------------------------------------------
  63 +# Your ideas
  64 +# ------------------------------------------------------------------------------
  65 +
  66 +.idea
  67 +
  68 +
  69 +# ------------------------------------------------------------------------------
  70 +# OS X and other IDE's folder attributes
  71 +# ------------------------------------------------------------------------------
  72 +
  73 +.DS_Store
  74 +Thumbs.db
  75 +.cache
  76 +.project
  77 +.settings
  78 +.tmproj
  79 +*.esproj
  80 +nbproject
  81 +*.sublime-project
  82 +*.sublime-workspace
  83 +
  84 +
  85 +# ------------------------------------------------------------------------------
  86 +# Dreamweaver added files
  87 +# ------------------------------------------------------------------------------
  88 +
  89 +_notes
  90 +dwsync.xml
  91 +
  92 +
  93 +# ------------------------------------------------------------------------------
  94 +# Komodo
  95 +# ------------------------------------------------------------------------------
  96 +
  97 +*.komodoproject
  98 +.komodotools
  99 +
  100 +
  101 +# ------------------------------------------------------------------------------
  102 +# Add your custom excludes below
  103 +# ------------------------------------------------------------------------------
  104 +coverage/
  1 +{
  2 + "predef": [
  3 + "jasmine",
  4 + "spyOn",
  5 + "it",
  6 + "console",
  7 + "describe",
  8 + "expect",
  9 + "beforeEach",
  10 + "waits",
  11 + "waitsFor",
  12 + "runs",
  13 + "alert",
  14 + "confirm",
  15 + "Modernizr",
  16 + "impress",
  17 + "exports",
  18 + "self",
  19 + "define",
  20 + "google"
  21 + ],
  22 +
  23 + "node" : true,
  24 + "es5" : false,
  25 + "browser" : true,
  26 + "jquery": true,
  27 +
  28 + "boss" : false,
  29 + "curly": false,
  30 + "debug": false,
  31 + "devel": false,
  32 + "eqeqeq": true,
  33 + "evil": true,
  34 + "forin": false,
  35 + "immed": false,
  36 + "laxbreak": true,
  37 + "laxcomma": true,
  38 + "newcap": true,
  39 + "noarg": true,
  40 + "noempty": false,
  41 + "nonew": false,
  42 + "nomen": false,
  43 + "onevar": false,
  44 + "plusplus": false,
  45 + "regexp": false,
  46 + "undef": true,
  47 + "sub": true,
  48 + "strict": false,
  49 + "white": false,
  50 + "asi": true
  51 +}
  1 +language: node_js
  2 +node_js:
  3 +- '0.10'
  4 +script: npm run-script test-travis
  5 +after_script:
  6 +- npm install -g coveralls@2
  7 +- cat ./coverage/lcov.info | coveralls
  8 +- npm install -g codeclimate-test-reporter
  9 +- cat ./coverage/lcov.info | codeclimate
  10 +notifications:
  11 + webhooks:
  12 + urls:
  13 + - https://webhooks.gitter.im/e/84f254ceb77918b5a899
  14 + on_success: change
  15 + on_failure: always
  16 + on_start: false
  17 + hipchat:
  18 + rooms:
  19 + secure: SZAghgIWk6b8rpItdkjSY8UJtTW8E8AIYE5KkTGa9FiV0/q9kTHIAiqxBAQyIgF9IVsrO2NZdO9FrsiKjkSUPXbkonxtuqptl2c7bJLm2gUNmku2DfG0hn8TY50MXxGpiyEySoxcX4igQTzSZlJM81T0EI+L0PQJJXUKEJGuadY=
  20 + slack:
  21 + secure: 3uMd7XP25L2pPQ+4yzWmlSwsTBaiTERvTrA/e+Nz69g+nMpNviMVMRtLF/wlp01eehgNJ9InVdQ7Bq+MqS4eJPtqjVass7a3IplMIvjugMPlzw9eCEDxrxdbW649jw2esVKOj0eB40zhLrsV1AtxPyENIWeOn8uvVNjMLhiNiUs=
  22 +addons:
  23 + code_climate:
  24 + repo_token:
  25 +secure: BZej5nHBS+etVd/Xlq0uFRxgsbhh1njqvQZkBIec1kW6u+g9k16y1GVKGzlZsThlSUkIcmZ36N7MGstRNCJ5cuY5dLq914xqm8gZGvK15EzcdqroLPLOctkeJ6/KADqCQwI9L9bhP87kNTAcuNAljAJthiO3swM4fM7nLEQMbkQ=
  1 +The MIT License
  2 +
  3 +Copyright (c) 2014- Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  6 +
  7 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  8 +
  9 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1 +
  2 +# Igloo
  3 +
  4 +[![NPM version][npm-image]][npm-url]
  5 +[![Build Status][travis-image]][travis-url]
  6 +[![NPM downloads][npm-downloads]][npm-url]
  7 +[![Test Coverage][coveralls-image]][coveralls-url]
  8 +[![Static Analysis][codeclimate-image]][codeclimate-url]
  9 +[![MIT License][license-image]][license-url]
  10 +[![Gitter][gitter-image]][gitter-url]
  11 +
  12 +![Igloo][igloo-image]
  13 +
  14 +> Igloo is a library of components built with [electrolyte][electrolyte] to be used as building blocks for an app.
  15 +
  16 +Need to view documentation for a given component? See [Components](#components) below.
  17 +
  18 +
  19 +## Index
  20 +
  21 +* [Install](#install)
  22 +* [Usage](#usage)
  23 +* [Components](#components)
  24 +* [Tests](#tests)
  25 +* [Conventions](#conventions)
  26 +* [Contributors](#contributors)
  27 +* [Credits](#credits)
  28 +* [License](#license)
  29 +
  30 +
  31 +
  32 +
  33 +## Install
  34 +
  35 +**Please use [eskimo][eskimo] which has igloo built-in.**
  36 +
  37 +However you can use igloo in your own project by installing it.
  38 +
  39 +```bash
  40 +npm install -s igloo
  41 +```
  42 +
  43 +
  44 +## Usage
  45 +
  46 +You will need to first `require('igloo')`, define a "config" component, and then use `electrolyte`'s `IoC.loader` method to load `igloo`'s components ([listed below][#components]).
  47 +
  48 +> **Note that `igloo` requires you to have defined a "config" component (since `igloo`'s "setting" component requires it as a dependency). For example, [Eskimo][eskimo] defines a "config" component [here][eskimo-default-config].**
  49 +
  50 +```js
  51 +// boot/config.js
  52 +
  53 +exports = module.exports = function() {
  54 + return {
  55 + defaults: {
  56 + logger: {
  57 + console: true,
  58 + requests: true,
  59 + mongo: false,
  60 + file: false,
  61 + hipchat: false
  62 + },
  63 + output: {
  64 + handleExceptions: true,
  65 + colorize: true,
  66 + prettyPrint: false
  67 + }
  68 + },
  69 + development: {
  70 + server: {
  71 + port: 3000
  72 + }
  73 + },
  74 + production: {
  75 + server: {
  76 + port: 3080
  77 + },
  78 + logger: {
  79 + requests: false
  80 + },
  81 + output: {
  82 + colorize: false,
  83 + prettyPrint: false
  84 + }
  85 + }
  86 + }
  87 +}
  88 +
  89 +exports['@singleton'] = true
  90 +
  91 +```
  92 +
  93 +You can have a `boot/local.js` with local settings if you need something unversioned (and load it when the environment is `development` — default).
  94 +
  95 +```js
  96 +// boot/local.js
  97 +
  98 +var path = require('path')
  99 +var uploadsDir = path.join(__dirname, '..', 'uploads')
  100 +var maxAge = 30 * 24 * 60 * 60 * 1000
  101 +
  102 +exports = module.exports = function() {
  103 +
  104 + return {
  105 + uploadsDir: uploadsDir,
  106 + server: {
  107 + host: '0.0.0.0',
  108 + env: 'local',
  109 + port: 3003,
  110 + },
  111 + mongo: {
  112 + dbname: 'igloo-local',
  113 + },
  114 + redis: {
  115 + prefix: 'igloo-local',
  116 + maxAge: maxAge
  117 + }
  118 +
  119 + }
  120 +
  121 +}
  122 +
  123 +exports['@singleton'] = true
  124 +```
  125 +
  126 +
  127 +```js
  128 +// app.js
  129 +
  130 +var IoC = require('electrolyte')
  131 +var igloo = require('igloo')
  132 +var path = require('path')
  133 +var express = require('express')
  134 +var winstonRequestLogger = require('winston-request-logger')
  135 +
  136 +IoC.loader(IoC.node(path.join(__dirname, 'boot')))
  137 +IoC.loader('igloo', igloo)
  138 +
  139 +// logger component
  140 +var logger = IoC.create('igloo/logger')
  141 +logger.info('Hello world')
  142 +
  143 +// settings component
  144 +var settings = IoC.create('igloo/settings')
  145 +
  146 +// basic server
  147 +
  148 +var app = express()
  149 +
  150 +// winston request logger before everything else
  151 +// but only if it was enabled in settings
  152 +if (settings.logger.requests)
  153 + app.use(winstonRequestLogger.create(logger))
  154 +
  155 +app.get('/', function(req, res, next) {
  156 + res.send("It's cold outside, but warm in the igloo")
  157 +})
  158 +
  159 +app.listen(settings.server.port, function() {
  160 + logger.info('Server started on port %d', settings.server.port)
  161 +})
  162 +
  163 +```
  164 +
  165 +
  166 +## Components
  167 +
  168 +* [email](#email)
  169 +* [error-handler](#error-handler)
  170 +* [knex](#knex)
  171 +* [logger](#logger)
  172 +* [mongo](#mongo)
  173 +* [mongoose-plugin](#mongoose-plugin)
  174 +* [server](#server)
  175 +* [sessions](#sessions)
  176 +* [settings](#settings)
  177 +* [update-notifier](#update-notifier)
  178 +
  179 +### email
  180 +
  181 +> Returns a function which accepts templateName, locals, headers, transport, and callback arguments. This component uses
  182 +
  183 +[View source](lib/boot/email.js)
  184 +
  185 +```js
  186 +// example - email
  187 +
  188 +// TODO: finish this!
  189 +
  190 +// mention that transport defaults to settings.email.transport
  191 +
  192 +// mention that headers inherits settings.email.headers
  193 +
  194 +// but only if you don't set `headers.useDefaults: false`
  195 +
  196 +// note that is also uses `settings.email.templates` object
  197 +// which can have `dir` string and `options` object for node email-templates pkg
  198 +
  199 +// also document about having `transport` of `settings.email.transport` be an
  200 +// actual transporter already pre-created (possibly with plugins like html-to-text)
  201 +
  202 +```
  203 +
  204 +### error-handler
  205 +
  206 +> Returns [Express][express] route middleware for error handling.
  207 +
  208 +[View source](lib/boot/error-handler.js)
  209 +
  210 +```js
  211 +// example - error handler
  212 +
  213 +var _ = require('underscore')
  214 +var errorHandler = IoC.create('igloo/error-handler')
  215 +
  216 +app.post('/user', createUser, errorHandler)
  217 +
  218 +// you could also do:
  219 +// app.post('/user', createUser)
  220 +// app.use(errorHandler)
  221 +// but make sure that `app.use(errorHandler)`
  222 +// is the very last route middleware to be `use`'d
  223 +
  224 +function createUser(req, res, next) {
  225 +
  226 + if (!_.isString(req.body.name))
  227 + return next({
  228 + param: 'name',
  229 + message: 'User name is missing'
  230 + })
  231 +
  232 + // ... create a user in your db
  233 + res.send('You successfully created a user')
  234 +
  235 +}
  236 +
  237 +```
  238 +
  239 +
  240 +### knex
  241 +
  242 +> Returns a [Knex][knex] SQL database connection using `settings.knex`.
  243 +
  244 +[View source](lib/boot/knex.js)
  245 +
  246 +```js
  247 +// example - knex
  248 +
  249 +var knex = IoC.create('igloo/knex')
  250 +
  251 +app.get('/users', function(req, res, next) {
  252 + knex
  253 + .raw('SELECT * FROM USERS LIMIT 10')
  254 + .exec(function(err, results) {
  255 + if (err) return next(err)
  256 + res.send(results)
  257 + })
  258 +})
  259 +
  260 +```
  261 +
  262 +
  263 +### logger
  264 +
  265 +> Returns a [Winston][winston] logger object with pre-configured transports using `settings.logger` and `settings.output`.
  266 +
  267 +[View source](lib/boot/logger.js)
  268 +
  269 +```js
  270 +// example - logger
  271 +
  272 +var logger = IoC.create('igloo/logger')
  273 +
  274 +logger.info('Hello world')
  275 +
  276 +```
  277 +
  278 +
  279 +### mongo
  280 +
  281 +> Returns a [MongoDB][mongodb] NoSQL connection using `settings.logger`, `settings.output`, and `settings.mongo` and [Mongoose][mongoose] ORM.
  282 +
  283 +> TODO: Should we rename this to `igloo/mongoose`?
  284 +
  285 +[View source](lib/boot/mongo.js)
  286 +
  287 +```js
  288 +// example - mongo
  289 +
  290 +var validator = require('validator')
  291 +var mongoose = IoC.create('igloo/mongo')
  292 +
  293 +var User = new mongoose.Schema({
  294 + email: {
  295 + type: String,
  296 + required: true,
  297 + unique: true,
  298 + validate: validator.isEmail
  299 + }
  300 +})
  301 +
  302 +mongoose.model('User', User)
  303 +
  304 +```
  305 +
  306 +
  307 +### mongoose-plugin
  308 +
  309 +> Returns a [Mongoose][mongoose] plugin helper for common schema additions.
  310 +
  311 +[View source](lib/boot/mongoose-plugin.js)
  312 +
  313 +```js
  314 +// example - mongoose plugin
  315 +
  316 +var validator = require('validator')
  317 +var mongoose = IoC.create('igloo/mongo')
  318 +var mongoosePlugin = IoC.create('igloo/mongoose-plugin')
  319 +
  320 +var User = new mongoose.Schema({
  321 + email: {
  322 + type: String,
  323 + required: true,
  324 + unique: true,
  325 + validate: validator.isEmail
  326 + }
  327 +})
  328 +
  329 +User.plugin(mongoosePlugin)
  330 +
  331 +mongoose.model('User', User)
  332 +
  333 +```
  334 +
  335 +
  336 +### server
  337 +
  338 +> Returns a function which accepts a callback argument (currently no `err` argument is returned when this callback function is executed). This component starts either a [cluster][cluster] (with one thread per CPU) or a simple single-threaded instance using `app.listen`. This should be used with `app.phase` using [bootable][bootable], since bootable's "phases" (`app.phase`) method accepts this callback function as a standard argument when defining a "phase" (read more about bootable to understand this please). This component uses `settings.logger`, `settings.output`, and `settings.server`.
  339 +
  340 +[View source](lib/boot/server.js)
  341 +
  342 +```js
  343 +// example - server
  344 +
  345 +var bootable = require('bootable')
  346 +var express = require('express')
  347 +
  348 +var app = bootable(express())
  349 +
  350 +var server = IoC.create('igloo/server')
  351 +var logger = IoC.create('igloo/logger')
  352 +
  353 +// this should always be the last phase
  354 +app.phase(server)
  355 +
  356 +app.boot(function(err) {
  357 +
  358 + if (err) {
  359 + logger.error(err.message)
  360 + if (settings.showStack)
  361 + logger.error(err.stack)
  362 + process.exit(-1)
  363 + return
  364 + }
  365 +
  366 + logger.info('app booted')
  367 +
  368 +})
  369 +
  370 +```
  371 +
  372 +
  373 +### sessions
  374 +
  375 +> Returns a [Redis][redis] connection using `settings.logger`, `settings.output`, and `settings.redis` with [express-session][express-session] and [connect-redis][connect-redis].
  376 +
  377 +[View source](lib/boot/sessions.js)
  378 +
  379 +```js
  380 +// example - sessions
  381 +
  382 +var session = require('express-session')
  383 +var express = require('express')
  384 +
  385 +var app = express()
  386 +
  387 +var sessions = IoC.create('igloo/sessions')
  388 +var settings = IoC.create('igloo/settings')
  389 +
  390 +// add req.session cookie support
  391 +settings.session.store = sessions
  392 +app.use(session(settings.session))
  393 +
  394 +```
  395 +
  396 +
  397 +### settings
  398 +
  399 +> Returns a settings object using a "config" component defined and loaded by [electrolyte][electrolyte] in a location such as "boot/config" (see initial example at the top of this Readme).
  400 +
  401 +For a full config example file, see [Eskimo's default config][eskimo-default-config].
  402 +
  403 +[View source](lib/boot/settings.js)
  404 +
  405 +
  406 +### update-notifier
  407 +
  408 +> Returns nothing, as it is used for notifying an app of module updates using [update-notifier][update-notifier].
  409 +
  410 +> TODO: Look into why this isn't working per <https://github.com/yeoman/update-notifier/issues/25>
  411 +
  412 +[View source](lib/boot/update-notifier.js)
  413 +
  414 +
  415 +## Tests
  416 +
  417 +```bash
  418 +npm install -d
  419 +npm test
  420 +```
  421 +
  422 +
  423 +## Conventions
  424 +
  425 +See [nifty-conventions][nifty-conventions] for code guidelines, general project requirements, and git workflow.
  426 +
  427 +
  428 +## Contributors
  429 +
  430 +* Nick Baugh <niftylettuce@gmail.com>
  431 +* Adnan Ibrišimbegović <adibih@gmail.com>
  432 +* Bruno Bernardino <me@brunobernardino.com>
  433 +
  434 +
  435 +## Credits
  436 +
  437 +* [Igloo](http://thenounproject.com/term/igloo/26547/) by VALÈRE DAYAN from The Noun Project
  438 +* [ESKIMO IGLOO](http://www.colourlovers.com/palette/1933518/ESKIMO_IGLOO) (color palette)
  439 +
  440 +
  441 +## License
  442 +
  443 +[MIT][license-url]
  444 +
  445 +
  446 +[codeclimate-image]: http://img.shields.io/codeclimate/github/niftylettuce/igloo.svg?style=flat
  447 +[codeclimate-url]: https://codeclimate.com/github/niftylettuce/igloo
  448 +[license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat
  449 +[license-url]: LICENSE
  450 +[nifty-conventions]: https://github.com/niftylettuce/nifty-conventions
  451 +[npm-image]: http://img.shields.io/npm/v/igloo.svg?style=flat
  452 +[npm-url]: https://npmjs.org/package/igloo
  453 +[npm-downloads]: http://img.shields.io/npm/dm/igloo.svg?style=flat
  454 +[travis-url]: http://travis-ci.org/niftylettuce/igloo
  455 +[travis-image]: http://img.shields.io/travis/niftylettuce/igloo.svg?style=flat
  456 +[coveralls-image]: https://img.shields.io/coveralls/niftylettuce/igloo.svg?style=flat
  457 +[coveralls-url]: https://coveralls.io/r/niftylettuce/igloo?branch=master
  458 +[igloo-image]: https://filenode.s3.amazonaws.com/igloo.png
  459 +[eskimo]: https://github.com/niftylettuce/eskimo
  460 +[electrolyte]: https://github.com/jaredhanson/electrolyte
  461 +[knex]: http://knexjs.org
  462 +[express]: http://expressjs.com
  463 +[winston]: https://github.com/flatiron/winston
  464 +[mongodb]: https://github.com/mongodb/node-mongodb-native
  465 +[mongoose]: http://mongoosejs.com/
  466 +[bootable]: https://github.com/jaredhanson/bootable
  467 +[cluster]: http://nodejs.org/api/cluster.html
  468 +[redis]: http://redis.io
  469 +[express-session]: https://github.com/expressjs/session
  470 +[connect-redis]: https://github.com/visionmedia/connect-redis
  471 +[eskimo-default-config]: https://github.com/niftylettuce/eskimo/blob/master/boot/config.js
  472 +[update-notifier]: https://github.com/yeoman/update-notifier
  473 +[gitter-url]: https://gitter.im/niftylettuce/igloo
  474 +[gitter-image]: http://img.shields.io/badge/chat-online-brightgreen.svg?style=flat
  1 +// app.js
  2 +
  3 +var IoC = require('electrolyte')
  4 +var igloo = require('../')
  5 +var path = require('path')
  6 +var express = require('express')
  7 +var winstonRequestLogger = require('winston-request-logger')
  8 +
  9 +IoC.loader(IoC.node(path.join(__dirname, 'boot')))
  10 +IoC.loader('igloo', igloo)
  11 +
  12 +// logger component
  13 +var logger = IoC.create('igloo/logger')
  14 +logger.info('Hello world')
  15 +
  16 +// settings component
  17 +var settings = IoC.create('igloo/settings')
  18 +
  19 +// basic server
  20 +
  21 +var app = express()
  22 +
  23 +// winston request logger before everything else
  24 +// but only if it was enabled in settings
  25 +if (settings.logger.requests)
  26 + app.use(winstonRequestLogger.create(logger))
  27 +
  28 +app.get('/', function(req, res, next) {
  29 + res.send("It's cold outside, but warm in the igloo")
  30 +})
  31 +
  32 +app.listen(settings.port, function() {
  33 + logger.info('Server started on port %d', settings.port)
  34 +})
  1 +// boot/config.js
  2 +
  3 +exports = module.exports = function() {
  4 + return {
  5 + defaults: {
  6 + logger: {
  7 + console: true,
  8 + requests: true,
  9 + mongo: false,
  10 + file: false,
  11 + hipchat: false
  12 + },
  13 + output: {
  14 + handleExceptions: true,
  15 + colorize: true,
  16 + prettyPrint: false
  17 + }
  18 + },
  19 + development: {
  20 + port: 3000,
  21 + },
  22 + production: {
  23 + port: 3080,
  24 + requests: false,
  25 + output: {
  26 + colorize: false,
  27 + prettyPrint: false
  28 + }
  29 + }
  30 + }
  31 +}
  32 +
  33 +exports['@singleton'] = true
  1 +{
  2 + "name": "example",
  3 + "version": "0.0.1",
  4 + "dependencies": {
  5 + "electrolyte": "0.0.4",
  6 + "express": "^4.9.0",
  7 + "winston-request-logger": "^1.0.5"
  8 + }
  9 +}
  1 +
  2 +// # boot - email
  3 +
  4 +var nodemailer = require('nodemailer')
  5 +var emailTemplates = require('email-templates')
  6 +var _ = require('underscore')
  7 +
  8 +exports = module.exports = function(settings) {
  9 +
  10 + // check to make sure we defined a default email object
  11 + if (!_.isObject(settings.email))
  12 + throw new Error('Settings did not have an `email` object')
  13 +
  14 + // check to make sure settings.email.templates is defined
  15 + if (!_.isObject(settings.email.templates))
  16 + throw new Error('Settings did not have a `settings.email.templates` object')
  17 +
  18 + // check to make sure settings.email.templates.dir is defined
  19 + if (!_.isString(settings.email.templates.dir))
  20 + throw new Error('Settings did not have a `settings.email.templates.dir` string')
  21 +
  22 + var templateName, locals, headers, callback, transporter
  23 +
  24 + function renderTemplate(_templateName, _locals, _headers, transport, _callback) {
  25 +
  26 + templateName = _templateName
  27 + locals = _locals
  28 + headers = _headers
  29 + callback = _callback
  30 +
  31 + // add ability to override default transport defined in settings
  32 + if (_.isFunction(transport)) {
  33 +
  34 + // check to make sure we defined a default transport object
  35 + if (!_.isObject(settings.email.transport))
  36 + throw new Error('Settings did not have an `email.transport` object')
  37 +
  38 + callback = transport
  39 +
  40 + //
  41 + // if the transport defined in settings is an actual transport
  42 + // (as opposed to just a config object) then we need to use that
  43 + // instead of creating a transport - to do so we'll look for the
  44 + // `.transporter` property that gets added to all transport objects
  45 + // <https://github.com/andris9/Nodemailer/blob/master/src/nodemailer.js#L39>
  46 + //
  47 + // the reason we have this support added is in case users add plugins to
  48 + // their transporter, for example the `nodemailer-html-to-text` plugin
  49 + // <https://github.com/andris9/nodemailer-html-to-text>
  50 + //
  51 + if (_.has(settings.email.transport, 'transporter'))
  52 + transporter = settings.email.transport
  53 + else
  54 + transporter = nodemailer.createTransport(settings.email.transport)
  55 +
  56 + } else {
  57 + transporter = nodemailer.createTransport(transport)
  58 + }
  59 +
  60 + if (_.isObject(settings.email.templates.options))
  61 + return emailTemplates(settings.email.templates.dir, settings.email.templates.options, createTemplate)
  62 +
  63 + emailTemplates(settings.email.templates.dir, createTemplate)
  64 +
  65 + }
  66 +
  67 + function createTemplate(err, template) {
  68 + if (err) throw err
  69 + template(templateName, locals, createEmail(headers))
  70 + }
  71 +
  72 + function createEmail(headers) {
  73 + return function(err, html, text) {
  74 + if (err) return callback(err)
  75 + // if we defined a default headers object, then inherit it here
  76 + // but only if we didn't set `useDefaults` to false in the headers
  77 + if (_.isObject(settings.email.headers) && !headers.useDefaults)
  78 + headers = _.defaults(headers, settings.email.headers)
  79 + if (_.isString(html))
  80 + headers.html = html
  81 + if (_.isString(text))
  82 + headers.text = text
  83 + transporter.sendMail(headers, callback)
  84 + }
  85 + }
  86 +
  87 + return renderTemplate
  88 +
  89 +}
  90 +
  91 +exports['@singleton'] = true
  92 +exports['@require'] = [ 'igloo/settings' ]
  1 +
  2 +// # boot - error handler
  3 +
  4 +var mergeDefaults = require('merge-defaults')
  5 +var _ = require('underscore')
  6 +var _str = require('underscore.string')
  7 +_.mixin(_str.exports())
  8 +var util = require('util')
  9 +
  10 +exports = module.exports = function(logger, settings) {
  11 +
  12 + return function(err, req, res, next) {
  13 +
  14 + // set default error status code
  15 + res.statusCode = (_.isNumber(err.status)) ? err.status : 500
  16 +
  17 + if (!_.isString(err.message))
  18 + err.message = 'An unknown error has occured, please try again'
  19 +
  20 + if (_.isString(err.name) && err.name === 'AuthenticationError')
  21 + err.message = 'Invalid login attempt, please try again'
  22 +
  23 + if (_.isObject(err) && _.isNumber(err.code) && err.code === 11000) {
  24 +
  25 + // <https://github.com/LearnBoost/mongoose/issues/2129>
  26 + var field = err.message.split('index: ')[1].split('.$')[1]
  27 + // now we have `email_1 dup key`
  28 + field = field.split(' dup key')[0]
  29 + field = field.substring(0, field.lastIndexOf('_'))
  30 + err.message = util.format('Duplicate %s already exists in database, try making a more unique value', field)
  31 + err.param = field
  32 + }
  33 +
  34 + // if we pass an error object, then we want to simply return the message...
  35 + // if we pass an object, then we want to do a stack trace, and then return the object + stack
  36 + var error = {}
  37 +
  38 + // set error type
  39 + error.type = res.statusCode < 500 ? 'invalid_request_error' : 'api_error'
  40 +
  41 + if (_.isString(err.param)) {
  42 + error.type = 'invalid_request_error'
  43 + if (res.statusCode === 500)
  44 + res.statusCode = 400
  45 + }
  46 +
  47 + /*
  48 + error.type = _.isString(err.param) ? 'invalid_request_error' : 'api_error'
  49 +
  50 + if (error.type === 'invalid_request_error' && res.statusCode === 500)
  51 + res.statusCode = 400
  52 + */
  53 +
  54 + // set error message and stack trace
  55 + if (util.isError(err)) {
  56 + error.message = err.message
  57 + } else {
  58 + _.extend(error, err)
  59 + }
  60 +
  61 + // set status code for BadRequestError
  62 + if (_.isString(err.name) && err.name === 'BadRequestError') {
  63 + error.type = 'invalid_request_error'
  64 + res.statusCode = 400
  65 + delete error.name
  66 + }
  67 +
  68 + if (settings.showStack)
  69 + error.stack = _.isUndefined(err.stack) ? new Error(err.message).stack : err.stack
  70 +
  71 + // set error level
  72 + var level = (res.statusCode < 500) ? 'warn' : 'error'
  73 +
  74 + // if we have a mongoose validation err
  75 + // then we know to output all the errors
  76 + if (_.isObject(err.errors) && !_.isEmpty(err.errors)) {
  77 + var messages = []
  78 + _.each(err.errors, function(errMsg) {
  79 + // <https://github.com/syntagma/mongoose-error-helper/blob/master/lib/mongoose-error-helper.js>
  80 + // TODO: add support for enum, min, and max?
  81 + if (_.isString(errMsg.type) && errMsg.type === 'required' && _.isString(errMsg.path)) {
  82 + messages.push(util.format('%s is required', _.humanize(errMsg.path)))
  83 + } else if (_.isString(errMsg.message)) {
  84 + messages.push(errMsg.message)
  85 + }
  86 + })
  87 + if (!_.isEmpty(messages)) {
  88 + error.message = messages.join(', ')
  89 + }
  90 + }
  91 +
  92 + res.format({
  93 + text: function() {
  94 + res.send(error.message)
  95 + },
  96 + html: function() {
  97 + // set error back to warning if it was warn
  98 + // logger level type = "warn"
  99 + // req.flash messages type = "warning"
  100 + req.flash(level === 'warn' ? 'warning' : level, error.message)
  101 + res.redirect('back')
  102 + },
  103 + json: function() {
  104 + res.json({ error: error })
  105 + }
  106 + })
  107 +
  108 + if (_.isObject(req.log)) {
  109 + req.log.response_time = new Date().getTime() - req.log.response_time
  110 + req.log.status = res.statusCode
  111 + req.log.response_type = res.get('Content-Type')
  112 + req.log.response_body = error
  113 + } else {
  114 + req.log = error
  115 + }
  116 +
  117 + logger[level](error.message, req.log)
  118 +
  119 + }
  120 +
  121 +}
  122 +
  123 +exports['@singleton'] = true
  124 +exports['@require'] = [ 'igloo/logger', 'igloo/settings' ]
  1 +
  2 +// # boot - knex
  3 +
  4 +exports = module.exports = function(logger, settings) {
  5 + var knex = require('knex')(settings.knex)
  6 + knex.on('error', logger.error)
  7 + return knex
  8 +}
  9 +
  10 +exports['@singleton'] = true
  11 +exports['@require'] = [ 'igloo/logger', 'igloo/settings' ]
  1 +
  2 +// # boot - logger
  3 +
  4 +var _ = require('underscore')
  5 +var mergeDefaults = require('merge-defaults')
  6 +var winston = require('winston')
  7 +var winstonMongoDB = require('winston-mongodb')
  8 +var winstonHipchat = require('winston-hipchat')
  9 +var slackWinston = require('slack-winston')
  10 +
  11 +exports = module.exports = function(settings) {
  12 +
  13 + if (!_.isObject(settings.logger))
  14 + throw new Error('Settings did not have a `logger` object')
  15 +
  16 + if (!_.isObject(settings.output))
  17 + throw new Error('Settings did not have a `output` object')
  18 +
  19 + var transports = []
  20 +
  21 + if (settings.logger['console'])
  22 + transports.push(
  23 + new winston.transports.Console(
  24 + settings.output
  25 + )
  26 + )
  27 +
  28 + if (settings.logger.mongo)
  29 + transports.push(
  30 + new winstonMongoDB.MongoDB(
  31 + mergeDefaults(
  32 + settings.output,
  33 + settings.mongo
  34 + )
  35 + )
  36 + )
  37 +
  38 + if (settings.logger.file)
  39 + transports.push(
  40 + new winston.transports.File(
  41 + mergeDefaults(
  42 + settings.output,
  43 + settings.logger.file
  44 + )
  45 + )
  46 + )
  47 +
  48 + if (settings.logger.hipchat)
  49 + transports.push(
  50 + new winstonHipchat.Hipchat(settings.hipchat)
  51 + )
  52 +
  53 + if (settings.logger.slack)
  54 + transports.push(
  55 + new slackWinston.Slack(settings.slack)
  56 + )
  57 +
  58 + var logger = new winston.Logger({
  59 + transports: transports
  60 + })
  61 +
  62 + if (settings.server.env === 'development')
  63 + logger = expandErrors(logger)
  64 +
  65 + return logger
  66 +
  67 + // Extend a winston by making it expand errors when passed in as the
  68 + // second argument (the first argument is the log level).
  69 + // <https://gist.github.com/johndgiese/59bd96360ce411042294>
  70 + // <https://gist.github.com/getvega/6211610>
  71 + function expandErrors(logger) {
  72 + var oldLogFunc = logger.log
  73 + logger.log = function() {
  74 + var args = Array.prototype.slice.call(arguments, 0)
  75 + if (args.length >= 2 && args[1] instanceof Error) {
  76 + args[1] = args[1].stack
  77 + }
  78 + return oldLogFunc.apply(this, args)
  79 + };
  80 + return logger
  81 + }
  82 +
  83 +
  84 +}
  85 +
  86 +exports['@singleton'] = true
  87 +exports['@require'] = [ 'igloo/settings' ]
  1 +
  2 +// # boot - mongo
  3 +
  4 +var mongoose = require('mongoose')
  5 +var _ = require('underscore')
  6 +
  7 +exports = module.exports = function(logger, settings) {
  8 +
  9 + var connection
  10 +
  11 + if (_.isString(settings.mongo.uri)) {
  12 + connection = mongoose.createConnection(settings.mongo.uri, settings.mongo.opts)
  13 + } else {
  14 + connection = mongoose.createConnection(
  15 + settings.mongo.host,
  16 + settings.mongo.dbname,
  17 + settings.mongo.port,
  18 + settings.mongo.opts
  19 + )
  20 + }
  21 +
  22 + connection.on('error', function(err) {
  23 + logger.error('mongo connection error: %s', err.message || err)
  24 + })
  25 +
  26 + connection.on('open', function() {
  27 + logger.info('mongo connection opened')
  28 + })
  29 +
  30 + connection = _.defaults(connection, mongoose)
  31 +
  32 + return connection
  33 +
  34 +}
  35 +
  36 +exports['@singleton'] = true
  37 +exports['@require'] = [ 'igloo/logger', 'igloo/settings' ]
  1 +
  2 +// # boot - mongoose plugin
  3 +
  4 +var _ = require('underscore')
  5 +var jsonSelect = require('mongoose-json-select')
  6 +
  7 +exports = module.exports = function() {
  8 +
  9 + return function(Schema) {
  10 +
  11 + // NOTE: To allow `.sort('-created_at')` to work
  12 + // we need to have these as actual paths
  13 + Schema.add({
  14 + updated_at: Date,
  15 + created_at: Date
  16 + })
  17 +
  18 + Schema.pre('save', function(next) {
  19 +
  20 + var that = this
  21 +
  22 + that.updated_at = _.isUndefined(that.created_at) ? that._id.getTimestamp() : new Date();
  23 +
  24 + if (!that.created_at)
  25 + that.created_at = that._id.getTimestamp()
  26 +
  27 + next()
  28 + })
  29 +
  30 + Schema.set('toObject', {
  31 + virtuals: true,
  32 + getters: true
  33 + })
  34 +
  35 + Schema.set('toJSON', {
  36 + virtuals: true,
  37 + getters: true
  38 + })
  39 +
  40 + Schema.plugin(jsonSelect, '-_id')
  41 +
  42 + return Schema
  43 +
  44 + }
  45 +
  46 +}
  47 +
  48 +exports['@singleton'] = true
  1 +
  2 +// # boot - server
  3 +
  4 +// Inspired by `bixby-server` by Jared Hanson
  5 +
  6 +var cluster = require('cluster')
  7 +var os = require('os')
  8 +var path = require('path')
  9 +
  10 +exports = module.exports = function(logger, settings) {
  11 +
  12 + return function(done) {
  13 +
  14 + if (cluster.isMaster && settings.server.cluster) {
  15 +
  16 + var size = settings.server.cluster.size || os.cpus().length
  17 +
  18 + logger.info('creating cluster with %d workers', size)
  19 +
  20 + for (var i=0; i<size; i++) {
  21 + logger.info('spawning worker #%d', i + 1)
  22 + cluster.fork()
  23 + }
  24 +
  25 + cluster.on('fork', function(worker) {
  26 + logger.info('worker #%s with pid %d spawned', worker.id, worker.process.pid)
  27 + })
  28 +
  29 + cluster.on('online', function(worker) {
  30 + logger.info('worker #%s with pid %d online', worker.id, worker.process.pid)
  31 + })
  32 +
  33 + cluster.on('listening', function(worker, addr) {
  34 + logger.info('worker #%s with pid %d listening on %s:%d', worker.id, worker.process.pid, addr.address, addr.port)
  35 + })
  36 +
  37 + cluster.on('disconnect', function(worker) {
  38 + logger.info('worker #%s with pid %d disconnected', worker.id, worker.process.pid)
  39 + })
  40 +
  41 + cluster.on('exit', function(worker, code, signal) {
  42 + logger.error('worker #%s with pid %d exited with code/signal', worker.id, worker.process.pid, signal, code)
  43 + if (worker.suicide) return
  44 + logger.info('worker #%s restarting', worker.id)
  45 + cluster.fork()
  46 + })
  47 +
  48 + } else {
  49 +
  50 + this.httpServer = this.server.listen(settings.server.port, settings.server.host, function() {
  51 + var addr = this.address()
  52 + logger.info('app listening on %s:%d', addr.address, addr.port)
  53 + done()
  54 + })
  55 +
  56 + }
  57 +
  58 + }
  59 +
  60 +}
  61 +
  62 +exports['@singleton'] = true
  63 +exports['@require'] = [ 'igloo/logger', 'igloo/settings' ]
  1 +
  2 +// # boot - sessions
  3 +
  4 +var session = require('express-session')
  5 +var RedisStore = require('connect-redis')(session)
  6 +
  7 +exports = module.exports = function(logger, settings) {
  8 +
  9 + var connection = new RedisStore(settings.redis)
  10 +
  11 + connection.on('error', function(err) {
  12 + logger.error('redis connection error: %s', err.message || err)
  13 + })
  14 +
  15 + connection.on('connect', function() {
  16 + logger.info('redis connected')
  17 + })
  18 +
  19 + return connection
  20 +
  21 +}
  22 +
  23 +exports['@singleton'] = true
  24 +exports['@require'] = [ 'igloo/logger', 'igloo/settings' ]
  1 +
  2 +// # boot - settings
  3 +
  4 +var _ = require('underscore')
  5 +var util = require('util')
  6 +var mergeDefaults = require('merge-defaults')
  7 +
  8 +exports = module.exports = function(IoC, config) {
  9 +
  10 + var settings = {}
  11 +
  12 + var env = process.env.NODE_ENV || 'development'
  13 +
  14 + if (!_.isObject(config[env]))
  15 + throw new Error(util.format('Unknown environment %s', env))
  16 +
  17 + if (env === 'development') {
  18 + try {
  19 + var local = IoC.create('local')
  20 + if (_.isObject(local))
  21 + mergeDefaults(settings, local)
  22 + } catch(e) {
  23 + }
  24 + }
  25 +
  26 + mergeDefaults(settings, config[env], config.defaults)
  27 +
  28 + return settings
  29 +
  30 +}
  31 +
  32 +exports['@singleton'] = true
  33 +exports['@require'] = [ '$container', 'config' ]
  1 +
  2 +// # boot - update notifier
  3 +
  4 +var _ = require('underscore')
  5 +var updateNotifier = require('update-notifier')
  6 +
  7 +exports = module.exports = function(logger, settings) {
  8 +
  9 + return function() {
  10 +
  11 + // check for updates to all packages when not in production
  12 + if (settings.updateNotifier.enabled)
  13 + _.each(settings.pkg.dependencies, function(version, name) {
  14 + var notifier = updateNotifier({
  15 + packageName: name,
  16 + packageVersion: version,
  17 + optOut: settings.updateNotifier.dependencies[name] || false,
  18 + updateCheckInterval: settings.updateNotifier.updateCheckInterval | 1000 * 60 * 60, // hourly
  19 + updateCheckTimeout: settings.updateNotifier.updateCheckTimeout | 1000 * 20 // 20 seconds
  20 + })
  21 + if (_.isUndefined(notifier.update) || !_.isString(notifier.update.latest)) return
  22 + logger.warn(
  23 + '%s of %s released (current: %s), run `npm install -S %s@%s` to upgrade',
  24 + notifier.update.latest,
  25 + name,
  26 + version,
  27 + name,
  28 + notifier.update.latest
  29 + )
  30 + })
  31 +
  32 + }
  33 +
  34 +}
  35 +
  36 +exports['@singleton'] = true
  37 +exports['@require'] = [ 'igloo/logger', 'igloo/settings' ]
  1 +
  2 +// igloo
  3 +// Copyright (c) 2014- Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com)
  4 +// MIT Licensed
  5 +
  6 +// Igloo is a lightweight, fast, and minimal framework for rapid development
  7 +
  8 +// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
  9 +// * Source: <https://github.com/niftylettuce/igloo>
  10 +
  11 +// Special thanks to TJ Holowaychuk for being an inspiration,
  12 +// Jared Hanson for introducing me to `electrolyte`,
  13 +// and many others which I'd love to credit...
  14 +
  15 +// # igloo
  16 +
  17 +var path = require('path')
  18 +
  19 +module.exports = function(id) {
  20 + return require(path.join(__dirname, 'boot', id))
  21 +}
  1 +{
  2 + "name": "igloo",
  3 + "version": "0.0.9",
  4 + "description": "Igloo is a lightweight, fast, and minimal framework for rapid development",
  5 + "main": "./lib",
  6 + "engines": {
  7 + "iojs": "2.0.x",
  8 + "node": ">=0.12.x"
  9 + },
  10 + "repository": {
  11 + "type": "git",
  12 + "url": "git://github.com/niftylettuce/igloo.git"
  13 + },
  14 + "author": "Nick Baugh <niftylettuce@gmail.com>",
  15 + "contributors": [
  16 + {
  17 + "name": "Nick Baugh",
  18 + "email": "niftylettuce@gmail.com"
  19 + },
  20 + {
  21 + "name": "Adnan Ibrišimbegović",
  22 + "email": "adibih@gmail.com"
  23 + },
  24 + {
  25 + "name": "Bruno Bernardino",
  26 + "email": "me@brunobernardino.com"
  27 + }
  28 + ],
  29 + "bugs": {
  30 + "url": "https://github.com/niftylettuce/igloo/issues"
  31 + },
  32 + "license": "MIT",
  33 + "homepage": "https://github.com/niftylettuce/igloo",
  34 + "dependencies": {
  35 + "bootable": "^0.2.4",
  36 + "chalk": "^1.0.0",
  37 + "commander": "^2.8.1",
  38 + "connect-redis": "~2.3.0",
  39 + "email-templates": "^1.2.1",
  40 + "express-session": "^1.11.2",
  41 + "merge-defaults": "^0.2.1",
  42 + "nodemailer": "^1.3.4",
  43 + "slack-winston": "^0.0.2",
  44 + "to-camel-case": "^0.2.1",
  45 + "underscore": "^1.8.3",
  46 + "underscore.string": "^3.0.3",
  47 + "update-notifier": "^0.5.0",
  48 + "winston": "git://github.com/niftylettuce/winston",
  49 + "winston-hipchat": "^0.1.3",
  50 + "winston-mongodb": "~1.1.1"
  51 + },
  52 + "devDependencies": {
  53 + "chai": "^2.3.0",
  54 + "istanbul": "^0.3.14",
  55 + "knex": "^0.8.5",
  56 + "mocha": "^2.2.5",
  57 + "mongoose": "~4.0.3",
  58 + "mongoose-json-select": "^0.2.1"
  59 + },
  60 + "scripts": {
  61 + "prepublish": "npm prune",
  62 + "test": "mocha --reporter spec --bail --check-leaks --require test/support/should test/",
  63 + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks --require test/support/should test/",
  64 + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks --require test/support/should test/"
  65 + }
  66 +}
  1 +
  2 +describe('igloo', function() {
  3 +
  4 + it('should return function', function() {
  5 + var igloo = require('../')
  6 + igloo.should.be.a('function')
  7 + })
  8 +
  9 + /*
  10 + describe('error-handler', function() {
  11 +
  12 + })
  13 +
  14 + describe('knex', function() {
  15 +
  16 + })
  17 +
  18 + describe('logger', function() {
  19 +
  20 + })
  21 +
  22 + describe('mongo', function() {
  23 +
  24 + })
  25 +
  26 + describe('mongoose-plugin', function() {
  27 +
  28 + })
  29 +
  30 + describe('server', function() {
  31 +
  32 + })
  33 +
  34 + describe('sessions', function() {
  35 +
  36 + })
  37 +
  38 + describe('settings', function() {
  39 +
  40 + })
  41 +
  42 + describe('update-notifier', function() {
  43 +
  44 + })
  45 + */
  46 +
  47 +})
  1 +var chai = require('chai')
  2 +chai.should()
注册登录 后发表评论