diff --git a/.gitignore b/.gitignore index daeba5f9..5c860964 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *~ node_modules .DS_Store +npm-debug.log +public/js/app.js diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 00000000..b93010dd --- /dev/null +++ b/.jscsrc @@ -0,0 +1,51 @@ +{ + "esprima" : "esprima-fb", + "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", + "requireCurlyBraces": ["for", "while", "do", "try", "catch", "if", "else"], + "requireOperatorBeforeLineBreak": [ + "=", + "+", + "-", + "/", + "*", + "==", + "===", + "!=", + "!==", + ">", + ">=", + "<", + "<=" + ], + + "maximumLineLength": { + "value": 120, + "allowComments": true, + "allowRegex": true + }, + "validateQuoteMarks": { + "escape": true, + "mark": "\"" + }, + "validateIndentation": 2, + + "disallowMultipleLineStrings": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowKeywords": ["with"], + "disallowKeywordsOnNewLine": ["else"], + + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], + "requireSpaceAfterLineComment": true, + "requireSpaceAfterBinaryOperators": true, + "requireSpacesInConditionalExpression": true, + "requireSpaceBeforeBlockStatements": true, + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + + "validateJSDoc": { + "checkParamNames": true, + "requireParamTypes": true + } +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..aaf0f3e3 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,40 @@ +{ + "eqeqeq": true, + "forin": true, + "freeze": true, + "immed": true, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "noempty": true, + "nonew": true, + "regexp": false, + "undef": true, + "unused": true, + "maxcomplexity": 12, + + "laxbreak": true, + "sub": true, + "expr": true, + + "browser": true, + "node": true, + "devel": true, + + "indent": 2, + "maxlen": 120, + "esnext": true, + + "globals": { + "define": true, + "require": true, + "describe": true, + "context": true, + "it": true, + "expect": true, + "before": true, + "after": true, + "beforeEach": true, + "afterEach": true + } +} diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..c43e1055 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +0.12 diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..6cb01a19 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,82 @@ +var path = require("path"); + +module.exports = function (grunt) { + require("load-grunt-tasks")(grunt); + + var INPUT_PATH = "./app/main.js"; + var OUTPUT_PATH = "./dist/js/app.js"; + + grunt.registerTask("app", "concurrent:app"); + grunt.registerTask("release", ["browserify:release", "exorcise", "uglify:release"]); + + grunt.initConfig({ + connect: { + app: { + options: { + port: 4500, + keepalive: true, + base: path.join(__dirname, "public") + } + } + }, + concurrent: { + app: { + tasks: ["browserify:watch_app", "connect:app"], + options: { + logConcurrentOutput: true + } + } + }, + + browserify: { + release: browserifyOptions({ + debug: true + }), + watch: browserifyOptions({ + watch: true, + debug: true + }), + watch_app: browserifyOptions({ + watch: true, + debug: true, + src: "./app/main.js", + dest: "./public/js/app.js" + }), + }, + exorcise: { + bundle: { + files: { + "./public/js/app.map": [OUTPUT_PATH] + } + } + }, + uglify: { + release: { + options: { + sourceMap: true, + sourceMapIncludeSources: true, + sourceMapIn: "public/js/app.map" + }, + files: { + "public/js/app.min.js": ["public/js/app.js"] + } + } + } + }); + + function browserifyOptions(options) { + options || (options = {}); + + return { + src: [options.src || INPUT_PATH], + dest: options.dest || OUTPUT_PATH, + options: { + watch: !!options.watch, + keepAlive: !!options.watch, + browserifyOptions: { + debug: !!options.debug + } + } + }; + } +}; diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..cc06b52a --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +BIN = ./node_modules/.bin + +.PHONY: bootstrap test lint app release; + +SRC = $(shell find ./app ./Gruntfile.js -type f -name '*.js') + +start: + @npm start + +server: + @node server/server + +lint: + @$(BIN)/jscs $(SRC); + @$(BIN)/jsxhint $(SRC); + +release: + @$(BIN)/grunt release + +bootstrap: package.json + @npm install + +test: lint + @echo "no test yet" diff --git a/README.md b/README.md index b7517b72..606d0613 100644 --- a/README.md +++ b/README.md @@ -1,40 +1 @@ -[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) - -# 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 -``` - -### Python - -```sh -pip install -r requirements.txt -python server.py -``` - -### Ruby -```sh -ruby server.rb -``` - -### PHP -```sh -php server.php -``` - -### Go -```sh -go run server.go -``` - -And visit . Try opening multiple tabs! +# React tutorial diff --git a/app.json b/app.json deleted file mode 100644 index a2a7f892..00000000 --- a/app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "React Tutorial Server", - "description": "Code from the React tutorial", - "keywords": [ "react", "reactjs", "tutorial" ], - "repository": "https://github.com/reactjs/react-tutorial", - "website": "http://facebook.github.io/react/docs/tutorial.html", - "success_url": "/", - "env" : { - "BUILDPACK_URL": "https://github.com/heroku/heroku-buildpack-nodejs.git" - } -} - diff --git a/app/components/Comment.js b/app/components/Comment.js new file mode 100644 index 00000000..752492ed --- /dev/null +++ b/app/components/Comment.js @@ -0,0 +1,20 @@ +var React = require("react"); +var Showdown = require("showdown"); + +var Converter = Showdown.Converter(); + +var Comment = React.createClass({ + render: function () { + var rawMarkup= Converter.makeHtml(this.props.text); + return ( +
+

+ {this.props.author} +

+ +
+ ); + } +}); + +module.exports = Comment; diff --git a/app/components/CommentBox.js b/app/components/CommentBox.js new file mode 100644 index 00000000..46e5f626 --- /dev/null +++ b/app/components/CommentBox.js @@ -0,0 +1,59 @@ +var $ = require("jquery"); +var React = require("react"); +var CommentList = require("./CommentList"); +var CommentForm = require("./CommentForm"); + +var CommentBox = React.createClass({ + getInitialState: function () { + return { + comments: [] + }; + }, + loadCommentsFromServer: function () { + $.ajax({ + url: this.props.url, + dataType: "json", + success: function(data) { + this.setState({comments: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error(this.props.url, status, err.toString()); + }.bind(this) + }); + }, + addCommentLocally: function (comment) { + var comments = this.state.comments; + var newComments = comments.concat([comment]); + this.setState({comments: newComments}); + }, + handleCommentSubmit: function (comment) { + this.addCommentLocally(comment); + $.ajax({ + url: this.props.url, + dataType: "json", + type: "POST", + data: comment, + success: function(data) { + this.setState({comments: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error(this.props.url, status, err.toString()); + }.bind(this) + }); + }, + componentDidMount: function() { + this.loadCommentsFromServer(); + setInterval(this.loadCommentsFromServer, this.props.pollInterval); + }, + render: function() { + return ( +
+

Comments

+ + +
+ ); + } +}); + +module.exports = CommentBox; diff --git a/app/components/CommentForm.js b/app/components/CommentForm.js new file mode 100644 index 00000000..e9256b1e --- /dev/null +++ b/app/components/CommentForm.js @@ -0,0 +1,39 @@ +var React = require("react"); + +var CommentForm = React.createClass({ + resetForm: function () { + React.findDOMNode(this.refs.author).value = ""; + React.findDOMNode(this.refs.text).value = ""; + }, + getInputComment: function() { + return { + author: React.findDOMNode(this.refs.author).value.trim(), + text: React.findDOMNode(this.refs.text).value.trim() + }; + }, + isValidComment: function () { + var comment = this.getInputComment(); + if (!comment.text || !comment.author) { + return false; + } + return true; + }, + handleSubmit: function (e) { + e.preventDefault(); + if (this.isValidComment()) { + this.props.onCommentSubmit(this.getInputComment()); + this.resetForm(); + } + }, + render: function() { + return ( +
+ + + +
+ ); + } +}); + +module.exports = CommentForm; diff --git a/app/components/CommentList.js b/app/components/CommentList.js new file mode 100644 index 00000000..e37da789 --- /dev/null +++ b/app/components/CommentList.js @@ -0,0 +1,18 @@ +var React = require("react"); +var Comment = require("./Comment"); + +var CommentList = React.createClass({ + render: function() { + var comments = this.props.comments + .map((comment) => ); + + return ( +
+ { comments } +
+ ); + + } +}); + +module.exports = CommentList; diff --git a/app/main.js b/app/main.js new file mode 100644 index 00000000..4c372afa --- /dev/null +++ b/app/main.js @@ -0,0 +1,9 @@ +var React = require("react"); +var CommentBox = require("./components/CommentBox"); + +function render(element, id) { + React.render(, + document.getElementById(id)); +} + +render(CommentBox, "content"); diff --git a/comments.json b/comments.json deleted file mode 100644 index 32938452..00000000 --- a/comments.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "author": "Pete Hunt", - "text": "Hey there!" - } -] diff --git a/package.json b/package.json index e7491981..d2a51008 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,43 @@ { - "name": "react-tutorial", - "version": "0.0.0", - "description": "Code from the React tutorial.", - "main": "server.js", + "name": "react-template", + "version": "0.0.1", + "main": "app/main.js", + "browserify": { + "transform": [ + "envify", + "babelify" + ] + }, + "scripts": { + "start": "grunt app" + }, "dependencies": { "body-parser": "^1.4.3", - "express": "^4.4.5" + "express": "^4.4.5", + "jquery": "^2.1.3", + "react": "^0.13.1", + "showdown": "git://github.com/showdownjs/showdown.git#showdown2" }, - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server.js" + "devDependencies": { + "grunt": "^0.4.5", + "aliasify": "^1.5.1", + "babelify": "^5.0.4", + "browserify": "^6.1.0", + "browserify-shim": "^3.8.3", + "envify": "^3.2.0", + "grunt-browserify": "^3.2.1", + "grunt-cli": "^0.1.13", + "grunt-concurrent": "^1.0.0", + "grunt-contrib-connect": "^0.9.0", + "grunt-contrib-uglify": "^0.6.0", + "grunt-contrib-watch": "^0.6.1", + "grunt-exorcise": "^0.2.0", + "grunt-nodemon": "^0.3.0", + "load-grunt-tasks": "1.0.0" }, "repository": { "type": "git", - "url": "https://github.com/reactjs/react-tutorial.git" + "url": "https://github.com/giuse88/react-tutorial.git" }, "keywords": [ "react", @@ -22,12 +45,7 @@ "comment", "example" ], - "author": "petehunt", - "bugs": { - "url": "https://github.com/reactjs/react-tutorial/issues" - }, - "homepage": "https://github.com/reactjs/react-tutorial", - "engines" : { - "node" : "0.12.x" + "engines": { + "node": "0.12.x" } } diff --git a/public/index.html b/public/index.html index 0c3a3387..671181b1 100644 --- a/public/index.html +++ b/public/index.html @@ -1,16 +1,11 @@ - Hello React - + React App - - - -
- + diff --git a/public/scripts/example.js b/public/scripts/example.js deleted file mode 100644 index 4fa306b8..00000000 --- a/public/scripts/example.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * 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 converter = new Showdown.converter(); - -var Comment = React.createClass({ - render: function() { - var rawMarkup = converter.makeHtml(this.props.children.toString()); - return ( -
-

- {this.props.author} -

- -
- ); - } -}); - -var CommentBox = React.createClass({ - loadCommentsFromServer: function() { - $.ajax({ - url: this.props.url, - dataType: 'json', - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - handleCommentSubmit: function(comment) { - var comments = this.state.data; - comments.push(comment); - this.setState({data: comments}, function() { - // `setState` accepts a callback. To avoid (improbable) race condition, - // `we'll send the ajax request right after we optimistically set the new - // `state. - $.ajax({ - url: this.props.url, - dataType: 'json', - type: 'POST', - data: comment, - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }); - }, - getInitialState: function() { - return {data: []}; - }, - componentDidMount: function() { - this.loadCommentsFromServer(); - setInterval(this.loadCommentsFromServer, this.props.pollInterval); - }, - render: function() { - return ( -
-

Comments

- - -
- ); - } -}); - -var CommentList = React.createClass({ - render: function() { - var commentNodes = this.props.data.map(function(comment, index) { - return ( - // `key` is a React-specific concept and is not mandatory for the - // purpose of this tutorial. if you're curious, see more here: - // http://facebook.github.io/react/docs/multiple-components.html#dynamic-children - - {comment.text} - - ); - }); - return ( -
- {commentNodes} -
- ); - } -}); - -var CommentForm = React.createClass({ - handleSubmit: function(e) { - e.preventDefault(); - var author = React.findDOMNode(this.refs.author).value.trim(); - var text = React.findDOMNode(this.refs.text).value.trim(); - if (!text || !author) { - return; - } - this.props.onCommentSubmit({author: author, text: text}); - React.findDOMNode(this.refs.author).value = ''; - React.findDOMNode(this.refs.text).value = ''; - }, - render: function() { - return ( -
- - - -
- ); - } -}); - -React.render( - , - document.getElementById('content') -); diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 632a1efa..00000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Flask==0.10.1 diff --git a/server.go b/server.go deleted file mode 100644 index cba31fd9..00000000 --- a/server.go +++ /dev/null @@ -1,96 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "sync" -) - -type comment struct { - Author string `json:"author"` - Text string `json:"text"` -} - -const dataFile = "./comments.json" - -var commentMutex = new(sync.Mutex) - -// Handle comments -func handleComments(w http.ResponseWriter, r *http.Request) { - // Since multiple requests could come in at once, ensure we have a lock - // around all file operations - commentMutex.Lock() - defer commentMutex.Unlock() - - // Stat the file, so we can find it's current permissions - fi, err := os.Stat(dataFile) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - // Read the comments from the file. - commentData, err := ioutil.ReadFile(dataFile) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - switch r.Method { - case "POST": - // Decode the JSON data - comments := make([]comment, 0) - if err := json.Unmarshal(commentData, &comments); err != nil { - http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - // Add a new comment to the in memory slice of comments - comments = append(comments, comment{Author: r.FormValue("author"), Text: r.FormValue("text")}) - - // Marshal the comments to indented json. - commentData, err = json.MarshalIndent(comments, "", " ") - if err != nil { - http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError) - return - } - - // Write out the comments to the file, preserving permissions - err := ioutil.WriteFile(dataFile, commentData, fi.Mode()) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Cache-Control", "no-cache") - io.Copy(w, bytes.NewReader(commentData)) - - case "GET": - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Cache-Control", "no-cache") - // stream the contents of the file to the response - io.Copy(w, bytes.NewReader(commentData)) - - default: - // Don't know the method, so error - http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed) - } -} - -func main() { - port := os.Getenv("PORT") - if port == "" { - port = "3000" - } - http.HandleFunc("/comments.json", handleComments) - http.Handle("/", http.FileServer(http.Dir("./public"))) - log.Println("Server started: http://localhost:" + port) - log.Fatal(http.ListenAndServe(":"+port, nil)) -} diff --git a/server.js b/server.js deleted file mode 100644 index fd4b81dd..00000000 --- a/server.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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(); - -app.set('port', (process.env.PORT || 3000)); - -app.use('/', express.static(path.join(__dirname, 'public'))); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: true})); - -app.get('/comments.json', function(req, res) { - fs.readFile('comments.json', function(err, data) { - res.setHeader('Content-Type', 'application/json'); - res.send(data); - }); -}); - -app.post('/comments.json', function(req, res) { - fs.readFile('comments.json', function(err, data) { - var comments = JSON.parse(data); - comments.push(req.body); - fs.writeFile('comments.json', JSON.stringify(comments, null, 4), function(err) { - res.setHeader('Content-Type', 'application/json'); - res.setHeader('Cache-Control', 'no-cache'); - res.send(JSON.stringify(comments)); - }); - }); -}); - - -app.listen(app.get('port'), function() { - console.log('Server started: http://localhost:' + app.get('port') + '/'); -}); diff --git a/server.php b/server.php deleted file mode 100644 index e140ea83..00000000 --- a/server.php +++ /dev/null @@ -1,41 +0,0 @@ - $_POST['author'], - 'text' => $_POST['text']]; - - $comments = json_encode($commentsDecoded, JSON_PRETTY_PRINT); - file_put_contents('comments.json', $comments); - } - header('Content-Type: application/json'); - header('Cache-Control: no-cache'); - echo $comments; - break; - default: - return false; - } -} - diff --git a/server.py b/server.py deleted file mode 100644 index 730e39b3..00000000 --- a/server.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 json -import os -from flask import Flask, Response, request - -app = Flask(__name__, static_url_path='', static_folder='public') -app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html')) - -@app.route('/comments.json', methods=['GET', 'POST']) -def comments_handler(): - - with open('comments.json', 'r') as file: - comments = json.loads(file.read()) - - if request.method == 'POST': - comments.append(request.form.to_dict()) - - with open('comments.json', 'w') as file: - file.write(json.dumps(comments, indent=4, separators=(',', ': '))) - - return Response(json.dumps(comments), mimetype='application/json', headers={'Cache-Control': 'no-cache'}) - -if __name__ == '__main__': - app.run(port=int(os.environ.get("PORT",3000))) diff --git a/server.rb b/server.rb deleted file mode 100644 index 18099ae5..00000000 --- a/server.rb +++ /dev/null @@ -1,38 +0,0 @@ -# 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' - -port = ENV['PORT'].nil? ? 3000 : ENV['PORT'].to_i - -puts "Server started: http://localhost:#{port}/" - -root = File.expand_path './public' -server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => root - -server.mount_proc '/comments.json' do |req, res| - comments = JSON.parse(File.read('./comments.json')) - - if req.request_method == 'POST' - # Assume it's well formed - comments << req.query - File.write('./comments.json', JSON.pretty_generate(comments, :indent => ' ')) - end - - # always return json - res['Content-Type'] = 'application/json' - res['Cache-Control'] = 'no-cache' - res.body = JSON.generate(comments) -end - -trap 'INT' do server.shutdown end - -server.start diff --git a/server/comments.json b/server/comments.json new file mode 100644 index 00000000..c45642d0 --- /dev/null +++ b/server/comments.json @@ -0,0 +1,19 @@ +[ + { + "id": "1", + "author": "Pete Hunt", + "text": "Hey there!" + }, + { + "author": "test", + "text": "teset" + }, + { + "author": "Giuseppe", + "text": "React is awesome" + }, + { + "author": "dsf", + "text": "dfsf" + } +] \ No newline at end of file diff --git a/server/server.js b/server/server.js new file mode 100644 index 00000000..a94ea895 --- /dev/null +++ b/server/server.js @@ -0,0 +1,39 @@ +var fs = require("fs"); +var path = require("path"); +var express = require("express"); +var bodyParser = require("body-parser"); +var app = express(); + +app.set("port", (process.env.PORT || 3000)); + + +app.use("/", express.static(path.join(__dirname, "../public"))); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({extended: true})); + +var commentsFile = path.join(__dirname, "comments.json"); + +app.get("/comments.json", function(req, res) { + fs.readFile(commentsFile, function(err, data) { + console.log(err, data); + res.setHeader("Content-Type", "application/json"); + res.send(data); + }); +}); + +app.post("/comments.json", function(req, res) { + fs.readFile(commentsFile, function(err, data) { + var comments = JSON.parse(data); + comments.push(req.body); + fs.writeFile(commentsFile, JSON.stringify(comments, null, 4), function() { + res.setHeader("Content-Type", "application/json"); + res.setHeader("Cache-Control", "no-cache"); + res.send(JSON.stringify(comments)); + }); + }); +}); + + +app.listen(app.get("port"), function() { + console.log("Server started: http://localhost:" + app.get("port") + "/"); +});