On Github nateabele / http-talk
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Header></soap:Header> <soap:Body> <m:SomeCommand xmlns:m="..."> <m:BodyOfMessage>...</m:BodyOfMessage> </m:SomeCommand> </soap:Body> </soap:Envelope>
<?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>40</i4></value> </param> </params> </methodCall>
application/json ...?
application/json
FALE
application/rss+xml
application/rss+xml
application/vnd.twitter.stream+json
// I can decode this!json_decode()/** * I can understand this! */ class Tweet { protected $username; protected $text; // ... }
<link rel="stylesheet" href="/css/app.css" /> <link rel="next" href="/next" /> <link rel="alternate" type="application/rss+xml" href="/posts.rss" />
<a href="/next" />Next</a> <form></form>
<link rel="foo" type="text/foo" href="http://foo" />So what's the solution? Atom? A great standard, but... you're using XML :-P
[{ title: "Finish the demo", completed: false, $links: { self: { href: "http://my.app/tasks/1138" }, owner: { href: "http://my.app/users/nate" }, subtasks: { href: "http://my.app/tasks/1138/subtasks"} } }]With a simple JSON structure, we can get the same benefits of named and annotated links
<input type="text" id="name" /> <h1>Hello!</h1> <script type="text/javascript"> $(document).ready(function() { $('#name').keydown(function(e) { $('h1').html("Hello " + e.target.value + "!") }); }); </script>
<input type="text" ng-model="name" /> <h1>Hello {{ name }}!</h1>
<ul class="greetings"></ul> <script type="text/javascript"> $(["Hello", "Hola", "Ciao"]).each(function(k, v) { $(".greeting").append("<li>" + v + " World</li>"); }); </script>
<ul> <li ng-repeat="greet in ['Hello', 'Hola', 'Ciao']"> {{ greet }} World </li> </ul>
<ul> <li ng-repeat="greet in ['Hello', 'Hola', 'Ciao']"> {{ greet }} World </li> </ul>
<div ng-controller="CheckoutController"> <div ng-repeat="item in items"> Item: <input type="text" ng-model="item.name"> Qty: <input type="number" ng-model="item.qty"> Price: <input type="number" ng-model="item.price"> Total: <div class="total"> {{ item.qty * item.price | currency: "$" }} </div> </div> <hr> <div>Total: {{ total() | currency: "$" }}</div> </div>
function CheckoutController($scope) { $scope.items = $scope.items || [{ price: 0, qty: 0 }]; $scope.total = function() { if ($scope.items[$scope.items.length - 1].qty) { $scope.items.push({ price: 0, qty: 0 }); } return $scope.items.map(function(item) { return item.qty * item.price; }).reduce(function(a, b) { return a + b; }); }; }
describe("Shopping cart", function() { describe("Checkout widget", function() { it("should create a default element", function() { var scope = {}, controller = new CheckoutController(scope); expect(scope.items.length).toBe(1); expect(scope.items[0].qty).toBe(0); expect(scope.items[0].price).toBe(0); }); }); });
describe("Shopping cart", function() { describe("Checkout widget", function() { // ... it("should calculate order total", function() { var scope = { items: [ { price: 2, qty: 4 }, { price: 10, qty: 1 } ]}; var controller = new CheckoutController(scope); expect(scope.total()).toBe(18); }); }); });
var Task = $resource("http://my.api/tasks/:id", { id: "@id" }); var newTask = new Task({ title: "New Task", description: "..." }); /* POST /tasks { "title": "New Task", ... } */ newPost.$save();
/* GET /tasks/5 */ var oneTask = Task.get({ id: 5 }); /* GET /tasks?completed=false&due=1381158984 */ var current = Task.query({ completed: false, description: "..." });
{ id: 5, title: "Something New", slug: "something-new" }
modelProvider.model("Tasks", { $instance: { finish: function() { this.completed = true; return this.$save(); }, isCompleted: function() { return !!this.completed; } } });So, does it do? - A simple way to attach business logic to resource endpoints - Leverage HTTP as it was meant to be used - All URLs come from the server
<link rel="resource" name="Tasks" href="<?=$this->url('Tasks'); ?>" />
{ "Tasks": "http://my.app/tasks", "Users": "http://my.app/users" }
{ "title": "string", "completed": "boolean", ... }