Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heroku integration #17

Merged
merged 13 commits into from
Aug 26, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ results

npm-debug.log
node_modules

*.sqlite
*.env
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: node server.js
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@ An open-source [Workflowy](http://workflowy.com) clone.

##Installation

* Edit `config/development.json` to your needs
* Edit `config/development.json` and `config/database.json` to your needs
* `npm install`
* Run migrations and initialize the database: `sequelize-cli -m --config config/database.json` and `node db/seed/initial_tasks.js`
* `node server.js`

##Heroku deploy

You can use our one-click heroku deploy:

[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy)

Or proceed manually as follow:

* heroku create --stack cedar
* heroku addons:add heroku-postgresql:dev
* heroku config:set NODE_ENV=production
* git push heroku heroku-integration:master

##Controls

* <kbd>UP</kbd> & <kbd>DOWN</kbd>: navigate through tasks
Expand Down
13 changes: 13 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "HackFlowy",
"description": "Workflowy clone, built using Backbone.js & Socket.io",
"keywords": ["express", "backbone", "socket.io"],
"website": "https://github.com/abhshkdz/HackFlowy",
"repository": "https://github.com/abhshkdz/HackFlowy",
"env": {
"NODE_ENV": "production"
},
"addons": [
"heroku-postgresql"
]
}
6 changes: 1 addition & 5 deletions config/database.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
"storage": "db/test.sqlite"
},
"production": {
"username": "postgres",
"password": "",
"database": "hackflowy",
"host" : "localhost",
"dialect" : "postgres"
"use_env_variable": "DATABASE_URL"
}
}
11 changes: 2 additions & 9 deletions config/production.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
{
"port": 3000,
"port": 80,
"database": {
"username": "postgres",
"password": "",
"database": "hackflowy",
"options" : {
"dialect": "postgres",
"host" : "localhost",
"port" : "5432"
}
"use_env_variable": "DATABASE_URL"
}
}
22 changes: 12 additions & 10 deletions db/seed/initial_tasks.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
var config = require('config'),
orm = require('../../orm').configure(config.get('database')),
Tasks = require('../../db/models/task').instance(orm);
orm = require('../../orm').configure(config.get('database')),
Tasks = require('../../db/models/task').instance(orm);

Tasks.bulkCreate([
{content: 'Welcome to HackFlowy!', isCompleted: false},
{content: 'An open-source WorkFlowy clone', isCompleted: false},
{content: 'Built using Backbone + Socket.IO', isCompleted: false},
{content: 'I pulled this together in a few hours to learn Backbone', isCompleted: false},
{content: 'Feel free to try it out and hack on it', isCompleted: false},
{content: 'Good Luck!', isCompleted: false}
]);
Tasks.destroy().success(function() {
Tasks.bulkCreate([
{content: 'Welcome to HackFlowy!', isCompleted: false},
{content: 'An open-source WorkFlowy clone', isCompleted: false},
{content: 'Built using Backbone + Socket.IO', isCompleted: false},
{content: 'I pulled this together in a few hours to learn Backbone', isCompleted: false},
{content: 'Feel free to try it out and hack on it', isCompleted: false},
{content: 'Good Luck!', isCompleted: false}
]);
});

orm.sync();
19 changes: 19 additions & 0 deletions orm.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
var Sequelize = require('sequelize');

/* http://sequelizejs.com/articles/heroku */
var parseUrl = function(url) {
var match = url.match(/([\w]+):\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/);
db = {
database : match[6],
username : match[2],
password : match[3],
options : {
port : match[5],
host : match[4],
dialect : match[1],
}
}
return db;
}

module.exports = {
configure: function(db) {
db.url = db.use_env_variable ? process.env[db.use_env_variable] : null;
db = db.url ? parseUrl(db.url) : db;
return new Sequelize(db.database, db.username, db.password, db.options);
}
}

19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@
"dependencies": {
"express": "~3.1.0",
"path": "~0.4.9",
"mysql": "~2.0.0",
"sqlite3": "~2.1.0",
"pg": "~3.4.1",
"sequelize": "~1.7.9",
"socket.io": "~1.0.6",
"config": "~1.0.2"
},
"devDependencies": {
"sqlite3": "~2.1.0"
},
"engines": {
"node": "0.10.x"
},
"scripts": {
"start": "node server.js",
"db:migrate": "./node_modules/sequelize/bin/sequelize -m --config config/database.json",
"db:seed": "node db/seed/initial_tasks.js",
"postinstall": "npm run db:migrate && npm run db:seed"
},
"repository": {
"type": "git",
"url": "git://github.com/abhshkdz/HackFlowy.git"
}
}
10 changes: 5 additions & 5 deletions public/javascripts/models/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ Backbone
var TaskModel = Backbone.Model.extend({

defaults: {
parent_id: '',
parent: 0,
content: '',
is_completed:'n'
isCompleted: 0
},

toggelCompletedStatus:function(isCompleted){
var prev_isCompleted = isCompleted,
self = this;
this.save({'is_completed':isCompleted},
this.save({'isCompleted':isCompleted},
{
success:function(){},
error:function(){
//REVERT BACK ON ERROR
self.set({'is_completed':prev_isCompleted});
self.set({'isCompleted':prev_isCompleted});
}
})
}
Expand All @@ -31,4 +31,4 @@ Backbone

return TaskModel;

});
});
4 changes: 2 additions & 2 deletions public/javascripts/views/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ TaskView
model: task
});
var a = taskView.render();
if (a.model.get('parent_id')!=0)
a.$el.insertAfter($('*[data-id="'+a.model.get('parent_id')+'"]').parents('li:first'));
if (a.model.get('parent')!=0)
a.$el.insertAfter($('*[data-id="'+a.model.get('parent')+'"]').parents('li:first'));
else
this.$el.append(a.el);
}
Expand Down
38 changes: 19 additions & 19 deletions public/javascripts/views/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ taskTemplate
var task = this;
this.socket.on('task', function(data){
if (task.model.id == data.id) {
task.model.set({'content':data.content, 'is_completed':data.is_completed});
task.model.set({'content':data.content, 'isCompleted':data.isCompleted});
}
});
},
Expand All @@ -47,9 +47,9 @@ taskTemplate
var tmpl = _.template(this.template);
var task = this;
this.$el.html(tmpl({model:this.model.toJSON()}));
if (this.model.get('parent_id')!=0) {
if (this.model.get('parentId')!=0) {
this.$el.addClass('shift1');
var className = $('*[data-id="'+this.model.get('parent_id')+'"]').parents('li:first').attr('class');
var className = $('*[data-id="'+this.model.get('parentId')+'"]').parents('li:first').attr('class');
if (className!=undefined && className!=0 && className.substring(0,5) == 'shift') {
this.$el.removeClass();
this.$el.addClass('shift' + (parseInt(className.charAt(5))+1));
Expand All @@ -74,31 +74,31 @@ taskTemplate
if (e.shiftKey && e.keyCode == 9) {
var model = this.$el.next('li').find('input').data('id');
model = Tasks.get(model);
var old_parent = model.get('parent_id');
var old_parent = model.get('parentId');
old_parent = Tasks.get(old_parent);
var new_parent = old_parent.get('parent_id');
var new_parent = old_parent.get('parentId');
if (new_parent == null) new_parent = 0;
model.set('parent_id',new_parent);
model.save({content: model.get('content'), parent_id: model.get('parent_id')});
model.set('parentId',new_parent);
model.save({content: model.get('content'), parentId: model.get('parentId')});
}
else if (e.keyCode == 9) {
var parent = this.$el.prev('li').prev('li').find('input').data('id');
var current = this.$el.prev('li').find('input').data('id');
var model = Tasks.get(current);
model.set('parent_id',parent);
model.save({content: model.get('content'), parent_id: model.get('parent_id')});
model.set('parentId',parent);
model.save({content: model.get('content'), parentId: model.get('parentId')});
}
this.socket.emit('task', {
id: this.model.id,
parent_id: this.model.parent_id,
parentId: this.model.parentId,
content: this.$input.val().trim(),
is_completed:this.model.toJSON().is_completed
isCompleted:this.model.toJSON().isCompleted
});
},

update: function(e) {
if ( e.which === constants.ENTER_KEY ) {
Tasks.add({content:'', parent_id: this.model.get('parent_id')});
Tasks.add({content:'', parentId: this.model.get('parentId')});
this.$input.blur();
this.$el.next('li').find('input').focus();
}
Expand All @@ -110,7 +110,7 @@ taskTemplate
this.model.destroy();
}
else {
this.model.save({content: value, parent_id: this.model.attributes.parent_id});
this.model.save({content: value, parentId: this.model.attributes.parentId});
}
this.$el.removeClass('editing');
},
Expand All @@ -124,22 +124,22 @@ taskTemplate
},

markComplete:function(){
this.model.toggelCompletedStatus('Y');
this.model.toggelCompletedStatus(true);
this.socket.emit('task', {
id: this.model.id,
parent_id: this.model.parent_id,
parentId: this.model.parentId,
content:this.model.toJSON().content,
is_completed: this.model.toJSON().is_completed
isCompleted: this.model.toJSON().isCompleted
});
},

unmarkComlete:function(){
this.model.toggelCompletedStatus('N');
this.model.toggelCompletedStatus(false);
this.socket.emit('task', {
id: this.model.id,
parent_id: this.model.parent_id,
parentId: this.model.parentId,
content:this.model.toJSON().content,
is_completed: this.model.toJSON().is_completed
isCompleted: this.model.toJSON().isCompleted
});
},

Expand Down
4 changes: 2 additions & 2 deletions public/templates/task.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="options">
<div class="options-left">
<div class="mouse-tip"></div>
<% if(model.is_completed=='Y'){%>
<% if(model.isCompleted){%>
<a class="uncomplete">Uncomplete<hr></a>
<%}else {%>
<a class="complete">Complete<hr></a>
Expand All @@ -16,7 +16,7 @@
</div>
</div>

<% if(model.is_completed=='Y'){%>
<% if(model.isCompleted){%>
<input value="<%= model.content %>" data-id="<%= model.id %>" class="edit task-completed">
<%}else {%>
<input value="<%= model.content %>" data-id="<%= model.id %>" class="edit">
Expand Down
10 changes: 6 additions & 4 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ app.configure(function() {
app.use(express.errorHandler({dumpExceptions: true, showStack: true}));
});

server.listen(config.get('port'), function() {
console.log( 'Express server listening on port %d in %s mode', config.get('port'), app.settings.env );
var port = process.env.PORT || config.get('port');
server.listen(port, function() {
console.log( 'Express server listening on port %d in %s mode', port, app.settings.env );
});

app.get('/tasks', function(req,res){
Expand All @@ -29,7 +30,7 @@ app.get('/tasks', function(req,res){
app.post('/tasks', function(req,res){
Tasks.create({
content: req.body.content,
parent: req.body.parent_id,
parent: parseInt(req.body.parent) || 0,
isCompleted: false
}).success(function(task){
res.send(task);
Expand All @@ -40,7 +41,8 @@ app.put('/tasks/:id', function(req,res){
console.log(req.body.isCompleted);
Tasks.find(req.params.id).success(function(task){
task.content = req.body.content;
task.isCompleted = req.body.isCompleted;
task.parent = parseInt(req.body.parent) || 0,
task.isCompleted = req.body.isCompleted == 1;
task.save().success(function(task){
res.send(task);
})
Expand Down