diff --git a/README.md b/README.md
index cb7dabbc..81d62e73 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,27 @@
-# React comment box example
+# React Tutorial
This is the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html).
## To use
-```
+There are several simple server implementations included. They all serve static files from `public/` and handle requests to `comments.json` to fetch or add data. Start a server with one of the following:
+
+### Node
+
+```sh
npm install
node server.js
```
-And visit http://localhost:3000/. Try opening multiple tabs!
+### Python
+
+```sh
+python server.py
+```
+
+### Ruby
+```sh
+ruby server.rb
+```
+
+And visit . Try opening multiple tabs!
diff --git a/_comments.json b/_comments.json
new file mode 100644
index 00000000..ea8ae2ce
--- /dev/null
+++ b/_comments.json
@@ -0,0 +1,6 @@
+[
+ {
+ "author": "Pete Hunt",
+ "text": "Hey there!"
+ }
+]
diff --git a/package.json b/package.json
index d1ce0454..b92ebf6b 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,6 @@
"example"
],
"author": "petehunt",
- "license": "MIT",
"bugs": {
"url": "https://github.com/reactjs/react-tutorial/issues"
},
diff --git a/css/base.css b/public/css/base.css
old mode 100755
new mode 100644
similarity index 100%
rename from css/base.css
rename to public/css/base.css
diff --git a/index.html b/public/index.html
old mode 100755
new mode 100644
similarity index 59%
rename from index.html
rename to public/index.html
index defed026..937b5212
--- a/index.html
+++ b/public/index.html
@@ -4,9 +4,9 @@
Hello React
-
-
-
+
+
+
diff --git a/scripts/example.js b/public/scripts/example.js
old mode 100755
new mode 100644
similarity index 84%
rename from scripts/example.js
rename to public/scripts/example.js
index 0d316b6e..dd29cbfd
--- a/scripts/example.js
+++ b/public/scripts/example.js
@@ -1,4 +1,16 @@
-/** @jsx React.DOM */
+/**
+ * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @jsx React.DOM
+ */
var converter = new Showdown.converter();
diff --git a/server.js b/server.js
index 7724fcf1..5dafe0ee 100644
--- a/server.js
+++ b/server.js
@@ -1,10 +1,24 @@
+/**
+ * This file provided by Facebook is for non-commercial testing and evaluation purposes only.
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+var fs = require('fs');
+var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
-var comments = [{author: 'Pete Hunt', text: 'Hey there!'}];
+var comments = JSON.parse(fs.readFileSync('_comments.json'))
-app.use('/', express.static(__dirname));
+app.use('/', express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
diff --git a/server.py b/server.py
new file mode 100644
index 00000000..7edb6f65
--- /dev/null
+++ b/server.py
@@ -0,0 +1,57 @@
+# This file provided by Facebook is for non-commercial testing and evaluation purposes only.
+# Facebook reserves all rights not expressly granted.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import os
+import json
+import cgi
+from BaseHTTPServer import HTTPServer
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+
+PUBLIC_PATH = "public"
+
+comments = json.loads(open('_comments.json').read())
+
+def sendJSON(res):
+ res.send_response(200)
+ res.send_header('Content-type', 'application/json')
+ res.end_headers()
+ res.wfile.write(json.dumps(comments))
+
+class MyHandler(SimpleHTTPRequestHandler):
+ def translate_path(self, path):
+ root = os.getcwd()
+ path = PUBLIC_PATH + path
+ return os.path.join(root, path)
+
+ def do_GET(self):
+ if (self.path == "/comments.json"):
+ sendJSON(self)
+ else:
+ SimpleHTTPRequestHandler.do_GET(self)
+
+ def do_POST(self):
+ if (self.path == "/comments.json"):
+ form = cgi.FieldStorage(
+ fp=self.rfile,
+ headers=self.headers,
+ environ={'REQUEST_METHOD':'POST',
+ 'CONTENT_TYPE':self.headers['Content-Type']}
+ )
+
+ # Save the data
+ comments.append({u"author": form.getfirst("author"), u"text": form.getfirst("text")})
+ sendJSON(self)
+ else:
+ SimpleHTTPRequestHandler.do_POST(self)
+
+if __name__ == '__main__':
+ print "Server started: http://localhost:3000/"
+ httpd = HTTPServer(('127.0.0.1', 3000), MyHandler)
+ httpd.serve_forever()
diff --git a/server.rb b/server.rb
new file mode 100644
index 00000000..1838a289
--- /dev/null
+++ b/server.rb
@@ -0,0 +1,34 @@
+# This file provided by Facebook is for non-commercial testing and evaluation purposes only.
+# Facebook reserves all rights not expressly granted.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+require 'webrick'
+require 'json'
+
+comments = react_version = JSON.parse(File.read('./_comments.json'))
+
+puts 'Server started: http://localhost:3000/'
+
+root = File.expand_path './public'
+server = WEBrick::HTTPServer.new :Port => 3000, :DocumentRoot => root
+
+server.mount_proc '/comments.json' do |req, res|
+ if req.request_method == 'POST'
+ # Assume it's well formed
+ comments << req.query
+ end
+
+ # always return json
+ res['Content-Type'] = 'application/json'
+ res.body = comments.to_json
+end
+
+trap 'INT' do server.shutdown end
+
+server.start