Skip to content

Commit cc8db89

Browse files
committed
initial commit
0 parents  commit cc8db89

File tree

5 files changed

+266
-0
lines changed

5 files changed

+266
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
static/**/*
2+
static/favicon.ico
3+
*.pyc
4+
5+
!static/css/base.css
6+
!static/js/application.coffee

server.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import web
2+
import json
3+
import random
4+
import pymongo
5+
import bson
6+
7+
8+
# custom RESTful response used when updating/deleting model
9+
# with no content in response
10+
no_content = NoContent = web.webapi._status_code("204 No Content")
11+
custom_error = CustomError = web.webapi._status_code("416 Error Test")
12+
13+
# set MongoDB connection
14+
connection = pymongo.Connection("localhost", 27017)
15+
db = connection.test
16+
17+
# fix the ObjectId issue when encoding/decoding JSON object
18+
def todo_json_encoding(obj):
19+
if isinstance(obj, bson.objectid.ObjectId):
20+
return str(obj)
21+
else:
22+
return obj
23+
24+
def todo_json_decoding(obj):
25+
obj['_id'] = bson.objectid.ObjectId(obj['_id'])
26+
return obj
27+
28+
29+
urls = (
30+
'/api/todos/([a-f0-9]+)', 'todos',
31+
'/api/todos', 'todos',
32+
'/', 'home',
33+
)
34+
35+
app = web.application(urls, globals())
36+
render = web.template.render('templates/')
37+
# session = web.session.Session(app, web.session.DiskStore('sessions'))
38+
39+
class home:
40+
def GET(self):
41+
return render.index()
42+
43+
class todos:
44+
def GET(self):
45+
web.header('Content-Type', 'application/json')
46+
return json.dumps(list(db.todos.find()), default=todo_json_encoding)
47+
48+
def POST(self):
49+
_id = db.todos.insert(json.loads(web.data()))
50+
web.header('Content-Type', 'application/json')
51+
res = json.dumps(db.todos.find_one(_id), default=todo_json_encoding)
52+
raise web.created(res)
53+
# raise custom_error(json.dumps(
54+
# { u'message': u'Custom Error raised' }
55+
# ))
56+
57+
def PUT(self, _id):
58+
data = json.loads(web.data(), object_hook=todo_json_decoding)
59+
db.todos.save(data)
60+
61+
# res = json.dumps(db.todos.find_one(bson.objectid.ObjectId(_id)), default=todo_json_encoding)
62+
return no_content()
63+
64+
def DELETE(self, _id):
65+
db.todos.remove(bson.objectid.ObjectId(_id))
66+
return no_content()
67+
68+
69+
if __name__ == "__main__":
70+
app.run()

static/css/base.css

Whitespace-only changes.

static/js/application.coffee

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# coffee --watch --compile application.coffee
2+
class @Todo extends Backbone.Model
3+
4+
defaults:
5+
type: 'pending'
6+
7+
url: ->
8+
u = "/api/todos"
9+
u += "/#{@get('_id')}" unless @isNew()
10+
return u
11+
12+
isNew: ->
13+
!@get('_id')?
14+
15+
validate: (attrs) ->
16+
if !attrs.content? or attrs.content.trim() is ""
17+
return message: "Title can't be blank."
18+
19+
20+
class @Todos extends Backbone.Collection
21+
model: Todo
22+
url: "/api/todos"
23+
24+
25+
class @TodoListView extends Backbone.View
26+
el: '#todos'
27+
28+
initialize: ->
29+
@collection.bind('reset', @render)
30+
@collection.fetch()
31+
@collection.bind('add', @renderAdded)
32+
new NewTodoView(collection: @collection)
33+
34+
render: =>
35+
@collection.forEach (todo) =>
36+
$(@el).append(new TodoListItemView(model: todo).el)
37+
38+
renderAdded: (todo) =>
39+
$("#new_todo").after(new TodoListItemView(model: todo).el)
40+
41+
42+
class @TodoListItemView extends Backbone.View
43+
tagName: 'li'
44+
events:
45+
'keypress .todo_title': 'handleKeypress'
46+
'change .todo_state': 'saveModel'
47+
'click .btn-danger': 'destroy'
48+
49+
initialize: ->
50+
@template = _.template($('#todo_template').html())
51+
@model.bind('change', @render)
52+
@model.bind('error', @modelSaveFailed)
53+
@render()
54+
55+
render: =>
56+
$(@el).html(@template(@model.toJSON()))
57+
if @model.get('type') is 'completed'
58+
@$('.todo_state').attr('checked', true)
59+
@$('label.active').removeClass('active')
60+
@$('.todo_title').addClass('completed').attr('disabled', true)
61+
return @
62+
63+
handleKeypress: (e) =>
64+
if e.keyCode is 13
65+
@saveModel(e)
66+
67+
saveModel: (e) =>
68+
e?.preventDefault()
69+
attrs = {content: @$('.todo_title').val()}
70+
if @$('.todo_state').attr('checked')?
71+
attrs.type = 'completed'
72+
else
73+
attrs.type = 'pending'
74+
@model.save attrs
75+
76+
modelSaveFailed: (model, error) =>
77+
if error.responseText?
78+
error = JSON.parse(error.responseText)
79+
alert error.message
80+
@$('.todo_title').val(@model.get('content'))
81+
82+
destroy: (e) =>
83+
e?.preventDefault()
84+
if confirm "Are You sure?"
85+
@model.destroy
86+
success: =>
87+
$(@el).remove()
88+
89+
90+
class @NewTodoView extends Backbone.View
91+
el: '#new_todo'
92+
events:
93+
'keypress .todo_title': 'handleKeypress'
94+
95+
initialize: ->
96+
@collection.bind('add', @resetForm)
97+
@$('.todo_title').focus()
98+
99+
handleKeypress: (e) =>
100+
if e.keyCode is 13
101+
@saveModel(e)
102+
103+
resetForm: (todo) =>
104+
@$('.todo_title').val('')
105+
106+
saveModel: (e) =>
107+
e?.preventDefault()
108+
model = new Todo()
109+
model.save {content: @$('.todo_title').val()},
110+
success: =>
111+
# console.log('added')
112+
@collection.add(model)
113+
error: (model, error) =>
114+
# console.log error.responseText
115+
if error.responseText?
116+
error = JSON.parse(error.responseText)
117+
alert error.message
118+
119+
120+
$ ->
121+
new TodoListView(collection: new Todos())

templates/index.html

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Todos</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<meta name="description" content="Backbone.js + CoffeeScript + WebPy">
8+
<meta name="author" content="mario <[email protected]>">
9+
10+
<!-- Le styles -->
11+
<link href="/static/css/bootstrap.css" rel="stylesheet">
12+
<link href="/static/css/base.css" rel="stylesheet">
13+
<style type="text/css">
14+
body {
15+
/* cutom css */
16+
17+
}
18+
</style>
19+
20+
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
21+
<!--[if lt IE 9]>
22+
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
23+
<![endif]-->
24+
25+
<!-- Le fav and touch icons -->
26+
<link rel="shortcut icon" href="/static/favicon.ico">
27+
28+
</head>
29+
30+
<body>
31+
<div class="container">
32+
<h1>Todo List</h1>
33+
<ul id="todos" class="unstyled">
34+
<li id="new_todo">
35+
<div class="clearfix">
36+
<div class="input">
37+
<div class="input-prepend">
38+
<span class="add-on">New Todo</span>
39+
<input class="xlarge todo_title" size="50" type="text" placeholder="Enter your new Todo here.."></input>
40+
</div>
41+
</div>
42+
</div>
43+
</li>
44+
</ul>
45+
</div><!-- /container -->
46+
<script type="template/javascript" id="todo_template">
47+
<li>
48+
<div class="clearfix">
49+
<div class="input">
50+
<div class="input-prepend input-append">
51+
<span class="add-on"><input class="todo_state" type="checkbox" value=""></span>
52+
<input class="xlarge todo_title" size="50" type="text" value="<%= content %>"></input>
53+
<button type="button" class="btn btn-danger">Delete</button>
54+
</div>
55+
</div>
56+
</div>
57+
</li>
58+
</script>
59+
<!-- Le javascript -->
60+
<script src="/static/js/jquery-1.8.1.min.js"></script>
61+
<script src="/static/js/bootstrap.js"></script>
62+
<script src="/static/js/json2.js"></script>
63+
<script src="/static/js/underscore.js"></script>
64+
<script src="/static/js/backbone.js"></script>
65+
<script src="/static/js/backbone_rails_sync.js"></script>
66+
67+
<script src="/static/js/application.js"></script>
68+
</body>
69+
</html>

0 commit comments

Comments
 (0)