Permalink
Browse files

Added support for JSON in the request body to support Backbone.js's …

…default model behavior. Returns proper 500 status codes for create and update failures with JSON, and places error messages in the JSON responses when errors occur.
  • Loading branch information...
1 parent 58d2d51 commit bb9478dc2bd103a8205697db947ad7a61e529f10 @napcs committed Jun 8, 2011
View
@@ -47,5 +47,7 @@ If you visit http://localhost:8080/help you'll see the current API as well as a
Point your browser to http://localhost:8080/products to manage the database.
+## Changing the Port
+Edit the startup scripts to specify the port you'd like to use. It's the `httpPort` option.
View
@@ -1,3 +1,4 @@
+* 0.5.5 - Added support for JSON in the request body to support Backbone.js's default model behavior. Returns proper 500 status codes for create and update failures with JSON, and places error messages in the JSON responses when errors occur.
* 0.5.0 - Removed root element from JSON responses to support Backbone.js and other frameworks. Added JSON-P support. Pass the `?callback=foo` query param to have JSON-P returned instead.
* 0.4.0 - Edit/update of products, creation and modification of categories, and association of products to categories, cleanup of user interface and stylesheets, changed startup to be less verbose, and added reloading in development mode, making it much easier to develop.
* 0.3.1 - User interface tweaks, navigation, error messages
View
@@ -59,6 +59,10 @@ Contributing
-----------
Fork, change, send a pull request. Please, please, please write specs!
+Roadmap
+-------
+* Configuration file to set db location and file location
+* Modify startup scripts to accept a port as an option
History
------
View
@@ -27,6 +27,7 @@ end
desc "create the war file"
task :war do
+ FileUtils.rm_rf "public"
sh "jruby -S warble compiled gemjar executable war"
end
View
@@ -1,3 +1,3 @@
echo Starting QED server...
@del products.sqlite3
-@java -Xmx256M -jar webserver.war --debug=1
+@java -Xmx256M -jar webserver.war --debug=1 --httpPort=8080
View
@@ -1,3 +1,4 @@
+#!/bin/sh
echo Starting QED server...
rm products.sqlite3
-java -Xmx256M -jar webserver.war --debug=1
+java -Xmx256M -jar webserver.war --debug=1 --httpPort=8080
View
@@ -199,14 +199,14 @@
<li><code>POST</code> <a href=
'http://localhost:8080/products.json'>http://localhost:8080/products.json</a>
- creates a new product. The fields need to be nested, so name
- your form fields <code>product[name]</code> , etc.</li>
+ creates a new product. The fields need to be nested if you form-encode the data, so name your form fields <code>product[name]</code>, etc. No nesting is required if you send the JSON in the request body.</li>
+
<li><code>PUT</code> <a href=
'http://localhost:8080/products/1.json'>http://localhost:8080/products/1.json</a>
- modifies the existing product with the id of 1. You'll need
+ modifies the existing product with the id of 1. If you form-encode the data, you'll need
to post data in the nested format <code>product[name]</code>
- , etc.</li>
+ , etc. If you send JSON in the request body, no nesting is required.</li>
<li><code>DELETE</code> <a href=
'http://localhost:8080/products/1.json'>http://localhost:8080/products/1.json</a>
@@ -223,9 +223,9 @@
<a href=
'http://localhost:8080/products'>http://localhost:8080/products</a>
. The<br>
- web server expects to find the parameters nested.</p>
+ web server expects to find the parameters nested if you form-encode the data. </p>
<pre><code>&lt;input type="text" name="product[name]"&gt;</code></pre>
-
+ <p>No nesting is required if you send the JSON in the request body.</p>
<p>When a product is created successfully via JSON, you'll get
this response:</p>
<pre><code>{success: true, message: "Created Camera."}</code></pre>
@@ -283,14 +283,14 @@
<li><code>POST</code> <a href=
'http://localhost:8080/categories.json'>http://localhost:8080/categories.json</a>
- creates a new category. The fields need to be nested, so name
- your form fields <code>category[name]</code> , etc.</li>
+ creates a new category. The fields need to be nested if you form-encode the data, so name
+ your form fields <code>category[name]</code> , etc. No nesting is required if you send the JSON data in the request body.</li>
<li><code>PUT</code> <a href=
'http://localhost:8080/categories/1.json'>http://localhost:8080/categories/1.json</a>
- modifies the existing category with the id of 1. You'll need
+ modifies the existing category with the id of 1. If you form-encode the data, you'll need
to post data in the nested format <code>category[name]</code>
- , etc.</li>
+ , etc. Nesting of data is not required if you send the JSON data in the request body.</li>
<li><code>DELETE</code> <a href=
'http://localhost:8080/categories/1.json'>http://localhost:8080/categories/1.json</a>
@@ -64,8 +64,9 @@
# Update a category.
# Respond with HTML or JSON.
put "/categories/:id/update" do
+ data = params[:category] || JSON.parse(request.body.read)
@category = Category.find(params[:id])
- @category.update_attributes(params[:category])
+ @category.update_attributes(data)
if @category.save
message = "Updated #{@category.name}"
respond_to do |format|
@@ -82,7 +83,10 @@
@message = message
haml :category_edit
end
- format.json { {:success => false, :message => message}.to_json }
+ format.json do
+ status "500"
+ {:success => false, :message => message, :errors => @category.errors}.to_json
+ end
end
end
end
@@ -107,7 +111,10 @@
# Create a new product.
# Respond with HTML or JSON.
post "/categories" do
- @category = Category.new(params[:category])
+ puts params[:category]
+
+ data = params[:category] || JSON.parse(request.body.read) rescue {}
+ @category = Category.new(data)
if @category.save
message = "Created #{@category.name}"
respond_to do |format|
@@ -128,7 +135,10 @@
haml :categories
end
- format.json { {:success => false, :message => message}.to_json }
+ format.json do
+ status "500"
+ {:success => false, :message => message, :errors => @category.errors}.to_json
+ end
end
end
end
@@ -63,8 +63,9 @@
# Update a product.
# Respond with HTML or JSON.
put "/products/:id/update" do
+ data = params[:product] || JSON.parse(request.body.read) rescue {}
@product = Product.find(params[:id])
- @product.update_attributes(params[:product])
+ @product.update_attributes(data)
if @product.save
message = "Updated #{@product.name}"
respond_to do |format|
@@ -81,7 +82,10 @@
@message = message
haml :product_edit
end
- format.json { {:success => false, :message => message}.to_json }
+ format.json do
+ status "500"
+ {:success => false, :message => message, :errors => @product.errors}.to_json
+ end
end
end
end
@@ -102,7 +106,8 @@
# Create a new product.
# Respond with HTML or JSON.
post "/products" do
- @product = Product.new(params[:product])
+ data = params[:product] || JSON.parse(request.body.read) rescue {}
+ @product = Product.new(data)
if @product.save
message = "Created #{@product.name}"
respond_to do |format|
@@ -123,7 +128,10 @@
@message = message
haml :products
end
- format.json { {:success => false, :message => message}.to_json }
+ format.json do
+ status "500"
+ {:success => false, :message => message, :errors => @product.errors}.to_json
+ end
end
end
end
View
@@ -2,6 +2,8 @@ class Category < ActiveRecord::Base
has_many :product_categories
has_many :products, :through => :product_categories
+ attr_accessible :name
+
validates_uniqueness_of :name
validates_presence_of :name
View
@@ -5,6 +5,8 @@ class Product < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
+ attr_accessible :name, :description, :category_ids, :price, :image_url
+
has_many :product_categories
has_many :categories, :through => :product_categories
View
@@ -1 +1 @@
-QED_SERVER_VERSION="0.5.0"
+QED_SERVER_VERSION="0.5.5"
View
@@ -83,8 +83,8 @@ The API that's currently implemented is:
* `GET` http://localhost:8080/products.xml fetches an XML feed of the products
* `GET` http://localhost:8080/product/1.json fetches the product with the ID of 1 as JSON
* `GET` http://localhost:8080/product/1.xml fetches the product with the ID of 1 as XML
-* `POST` http://localhost:8080/products.json creates a new product. The fields need to be nested, so name your form fields `product[name]`, etc.
-* `PUT` http://localhost:8080/products/1.json modifies the existing product with the id of 1. You'll need to post data in the nested format `product[name]`, etc.
+* `POST` http://localhost:8080/products.json creates a new product. The fields need to be nested if you form-encode the data, so name your form fields `product[name]`, etc. No nesting is required if you send the JSON in the request body.
+* `PUT` http://localhost:8080/products/1.json modifies the existing product with the id of 1. If you form-encode the data, you'll need to post data in the nested format `product[name]`, etc. If you send JSON in the request body, no nesting is required.
* `DELETE` http://localhost:8080/products/1.json deletes the product with the ID of 1.
* `GET` http://localhost:8080/products.rss fetches an RSS 2.0 feed of the products
@@ -94,6 +94,8 @@ web server expects to find the parameters nested.
<input type="text" name="product[name]">
+No nesting is required if you send the JSON in the request body.
+
When a product is created successfully via JSON, you'll get this response:
{success: true, message: "Created Camera."}
@@ -126,8 +128,8 @@ This works with RSS, XML, and JSON, with or without keywords.
* `GET` http://localhost:8080/categories.xml fetches an XML feed of the categories
* `GET` http://localhost:8080/categories/1.json fetches the categoy with the ID of 1 as JSON
* `GET` http://localhost:8080/categories/1.xml fetches the category with the ID of 1 as XML
-* `POST` http://localhost:8080/categories.json creates a new category. The fields need to be nested, so name your form fields `category[name]`, etc.
-* `PUT` http://localhost:8080/categories/1.json modifies the existing category with the id of 1. You'll need to post data in the nested format `category[name]`, etc.
+* `POST` http://localhost:8080/categories.json creates a new category. The fields need to be nested if you form-encode the data, so name your form fields `category[name]`, etc. No nesting is required if you send the JSON in the request body.
+* `PUT` http://localhost:8080/categories/1.json modifies the existing category with the id of 1. If you form-encode the data, you'll need to post data in the nested format `category[name]`, etc. If you send JSON in the request body, no nesting is required.
* `DELETE` http://localhost:8080/categories/1.json deletes the category with the ID of 1.
* `GET` http://localhost:8080/categories.rss fetches an RSS 2.0 feed of the categories
* `GET` http://localhost:8080/categories/1/products.json fetches products in the category with the ID of 1
View
@@ -113,7 +113,7 @@ nav
margin: 0
padding: 0
li
- width: ($measure * 4)
+ width: ($measure * 5)
text-align: center
float: left
margin: 0 4px 0 0
View
@@ -1 +1 @@
-@java -Xmx256M -jar webserver.war --debug=1
+@java -Xmx256M -jar webserver.war --debug=1 --httpPort=8080
View
@@ -1 +1,2 @@
-java -Xmx256M -jar webserver.war --debug=1
+#!/bin/sh
+java -Xmx256M -jar webserver.war --debug=1 --httpPort=8080
View
@@ -105,15 +105,24 @@ def app
end
end
+ describe "with JSON in the body of the request" do
+ it "displays the success message" do
+ post "/products.json", {:name => "Foo"}.to_json
+ last_response.body.should == {:success => true, :message => "Created Foo"}.to_json
+ end
+ end
+
describe "with JSON" do
it "displays the success message" do
post "/products.json", {:product => {:name => "Foo"}}
last_response.body.should == {:success => true, :message => "Created Foo"}.to_json
end
+
it "displays the error message when not created" do
post "/products.json"
- last_response.body.should == {:success => false, :message => "The product was not saved."}.to_json
+ p=Product.create
+ last_response.body.should == {:success => false, :message => "The product was not saved.", :errors => p.errors}.to_json
end
end
@@ -170,6 +179,13 @@ def app
end
end
+ describe "with JSON in the request body" do
+ it "displays the success message" do
+ put "/products/#{@product.id}/update.json", {:name => "Bar"}.to_json
+ last_response.body.should == {:success => true, :message => "Updated Bar"}.to_json
+ end
+ end
+
describe "with JSON" do
it "displays the success message" do
put "/products/#{@product.id}/update.json", {:product => {:name => "Bar"}}
@@ -178,7 +194,8 @@ def app
it "displays the error message when not created" do
put "/products/#{@product.id}/update.json", {:product => {:name => ""}}
- last_response.body.should == {:success => false, :message => "The product was not saved."}.to_json
+ p = Product.create
+ last_response.body.should == {:success => false, :message => "The product was not saved.", :errors => p.errors}.to_json
end
end
@@ -293,6 +310,14 @@ def app
end
end
+ describe "with JSON in the request body" do
+ it "displays the success message" do
+ post "/categories.json", {:name => "Foo"}.to_json
+ last_response.body.should == {:success => true, :message => "Created Foo"}.to_json
+ end
+
+ end
+
describe "with JSON" do
it "displays the success message" do
post "/categories.json", {:category => {:name => "Foo"}}
@@ -301,7 +326,8 @@ def app
it "displays the error message when not created" do
post "/categories.json"
- last_response.body.should == {:success => false, :message => "The category was not saved."}.to_json
+ c = Category.create # fails, but this is just to get the error messages
+ last_response.body.should == {:success => false, :message => "The category was not saved.", :errors => c.errors}.to_json
end
end
@@ -366,7 +392,8 @@ def app
it "displays the error message when not created" do
put "/categories/#{@category.id}/update.json", {:category => {:name => ""}}
- last_response.body.should == {:success => false, :message => "The category was not saved."}.to_json
+ c=Category.create(:name => "")
+ last_response.body.should == {:success => false, :message => "The category was not saved.", :errors => c.errors}.to_json
end
end

0 comments on commit bb9478d

Please sign in to comment.