Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
asinyagin committed Feb 13, 2015
0 parents commit 1fef065
Show file tree
Hide file tree
Showing 29 changed files with 443 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .bowerrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"directory": "target/app/public/lib"
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
target/
78 changes: 78 additions & 0 deletions Gruntfile.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
module.exports = (grunt) ->
grunt.initConfig
pkg: grunt.file.readJSON('package.json')
coffee:
compileSrc:
files: [
expand: true
cwd: 'src'
src: ['**/*.coffee']
dest: 'target/app'
rename: (dest, src) ->
dest + '/' + src.substring(0, src.lastIndexOf('.')) + '.js'
]
compileTest:
files: [
expand: true
cwd: 'test'
src: ['**/*.coffee']
dest: 'target/test'
rename: (dest, src) ->
dest + '/' + src.substring(0, src.lastIndexOf('.')) + '.js'
]
copy:
views:
files: [
expand: true
cwd: 'src/views'
src: ['**']
dest: 'target/app/views'
]
templates:
files: [
expand: true
cwd: 'src/public/templates'
src: ['**']
dest: 'target/app/public/templates'
]
styles:
files: [
expand: true
cwd: 'src/public/css'
src: ['**']
dest: 'target/app/public/css'
]
protractor_webdriver:
start:
options:
path: 'node_modules/protractor/bin/'
command: 'webdriver-manager start'
protractor:
options:
configFile: 'protractor.conf.js'
run: {}
watch:
compileSrc:
files: 'src/**/*.coffee'
tasks: 'coffee:compileSrc'
compileTest:
files: 'test/**/*.coffee'
tasks: 'coffee:compileTest'
views:
files: 'src/views/**'
tasks: 'copy:views'
templates:
files: 'src/public/templates/**'
tasks: 'copy:templates'
styles:
files: 'src/public/css/**'
tasks: 'copy:styles'

grunt.loadNpmTasks('grunt-contrib-coffee')
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.loadNpmTasks('grunt-contrib-watch')
grunt.loadNpmTasks('grunt-protractor-webdriver')
grunt.loadNpmTasks('grunt-protractor-runner')

grunt.registerTask('compile', ['coffee', 'copy'])
grunt.registerTask('test', ['protractor_webdriver', 'protractor'])
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Track

Simple issue tracker in CoffeeScript, AngularJS, Express, Node.js, and MongoDB.

Install dependencies: ```$ npm install && bower install```

Compile: ```$ grunt compile```

Run: ```$ DEBUG=track ./run.sh```

Functional tests with Protractor: ```$ grunt test```
25 changes: 25 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "track",
"version": "0.0.1",
"authors": [
"Alexander Sinyagin <[email protected]>"
],
"description": "Simple issue tracker.",
"main": "run.sh",
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"target/public/js/lib",
"test",
"tests"
],
"dependencies": {
"bootstrap": "~3.3.2",
"angular": "~1.3.13",
"jquery": "~2.1.3",
"angular-route": "~1.3.13",
"angular-resource": "~1.3.13"
}
}
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "Track",
"version": "0.0.1",
"engines": {
"node": ">= 0.10.0"
},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-coffee": "^0.12.0",
"grunt-contrib-copy": "^0.7.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-protractor-runner": "^1.2.1",
"grunt-protractor-webdriver": "^0.2.0",
"protractor": "^1.6.1"
},
"dependencies": {
"body-parser": "^1.10.2",
"cookie-parser": "^1.3.3",
"debug": "^2.1.1",
"express": "^4.11.1",
"jade": "^1.9.1",
"mongodb": "^1.4.29",
"mongoose": "^3.8.23",
"morgan": "^1.5.1",
"serve-favicon": "^2.2.0"
}
}
5 changes: 5 additions & 0 deletions protractor.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['target/test/e2e/*.spec.js'],
baseUrl: 'http://localhost:3000'
};
3 changes: 3 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh

nodemon --watch target/app --ext js target/app/run.js
49 changes: 49 additions & 0 deletions src/app.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
express = require 'express'
path = require 'path'
favicon = require 'serve-favicon'
logger = require 'morgan'
cookieParser = require 'cookie-parser'
bodyParser = require 'body-parser'
mongoose = require 'mongoose'

mongoose.connect('mongodb://localhost/track')
require('./models/task')

index = require './routes/index'
tasks = require './routes/tasks'

app = express()
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')

# app.use(favicon(__dirname + '/public/favicon.ico'))
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded(extended: false))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))

app.use('/', index)
app.use('/tasks', tasks)

app.use (req, res, next) ->
err = new Error('Not Found')
err.status = 404
next(err)

# development error handler
if app.get('env') == 'development'
app.use (err, req, res, next) ->
res.status(err.status || 500)
res.render 'error',
message: err.message
error: err

# production error handler
app.use (err, req, res, next) ->
res.status(err.status || 500)
res.render 'error',
message: err.message
error: {}

module.exports = app
6 changes: 6 additions & 0 deletions src/models/task.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mongoose = require 'mongoose'
Schema = mongoose.Schema

TaskSchema = new Schema(text: String)

mongoose.model('Task', TaskSchema)
1 change: 1 addition & 0 deletions src/public/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@media (min-width: 768px) { .container { max-width: 750px; } }
11 changes: 11 additions & 0 deletions src/public/scripts/new_task.ctrl.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
NewTaskCtrl = ($scope, $location, Tasks) ->
$scope.error = false
$scope.create = ->
Tasks.save text: $scope.text,
->
$scope.error = false
$location.path('/tasks')
(error) -> $scope.error = true

angular.module('track')
.controller('NewTaskCtrl', ['$scope', '$location', 'Tasks', NewTaskCtrl])
8 changes: 8 additions & 0 deletions src/public/scripts/task.ctrl.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
TaskCtrl = ($routeParams, $scope, Tasks) ->
$scope.error = false
Tasks.get id: $routeParams.id,
(task) -> $scope.task = task
(error) -> $scope.error = true

angular.module('track')
.controller('TaskCtrl', ['$routeParams', '$scope', 'Tasks', TaskCtrl])
5 changes: 5 additions & 0 deletions src/public/scripts/tasks.ctrl.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TasksCtrl = ($scope, Tasks) ->
$scope.tasks = Tasks.query()

angular.module('track')
.controller('TasksCtrl', ['$scope', 'Tasks', TasksCtrl])
3 changes: 3 additions & 0 deletions src/public/scripts/tasks.service.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Tasks = ($resource) -> $resource('tasks/:id')

angular.module('track').factory('Tasks', ['$resource', Tasks])
1 change: 1 addition & 0 deletions src/public/scripts/track.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
angular.module('track', ['ngRoute', 'ngResource', 'ngMessages'])
25 changes: 25 additions & 0 deletions src/public/scripts/track.router.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
config = ($locationProvider, $routeProvider) ->
$locationProvider.hashPrefix('!')

$routeProvider
.when '/tasks',
title: 'Home'
controller: 'TasksCtrl'
templateUrl: 'templates/tasks.html'
.when '/new-task',
title: 'New Task'
controller: 'NewTaskCtrl'
templateUrl: 'templates/new_task.html'
.when '/tasks/:id',
title: 'Task'
controller: 'TaskCtrl'
templateUrl: 'templates/task.html'
.otherwise(redirectTo: '/tasks')

titleHandler = ($location, $rootScope) ->
$rootScope.$on '$routeChangeSuccess', (event, current, previous) ->
$rootScope.title = current.$$route.title if current.$$route

angular.module('track')
.config(['$locationProvider', '$routeProvider', config])
.run(['$location', '$rootScope', titleHandler])
15 changes: 15 additions & 0 deletions src/public/templates/new_task.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="row" ng-show="error">
<div class="alert alert-danger">Could not create the task!</div>
</div>
<div class="row">
<form>
<div ng-form="form">
<div class="input-group" ng-class="{'has-error': form.text.$invalid &amp;&amp; form.text.$dirty}">
<input type="text" name="text" class="form-control" placeholder="Task text" ng-model="text" required/>
<div class="input-group-btn">
<button type="submit" class="btn btn-primary" ng-click="create()" ng-disabled="form.$invalid">Create</button>
</div>
</div>
</div>
</form>
</div>
8 changes: 8 additions & 0 deletions src/public/templates/task.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="row" ng-show="error">
<div class="alert alert-danger">Could not load the task!</div>
</div>
<div class="row">
<div>
<p>{{ task.text }}</p>
</div>
</div>
3 changes: 3 additions & 0 deletions src/public/templates/tasks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<ul ng-repeat="task in tasks">
<li><a href="#!/tasks/{{ task._id }}">{{ task.text }}</a></li>
</ul>
6 changes: 6 additions & 0 deletions src/routes/index.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
router = require('express').Router()

router.get '/', (req, res, next) ->
res.render 'index', title: 'Home'

module.exports = router
20 changes: 20 additions & 0 deletions src/routes/tasks.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
router = require('express').Router()
mongoose = require 'mongoose'
Task = mongoose.model('Task')

router.get '/', (req, res, next) ->
Task.find {}, (error, tasks) ->
if (error) then next(error)
else res.json(tasks)

router.get '/:id', (req, res, next) ->
Task.findById req.params.id, (error, task) ->
if (error) then next(error)
else res.json(task)

router.post '/', (req, res, next) ->
Task.create text: req.body.text, (error, task) ->
if (error) then next(error)
else res.sendStatus(201);

module.exports = router
45 changes: 45 additions & 0 deletions src/run.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
app = require './app'
debug = require('debug')('track')
http = require 'http'

normalizePort = (val) ->
port = parseInt(val, 10)

if isNaN port then val
else if port >= 0 then port
else false

onError = (error) ->
if error.syscall != 'listen' then throw error

bind = if typeof port == 'string'
"Pipe #{port}"
else
"Port #{port}"

switch error.code
when 'EACCESS'
console.error "#{bind} requires elevated priviliges"
process.exit(1)
when 'EADDRINUSE'
console.error "#{bind} is already in use"
process.exit(1)
else
throw error

onListening = ->
addr = server.address()
bind = if typeof addr == 'string'
"pipe #{addr}"
else
"port #{addr.port}"
debug "Listening on #{bind}"

port = normalizePort(process.env.PORT || '3000');
app.set('port', port)

server = http.createServer(app)

server.listen(port)
server.on('error', onError)
server.on('listening', onListening)
6 changes: 6 additions & 0 deletions src/views/error.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extends layout

block content
h1= message
h2= error.status
pre #{error.stack}
Loading

0 comments on commit 1fef065

Please sign in to comment.