cloudkit 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +6 -0
 - data/COPYING +1 -1
 - data/README +7 -7
 - data/TODO +4 -3
 - data/cloudkit.gemspec +4 -4
 - data/doc/curl.html +133 -72
 - data/doc/index.html +1 -1
 - data/doc/rest-api.html +109 -0
 - data/examples/3.ru +1 -1
 - data/examples/4.ru +1 -1
 - data/examples/5.ru +2 -2
 - data/examples/6.ru +2 -2
 - data/lib/cloudkit.rb +7 -1
 - data/lib/cloudkit/constants.rb +40 -0
 - data/lib/cloudkit/oauth_filter.rb +9 -9
 - data/lib/cloudkit/oauth_store.rb +1 -5
 - data/lib/cloudkit/openid_filter.rb +11 -11
 - data/lib/cloudkit/rack/builder.rb +3 -3
 - data/lib/cloudkit/request.rb +18 -12
 - data/lib/cloudkit/service.rb +26 -8
 - data/lib/cloudkit/store.rb +109 -34
 - data/lib/cloudkit/store/adapter.rb +0 -1
 - data/lib/cloudkit/store/response.rb +1 -0
 - data/lib/cloudkit/store/sql_adapter.rb +2 -2
 - data/lib/cloudkit/util.rb +2 -37
 - data/test/helper.rb +6 -39
 - data/test/oauth_filter_test.rb +13 -13
 - data/test/openid_filter_test.rb +14 -8
 - data/test/request_test.rb +6 -6
 - data/test/service_test.rb +337 -113
 - data/test/store_test.rb +4 -10
 - metadata +4 -4
 - data/lib/cloudkit/rack/lint.rb +0 -12
 
    
        data/CHANGES
    CHANGED
    
    | 
         @@ -1,3 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            0.10.0
         
     | 
| 
      
 2 
     | 
    
         
            +
            - Updated for Rack 0.9
         
     | 
| 
      
 3 
     | 
    
         
            +
            - Added batch URI resolution for resource collections
         
     | 
| 
      
 4 
     | 
    
         
            +
            - Refactored use of constants
         
     | 
| 
      
 5 
     | 
    
         
            +
            - Updated documentation
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       1 
7 
     | 
    
         
             
            0.9.1
         
     | 
| 
       2 
8 
     | 
    
         
             
            - Fixed Rack::Lint/rackup errors related to Content-Type headers
         
     | 
| 
       3 
9 
     | 
    
         
             
            - Patched Rack to support StringIO#string in Rack::Lint::InputWrapper
         
     | 
    
        data/COPYING
    CHANGED
    
    
    
        data/README
    CHANGED
    
    | 
         @@ -24,7 +24,7 @@ In a different rackup file: 
     | 
|
| 
       24 
24 
     | 
    
         
             
              require 'cloudkit'
         
     | 
| 
       25 
25 
     | 
    
         
             
              contain :notes, :todos
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
            The  
     | 
| 
      
 27 
     | 
    
         
            +
            The above provides the same API as example 1 with added authentication and authorization via OpenID and OAuth, respectively.
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
            An explicit version of example 2, minus the default developer landing page:
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
         @@ -33,19 +33,19 @@ An explicit version of example 2, minus the default developer landing page: 
     | 
|
| 
       33 
33 
     | 
    
         
             
              use CloudKit::OAuthFilter
         
     | 
| 
       34 
34 
     | 
    
         
             
              use CloudKit::OpenIDFilter
         
     | 
| 
       35 
35 
     | 
    
         
             
              use CloudKit::Service, :collections => [:notes, :todos]
         
     | 
| 
       36 
     | 
    
         
            -
              run lambda {|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
         
     | 
| 
      
 36 
     | 
    
         
            +
              run lambda {|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
            The same as above, using MySQL:
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
              require 'cloudkit'
         
     | 
| 
       41 
     | 
    
         
            -
              use Rack::Config  
     | 
| 
       42 
     | 
    
         
            -
                env[ 
     | 
| 
       43 
     | 
    
         
            -
               
     | 
| 
      
 41 
     | 
    
         
            +
              use Rack::Config do |env|
         
     | 
| 
      
 42 
     | 
    
         
            +
                env[CLOUDKIT_STORAGE_URI] = 'mysql://user:pass@localhost/cloudkit_example'
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
       44 
44 
     | 
    
         
             
              use Rack::Pool::Session
         
     | 
| 
       45 
45 
     | 
    
         
             
              use CloudKit::OAuthFilter
         
     | 
| 
       46 
46 
     | 
    
         
             
              use CloudKit::OpenIDFilter
         
     | 
| 
       47 
47 
     | 
    
         
             
              use CloudKit::Service, :collections => [:notes, :todos]
         
     | 
| 
       48 
     | 
    
         
            -
              run lambda {|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
         
     | 
| 
      
 48 
     | 
    
         
            +
              run lambda {|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
            See the examples directory for more.
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
         @@ -61,7 +61,7 @@ Source: http://github.com/jcrosby/cloudkit 
     | 
|
| 
       61 
61 
     | 
    
         | 
| 
       62 
62 
     | 
    
         
             
            ===License
         
     | 
| 
       63 
63 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
            Copyright (c) 2008 Jon Crosby http://joncrosby.me
         
     | 
| 
      
 64 
     | 
    
         
            +
            Copyright (c) 2008, 2009 Jon Crosby http://joncrosby.me
         
     | 
| 
       65 
65 
     | 
    
         | 
| 
       66 
66 
     | 
    
         
             
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
       67 
67 
     | 
    
         
             
            a copy of this software and associated documentation files (the
         
     | 
    
        data/TODO
    CHANGED
    
    | 
         @@ -2,9 +2,6 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            - jquery.cloudkit
         
     | 
| 
       3 
3 
     | 
    
         
             
                titanium/gears
         
     | 
| 
       4 
4 
     | 
    
         
             
            - openid sreg
         
     | 
| 
       5 
     | 
    
         
            -
            - titanium middleware
         
     | 
| 
       6 
     | 
    
         
            -
                version/upgrade middleware
         
     | 
| 
       7 
     | 
    
         
            -
                rake automation
         
     | 
| 
       8 
5 
     | 
    
         
             
            - oauth token management
         
     | 
| 
       9 
6 
     | 
    
         
             
            - oauth consumer registration
         
     | 
| 
       10 
7 
     | 
    
         
             
            - acls i.e. allowed methods per user per uri
         
     | 
| 
         @@ -14,9 +11,13 @@ 
     | 
|
| 
       14 
11 
     | 
    
         
             
            - custom templates for openid / oauth
         
     | 
| 
       15 
12 
     | 
    
         | 
| 
       16 
13 
     | 
    
         
             
            Backlog
         
     | 
| 
      
 14 
     | 
    
         
            +
            - batch updates (post, put, delete)
         
     | 
| 
       17 
15 
     | 
    
         
             
            - expect header/100-continue
         
     | 
| 
       18 
16 
     | 
    
         
             
            - deployable gem + admin app
         
     | 
| 
       19 
17 
     | 
    
         
             
            - cappuccino adapter
         
     | 
| 
       20 
18 
     | 
    
         
             
            - sproutcore adapter
         
     | 
| 
       21 
19 
     | 
    
         
             
            - tokyocabinet
         
     | 
| 
       22 
20 
     | 
    
         
             
            - ldap adapter for UserStore
         
     | 
| 
      
 21 
     | 
    
         
            +
            - titanium middleware
         
     | 
| 
      
 22 
     | 
    
         
            +
                version/upgrade middleware
         
     | 
| 
      
 23 
     | 
    
         
            +
                rake automation
         
     | 
    
        data/cloudkit.gemspec
    CHANGED
    
    | 
         @@ -2,8 +2,8 @@ Gem::Specification.new do |s| 
     | 
|
| 
       2 
2 
     | 
    
         
             
              s.specification_version = 2 if s.respond_to? :specification_version=
         
     | 
| 
       3 
3 
     | 
    
         
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         
     | 
| 
       4 
4 
     | 
    
         
             
              s.name              = "cloudkit"
         
     | 
| 
       5 
     | 
    
         
            -
              s.version           = "0. 
     | 
| 
       6 
     | 
    
         
            -
              s.date              = "2008- 
     | 
| 
      
 5 
     | 
    
         
            +
              s.version           = "0.10.0"
         
     | 
| 
      
 6 
     | 
    
         
            +
              s.date              = "2008-01-09"
         
     | 
| 
       7 
7 
     | 
    
         
             
              s.summary           = "An Open Web JSON Appliance."
         
     | 
| 
       8 
8 
     | 
    
         
             
              s.description       = "An Open Web JSON Appliance."
         
     | 
| 
       9 
9 
     | 
    
         
             
              s.authors           = ["Jon Crosby"]
         
     | 
| 
         @@ -32,13 +32,13 @@ Gem::Specification.new do |s| 
     | 
|
| 
       32 
32 
     | 
    
         
             
                examples/6.ru
         
     | 
| 
       33 
33 
     | 
    
         
             
                examples/TOC
         
     | 
| 
       34 
34 
     | 
    
         
             
                lib/cloudkit.rb
         
     | 
| 
      
 35 
     | 
    
         
            +
                lib/cloudkit/constants.rb
         
     | 
| 
       35 
36 
     | 
    
         
             
                lib/cloudkit/flash_session.rb
         
     | 
| 
       36 
37 
     | 
    
         
             
                lib/cloudkit/oauth_filter.rb
         
     | 
| 
       37 
38 
     | 
    
         
             
                lib/cloudkit/oauth_store.rb
         
     | 
| 
       38 
39 
     | 
    
         
             
                lib/cloudkit/openid_filter.rb
         
     | 
| 
       39 
40 
     | 
    
         
             
                lib/cloudkit/openid_store.rb
         
     | 
| 
       40 
41 
     | 
    
         
             
                lib/cloudkit/rack/builder.rb
         
     | 
| 
       41 
     | 
    
         
            -
                lib/cloudkit/rack/lint.rb
         
     | 
| 
       42 
42 
     | 
    
         
             
                lib/cloudkit/rack/router.rb
         
     | 
| 
       43 
43 
     | 
    
         
             
                lib/cloudkit/request.rb
         
     | 
| 
       44 
44 
     | 
    
         
             
                lib/cloudkit/service.rb
         
     | 
| 
         @@ -73,7 +73,7 @@ Gem::Specification.new do |s| 
     | 
|
| 
       73 
73 
     | 
    
         
             
              s.test_files        = s.files.select {|path| path =~ /^test\/.*_test.rb/}
         
     | 
| 
       74 
74 
     | 
    
         
             
              s.rubyforge_project = "cloudkit"
         
     | 
| 
       75 
75 
     | 
    
         
             
              s.rubygems_version  = "1.1.1"
         
     | 
| 
       76 
     | 
    
         
            -
              s.add_dependency 'rack', '~> 0. 
     | 
| 
      
 76 
     | 
    
         
            +
              s.add_dependency 'rack', '~> 0.9'
         
     | 
| 
       77 
77 
     | 
    
         
             
              s.add_dependency 'rack-config', '>= 0.9'
         
     | 
| 
       78 
78 
     | 
    
         
             
              s.add_dependency 'uuid', '= 2.0.1'
         
     | 
| 
       79 
79 
     | 
    
         
             
              s.add_dependency 'sequel', '= 2.6.0'
         
     | 
    
        data/doc/curl.html
    CHANGED
    
    | 
         @@ -37,6 +37,17 @@ If you haven't already installed the gem: 
     | 
|
| 
       37 
37 
     | 
    
         
             
              </div>
         
     | 
| 
       38 
38 
     | 
    
         
             
            </p>
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
      
 40 
     | 
    
         
            +
            <p>
         
     | 
| 
      
 41 
     | 
    
         
            +
            If you already have the gem, make sure you're running the latest version (0.10.0):
         
     | 
| 
      
 42 
     | 
    
         
            +
              <div class="code">
         
     | 
| 
      
 43 
     | 
    
         
            +
                $ gem list cloudkit<br/>
         
     | 
| 
      
 44 
     | 
    
         
            +
                cloudkit (0.9.1) <-- need to upgrade<br/>
         
     | 
| 
      
 45 
     | 
    
         
            +
                $ gem update cloudkit<br/>
         
     | 
| 
      
 46 
     | 
    
         
            +
                $ gem list cloudkit<br/>
         
     | 
| 
      
 47 
     | 
    
         
            +
                cloudkit (0.10.0, 0.9.1) <-- 0.10.0 is now in the list
         
     | 
| 
      
 48 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 49 
     | 
    
         
            +
            </p>
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
       40 
51 
     | 
    
         
             
            <p>
         
     | 
| 
       41 
52 
     | 
    
         
             
            Create a rackup file named config.ru, containing these two lines of code:
         
     | 
| 
       42 
53 
     | 
    
         
             
              <div class="code">
         
     | 
| 
         @@ -46,9 +57,9 @@ Create a rackup file named config.ru, containing these two lines of code: 
     | 
|
| 
       46 
57 
     | 
    
         
             
            </p>
         
     | 
| 
       47 
58 
     | 
    
         | 
| 
       48 
59 
     | 
    
         
             
            <p>
         
     | 
| 
       49 
     | 
    
         
            -
            Run the app 
     | 
| 
      
 60 
     | 
    
         
            +
            Run the app:
         
     | 
| 
       50 
61 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       51 
     | 
    
         
            -
                $  
     | 
| 
      
 62 
     | 
    
         
            +
                $ rackup config.ru
         
     | 
| 
       52 
63 
     | 
    
         
             
              </div>
         
     | 
| 
       53 
64 
     | 
    
         
             
            </p>
         
     | 
| 
       54 
65 
     | 
    
         | 
| 
         @@ -56,13 +67,12 @@ Run the app using <a href="http://code.macournoyer.com/thin">Thin</a>: 
     | 
|
| 
       56 
67 
     | 
    
         
             
            CloudKit is discoverable from top to bottom. Let's see what resource collections
         
     | 
| 
       57 
68 
     | 
    
         
             
            we're hosting:
         
     | 
| 
       58 
69 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       59 
     | 
    
         
            -
                $ curl -i http://localhost: 
     | 
| 
      
 70 
     | 
    
         
            +
                $ curl -i http://localhost:9292/cloudkit-meta<br/>
         
     | 
| 
       60 
71 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       61 
72 
     | 
    
         
             
                ETag: "ef2f29b1834ef8c2bf0d8f1abb100177"<br/>
         
     | 
| 
       62 
73 
     | 
    
         
             
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
       63 
74 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       64 
     | 
    
         
            -
                Content-Length: 20<br/>
         
     | 
| 
       65 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 75 
     | 
    
         
            +
                Content-Length: 20<br/><br/>
         
     | 
| 
       66 
76 
     | 
    
         | 
| 
       67 
77 
     | 
    
         
             
                {"uris":["\/notes"]}
         
     | 
| 
       68 
78 
     | 
    
         
             
              </div>
         
     | 
| 
         @@ -71,39 +81,47 @@ we're hosting: 
     | 
|
| 
       71 
81 
     | 
    
         
             
            <p>
         
     | 
| 
       72 
82 
     | 
    
         
             
            See what we can do with these note resources:
         
     | 
| 
       73 
83 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       74 
     | 
    
         
            -
                $ curl -i -XOPTIONS http://localhost: 
     | 
| 
      
 84 
     | 
    
         
            +
                $ curl -i -XOPTIONS http://localhost:9292/notes<br/>
         
     | 
| 
       75 
85 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       76 
86 
     | 
    
         
             
                Content-Length: 0<br/>
         
     | 
| 
       77 
87 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       78 
88 
     | 
    
         
             
                Allow: GET, HEAD, POST, OPTIONS<br/>
         
     | 
| 
       79 
     | 
    
         
            -
                Connection: keep-alive<br/>
         
     | 
| 
       80 
89 
     | 
    
         
             
              </div>
         
     | 
| 
       81 
90 
     | 
    
         
             
            </p>
         
     | 
| 
       82 
91 
     | 
    
         | 
| 
       83 
92 
     | 
    
         
             
            <p>
         
     | 
| 
       84 
93 
     | 
    
         
             
            List the currently available notes:
         
     | 
| 
       85 
94 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       86 
     | 
    
         
            -
                $ curl -i http://localhost: 
     | 
| 
      
 95 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes<br/>
         
     | 
| 
       87 
96 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       88 
     | 
    
         
            -
                ETag: " 
     | 
| 
      
 97 
     | 
    
         
            +
                ETag: "ffc5e6012614d759283199e67f79071b"<br/>
         
     | 
| 
      
 98 
     | 
    
         
            +
                Link: <http://localhost:9292/notes/_resolved>; rel="http://joncrosby.me/cloudkit/1.0/rel/resolved"<br/>
         
     | 
| 
       89 
99 
     | 
    
         
             
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
       90 
100 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       91 
     | 
    
         
            -
                Content-Length: 32<br/>
         
     | 
| 
       92 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 101 
     | 
    
         
            +
                Content-Length: 32<br/><br/>
         
     | 
| 
       93 
102 
     | 
    
         | 
| 
       94 
103 
     | 
    
         
             
                {"uris":[],"total":0,"offset":0}
         
     | 
| 
       95 
104 
     | 
    
         
             
              </div>
         
     | 
| 
       96 
105 
     | 
    
         
             
            </p>
         
     | 
| 
       97 
106 
     | 
    
         | 
| 
       98 
107 
     | 
    
         
             
            <p>
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
      
 108 
     | 
    
         
            +
              Along with the usual metadata, many responses also provide discovery
         
     | 
| 
      
 109 
     | 
    
         
            +
              information via
         
     | 
| 
      
 110 
     | 
    
         
            +
              <a href="http://www.mnot.net/drafts/draft-nottingham-http-link-header-03.txt">Link Headers</a>
         
     | 
| 
      
 111 
     | 
    
         
            +
              as shown above. These links allow user agents to find related resources. The
         
     | 
| 
      
 112 
     | 
    
         
            +
              purpose of the above rel="resolved" Link Header is to offer a complete representation of
         
     | 
| 
      
 113 
     | 
    
         
            +
              all documents in a collection as an alternative to the simple list of links provided above.
         
     | 
| 
      
 114 
     | 
    
         
            +
              We'll look at the the result of using the "resolved" URI after we have created a few resources.
         
     | 
| 
      
 115 
     | 
    
         
            +
            </p>
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
            <p>
         
     | 
| 
      
 118 
     | 
    
         
            +
            Let's move on, creating a note using <a href="http://tools.ietf.org/html/rfc2616#section-9.5">POST</a>:
         
     | 
| 
       100 
119 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       101 
     | 
    
         
            -
                $ curl -i -XPOST -d'{"title":"projects"}' http://localhost: 
     | 
| 
      
 120 
     | 
    
         
            +
                $ curl -i -XPOST -d'{"title":"projects"}' http://localhost:9292/notes<br/>
         
     | 
| 
       102 
121 
     | 
    
         
             
                HTTP/1.1 201 Created<br/>
         
     | 
| 
       103 
122 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       104 
123 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       105 
     | 
    
         
            -
                Content-Length: 159<br/>
         
     | 
| 
       106 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 124 
     | 
    
         
            +
                Content-Length: 159<br/><br/>
         
     | 
| 
       107 
125 
     | 
    
         | 
| 
       108 
126 
     | 
    
         
             
                {<br/>
         
     | 
| 
       109 
127 
     | 
    
         
             
                  "uri":"\/notes\/0dda06f0-b134-012b-a2d8-0017f2c62348",<br/>
         
     | 
| 
         @@ -115,35 +133,14 @@ Create a note using <a href="http://tools.ietf.org/html/rfc2616#section-9.5">POS 
     | 
|
| 
       115 
133 
     | 
    
         
             
            </p>
         
     | 
| 
       116 
134 
     | 
    
         | 
| 
       117 
135 
     | 
    
         
             
            <p>
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
              <div class="code">
         
     | 
| 
       120 
     | 
    
         
            -
                $ curl -i http://localhost:3000/notes<br/>
         
     | 
| 
       121 
     | 
    
         
            -
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       122 
     | 
    
         
            -
                Last-Modified: Sun, 21 Dec 2008 02:21:52 GMT<br/>
         
     | 
| 
       123 
     | 
    
         
            -
                ETag: "dd30142ff023386d2515b41fb88447a5"<br/>
         
     | 
| 
       124 
     | 
    
         
            -
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
       125 
     | 
    
         
            -
                Content-Type: application/json<br/>
         
     | 
| 
       126 
     | 
    
         
            -
                Content-Length: 79<br/>
         
     | 
| 
       127 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                {<br/>
         
     | 
| 
       130 
     | 
    
         
            -
                  "uris":["\/notes\/0dda06f0-b134-012b-a2d8-0017f2c62348"],<br/>
         
     | 
| 
       131 
     | 
    
         
            -
                  "total":1,<br/>
         
     | 
| 
       132 
     | 
    
         
            -
                  "offset":0<br/>
         
     | 
| 
       133 
     | 
    
         
            -
                }
         
     | 
| 
       134 
     | 
    
         
            -
              </div>
         
     | 
| 
       135 
     | 
    
         
            -
            </p>
         
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
            <p>
         
     | 
| 
       138 
     | 
    
         
            -
            Create a note using <a href="http://tools.ietf.org/html/rfc2616#section-9.6">PUT</a>
         
     | 
| 
      
 136 
     | 
    
         
            +
            Create a different note using <a href="http://tools.ietf.org/html/rfc2616#section-9.6">PUT</a>
         
     | 
| 
       139 
137 
     | 
    
         
             
            so that we can specify its location:
         
     | 
| 
       140 
138 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       141 
     | 
    
         
            -
                $ curl -i -XPUT -d'{"title":"reminders"}' http://localhost: 
     | 
| 
      
 139 
     | 
    
         
            +
                $ curl -i -XPUT -d'{"title":"reminders"}' http://localhost:9292/notes/abc<br/>
         
     | 
| 
       142 
140 
     | 
    
         
             
                HTTP/1.1 201 Created<br/>
         
     | 
| 
       143 
141 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       144 
142 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       145 
     | 
    
         
            -
                Content-Length: 126<br/>
         
     | 
| 
       146 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 143 
     | 
    
         
            +
                Content-Length: 126<br/><br/>
         
     | 
| 
       147 
144 
     | 
    
         | 
| 
       148 
145 
     | 
    
         
             
                {<br/>
         
     | 
| 
       149 
146 
     | 
    
         
             
                  "uri":"\/notes\/abc",<br/>
         
     | 
| 
         @@ -157,37 +154,33 @@ so that we can specify its location: 
     | 
|
| 
       157 
154 
     | 
    
         
             
            <p>
         
     | 
| 
       158 
155 
     | 
    
         
             
            View the new note:
         
     | 
| 
       159 
156 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       160 
     | 
    
         
            -
                $ curl -i http://localhost: 
     | 
| 
      
 157 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes/abc<br/>
         
     | 
| 
       161 
158 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       162 
159 
     | 
    
         
             
                Last-Modified: Sun, 21 Dec 2008 02:25:19 GMT<br/>
         
     | 
| 
       163 
160 
     | 
    
         
             
                ETag: "89487620-b134-012b-a2d8-0017f2c62348"<br/>
         
     | 
| 
       164 
     | 
    
         
            -
                Link: <http://localhost: 
     | 
| 
      
 161 
     | 
    
         
            +
                Link: <http://localhost:9292/notes/abc/versions>; rel="http://joncrosby.me/cloudkit/1.0/rel/versions"<br/>
         
     | 
| 
       165 
162 
     | 
    
         
             
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
       166 
163 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       167 
     | 
    
         
            -
                Content-Length: 21<br/>
         
     | 
| 
       168 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 164 
     | 
    
         
            +
                Content-Length: 21<br/><br/>
         
     | 
| 
       169 
165 
     | 
    
         | 
| 
       170 
166 
     | 
    
         
             
                {"title":"reminders"}
         
     | 
| 
       171 
167 
     | 
    
         
             
              </div>
         
     | 
| 
       172 
168 
     | 
    
         
             
            </p>
         
     | 
| 
       173 
169 
     | 
    
         | 
| 
       174 
170 
     | 
    
         
             
            <p>
         
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
            as shown above. These links allow user agents to find related resources such as
         
     | 
| 
       179 
     | 
    
         
            -
            the complete history of a document.
         
     | 
| 
      
 171 
     | 
    
         
            +
            Once again, we see a Link header. This one lists the location of the complete history
         
     | 
| 
      
 172 
     | 
    
         
            +
            of this particular document. This history contains all versions of the document including
         
     | 
| 
      
 173 
     | 
    
         
            +
            the most recent. We will see it in action in a moment.
         
     | 
| 
       180 
174 
     | 
    
         
             
            </p>
         
     | 
| 
       181 
175 
     | 
    
         | 
| 
       182 
176 
     | 
    
         
             
            <p>
         
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
      
 177 
     | 
    
         
            +
            Next, attempt a careless update of our newest resource and enjoy the failure:
         
     | 
| 
       184 
178 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       185 
     | 
    
         
            -
                $ curl -i -XPUT -d'{"title":"foo"}' http://localhost: 
     | 
| 
      
 179 
     | 
    
         
            +
                $ curl -i -XPUT -d'{"title":"foo"}' http://localhost:9292/notes/abc<br/>
         
     | 
| 
       186 
180 
     | 
    
         
             
                HTTP/1.1 400 Bad Request<br/>
         
     | 
| 
       187 
181 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       188 
182 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       189 
     | 
    
         
            -
                Content-Length: 25<br/>
         
     | 
| 
       190 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 183 
     | 
    
         
            +
                Content-Length: 25<br/><br/>
         
     | 
| 
       191 
184 
     | 
    
         | 
| 
       192 
185 
     | 
    
         
             
                {"error":"etag required"}
         
     | 
| 
       193 
186 
     | 
    
         
             
              </div>
         
     | 
| 
         @@ -196,7 +189,7 @@ Attempt a careless update and enjoy the failure: 
     | 
|
| 
       196 
189 
     | 
    
         
             
            <p>
         
     | 
| 
       197 
190 
     | 
    
         
             
            Succeed in updating by being specific:
         
     | 
| 
       198 
191 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       199 
     | 
    
         
            -
                $ curl -i -XPUT -H'If-Match:89487620-b134-012b-a2d8-0017f2c62348' -d'{"title":"foo"}' http://localhost: 
     | 
| 
      
 192 
     | 
    
         
            +
                $ curl -i -XPUT -H'If-Match:89487620-b134-012b-a2d8-0017f2c62348' -d'{"title":"foo"}' http://localhost:9292/notes/abc<br/>
         
     | 
| 
       200 
193 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       201 
194 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       202 
195 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
         @@ -221,12 +214,11 @@ created your own "abc" resource.) 
     | 
|
| 
       221 
214 
     | 
    
         
             
            <p>
         
     | 
| 
       222 
215 
     | 
    
         
             
            Watch a secondary, out-of-date client fail at updating by being specific but also being stale:
         
     | 
| 
       223 
216 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       224 
     | 
    
         
            -
                $ curl -i -XPUT -H'If-Match:89487620-b134-012b-a2d8-0017f2c62348' -d'{"title":" 
     | 
| 
      
 217 
     | 
    
         
            +
                $ curl -i -XPUT -H'If-Match:89487620-b134-012b-a2d8-0017f2c62348' -d'{"title":"bar"}' http://localhost:9292/notes/abc<br/>
         
     | 
| 
       225 
218 
     | 
    
         
             
                HTTP/1.1 412 Precondition Failed<br/>
         
     | 
| 
       226 
219 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       227 
220 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       228 
     | 
    
         
            -
                Content-Length: 31<br/>
         
     | 
| 
       229 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 221 
     | 
    
         
            +
                Content-Length: 31<br/><br/>
         
     | 
| 
       230 
222 
     | 
    
         | 
| 
       231 
223 
     | 
    
         
             
                {"error":"precondition failed"}
         
     | 
| 
       232 
224 
     | 
    
         
             
              </div>
         
     | 
| 
         @@ -241,17 +233,17 @@ version of the resource. 
     | 
|
| 
       241 
233 
     | 
    
         
             
            </p>
         
     | 
| 
       242 
234 
     | 
    
         | 
| 
       243 
235 
     | 
    
         
             
            <p>
         
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
      
 236 
     | 
    
         
            +
            We can list all versions of the document using the URI tagged with rel="versions" in the
         
     | 
| 
       245 
237 
     | 
    
         
             
            link header mentioned above, reverse sorted by Last-Modified, feed style:
         
     | 
| 
       246 
238 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       247 
     | 
    
         
            -
                $ curl -i http://localhost: 
     | 
| 
      
 239 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes/abc/versions<br/>
         
     | 
| 
       248 
240 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       249 
241 
     | 
    
         
             
                Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
         
     | 
| 
       250 
     | 
    
         
            -
                ETag: " 
     | 
| 
      
 242 
     | 
    
         
            +
                ETag: "28ecf6899a45d3cdd0ad82bad56991d1"<br/>
         
     | 
| 
      
 243 
     | 
    
         
            +
                Link: <http://localhost:9292/notes/abc/versions/_resolved>; rel="http://joncrosby.me/cloudkit/1.0/rel/resolved"<br/>
         
     | 
| 
       251 
244 
     | 
    
         
             
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
       252 
245 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       253 
     | 
    
         
            -
                Content-Length: 109<br/>
         
     | 
| 
       254 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 246 
     | 
    
         
            +
                Content-Length: 109<br/><br/>
         
     | 
| 
       255 
247 
     | 
    
         | 
| 
       256 
248 
     | 
    
         
             
                {<br/>
         
     | 
| 
       257 
249 
     | 
    
         
             
                  "uris":[<br/>
         
     | 
| 
         @@ -265,14 +257,84 @@ link header mentioned above, reverse sorted by Last-Modified, feed style: 
     | 
|
| 
       265 
257 
     | 
    
         
             
            </p>
         
     | 
| 
       266 
258 
     | 
    
         | 
| 
       267 
259 
     | 
    
         
             
            <p>
         
     | 
| 
       268 
     | 
    
         
            -
             
     | 
| 
      
 260 
     | 
    
         
            +
            List all versions again, this time using the "resolved" URI from the Link header.
         
     | 
| 
      
 261 
     | 
    
         
            +
            This effectively delivers the same information that would be obtained by first listing
         
     | 
| 
      
 262 
     | 
    
         
            +
            the URIs, then fetching each one of them individually.
         
     | 
| 
      
 263 
     | 
    
         
            +
              <div class="code">
         
     | 
| 
      
 264 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes/abc/versions/_resolved<br/>
         
     | 
| 
      
 265 
     | 
    
         
            +
                HTTP/1.1 200 OK<br/>
         
     | 
| 
      
 266 
     | 
    
         
            +
                Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
         
     | 
| 
      
 267 
     | 
    
         
            +
                ETag: "282819afc09d7735fd6801532c0c7033"<br/>
         
     | 
| 
      
 268 
     | 
    
         
            +
                Link: <http://localhost:9292/notes/abc/versions>; rel="index"<br/>
         
     | 
| 
      
 269 
     | 
    
         
            +
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
      
 270 
     | 
    
         
            +
                Content-Type: application/json<br/>
         
     | 
| 
      
 271 
     | 
    
         
            +
                Content-Length: 390<br/><br/>
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                {<br/>
         
     | 
| 
      
 274 
     | 
    
         
            +
                  "documents":[<br/>
         
     | 
| 
      
 275 
     | 
    
         
            +
                    {<br/>
         
     | 
| 
      
 276 
     | 
    
         
            +
                      "etag":"522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
         
     | 
| 
      
 277 
     | 
    
         
            +
                      "last_modified":"Sun, 21 Dec 2008 02:30:56 GMT",<br/>
         
     | 
| 
      
 278 
     | 
    
         
            +
                      "document":"{\"title\":\"foo\"}",<br/>
         
     | 
| 
      
 279 
     | 
    
         
            +
                      "uri":"\/notes\/abc"<br/>
         
     | 
| 
      
 280 
     | 
    
         
            +
                    },<br/>
         
     | 
| 
      
 281 
     | 
    
         
            +
                    {<br/>
         
     | 
| 
      
 282 
     | 
    
         
            +
                      "etag":"89487620-b134-012b-a2d8-0017f2c62348",<br/>
         
     | 
| 
      
 283 
     | 
    
         
            +
                      "last_modified":"Sun, 21 Dec 2008 02:25:19 GMT",<br/>
         
     | 
| 
      
 284 
     | 
    
         
            +
                      "document":"{\"title\":\"reminders\"}",<br/>
         
     | 
| 
      
 285 
     | 
    
         
            +
                      "uri":"\/notes\/abc\/versions\/89487620-b134-012b-a2d8-0017f2c62348"}<br/>
         
     | 
| 
      
 286 
     | 
    
         
            +
                  ],<br/>
         
     | 
| 
      
 287 
     | 
    
         
            +
                  "total":2,<br/>
         
     | 
| 
      
 288 
     | 
    
         
            +
                  "offset":0<br/>
         
     | 
| 
      
 289 
     | 
    
         
            +
                }
         
     | 
| 
      
 290 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 291 
     | 
    
         
            +
            </p>
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
            <p>
         
     | 
| 
      
 294 
     | 
    
         
            +
              Notice the resolved response includes a Link header pointing back to its index.
         
     | 
| 
      
 295 
     | 
    
         
            +
            </p>
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
            <p>
         
     | 
| 
      
 298 
     | 
    
         
            +
              We can use this same "resolved" technique on the main "notes" listing:
         
     | 
| 
       269 
299 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       270 
     | 
    
         
            -
                $ curl -i  
     | 
| 
      
 300 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes/_resolved<br/>
         
     | 
| 
      
 301 
     | 
    
         
            +
                HTTP/1.1 200 OK<br/>
         
     | 
| 
      
 302 
     | 
    
         
            +
                Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
         
     | 
| 
      
 303 
     | 
    
         
            +
                ETag: "6628242625a7f71cce838a02deb27912"<br/>
         
     | 
| 
      
 304 
     | 
    
         
            +
                Link: <http://localhost:9292/notes> rel="index"<br/>
         
     | 
| 
      
 305 
     | 
    
         
            +
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
      
 306 
     | 
    
         
            +
                Content-Type: application/json<br/>
         
     | 
| 
      
 307 
     | 
    
         
            +
                Content-Length: 374<br/><br/>
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
                {<br/>
         
     | 
| 
      
 310 
     | 
    
         
            +
                  "documents":[<br/>
         
     | 
| 
      
 311 
     | 
    
         
            +
                    {<br/>
         
     | 
| 
      
 312 
     | 
    
         
            +
                      "etag":"522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
         
     | 
| 
      
 313 
     | 
    
         
            +
                      "last_modified":"Sun, 21 Dec 2008 02:30:56 GMT",<br/>
         
     | 
| 
      
 314 
     | 
    
         
            +
                      "document":"{\"title\":\"foo\"}",<br/>
         
     | 
| 
      
 315 
     | 
    
         
            +
                      "uri":"\/notes\/abc"<br/>
         
     | 
| 
      
 316 
     | 
    
         
            +
                    },<br/>
         
     | 
| 
      
 317 
     | 
    
         
            +
                    {<br/>
         
     | 
| 
      
 318 
     | 
    
         
            +
                      "etag":"0dda0de0-b134-012b-a2d8-0017f2c62348",<br/>
         
     | 
| 
      
 319 
     | 
    
         
            +
                      "last_modified":"Sun, 21 Dec 2008 02:21:52 GMT",<br/>
         
     | 
| 
      
 320 
     | 
    
         
            +
                      "document":"{\"title\":\"projects\"}",<br/>
         
     | 
| 
      
 321 
     | 
    
         
            +
                      "uri":"\/notes\/0dda06f0-b134-012b-a2d8-0017f2c62348"<br/>
         
     | 
| 
      
 322 
     | 
    
         
            +
                    }<br/>
         
     | 
| 
      
 323 
     | 
    
         
            +
                  ],<br/>
         
     | 
| 
      
 324 
     | 
    
         
            +
                  "total":2,<br/>
         
     | 
| 
      
 325 
     | 
    
         
            +
                  "offset":0<br/>
         
     | 
| 
      
 326 
     | 
    
         
            +
                }
         
     | 
| 
      
 327 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 328 
     | 
    
         
            +
            </p>
         
     | 
| 
      
 329 
     | 
    
         
            +
             
     | 
| 
      
 330 
     | 
    
         
            +
            <p>
         
     | 
| 
      
 331 
     | 
    
         
            +
            Next, let's delete the our most recent document:
         
     | 
| 
      
 332 
     | 
    
         
            +
              <div class="code">
         
     | 
| 
      
 333 
     | 
    
         
            +
                $ curl -i -XDELETE -H'If-Match:522be9f0-b135-012b-a2d8-0017f2c62348' http://localhost:9292/notes/abc<br/>
         
     | 
| 
       271 
334 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       272 
335 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       273 
336 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       274 
     | 
    
         
            -
                Content-Length: 174<br/>
         
     | 
| 
       275 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 337 
     | 
    
         
            +
                Content-Length: 174<br/><br/>
         
     | 
| 
       276 
338 
     | 
    
         | 
| 
       277 
339 
     | 
    
         
             
                {<br/>
         
     | 
| 
       278 
340 
     | 
    
         
             
                  "uri":"\/notes\/abc\/versions\/522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
         
     | 
| 
         @@ -286,13 +348,12 @@ Delete the document: 
     | 
|
| 
       286 
348 
     | 
    
         
             
            <p>
         
     | 
| 
       287 
349 
     | 
    
         
             
            Try to GET it again and notice the helpful <a href="http://tools.ietf.org/html/rfc2616#section-10.4.11">410</a>:
         
     | 
| 
       288 
350 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       289 
     | 
    
         
            -
                $ curl -i http://localhost: 
     | 
| 
      
 351 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes/abc<br/>
         
     | 
| 
       290 
352 
     | 
    
         
             
                HTTP/1.1 410 Gone<br/>
         
     | 
| 
       291 
     | 
    
         
            -
                Link: <http://localhost: 
     | 
| 
      
 353 
     | 
    
         
            +
                Link: <http://localhost:9292/notes/abc/versions>; rel="http://joncrosby.me/cloudkit/1.0/rel/versions"<br/>
         
     | 
| 
       292 
354 
     | 
    
         
             
                Cache-Control: no-cache<br/>
         
     | 
| 
       293 
355 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       294 
     | 
    
         
            -
                Content-Length: 37<br/>
         
     | 
| 
       295 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 356 
     | 
    
         
            +
                Content-Length: 37<br/><br/>
         
     | 
| 
       296 
357 
     | 
    
         | 
| 
       297 
358 
     | 
    
         
             
                {"error":"entity previously deleted"}
         
     | 
| 
       298 
359 
     | 
    
         
             
              </div>
         
     | 
| 
         @@ -301,14 +362,14 @@ Try to GET it again and notice the helpful <a href="http://tools.ietf.org/html/r 
     | 
|
| 
       301 
362 
     | 
    
         
             
            <p>
         
     | 
| 
       302 
363 
     | 
    
         
             
            Notice the history is preserved:
         
     | 
| 
       303 
364 
     | 
    
         
             
              <div class="code">
         
     | 
| 
       304 
     | 
    
         
            -
                $ curl -i http://localhost: 
     | 
| 
      
 365 
     | 
    
         
            +
                $ curl -i http://localhost:9292/notes/abc/versions<br/>
         
     | 
| 
       305 
366 
     | 
    
         
             
                HTTP/1.1 200 OK<br/>
         
     | 
| 
       306 
367 
     | 
    
         
             
                Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
         
     | 
| 
       307 
368 
     | 
    
         
             
                ETag: "2308ee33e953c9be41221ff7612e5217"<br/>
         
     | 
| 
      
 369 
     | 
    
         
            +
                Link: <http://localhost:9292/notes/abc/versions/_resolved>; rel="http://joncrosby.me/cloudkit/1.0/rel/resolved"<br/>
         
     | 
| 
       308 
370 
     | 
    
         
             
                Cache-Control: proxy-revalidate<br/>
         
     | 
| 
       309 
371 
     | 
    
         
             
                Content-Type: application/json<br/>
         
     | 
| 
       310 
     | 
    
         
            -
                Content-Length: 157<br/>
         
     | 
| 
       311 
     | 
    
         
            -
                Connection: keep-alive<br/><br/>
         
     | 
| 
      
 372 
     | 
    
         
            +
                Content-Length: 157<br/><br/>
         
     | 
| 
       312 
373 
     | 
    
         | 
| 
       313 
374 
     | 
    
         
             
                {<br/>
         
     | 
| 
       314 
375 
     | 
    
         
             
                  "uris":[<br/>
         
     |