{"id":13571848,"url":"https://github.com/1602/compound","last_synced_at":"2025-05-15T04:06:53.563Z","repository":{"id":1293487,"uuid":"1234795","full_name":"1602/compound","owner":"1602","description":"MVC framework. Built on Node.JS. Works on server and browser.","archived":false,"fork":false,"pushed_at":"2017-05-29T19:52:48.000Z","size":2236,"stargazers_count":1598,"open_issues_count":47,"forks_count":182,"subscribers_count":65,"default_branch":"master","last_synced_at":"2025-04-14T05:55:38.387Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://compoundjs.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1602.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-01-09T09:48:00.000Z","updated_at":"2025-03-06T13:38:52.000Z","dependencies_parsed_at":"2022-08-16T13:00:18.346Z","dependency_job_id":null,"html_url":"https://github.com/1602/compound","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1602%2Fcompound","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1602%2Fcompound/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1602%2Fcompound/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1602%2Fcompound/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1602","download_url":"https://codeload.github.com/1602/compound/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270646,"owners_count":22042859,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T14:01:07.179Z","updated_at":"2025-05-15T04:06:48.549Z","avatar_url":"https://github.com/1602.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"About [\u003cimg src=\"https://secure.travis-ci.org/1602/compound.png\" /\u003e](http://travis-ci.org/#!/1602/compound) [![Code Climate](https://codeclimate.com/repos/550d7c5c69568077bd01488f/badges/e7b56f30b1a89120a1f5/gpa.svg)](https://codeclimate.com/repos/550d7c5c69568077bd01488f/feed)\n=====\n\n\u003cimg src=\"https://raw.github.com/1602/compound/master/templates/public/images/compound.png\" /\u003e\n\nCompoundJS - MVC framework for NodeJS\u0026trade;. It allows you to build web application in minutes.\n\nCompound modules now available at https://github.com/compoundjs\n\nFull documentation is available at http://compoundjs.com/ and using man(1).\n\nInstallation\n============\n\nOption 1: npm\n\n```sh\nsudo npm install compound -g\n```\n\nOption 2: GitHub\n\n```sh\nsudo npm install 1602/compound\n```\n\nUsage\n=====\n\n```sh\n# initialize app\ncompound init blog \u0026\u0026 cd blog\nnpm install\n\n# generate scaffold\ncompound generate crud post title content published:boolean\n\n# run server on port 3000\ncompound s 3000\n\n# visit app\nopen http://localhost:3000/posts\n```\n\nShort functionality review\n==========================\n\nCLI tool\n--------\n\n```\n$ compound help\nUsage: compound command [argument(s)]\n\nCommands:\n  h,  help                     Display usage information\n  i,  init                     Initialize compound app\n  g,  generate [smth]          Generate something awesome\n  r,  routes [filter]          Display application routes\n  c,  console                  Debug console\n  s,  server [port]            Run compound server\n  install [module]             Installs a compound module and patches the autoload file\n```\n\n#### compound init [appname][ option(s)]\n\n```\noptions:\n  --coffee                 # Default: no coffee by default\n  --tpl jade|ejs           # Default: ejs\n  --css sass|less|stylus   # Default: stylus\n  --db redis|mongodb|nano|mysql|sqlite3|postgres\n                           # Default: memory\n```\n\n#### compound generate smth\n\nsmth = generator name (controller, model, scaffold, ...can be extended via plugins)\n\nmore information about generators available here:\nhttp://compoundjs.github.com/generators\n\n#### compound server 8000\n\nequals to `PORT=8000 node server` - run server on port `8000`\n\n#### compound console\n\nrun debugging console (see details below)\n\n#### compound routes\n\nprint routes map (see details below)\n\n\nDirectory structure\n-------------------\n\nOn initialization directories tree generated, like that:\n\n```\n.\n|-- app\n|   |-- assets\n|   |   |-- coffeescripts\n|   |   |   `-- application.coffee\n|   |   `-- stylesheets\n|   |       `-- application.styl\n|   |-- controllers\n|   |   |-- admin\n|   |   |   |-- categories_controller.js\n|   |   |   |-- posts_controller.js\n|   |   |   `-- tags_controller.js\n|   |   |-- comments_controller.js\n|   |   `-- posts_controller.js\n|   |-- models\n|   |   |-- category.js\n|   |   |-- post.js\n|   |   `-- tag.js\n|   |-- tools\n|   |   `-- database.js\n|   |-- views\n|   |   |-- admin\n|   |   |   `-- posts\n|   |   |       |-- edit.ejs\n|   |   |       |-- index.ejs\n|   |   |       |-- new.ejs\n|   |   |-- layouts\n|   |   |   `-- application_layout.ejs\n|   |   |-- partials\n|   |   `-- posts\n|   |       |-- index.ejs\n|   |       `-- show.ejs\n|   `-- helpers\n|       |-- admin\n|       |   |-- posts_helper.js\n|       |   `-- tags_helper.js\n|       `-- posts_helper.js\n`-- config\n    |-- database.json\n    |-- routes.js\n    |-- tls.cert\n    `-- tls.key\n```\n\nHTTPS Support\n-------------\n\nJust place your key and cert into config directory, compound will use it.\nDefault names for keys are `tls.key` and `tls.cert`, but you can store in in another place, in that case just pass filenames to createServer function:\n`server.js`\n\n```js\nrequire('compound').createServer({\n    key: fs.readFileSync('/tmp/tls.key').toString(),\n    cert: fs.readFileSync('/tmp/tls.cert').toString()\n});\n```\n\nFew helpful commands:\n\n```sh\n# generate private key\nopenssl genrsa -out /tmp/tls.key\n# generate cert\nopenssl req -new -x509 -key /tmp/tls.key  -out /tmp/tls.cert -days 1095 -batch\n```\n\nRouting\n-------\n\nNow we do not have to tediously describe REST routes for each resource, enough to write in `config/routes.js` code like this:\n\n```js\nexports.routes = function (map) {\n    map.resources('posts', function (post) {\n        post.resources('comments');\n    });\n};\n```\n\ninstead of:\n\n```js\nvar ctl = require('./lib/posts_controller.js');\napp.get('/posts/new.:format?', ctl.new);\napp.get('/posts.:format?', ctl.index);\napp.post('/posts.:format?', ctl.create);\napp.get('/posts/:id.:format?', ctl.show);\napp.put('/posts/:id.:format?', ctl.update);\napp.delete('/posts/:id.:format?', ctl.destroy);\napp.get('/posts/:id/edit.:format?', ctl.edit);\n\nvar com_ctl = require('./lib/comments_controller.js');\napp.get('/posts/:post_id/comments/new.:format?', com_ctl.new);\napp.get('/posts/:post_id/comments.:format?', com_ctl.index);\napp.post('/posts/:post_id/comments.:format?', com_ctl.create);\napp.get('/posts/:post_id/comments/:id.:format?', com_ctl.show);\napp.put('/posts/:post_id/comments/:id.:format?', com_ctl.update);\napp.delete('/posts/:post_id/comments/:id.:format?', com_ctl.destroy);\napp.get('/posts/:post_id/comments/:id/edit.:format?', com_ctl.edit);\n```\n\nand you can more finely tune the resources to specify certain actions, middleware, and other. Here are example routes for [my blog][1]:\n\n```js\nexports.routes = function (map) {\n    map.get('/', 'posts#index');\n    map.get(':id', 'posts#show');\n    map.get('sitemap.txt', 'posts#map');\n\n    map.namespace('admin', function (admin) {\n        admin.resources('posts', {middleware: basic_auth, except: ['show']}, function (post) {\n            post.resources('comments');\n            post.get('likes', 'posts#likes')\n        });\n    });\n};\n```\n\nsince version 0.2.0, it is possible to use generic routes:\n\n```js\nexports.routes = function (map) {\n    map.get(':controller/:action/:id');\n    map.all(':controller/:action');\n};\n```\n\nif you have `custom_controller` with `test` action inside it you can now do:\n\n```\nGET /custom/test\nPOST /custom/test\nGET /custom/test/1 // also sets params.id to 1\n```\n\nfor debugging routes described in `config/routes.js` you can use `compound routes` command:\n\n```\n$ compound routes\n               GET    /                               posts#index\n               GET    /:id                            posts#show\n   sitemap.txt GET    /sitemap.txt                    posts#map\n    adminPosts GET    /admin/posts.:format?           admin/posts#index\n    adminPosts POST   /admin/posts.:format?           admin/posts#create\n  newAdminPost GET    /admin/posts/new.:format?       admin/posts#new\n editAdminPost GET    /admin/posts/:id/edit.:format?  admin/posts#edit\n     adminPost DELETE /admin/posts/:id.:format?       admin/posts#destroy\n     adminPost PUT    /admin/posts/:id.:format?       admin/posts#update\nlikesAdminPost PUT    /admin/posts/:id/likes.:format? admin/posts#likes\n```\n\nFilter by method:\n\n```\n$ compound routes GET\n               GET    /                               posts#index\n               GET    /:id                            posts#show\n   sitemap.txt GET    /sitemap.txt                    posts#map\n    adminPosts GET    /admin/posts.:format?           admin/posts#index\n  newAdminPost GET    /admin/posts/new.:format?       admin/posts#new\n editAdminPost GET    /admin/posts/:id/edit.:format?  admin/posts#edit\n```\n\nFilter by helper name:\n\n```\n$ compound routes Admin\n  newAdminPost GET    /admin/posts/new.:format?       admin/posts#new\n editAdminPost GET    /admin/posts/:id/edit.:format?  admin/posts#edit\nlikesAdminPost PUT    /admin/posts/:id/likes.:format? admin/posts#likes\n```\n\n\nHelpers\n-------\n\nIn addition to regular helpers `linkTo`, `formFor`, `javascriptIncludeTag`, `formFor`, etc. there are also helpers for routing: each route generates a helper method that can be invoked in a view:\n\n```html\n\u003c%- link_to(\"New post\", newAdminPost) %\u003e\n\u003c%- link_to(\"New post\", editAdminPost(post)) %\u003e\n```\n\ngenerates output:\n\n```html\n\u003ca href=\"/admin/posts/new\"\u003eNew post\u003c/a\u003e\n\u003ca href=\"/admin/posts/10/edit\"\u003eNew post\u003c/a\u003e\n```\n\nControllers\n-----------\n\nThe controller is a module containing the declaration of actions such as this:\n\n```js\nbeforeFilter(loadPost, {only: ['edit', 'update', 'destroy']});\n\naction('index', function () {\n    Post.allInstances({order: 'created_at'}, function (collection) {\n        render({ posts: collection });\n    });\n});\n\naction('create', function () {\n    Post.create(req.body, function () {\n        redirect(pathTo.adminPosts);\n    });\n});\n\naction('new', function () {\n    render({ post: new Post });\n});\n\naction('edit', function () {\n    render({ post: request.post });\n});\n\naction('update', function () {\n    request.post.save(req.locale, req.body, function () {\n        redirect(pathTo.adminPosts);\n    });\n});\n\nfunction loadPost () {\n    Post.find(req.params.id, function () {\n        request.post = this;\n        next();\n    });\n}\n```\n\n## Generators ##\n\nCompound offers several built-in generators: for a model, controller and for\ninitialization. Can be invoked as follows:\n\n```js\ncompound generate [what] [params]\n```\n\n`what` can be `model`, `controller` or `scaffold`. Example of controller generation:\n\n```\n$ compound generate controller admin/posts index new edit update\nexists  app/\nexists  app/controllers/\ncreate  app/controllers/admin/\ncreate  app/controllers/admin/posts_controller.js\ncreate  app/helpers/\ncreate  app/helpers/admin/\ncreate  app/helpers/admin/posts_helper.js\nexists  app/views/\ncreate  app/views/admin/\ncreate  app/views/admin/posts/\ncreate  app/views/admin/posts/index.ejs\ncreate  app/views/admin/posts/new.ejs\ncreate  app/views/admin/posts/edit.ejs\ncreate  app/views/admin/posts/update.ejs\n```\n\nCurrently it generates only `*.ejs` views\n\nModels\n------\n\nCheckout [JugglingDB][2] docs to see how to work with models.\n\nCompoundJS Event model\n----------------------\n\nCompound application loading process supports following events to be attached\n(in chronological order):\n\n1. configure\n2. after configure\n3. routes\n4. extensions\n5. after extensions\n6. structure\n7. models\n8. initializers\n\nREPL console\n------------\n\nTo run REPL console use command\n\n```sh\ncompound console\n```\n\nor its shortcut\n\n```sh\ncompound c\n```\n\nIt's just simple node-js console with some Compound bindings, e.g. models. Just one note\nabout working with console: Node.js is asynchronous by its nature, and it's great\nbut it made console debugging much more complicated, because you should use callbacks\nto fetch results from the database, for example. I have added one useful method to\nsimplify async debugging using compound console. It's named `c`. You can pass it\nas a parameter to any function requiring callbacks, and it will store parameters passed\nto the callback as variables `_0, _1, ..., _N` where N is index in `arguments`.\n\nExample:\n\n```\n$ compound c\ncompound\u003e User.find(53, c)\nCallback called with 2 arguments:\n_0 = null\n_1 = [object Object]\ncompound\u003e _1\n{ email: [Getter/Setter],\n  password: [Getter/Setter],\n  activationCode: [Getter/Setter],\n  activated: [Getter/Setter],\n  forcePassChange: [Getter/Setter],\n  isAdmin: [Getter/Setter],\n  id: [Getter/Setter] }\n```\n\nLocalization\n------------\n\nTo add another language to app just create a .yml file in `config/locales`,\nfor example `config/locales/jp.yml`, copy contents of `config/locales/en.yml` to new\nfile and rename root node (`en` to `jp` in that case), also in `lang` section rename\n`name` to Japanese (for example).\n\nNext step - rename email files in `app/views/emails`, copy all files `*.en.html`\nand `*.en.text` to `*.jp.html` and `*.jp.text` and translate new files.\n\nNOTE: translation can contain `%` symbol(s), that means variable substitution\n\nIf you don't need locales support you can turn it off in `config/environment`:\n\n```js\napp.set('i18n', 'off');\n```\n\nLogger\n-----\n\n```js\napp.set('quiet', true); // force logger to log into `log/#{app.settings.env}.log`\ncompound.logger.write(msg); // to log message\n```\n\nsetup custom log dir:\n\n```javascript\napp.get('log dir', '/var/log/compound-app/');\n```\n\nConfiguring\n===========\n\nCompound has some configuration options allows to customize app behavior\n\neval cache\n----------\n\nEnable controller caching, should be turned on in prod. In development mode,\ndisabling the cache allows the avoidance of server restarts after each model/controller change.\n\n```js\napp.disable('eval cache'); // in config/environments/development.js\napp.enable('eval cache'); // in config/environments/production.js\n```\n\nmodel cache\n-----------\n\nSame option for models. When disabled, model files evaluated per each request.\n\n```js\napp.disable('model cache'); // in config/environments/development.js\n```\n\nview cache\n----------\n\nExpress.js option, enables view caching.\n\n```js\napp.disable('view cache'); // in config/environments/development.js\n```\n\nquiet\n-----\n\nWrite logs to `log/NODE_ENV.log`\n\n```js\napp.set('quiet', true); // in config/environments/test.js\n```\n\nmerge javascripts\n-----------------\n\nJoin all javascript files listed in `javascript_include_tag` into one\n\n```js\napp.enable('merge javascripts'); // in config/environments/production.js\n```\n\nmerge stylesheets\n-----------------\n\nJoin all stylesheet files listed in `stylesheets_include_tag` into one\n\n```js\napp.enable('merge stylesheets'); // in config/environments/production.js\n```\n\n## Custom tools\n\nPut your function to ./app/tools/toolname.js to be able to run it within application\nenvironment as `compound toolname` command via CLI. See example tool in generated\nexample: ./app/tools/dabatase.js\n\nOptionally you can specify some usage information on your function to be able to see\nit in list of available commands (using `compound` command).\n\n```javascript\nmodule.exports.help = {\n    shortcut:    'db',\n    usage:       'db [migrate|update]',\n    description: 'Migrate or update database(s)'\n};\n```\n\n\nMIT License\n===========\n\n    Copyright (C) 2011 by Anatoliy Chakkaev \u003cmail [åt] anatoliy [døt] in\u003e\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in\n    all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n    THE SOFTWARE.\n\n  [1]: http://anatoliy.in\n  [2]: https://github.com/1602/jugglingdb\n\n\n[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/1602/compound/trend.png)](https://bitdeli.com/free \"Bitdeli Badge\")\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1602%2Fcompound","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1602%2Fcompound","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1602%2Fcompound/lists"}