Skip to content
This repository has been archived by the owner on Nov 10, 2017. It is now read-only.

Commit

Permalink
initial import. nothing barely works.
Browse files Browse the repository at this point in the history
  • Loading branch information
n1k0 committed Jan 23, 2012
0 parents commit 7c451ad
Show file tree
Hide file tree
Showing 18 changed files with 300 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
/node_modules
11 changes: 11 additions & 0 deletions README.md
@@ -0,0 +1,11 @@
NodeTunes
=========

NodeTunes' a node & express application written in coffeescript; it stores fortunes (snippets of quotes, eg. IRC/IM ones). Originally inspired by the « [Fortunes](http://fortunes.inertie.org/) » application, by [Maurice Svay](http://svay.com/).

I'm discovering and learning both [CoffeeScript](http://coffeescript.org/) and [Node](http://nodejs.org/) while coding it, so don't expect too much reliability, but I would warmly welcome any code review against the code :)

If you're curious enough, you might check out the:

- [Django version of this app](http://github.com/n1k0/djortunes)
- [Symfony version of this app](http://github.com/n1k0/sftunes)
68 changes: 68 additions & 0 deletions app.coffee
@@ -0,0 +1,68 @@
#!/usr/bin/env coffee

express = require "express"
mongoose = require "mongoose"
routes = require "./routes"
coffeekup = require "coffeekup"

class NotFound extends Error
constructor: (@path) ->
@.name = "NotFound"
if path
Error.call @, "Cannot find #{path}"
@.path = path
else
Error.call @, "Not Found"
Error.captureStackTrace @, arguments.callee

app = module.exports = express.createServer()

app.configure ->
@set "views", "#{__dirname}/views"
@set "view engine", "coffee"
@register '.coffee', coffeekup.adapters.express
@use express.bodyParser()
@use express.methodOverride()
@use express.cookieParser()
@use express.session(secret: "rocknroll")
@use @router
@use express.static "#{__dirname}/public"

app.configure "development", ->
mongoose.connect 'mongodb://localhost/nodetunes-dev'
@use express.errorHandler
dumpExceptions: true
showStack: true

app.configure "production", ->
mongoose.connect 'mongodb://localhost/nodetunes'
@use express.errorHandler()

app.dynamicHelpers
get_messages: (req, res) -> req.flash()
session: (req, res) -> req.session

app.get "/", routes.index
app.get "/add", routes.add
app.post "/add", routes.add
app.get "/fortune/:fortuneId", routes.show

app.error (err, req, res, next) ->
if err instanceof NotFound
return res.render '404',
status: 404,
error: err,
title: '404 Error - Page Not Found'
next err

# app.param 'fortuneId', (req, res, next, id) ->
# fortunes = require("./models/fortune").fortunes
# if not fortunes.find ~~id
# return res.render "404",
# status: 404
# title: "Fortune not found"
# message: "Fortune with id=#{id} not found"
# next()

app.listen 3000
console.log "Server listening on port #{app.address().port} in #{app.settings.env} mode"
34 changes: 34 additions & 0 deletions data/fixtures.coffee
@@ -0,0 +1,34 @@
mongoose = require "mongoose"
mongoose.connect 'mongodb://localhost/nodetunes-dev'

Fortune = require "../models/Fortune"

fixtures = [{
title: "First fortune",
content: """
<niko> Hello!
<dave> Hello.
""",
date: new Date()
}, {
title: "Second fortune",
content: """
<bob> I'm so happy this works
<joe> Me neither actually.
""",
date: new Date()
}]

Fortune.remove ->
processed = 0
for fixture in fixtures
new Fortune(fixture).save (err, f) ->
if err
console.error "Errors encountered"
for error of err.errors
console.error "- #{error}: #{err.errors[error].type}"
else
console.log "Fixture saved: '#{f.title}'"
if ++processed == fixtures.length
console.log "Processed #{fixtures.length} fixtures."
process.exit()
28 changes: 28 additions & 0 deletions forms/Form.coffee
@@ -0,0 +1,28 @@
class Form
constructor: (model, data) ->
@model = model
@bound = false
@valid = undefined
@values = {}
@errors = {}
if data
@bind data

bind: (params) ->
for field of params
@values[field] = params[field]
@bound = true

save: (callback) ->
if not @bound
throw new Error "Cannot save unbound form"
new @model(@values).save (err, instance) =>
@valid = true
if err
for field of err.errors
@errors[field] = err.errors[field].type
@valid = false
if typeof callback == "function"
callback err, instance

exports.Form = Form
15 changes: 15 additions & 0 deletions lib/helpers.coffee
@@ -0,0 +1,15 @@
exports.fortunize = (value) ->
out = ""
i = 0
for line in value.split '\n'
i++
match = /<(\w+)>\s?(.*)\n?/g.match line.trim()
className = if i % 2 == 0 then "even" else "odd"
if match
for match in m
nick = match[0]
quote = escape match[1]
out += "<dt class=\"#{className}\">&lt;#{nick}&gt;</dt><dd><q>#{quote}</q></dd>\n"
else
out += "<dt>&nbsp;</dt><dd>#{escape(line)}</dd>\n"
"<dl>#{out}</dl>"
25 changes: 25 additions & 0 deletions models/Fortune.coffee
@@ -0,0 +1,25 @@
mongoose = require "mongoose"

Fortune = new mongoose.Schema
title:
type: String
trim: true
required: true
validate: [
(v) -> v.length >= 3 and v.length <= 255,
"Title length must be comprised between 3 and 255 chars"
]
content:
type: String
trim: true
required: true
validate: [
(v) -> v.length >= 10 and v.length <= 5000,
"Contents length must be comprised between 10 and 5000 chars"
]
date:
type: Date
default: Date.now
index: true

module.exports = mongoose.model 'Fortune', Fortune
14 changes: 14 additions & 0 deletions package.json
@@ -0,0 +1,14 @@
{
"name": "nodetunes",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "2.5.5",
"coffeekup": "0.3.1",
"coffee-script": "1.2.0"
},
"devDependencies": {
"mocha": "0.11.0",
"should": "0.5.1"
}
}
8 changes: 8 additions & 0 deletions public/stylesheets/style.css
@@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

a {
color: #00B7FF;
}
30 changes: 30 additions & 0 deletions routes/index.coffee
@@ -0,0 +1,30 @@
Fortune = require "../models/Fortune"
Form = require("../forms/Form").Form

exports.index = (req, res) ->
Fortune.find {}, (err, fortunes) ->
res.render "index",
title: "NodeTunes",
fortunes: fortunes

exports.add = (req, res) ->
form = new Form Fortune
stdRes = (form) ->
return res.render "add", form: form, title: "Add a fortune"
if req.method != "POST"
return stdRes form
form.bind req.body.fortune
form.save (err) ->
if err
req.flash "error", "Unable to save fortune"
stdRes form
else
req.flash "info", "Fortune added"
res.redirect "/"

exports.show = (req, res) ->
Fortune.findById req.param('fortuneId'), (err, fortune) ->
res.render "show",
fortune: fortune
fortunize: require("../lib/helpers").fortunize
title: fortune.title
7 changes: 7 additions & 0 deletions test/helpers-test.coffee
@@ -0,0 +1,7 @@
require "../routes"

describe 'feature', ->
it "should add two numbers", ->
(2+2).should.equal 4
it "should add two numbers", ->
(4+4).should.equal 8
3 changes: 3 additions & 0 deletions test/mocha.opts
@@ -0,0 +1,3 @@
--require should
--growl

3 changes: 3 additions & 0 deletions views/404.coffee
@@ -0,0 +1,3 @@
h2 "Not found, dude."

blockquote @message if @message?
4 changes: 4 additions & 0 deletions views/_fortune.coffee
@@ -0,0 +1,4 @@
div class: "fortune", ->
h2 -> a href: "/fortune/#{@fortune.id}", -> @fortune.title
blockquote @fortune.content
p "— posted on #{@fortune.date.toDateString()}"
18 changes: 18 additions & 0 deletions views/add.coffee
@@ -0,0 +1,18 @@
form action: "/add", method: "post", ->
if Object.keys(@form.errors).length
div class: "alert-message block-message error", ->
p "Errors have been encountered"
fieldset ->
div class: "clearfix" + (if @form.errors.title? then " error" else ""), ->
label for: "title", -> "Title"
div class: "input", ->
input type: "text", name: "fortune[title]", id: "title", value: @form.values.title
p @form.errors.title if @form.errors.title?
div class: "clearfix" + (if @form.errors.content? then " error" else "") , ->
label for: "content", -> "Content"
div class: "input", ->
textarea name: "fortune[content]", id: "content", ->
@form.values.content
p @form.errors.content if @form.errors.content?
div class: "clearfix", ->
button type: "submit", class: "btn primary", -> "Submit"
7 changes: 7 additions & 0 deletions views/index.coffee
@@ -0,0 +1,7 @@
if @fortunes.length
p "#{@fortunes.length} fortunes"
for fortune in @fortunes
partial '_fortune', fortune: fortune
hr
else
p "No fortune"
23 changes: 23 additions & 0 deletions views/layout.coffee
@@ -0,0 +1,23 @@
doctype 5
html ->
head ->
meta charset: 'utf-8'
title "#{@title} | My Site" if @title?
link rel: 'stylesheet', href: 'http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css'
body ->
div class: "container", ->
header ->
h1 "NodeTunes"
ul ->
li -> a href: "/", -> "Home"
li -> a href: "/add", -> "New fortune"
messages = @get_messages
for level of messages
div class: "alert-message #{level}", ->
a class: "close", href: "#", onclick: "this.parentNode.style.display='none'", -> "×"
p message for message in messages[level]

div id: "content", ->
@body
footer ->
p "© 2012 Nicolas Perriault"
1 change: 1 addition & 0 deletions views/show.coffee
@@ -0,0 +1 @@
partial "_fortune", fortune: @fortune

0 comments on commit 7c451ad

Please sign in to comment.