├── 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 |
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 |
--------------------------------------------------------------------------------