├── README.md ├── coffeemate.coffee ├── examples ├── coffeekup │ ├── app.coffee │ └── views │ │ ├── layout.coffeekup │ │ ├── main.coffeekup │ │ ├── nested1.coffeekup │ │ ├── nested2.coffeekup │ │ └── nested3.coffeekup ├── connect │ ├── app.coffee │ ├── layout.eco │ ├── main.eco │ └── public │ │ └── coffeescript.png ├── extensions │ └── app.coffee ├── helloworld │ └── app.coffee ├── helpers │ ├── app.coffee │ ├── layout.eco │ └── main.eco ├── layout │ ├── app.coffee │ ├── layout.eco │ └── main.eco ├── now │ ├── app.coffee │ └── main.eco ├── routes │ └── app.coffee ├── sandbox │ ├── base │ │ └── index.coffee │ ├── comments │ │ └── index.coffee │ ├── index.coffee │ ├── posts │ │ └── index.coffee │ ├── public │ │ └── coffeescript.png │ ├── server.js │ ├── settings.coffee │ └── templates │ │ ├── base │ │ └── index.html │ │ ├── comment_layout.html │ │ ├── comments │ │ └── index.html │ │ ├── layout.html │ │ └── posts │ │ └── index.html ├── simple_auth │ ├── app.coffee │ ├── layout.eco │ ├── login.eco │ └── main.eco └── templating │ ├── app.coffee │ ├── layout.eco │ ├── leaf.eco │ ├── main.eco │ └── nested.eco └── package.json /README.md: -------------------------------------------------------------------------------- 1 | Coffeemate! 2 | =========== 3 | ``` 4 | ) 5 | ( 6 | C[_] coffeemate, the coffee creamer! 7 | ``` 8 | coffeemate is a web framework built on top of connect and specialized for writing web apps comfortably in coffeescript. 9 | It uses eco template engine by default for fullfill coffeescript experience. But also using coffeekup or any other template engine is only a matter of few seconds. 10 | 11 | coffeemate's magic is hidden inside that, it is perfectly a singleton instance of connect framework's `HTTPServer` implementation. So hacking coffeemate is pretty easy and funny :) 12 | 13 | 14 | What is it look like? 15 | --------------------- 16 | 17 | ``` coffeescript 18 | # app.coffee 19 | 20 | # Require it 21 | mate = require 'coffeemate' 22 | 23 | # Connect it 24 | mate.logger() 25 | mate.static("#{__dirname}/public") 26 | 27 | # Extend it 28 | mate.context.highlight = (color, txt) -> 29 | "#{txt}" 30 | 31 | # Route it 32 | mate.get '/greet/:name', -> 33 | # this is context variable 34 | @greet_msg = "Hello, #{@req.params.name}" 35 | @render 'main' 36 | 37 | # Remote it 38 | mate.now.greet = -> 39 | console.log 'Hello, World!' 40 | 41 | # Listen it 42 | mate.listen 3000 43 | ``` 44 | 45 | ``` html 46 | 47 | 48 | 49 | 50 | 55 | 56 | 57 | <%- @include @body %> 58 | 59 | 60 | ``` 61 | 62 | ``` html 63 | 64 | 65 |

Welcome to Coffeemate

66 |
<%- @highlight '#f00', @greet_msg %>
67 |
<%- @include 'nested' %>
68 | ``` 69 | 70 | ``` html 71 | 72 | 73 |

I'm a partial template

74 |
<%- @highlight '#ff0', @greet_msg %>
75 | ``` 76 | 77 | Hmmm, What to expect more? 78 | 79 | Install 80 | ------- 81 | 82 | ``` 83 | $ npm install coffeemate 84 | ``` 85 | 86 | Documentation 87 | ------------- 88 | 89 | Annotated source documentation generated by docco can be found here 90 | 91 | Demos & Examples 92 | ---------------- 93 | 94 | See plenty of examples 95 | 96 | 97 | 98 | Licence 99 | ------- 100 | Copyright (c) 2011 Kadir Pekel. 101 | 102 | Permission is hereby granted, free of charge, to any person obtaining a copy of 103 | this software and associated documentation files (the 'Software'), to deal in 104 | the Software without restriction, including without limitation the rights to 105 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 106 | the Software, and to permit persons to whom the Software is furnished to do so, 107 | subject to the following conditions: 108 | 109 | The above copyright notice and this permission notice shall be included in all 110 | copies or substantial portions of the Software. 111 | 112 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 113 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 114 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 115 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 116 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 117 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 118 | -------------------------------------------------------------------------------- /coffeemate.coffee: -------------------------------------------------------------------------------- 1 | # coffeemate 2 | # Copyright(c) 2011 Kadir Pekel. 3 | # MIT Licensed 4 | 5 | # version info 6 | VERSION = '0.5.1' 7 | 8 | # Module dependencies 9 | 10 | fs = require 'fs' 11 | path = require 'path' 12 | connect = require 'connect' 13 | eco = require 'eco' 14 | 15 | # Context object that instantiated in every request to 16 | # form router handlers' and templates' @/this reference 17 | class CoffeemateContext 18 | # constructor 19 | # 20 | # @param {Object} cnt (container coffeemate instance) 21 | # @param {Object} resp 22 | # @param {Object} resp 23 | # @api public 24 | constructor: (@cnt, @req, @resp, @next) -> 25 | 26 | # Simple built-in extension that sends http redirect to client 27 | # 28 | # @param {String} location 29 | # @api public 30 | redirect: (location) -> 31 | @resp.writeHead 301, location: location 32 | @resp.end() 33 | 34 | # This method renders the template that read from given templateName 35 | # using eco template engine as default. 36 | # It uses sync file read operation to obtain template contents 37 | # 38 | # @param {String} templateName 39 | # @return {String} 40 | # @api public 41 | include: (templateName) -> 42 | templatePath = path.join process.cwd(), @cnt.options.renderDir, "#{templateName}#{@cnt.options.renderExt}" 43 | template = fs.readFileSync templatePath 44 | @cnt.options.renderFunc "#{template}", @ 45 | 46 | # This method renders the template that read from given templateName 47 | # and writes the output to the client socket stream 48 | # 49 | # You can explicitly set 'layoutName' if want to override the default 50 | # 'renderLayoutName' option value 51 | # 52 | # @param {String} templateName 53 | # @param {String} layoutName 54 | # @api public 55 | render: (templateName, layoutName=@cnt.options.renderLayoutName, layout=@cnt.options.renderLayout) -> 56 | @[@cnt.options.renderLayoutPlaceholder] = templateName 57 | templateName = layout and layoutName or templateName 58 | @resp.end @include templateName 59 | 60 | # Coffeemate core object 61 | # Kindly extends connect.HTTPServer and pours some sugar on it. 62 | # 63 | # @api private 64 | class Coffeemate extends connect.HTTPServer 65 | 66 | # Expose CoffeemateContext constructor for external access 67 | Context: CoffeemateContext 68 | 69 | # Shorthand reference for CoffeemateContext.prototype to simplify the extension mechanism 70 | context: CoffeemateContext:: 71 | 72 | # Expose connect's built-in middleware stack explicitly for external access 73 | middleware: connect.middleware 74 | 75 | # constructor 76 | # 77 | # @api public 78 | constructor: (@version=VERSION) -> 79 | @options = renderFunc: eco.render, renderDir: '', renderExt: '.eco', renderLayout: yes, renderLayoutName: 'layout', renderLayoutPlaceholder: 'body' 80 | @routeMap = {} 81 | @baseUrl = '/' 82 | connect.HTTPServer.call @, [] 83 | 84 | # enable nowjs if available 85 | try 86 | @nowjs = require('now') 87 | @now = @nowjs.initialize(@).now 88 | 89 | # This method helps you define sub applications under given base path. 90 | # The context of callback is coffeemate instance itself and any router definition 91 | # in this context will be constructed based on the baseUrl 92 | # 93 | # @param {String} baseUrl 94 | # @param {String} callback 95 | # @api public 96 | sub: (baseUrl, callback) -> 97 | previousBaseUrl = @baseUrl 98 | @baseUrl = baseUrl 99 | callback.call @ 100 | @baseUrl = previousBaseUrl 101 | 102 | # Factory method for creating new Coffeemate instances 103 | # 104 | # @return {Object} 105 | # @api public 106 | newInstance: -> 107 | new Coffeemate 108 | 109 | # Override 'connect.HTTPServer.use' method so if any argument is a Coffeemate instance 110 | # build its internal route stack as a self used middleware. 111 | # 112 | # @param {String|Function} 113 | # @param {Function} handle 114 | # @return {Server} 115 | # @api public 116 | use: (args...) -> 117 | arg.buildRouter() for arg in args when arg instanceof Coffeemate 118 | connect.HTTPServer::use.apply @, args 119 | 120 | # Enable special coffeekup templating magic! 121 | # 122 | # @api public 123 | coffeekup: (locals) -> 124 | renderFunc = require('coffeekup').render 125 | locals ?= {} 126 | locals.include ?= (partialName) -> text ck_options.context.include partialName 127 | @options.renderFunc = (tmpl, ctx) -> renderFunc tmpl, context: ctx, locals: locals 128 | 129 | # Build connect router middleware from internal route stack and automatically use it. 130 | # 131 | # @api private 132 | buildRouter: -> 133 | self = @ 134 | for root, routes of @routeMap 135 | @use root, @middleware.router (app) -> 136 | for route in routes 137 | do (route) -> 138 | app[route.method] route.pattern, (req, resp, next) -> 139 | route.callback.apply new CoffeemateContext(self, req, resp, next) 140 | 141 | # Override 'connect.HTTPServer.listen' to create a pre-hook space for 142 | # preparing router definitions 143 | # 144 | # @param {Number} port 145 | # @param {String} hostname 146 | # @param {Function} callback 147 | # @api public 148 | listen: (args...) -> 149 | @buildRouter() 150 | connect.HTTPServer::listen.apply @, args 151 | 152 | # Create shorthand middleware definitions using connect's built-in middleware stack 153 | for item of Coffeemate::middleware 154 | do (item) -> 155 | Coffeemate::[item] = (args...) -> 156 | @use @middleware[item].apply @, args 157 | 158 | # Create shorthand router definitions using connect's router middleware 159 | for method in Coffeemate::middleware.router.methods 160 | do (method) -> 161 | Coffeemate::[method] = (pattern, callback) -> 162 | @routeMap[@baseUrl] ?= [] 163 | @routeMap[@baseUrl].push method: method, pattern: pattern, callback: callback 164 | @ 165 | 166 | # Handle uncaught exceptions explicitly to prevent node exiting 167 | # current process. Exception stack trace is sent to stderr. 168 | process.on 'uncaughtException', (err) -> 169 | console.error err instanceof Error and err.stack or err 170 | 171 | # Export Coffeemate as a pre-instantiated instance 172 | # use 'newInstance' method to create another ones 173 | module.exports = new Coffeemate 174 | -------------------------------------------------------------------------------- /examples/coffeekup/app.coffee: -------------------------------------------------------------------------------- 1 | coffeekup = require 'coffeekup' 2 | mate = require '../../coffeemate' 3 | 4 | 5 | mate.options.renderExt = '.coffeekup' 6 | mate.options.renderDir = 'views' 7 | 8 | # Bind coffeekup with an helper 9 | 10 | mate.coffeekup highlight: (color, msg) -> 11 | text "#{msg}" 12 | 13 | mate.get '/', -> 14 | @foo = 'bar' 15 | @render 'main' 16 | 17 | mate.listen 3000 18 | -------------------------------------------------------------------------------- /examples/coffeekup/views/layout.coffeekup: -------------------------------------------------------------------------------- 1 | html -> 2 | head 3 | body -> 4 | include @body -------------------------------------------------------------------------------- /examples/coffeekup/views/main.coffeekup: -------------------------------------------------------------------------------- 1 | h1 "A cup of coffee demonstration" 2 | div -> 3 | include "nested1" 4 | div -> 5 | include "nested2" 6 | div -> 7 | include "nested3" -------------------------------------------------------------------------------- /examples/coffeekup/views/nested1.coffeekup: -------------------------------------------------------------------------------- 1 | p -> 2 | highlight "#0ff", "This is a highlighted #{@foo}" -------------------------------------------------------------------------------- /examples/coffeekup/views/nested2.coffeekup: -------------------------------------------------------------------------------- 1 | p -> 2 | highlight "#f00", "This is a highlighted #{@foo}" -------------------------------------------------------------------------------- /examples/coffeekup/views/nested3.coffeekup: -------------------------------------------------------------------------------- 1 | p -> 2 | highlight "#ff0", "This is a highlighted #{@foo}" -------------------------------------------------------------------------------- /examples/connect/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.logger() 4 | mate.static(__dirname + '/public') 5 | 6 | mate.get '/', -> 7 | @render 'main' 8 | 9 | mate.listen 3000 10 | -------------------------------------------------------------------------------- /examples/connect/layout.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- @include @body %> 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/connect/main.eco: -------------------------------------------------------------------------------- 1 |

coffeemate, the coffee creamer!

2 | 3 | -------------------------------------------------------------------------------- /examples/connect/public/coffeescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadirpekel/coffeemate/53c0f730931998a2163d644f32fee3014f93f880/examples/connect/public/coffeescript.png -------------------------------------------------------------------------------- /examples/extensions/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.context.send_xml = (msg) -> 4 | @resp.setHeader 'Content-Type', 'text/xml' 5 | @resp.end "#{msg}" 6 | 7 | mate.get '/', -> 8 | @send_xml 'Hello World' 9 | 10 | mate.listen 3000 11 | -------------------------------------------------------------------------------- /examples/helloworld/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.get '/', -> 4 | @resp.end 'Hello World' 5 | 6 | mate.listen 3000 7 | -------------------------------------------------------------------------------- /examples/helpers/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.context.highlight = (msg) -> 4 | "#{msg}" 5 | 6 | mate.get '/', -> 7 | @render 'main' 8 | 9 | mate.listen 3000 10 | -------------------------------------------------------------------------------- /examples/helpers/layout.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- @include @body %> 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/helpers/main.eco: -------------------------------------------------------------------------------- 1 |

Demonstrating helpers

2 |
3 | <%- @highlight "This is a highlighted text" %> 4 |
5 | -------------------------------------------------------------------------------- /examples/layout/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.get '/', -> 4 | @foo = 'bar' 5 | @render 'main' 6 | 7 | mate.listen 3000 8 | -------------------------------------------------------------------------------- /examples/layout/layout.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
This is HEADER
5 |
<%- @include @body %>
6 |
This is FOOTER
7 | 8 | -------------------------------------------------------------------------------- /examples/layout/main.eco: -------------------------------------------------------------------------------- 1 |
2 | This is main content 3 |

Also this is foo: <%= @foo %>

4 |
5 | -------------------------------------------------------------------------------- /examples/now/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate.coffee' 2 | 3 | mate.options.renderLayout = no 4 | 5 | mate.get '/', -> 6 | @render 'main' 7 | 8 | mate.now.greet = -> 9 | console.log "Hello World" 10 | 11 | mate.listen 3000 12 | -------------------------------------------------------------------------------- /examples/now/main.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/routes/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.get '/', -> 4 | @resp.end 'Hello World' 5 | 6 | mate.get '/greet/:name', -> 7 | @resp.end 'Hello ' + @req.params.name 8 | 9 | mate.get '/greet_if/:name?', -> 10 | @resp.end 'Hello ' + (@req.params.name or 'World') 11 | 12 | mate.listen 3000 13 | -------------------------------------------------------------------------------- /examples/sandbox/base/index.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../../coffeemate' 2 | 3 | mate.get '/', -> @render 'base/index' 4 | -------------------------------------------------------------------------------- /examples/sandbox/comments/index.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../../coffeemate' 2 | 3 | mate.get '/', -> @render 'comments/index', 'comment_layout' 4 | -------------------------------------------------------------------------------- /examples/sandbox/index.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | # import configuration 4 | require './settings' 5 | 6 | # import sub applications 7 | mate.sub '/base', -> require './base' 8 | mate.sub '/posts', -> require './posts' 9 | mate.sub '/comments', -> require './comments' 10 | 11 | # redirect root 12 | mate.get '/', -> @redirect '/base' 13 | 14 | # start the engines 15 | mate.listen 3000 16 | -------------------------------------------------------------------------------- /examples/sandbox/posts/index.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../../coffeemate' 2 | 3 | mate.get '/', -> @render 'posts/index' 4 | -------------------------------------------------------------------------------- /examples/sandbox/public/coffeescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kadirpekel/coffeemate/53c0f730931998a2163d644f32fee3014f93f880/examples/sandbox/public/coffeescript.png -------------------------------------------------------------------------------- /examples/sandbox/server.js: -------------------------------------------------------------------------------- 1 | require('coffee-script'); 2 | module.exports = require('./index.coffee'); 3 | -------------------------------------------------------------------------------- /examples/sandbox/settings.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.options.renderDir = 'templates' 4 | mate.options.renderExt = '.html' 5 | 6 | # use some of connect middlewares 7 | mate.logger() 8 | mate.static("#{__dirname}/public") 9 | -------------------------------------------------------------------------------- /examples/sandbox/templates/base/index.html: -------------------------------------------------------------------------------- 1 |

Coffeemate Sandbox

2 | posts 3 | -------------------------------------------------------------------------------- /examples/sandbox/templates/comment_layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 |
12 |
13 | <%- @include @body %> 14 |
15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/sandbox/templates/comments/index.html: -------------------------------------------------------------------------------- 1 |

Comment List

2 | -------------------------------------------------------------------------------- /examples/sandbox/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | <%- @include @body %> 9 |
10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/sandbox/templates/posts/index.html: -------------------------------------------------------------------------------- 1 |

Post List

2 | comments 3 | -------------------------------------------------------------------------------- /examples/simple_auth/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.cookieParser() 4 | mate.bodyParser() 5 | 6 | mate.context.authenticate = -> 7 | authenticated = @req.cookies.authenticated is 'yes' 8 | @redirect '/login' unless authenticated 9 | return authenticated 10 | 11 | mate.get '/', -> 12 | @render 'main.eco' if @authenticate() 13 | 14 | mate.get '/login', -> 15 | @render 'login.eco' 16 | 17 | mate.post '/login', -> 18 | authenticated = @req.body.user is 'coffee' and @req.body.pass is 'mate' 19 | if authenticated 20 | @resp.setHeader("Set-Cookie", ["authenticated=yes"]) 21 | @redirect '/' 22 | else 23 | @redirect '/login' 24 | 25 | mate.listen 3000 26 | -------------------------------------------------------------------------------- /examples/simple_auth/layout.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- @include @body %> 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/simple_auth/login.eco: -------------------------------------------------------------------------------- 1 |

Login Page

2 |
3 | 4 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /examples/simple_auth/main.eco: -------------------------------------------------------------------------------- 1 |

This page is protected

2 | -------------------------------------------------------------------------------- /examples/templating/app.coffee: -------------------------------------------------------------------------------- 1 | mate = require '../../coffeemate' 2 | 3 | mate.get '/:page?', -> 4 | @foo = 'bar' # this is a context variable 5 | @render 'main' 6 | 7 | mate.listen 3000 8 | -------------------------------------------------------------------------------- /examples/templating/layout.eco: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%- @include @body %> 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/templating/leaf.eco: -------------------------------------------------------------------------------- 1 |

this is leaf template

2 |
This is foo: <%= @foo %>
3 | -------------------------------------------------------------------------------- /examples/templating/main.eco: -------------------------------------------------------------------------------- 1 |

this is main template for path: <%= @req.url %>

2 |
This is foo: <%= @foo %>
3 |
<%- @include 'nested' %>
4 | -------------------------------------------------------------------------------- /examples/templating/nested.eco: -------------------------------------------------------------------------------- 1 |

this is nested template

2 |
This is foo: <%= @foo %>
3 |
<%- @include 'leaf' %>
4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.5.1", 3 | "author": "Kadir Pekel ", 4 | "name": "coffeemate", 5 | "description": "the coffee creamer!", 6 | "readme": "http://github.com/coffeemate/coffeematel#readme", 7 | "main": "coffeemate.coffee", 8 | "engines": { 9 | "node": ">= v0.4.12 < 0.7.0" 10 | }, 11 | "dependencies": { 12 | "eco": "1.1.0-rc-1", 13 | "connect": "1.7.2", 14 | "now": "0.7.5", 15 | "coffee-script": "1.1.3" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "http://github.com/kadirpekel/coffeemate" 20 | } 21 | } 22 | --------------------------------------------------------------------------------