cloudkit-jruby 0.11.2
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 +47 -0
- data/COPYING +20 -0
- data/README +84 -0
- data/Rakefile +42 -0
- data/TODO +21 -0
- data/cloudkit.gemspec +89 -0
- data/doc/curl.html +388 -0
- data/doc/images/example-code.gif +0 -0
- data/doc/images/json-title.gif +0 -0
- data/doc/images/oauth-discovery-logo.gif +0 -0
- data/doc/images/openid-logo.gif +0 -0
- data/doc/index.html +90 -0
- data/doc/main.css +151 -0
- data/doc/rest-api.html +467 -0
- data/examples/1.ru +3 -0
- data/examples/2.ru +3 -0
- data/examples/3.ru +6 -0
- data/examples/4.ru +5 -0
- data/examples/5.ru +9 -0
- data/examples/6.ru +11 -0
- data/examples/TOC +17 -0
- data/lib/cloudkit.rb +92 -0
- data/lib/cloudkit/constants.rb +34 -0
- data/lib/cloudkit/exceptions.rb +10 -0
- data/lib/cloudkit/flash_session.rb +20 -0
- data/lib/cloudkit/oauth_filter.rb +266 -0
- data/lib/cloudkit/oauth_store.rb +48 -0
- data/lib/cloudkit/openid_filter.rb +236 -0
- data/lib/cloudkit/openid_store.rb +100 -0
- data/lib/cloudkit/rack/builder.rb +120 -0
- data/lib/cloudkit/rack/router.rb +20 -0
- data/lib/cloudkit/request.rb +177 -0
- data/lib/cloudkit/service.rb +162 -0
- data/lib/cloudkit/store.rb +349 -0
- data/lib/cloudkit/store/memory_table.rb +99 -0
- data/lib/cloudkit/store/resource.rb +269 -0
- data/lib/cloudkit/store/response.rb +52 -0
- data/lib/cloudkit/store/response_helpers.rb +84 -0
- data/lib/cloudkit/templates/authorize_request_token.erb +19 -0
- data/lib/cloudkit/templates/oauth_descriptor.erb +43 -0
- data/lib/cloudkit/templates/oauth_meta.erb +8 -0
- data/lib/cloudkit/templates/openid_login.erb +31 -0
- data/lib/cloudkit/templates/request_authorization.erb +23 -0
- data/lib/cloudkit/templates/request_token_denied.erb +18 -0
- data/lib/cloudkit/uri.rb +88 -0
- data/lib/cloudkit/user_store.rb +37 -0
- data/lib/cloudkit/util.rb +25 -0
- data/spec/ext_spec.rb +76 -0
- data/spec/flash_session_spec.rb +20 -0
- data/spec/memory_table_spec.rb +86 -0
- data/spec/oauth_filter_spec.rb +326 -0
- data/spec/oauth_store_spec.rb +10 -0
- data/spec/openid_filter_spec.rb +81 -0
- data/spec/openid_store_spec.rb +101 -0
- data/spec/rack_builder_spec.rb +39 -0
- data/spec/request_spec.rb +191 -0
- data/spec/resource_spec.rb +310 -0
- data/spec/service_spec.rb +1039 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/store_spec.rb +10 -0
- data/spec/uri_spec.rb +93 -0
- data/spec/user_store_spec.rb +10 -0
- data/spec/util_spec.rb +11 -0
- metadata +180 -0
data/CHANGES
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
0.11.2
|
2
|
+
- Added Location header for 201s
|
3
|
+
- Fixed JSON response for DELETE operations
|
4
|
+
- Updated MD5 dependency for Ruby 1.9 (Andreas Haller)
|
5
|
+
- Updated spec setup for RSpec 1.2 (Andreas Haller)
|
6
|
+
- Updated gem dependencies for Rack, OpenID, and JSON
|
7
|
+
- Other minor fixes
|
8
|
+
|
9
|
+
0.11.1
|
10
|
+
- Added a block option for configuring OpenID bypassed routes (Devlin Daley)
|
11
|
+
- Added write locks for Tokyo Tyrant Tables
|
12
|
+
- Added Tokyo Tyrant Table example
|
13
|
+
- Fixed POST method tunneling bug (Saimon Moore)
|
14
|
+
- Fixed escaping of nested JSON Objects and Arrays
|
15
|
+
|
16
|
+
0.11.0
|
17
|
+
- Added Tokyo Cabinet storage
|
18
|
+
- Added MemoryTable development-time storage
|
19
|
+
- Improved router performance
|
20
|
+
- Added CloudKit::Resource model
|
21
|
+
- Added custom URIs for OpenID bypass
|
22
|
+
- Removed internal undocumented ExtractionViews
|
23
|
+
- Removed SQL backends
|
24
|
+
- Removed Sequel and Rack::Config dependencies
|
25
|
+
- Switched test framework to RSpec
|
26
|
+
|
27
|
+
0.10.1
|
28
|
+
- Updated oauth and sequel gem dependencies
|
29
|
+
- Fixed 410 responses for stale PUT operations
|
30
|
+
- Fixed MySQL content encoding (Harry Weppner)
|
31
|
+
- Various fixes for filter_merge!, rekey!, and excluding (Cameron Walters)
|
32
|
+
|
33
|
+
0.10.0
|
34
|
+
- Updated for Rack 0.9
|
35
|
+
- Added batch URI resolution for resource collections
|
36
|
+
- Refactored use of constants
|
37
|
+
- Updated documentation
|
38
|
+
|
39
|
+
0.9.1
|
40
|
+
- Fixed Rack::Lint/rackup errors related to Content-Type headers
|
41
|
+
- Patched Rack to support StringIO#string in Rack::Lint::InputWrapper
|
42
|
+
- Fixed server_url encoding in OpenIDStore
|
43
|
+
- Added sqlite3-ruby dependency in gemspec
|
44
|
+
- Updated documentation
|
45
|
+
|
46
|
+
0.9.0
|
47
|
+
- First public gem release
|
data/COPYING
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008, 2009 Jon Crosby http://joncrosby.me
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
=CloudKit
|
2
|
+
|
3
|
+
CloudKit is an Open Web JSON Appliance.
|
4
|
+
|
5
|
+
CloudKit provides schema-free, auto-versioned, RESTful JSON storage with optional OpenID and OAuth support, including OAuth Discovery.
|
6
|
+
|
7
|
+
CloudKit is Rack middleware. It can be used on its own or alongside other Rack-based applications or middleware components like Rails, Merb or Sinatra.
|
8
|
+
|
9
|
+
===Install
|
10
|
+
|
11
|
+
gem install cloudkit
|
12
|
+
|
13
|
+
===Examples
|
14
|
+
|
15
|
+
In a rackup file called config.ru:
|
16
|
+
|
17
|
+
require 'cloudkit'
|
18
|
+
expose :notes, :todos
|
19
|
+
|
20
|
+
The above creates a versioned HTTP/REST service using JSON to represent two types of resource collections -- Notes and ToDos.
|
21
|
+
|
22
|
+
In a different rackup file:
|
23
|
+
|
24
|
+
require 'cloudkit'
|
25
|
+
contain :notes, :todos
|
26
|
+
|
27
|
+
The above provides the same API as example 1 with added authentication and authorization via OpenID and OAuth, respectively.
|
28
|
+
|
29
|
+
An explicit version of example 2, minus the default developer landing page:
|
30
|
+
|
31
|
+
require 'cloudkit'
|
32
|
+
use Rack::Session::Pool
|
33
|
+
use CloudKit::OAuthFilter
|
34
|
+
use CloudKit::OpenIDFilter
|
35
|
+
use CloudKit::Service, :collections => [:notes, :todos]
|
36
|
+
run lambda {|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
37
|
+
|
38
|
+
The same as above, using Tokyo Cabinet:
|
39
|
+
|
40
|
+
require 'cloudkit'
|
41
|
+
require 'rufus/tokyo' # gem install rufus-tokyo
|
42
|
+
CloudKit.setup_storage_adapter(Rufus::Tokyo::Table.new('cloudkit.tdb'))
|
43
|
+
use Rack::Session::Pool
|
44
|
+
use CloudKit::OAuthFilter
|
45
|
+
use CloudKit::OpenIDFilter
|
46
|
+
use CloudKit::Service, :collections => [:notes, :todos]
|
47
|
+
run lambda {|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
48
|
+
|
49
|
+
See the examples directory for more.
|
50
|
+
|
51
|
+
===Online
|
52
|
+
|
53
|
+
Main Site: http://getcloudkit.com
|
54
|
+
|
55
|
+
Blog: http://blog.joncrosby.me
|
56
|
+
|
57
|
+
Google Group: http://groups.google.com/group/cloudkit
|
58
|
+
|
59
|
+
Source: http://github.com/jcrosby/cloudkit
|
60
|
+
|
61
|
+
IRC: #cloudkit on freenode
|
62
|
+
|
63
|
+
===License
|
64
|
+
|
65
|
+
Copyright (c) 2008, 2009 Jon Crosby http://joncrosby.me
|
66
|
+
|
67
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
68
|
+
a copy of this software and associated documentation files (the
|
69
|
+
'Software'), to deal in the Software without restriction, including
|
70
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
71
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
72
|
+
permit persons to whom the Software is furnished to do so, subject to
|
73
|
+
the following conditions:
|
74
|
+
|
75
|
+
The above copyright notice and this permission notice shall be
|
76
|
+
included in all copies or substantial portions of the Software.
|
77
|
+
|
78
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
79
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
80
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
81
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
82
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
83
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
84
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
CLEAN.include 'doc/api'
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc "Run all examples (or a specific spec with TASK=xxxx)"
|
9
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
10
|
+
t.spec_opts = ["-c"]
|
11
|
+
t.spec_files = begin
|
12
|
+
if ENV["TASK"]
|
13
|
+
ENV["TASK"].split(',').map { |task| "spec/**/#{task}_spec.rb" }
|
14
|
+
else
|
15
|
+
FileList['spec/**/*_spec.rb']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Generate rdoc'
|
21
|
+
task :rdoc do
|
22
|
+
rm_rf 'doc/api'
|
23
|
+
sh((<<-SH).gsub(/[\s\n]+/, ' ').strip)
|
24
|
+
hanna
|
25
|
+
--inline-source
|
26
|
+
--line-numbers
|
27
|
+
--include=lib/cloudkit.rb
|
28
|
+
--include=lib/cloudkit/*.rb
|
29
|
+
--include=lib/cloudkit/*/*.rb
|
30
|
+
--exclude=Rakefile
|
31
|
+
--exclude=TODO
|
32
|
+
--exclude=cloudkit.gemspec
|
33
|
+
--exclude=templates/*
|
34
|
+
--exclude=examples/*
|
35
|
+
--exclude=spec/*
|
36
|
+
--exclude=doc/index.html
|
37
|
+
--exclude=doc/curl.html
|
38
|
+
--exclude=doc/rest-api.html
|
39
|
+
--exclude=doc/main.css
|
40
|
+
--op=doc/api
|
41
|
+
SH
|
42
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
1.0
|
2
|
+
- openid sreg
|
3
|
+
- oauth token management
|
4
|
+
- oauth consumer registration
|
5
|
+
- jsonquery
|
6
|
+
- jsonschema
|
7
|
+
- user lookup via a block for integration with existing stores
|
8
|
+
- custom templates for openid / oauth
|
9
|
+
|
10
|
+
Backlog
|
11
|
+
- acls i.e. allowed methods per user per uri
|
12
|
+
- method filtering on collections
|
13
|
+
- js functions as observers (validation, mapping, etc.)
|
14
|
+
- complete user data export
|
15
|
+
- expect header/100-continue
|
16
|
+
- deployable gem + admin app
|
17
|
+
- cappuccino adapter
|
18
|
+
- sproutcore adapter
|
19
|
+
- titanium middleware
|
20
|
+
version/upgrade middleware
|
21
|
+
rake automation
|
data/cloudkit.gemspec
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
3
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
|
+
s.name = "cloudkit-jruby"
|
5
|
+
s.version = "0.11.2"
|
6
|
+
s.date = "2008-05-05"
|
7
|
+
s.summary = "An Open Web JSON Appliance."
|
8
|
+
s.description = "An Open Web JSON Appliance."
|
9
|
+
s.authors = ["Jon Crosby"]
|
10
|
+
s.email = "jon@joncrosby.me"
|
11
|
+
s.homepage = "http://getcloudkit.com"
|
12
|
+
s.files = %w[
|
13
|
+
CHANGES
|
14
|
+
COPYING
|
15
|
+
README
|
16
|
+
Rakefile
|
17
|
+
TODO
|
18
|
+
cloudkit.gemspec
|
19
|
+
doc/curl.html
|
20
|
+
doc/images/example-code.gif
|
21
|
+
doc/images/json-title.gif
|
22
|
+
doc/images/oauth-discovery-logo.gif
|
23
|
+
doc/images/openid-logo.gif
|
24
|
+
doc/index.html
|
25
|
+
doc/main.css
|
26
|
+
doc/rest-api.html
|
27
|
+
examples/1.ru
|
28
|
+
examples/2.ru
|
29
|
+
examples/3.ru
|
30
|
+
examples/4.ru
|
31
|
+
examples/5.ru
|
32
|
+
examples/6.ru
|
33
|
+
examples/TOC
|
34
|
+
lib/cloudkit.rb
|
35
|
+
lib/cloudkit/constants.rb
|
36
|
+
lib/cloudkit/exceptions.rb
|
37
|
+
lib/cloudkit/flash_session.rb
|
38
|
+
lib/cloudkit/oauth_filter.rb
|
39
|
+
lib/cloudkit/oauth_store.rb
|
40
|
+
lib/cloudkit/openid_filter.rb
|
41
|
+
lib/cloudkit/openid_store.rb
|
42
|
+
lib/cloudkit/rack/builder.rb
|
43
|
+
lib/cloudkit/rack/router.rb
|
44
|
+
lib/cloudkit/request.rb
|
45
|
+
lib/cloudkit/service.rb
|
46
|
+
lib/cloudkit/store.rb
|
47
|
+
lib/cloudkit/store/memory_table.rb
|
48
|
+
lib/cloudkit/store/resource.rb
|
49
|
+
lib/cloudkit/store/response.rb
|
50
|
+
lib/cloudkit/store/response_helpers.rb
|
51
|
+
lib/cloudkit/templates/authorize_request_token.erb
|
52
|
+
lib/cloudkit/templates/oauth_descriptor.erb
|
53
|
+
lib/cloudkit/templates/oauth_meta.erb
|
54
|
+
lib/cloudkit/templates/openid_login.erb
|
55
|
+
lib/cloudkit/templates/request_authorization.erb
|
56
|
+
lib/cloudkit/templates/request_token_denied.erb
|
57
|
+
lib/cloudkit/uri.rb
|
58
|
+
lib/cloudkit/user_store.rb
|
59
|
+
lib/cloudkit/util.rb
|
60
|
+
spec/ext_spec.rb
|
61
|
+
spec/flash_session_spec.rb
|
62
|
+
spec/memory_table_spec.rb
|
63
|
+
spec/oauth_filter_spec.rb
|
64
|
+
spec/oauth_store_spec.rb
|
65
|
+
spec/openid_filter_spec.rb
|
66
|
+
spec/openid_store_spec.rb
|
67
|
+
spec/rack_builder_spec.rb
|
68
|
+
spec/request_spec.rb
|
69
|
+
spec/resource_spec.rb
|
70
|
+
spec/service_spec.rb
|
71
|
+
spec/spec_helper.rb
|
72
|
+
spec/store_spec.rb
|
73
|
+
spec/uri_spec.rb
|
74
|
+
spec/user_store_spec.rb
|
75
|
+
spec/util_spec.rb
|
76
|
+
]
|
77
|
+
s.test_files = s.files.select {|path| path =~ /^spec\/.*_spec.rb/}
|
78
|
+
s.rubyforge_project = "cloudkit"
|
79
|
+
s.rubygems_version = "1.1.1"
|
80
|
+
s.add_dependency 'rack', '>= 1.0'
|
81
|
+
s.add_dependency 'uuid', '= 2.0.1'
|
82
|
+
s.add_dependency 'oauth', '~> 0.3'
|
83
|
+
s.add_dependency 'ruby-openid', '~> 2.1'
|
84
|
+
if Gem::Platform::CURRENT =~ /java/
|
85
|
+
s.add_dependency 'json-jruby', '> 0'
|
86
|
+
else
|
87
|
+
s.add_dependency 'json', '~> 1.1'
|
88
|
+
end
|
89
|
+
end
|
data/doc/curl.html
ADDED
@@ -0,0 +1,388 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
6
|
+
<title>CloudKit via cURL</title>
|
7
|
+
<link rel="stylesheet" href="main.css" type="text/css" charset="utf-8"/>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<div class="wrapper">
|
11
|
+
<ul class="nav">
|
12
|
+
<li><a href="rest-api.html" title="REST API">REST API</a></li>
|
13
|
+
<li><a href="curl.html" title="cURL Tutorial">cURL</a></li>
|
14
|
+
<li><a href="api" title="RDoc">RDoc</a></li>
|
15
|
+
<li><a href="http://github.com/jcrosby/cloudkit" title="GitHub Source Repository">Code</a></li>
|
16
|
+
<li><a href="http://blog.joncrosby.me" title="Author's Blog">Blog</a></li>
|
17
|
+
</ul>
|
18
|
+
</div>
|
19
|
+
<div id="header">
|
20
|
+
<div class="wrapper">
|
21
|
+
<h1><a href="index.html">CloudKit</a></h1>
|
22
|
+
<div class="subpage-subtitle">via cURL</div>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
<div class="meta">
|
26
|
+
<p class="wrapper">
|
27
|
+
This is a tour of the CloudKit <a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">REST</a>/HTTP 1.1 API
|
28
|
+
using <a href="http://curl.haxx.se/docs/manpage.html">curl</a>. For the complete
|
29
|
+
spec, see the <a href="rest-api.html">CloudKit REST API</a>.
|
30
|
+
</p>
|
31
|
+
</div>
|
32
|
+
<div class="wrapper">
|
33
|
+
<p>
|
34
|
+
If you haven't already installed the gem:
|
35
|
+
<div class="code">
|
36
|
+
$ gem install cloudkit
|
37
|
+
</div>
|
38
|
+
</p>
|
39
|
+
|
40
|
+
<p>
|
41
|
+
If you already have the gem, make sure you're running the latest version (0.11.2):
|
42
|
+
<div class="code">
|
43
|
+
$ gem list cloudkit<br/>
|
44
|
+
cloudkit (0.10.0) <-- need to upgrade<br/>
|
45
|
+
$ gem update cloudkit<br/>
|
46
|
+
$ gem list cloudkit<br/>
|
47
|
+
cloudkit (0.11.2, 0.10.0) <-- 0.11.2 is now in the list
|
48
|
+
</div>
|
49
|
+
</p>
|
50
|
+
|
51
|
+
<p>
|
52
|
+
Create a rackup file named config.ru, containing these two lines of code:
|
53
|
+
<div class="code">
|
54
|
+
require 'cloudkit'<br/>
|
55
|
+
expose :notes
|
56
|
+
</div>
|
57
|
+
</p>
|
58
|
+
|
59
|
+
<p>
|
60
|
+
Run the app:
|
61
|
+
<div class="code">
|
62
|
+
$ rackup config.ru
|
63
|
+
</div>
|
64
|
+
</p>
|
65
|
+
|
66
|
+
<p>
|
67
|
+
CloudKit is discoverable from top to bottom. Let's see what resource collections
|
68
|
+
we're hosting:
|
69
|
+
<div class="code">
|
70
|
+
$ curl -i http://localhost:9292/cloudkit-meta<br/>
|
71
|
+
HTTP/1.1 200 OK<br/>
|
72
|
+
ETag: "ef2f29b1834ef8c2bf0d8f1abb100177"<br/>
|
73
|
+
Cache-Control: proxy-revalidate<br/>
|
74
|
+
Content-Type: application/json<br/>
|
75
|
+
Content-Length: 20<br/><br/>
|
76
|
+
|
77
|
+
{"uris":["\/notes"]}
|
78
|
+
</div>
|
79
|
+
</p>
|
80
|
+
|
81
|
+
<p>
|
82
|
+
See what we can do with these note resources:
|
83
|
+
<div class="code">
|
84
|
+
$ curl -i -XOPTIONS http://localhost:9292/notes<br/>
|
85
|
+
HTTP/1.1 200 OK<br/>
|
86
|
+
Content-Length: 0<br/>
|
87
|
+
Content-Type: application/json<br/>
|
88
|
+
Allow: GET, HEAD, POST, OPTIONS<br/>
|
89
|
+
</div>
|
90
|
+
</p>
|
91
|
+
|
92
|
+
<p>
|
93
|
+
List the currently available notes:
|
94
|
+
<div class="code">
|
95
|
+
$ curl -i http://localhost:9292/notes<br/>
|
96
|
+
HTTP/1.1 200 OK<br/>
|
97
|
+
ETag: "ffc5e6012614d759283199e67f79071b"<br/>
|
98
|
+
Link: <http://localhost:9292/notes/_resolved>; rel="http://joncrosby.me/cloudkit/1.0/rel/resolved"<br/>
|
99
|
+
Cache-Control: proxy-revalidate<br/>
|
100
|
+
Content-Type: application/json<br/>
|
101
|
+
Content-Length: 32<br/><br/>
|
102
|
+
|
103
|
+
{"uris":[],"total":0,"offset":0}
|
104
|
+
</div>
|
105
|
+
</p>
|
106
|
+
|
107
|
+
<p>
|
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>:
|
119
|
+
<div class="code">
|
120
|
+
$ curl -i -XPOST -d'{"title":"projects"}' http://localhost:9292/notes<br/>
|
121
|
+
HTTP/1.1 201 Created<br/>
|
122
|
+
Cache-Control: no-cache<br/>
|
123
|
+
Location: http://localhost:9292/notes/0dda06f0-b134-012b-a2d8-0017f2c62348<br/>
|
124
|
+
Content-Type: application/json<br/>
|
125
|
+
Content-Length: 159<br/><br/>
|
126
|
+
|
127
|
+
{<br/>
|
128
|
+
"uri":"\/notes\/0dda06f0-b134-012b-a2d8-0017f2c62348",<br/>
|
129
|
+
"ok":true,<br/>
|
130
|
+
"etag":"0dda0de0-b134-012b-a2d8-0017f2c62348",<br/>
|
131
|
+
"last_modified":"Sun, 21 Dec 2008 02:21:52 GMT"<br/>
|
132
|
+
}
|
133
|
+
</div>
|
134
|
+
</p>
|
135
|
+
|
136
|
+
<p>
|
137
|
+
Create a different note using <a href="http://tools.ietf.org/html/rfc2616#section-9.6">PUT</a>
|
138
|
+
so that we can specify its location:
|
139
|
+
<div class="code">
|
140
|
+
$ curl -i -XPUT -d'{"title":"reminders"}' http://localhost:9292/notes/abc<br/>
|
141
|
+
HTTP/1.1 201 Created<br/>
|
142
|
+
Cache-Control: no-cache<br/>
|
143
|
+
Location: http://localhost:9292/notes/abc<br/>
|
144
|
+
Content-Type: application/json<br/>
|
145
|
+
Content-Length: 126<br/><br/>
|
146
|
+
|
147
|
+
{<br/>
|
148
|
+
"uri":"\/notes\/abc",<br/>
|
149
|
+
"ok":true,<br/>
|
150
|
+
"etag":"89487620-b134-012b-a2d8-0017f2c62348",<br/>
|
151
|
+
"last_modified":"Sun, 21 Dec 2008 02:25:19 GMT"<br/>
|
152
|
+
}
|
153
|
+
</div>
|
154
|
+
</p>
|
155
|
+
|
156
|
+
<p>
|
157
|
+
View the new note:
|
158
|
+
<div class="code">
|
159
|
+
$ curl -i http://localhost:9292/notes/abc<br/>
|
160
|
+
HTTP/1.1 200 OK<br/>
|
161
|
+
Last-Modified: Sun, 21 Dec 2008 02:25:19 GMT<br/>
|
162
|
+
ETag: "89487620-b134-012b-a2d8-0017f2c62348"<br/>
|
163
|
+
Link: <http://localhost:9292/notes/abc/versions>; rel="http://joncrosby.me/cloudkit/1.0/rel/versions"<br/>
|
164
|
+
Cache-Control: proxy-revalidate<br/>
|
165
|
+
Content-Type: application/json<br/>
|
166
|
+
Content-Length: 21<br/><br/>
|
167
|
+
|
168
|
+
{"title":"reminders"}
|
169
|
+
</div>
|
170
|
+
</p>
|
171
|
+
|
172
|
+
<p>
|
173
|
+
Once again, we see a Link header. This one lists the location of the complete history
|
174
|
+
of this particular document. This history contains all versions of the document including
|
175
|
+
the most recent. We will see it in action in a moment.
|
176
|
+
</p>
|
177
|
+
|
178
|
+
<p>
|
179
|
+
Next, attempt a careless update of our newest resource and enjoy the failure:
|
180
|
+
<div class="code">
|
181
|
+
$ curl -i -XPUT -d'{"title":"foo"}' http://localhost:9292/notes/abc<br/>
|
182
|
+
HTTP/1.1 400 Bad Request<br/>
|
183
|
+
Cache-Control: no-cache<br/>
|
184
|
+
Content-Type: application/json<br/>
|
185
|
+
Content-Length: 25<br/><br/>
|
186
|
+
|
187
|
+
{"error":"etag required"}
|
188
|
+
</div>
|
189
|
+
</p>
|
190
|
+
|
191
|
+
<p>
|
192
|
+
Succeed in updating by being specific:
|
193
|
+
<div class="code">
|
194
|
+
$ curl -i -XPUT -H'If-Match:89487620-b134-012b-a2d8-0017f2c62348' -d'{"title":"foo"}' http://localhost:9292/notes/abc<br/>
|
195
|
+
HTTP/1.1 200 OK<br/>
|
196
|
+
Cache-Control: no-cache<br/>
|
197
|
+
Content-Type: application/json<br/>
|
198
|
+
Content-Length: 126<br/>
|
199
|
+
Connection: keep-alive<br/><br/>
|
200
|
+
|
201
|
+
{<br/>
|
202
|
+
"uri":"\/notes\/abc",<br/>
|
203
|
+
"ok":true,<br/>
|
204
|
+
"etag":"522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
|
205
|
+
"last_modified":"Sun, 21 Dec 2008 02:30:56 GMT"<br/>
|
206
|
+
}
|
207
|
+
</div>
|
208
|
+
</p>
|
209
|
+
|
210
|
+
<p>
|
211
|
+
(Note: Your <a href="http://tools.ietf.org/html/rfc2616#section-14.19">ETag</a>
|
212
|
+
will likely be different so substitute the one that curl provided when you
|
213
|
+
created your own "abc" resource.)
|
214
|
+
</p>
|
215
|
+
|
216
|
+
<p>
|
217
|
+
Watch a secondary, out-of-date client fail at updating by being specific but also being stale:
|
218
|
+
<div class="code">
|
219
|
+
$ curl -i -XPUT -H'If-Match:89487620-b134-012b-a2d8-0017f2c62348' -d'{"title":"bar"}' http://localhost:9292/notes/abc<br/>
|
220
|
+
HTTP/1.1 412 Precondition Failed<br/>
|
221
|
+
Cache-Control: no-cache<br/>
|
222
|
+
Content-Type: application/json<br/>
|
223
|
+
Content-Length: 31<br/><br/>
|
224
|
+
|
225
|
+
{"error":"precondition failed"}
|
226
|
+
</div>
|
227
|
+
</p>
|
228
|
+
|
229
|
+
<p>
|
230
|
+
A <a href="http://tools.ietf.org/html/rfc2616#section-10.4.13">412</a> code is
|
231
|
+
returned indicating a precondition for this request failed. Specifically, the
|
232
|
+
ETag was out of date. In this case, our second client can fall back on the
|
233
|
+
resource's history to "catch up" and apply its changes to the most recent
|
234
|
+
version of the resource.
|
235
|
+
</p>
|
236
|
+
|
237
|
+
<p>
|
238
|
+
We can list all versions of the document using the URI tagged with rel="versions" in the
|
239
|
+
link header mentioned above, reverse sorted by Last-Modified, feed style:
|
240
|
+
<div class="code">
|
241
|
+
$ curl -i http://localhost:9292/notes/abc/versions<br/>
|
242
|
+
HTTP/1.1 200 OK<br/>
|
243
|
+
Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
|
244
|
+
ETag: "28ecf6899a45d3cdd0ad82bad56991d1"<br/>
|
245
|
+
Link: <http://localhost:9292/notes/abc/versions/_resolved>; rel="http://joncrosby.me/cloudkit/1.0/rel/resolved"<br/>
|
246
|
+
Cache-Control: proxy-revalidate<br/>
|
247
|
+
Content-Type: application/json<br/>
|
248
|
+
Content-Length: 109<br/><br/>
|
249
|
+
|
250
|
+
{<br/>
|
251
|
+
"uris":[<br/>
|
252
|
+
"\/notes\/abc",<br/>
|
253
|
+
"\/notes\/abc\/versions\/89487620-b134-012b-a2d8-0017f2c62348"<br/>
|
254
|
+
],<br/>
|
255
|
+
"total":2,<br/>
|
256
|
+
"offset":0<br/>
|
257
|
+
}
|
258
|
+
</div>
|
259
|
+
</p>
|
260
|
+
|
261
|
+
<p>
|
262
|
+
List all versions again, this time using the "resolved" URI from the Link header.
|
263
|
+
This effectively delivers the same information that would be obtained by first listing
|
264
|
+
the URIs, then fetching each one of them individually.
|
265
|
+
<div class="code">
|
266
|
+
$ curl -i http://localhost:9292/notes/abc/versions/_resolved<br/>
|
267
|
+
HTTP/1.1 200 OK<br/>
|
268
|
+
Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
|
269
|
+
ETag: "282819afc09d7735fd6801532c0c7033"<br/>
|
270
|
+
Link: <http://localhost:9292/notes/abc/versions>; rel="index"<br/>
|
271
|
+
Cache-Control: proxy-revalidate<br/>
|
272
|
+
Content-Type: application/json<br/>
|
273
|
+
Content-Length: 390<br/><br/>
|
274
|
+
|
275
|
+
{<br/>
|
276
|
+
"documents":[<br/>
|
277
|
+
{<br/>
|
278
|
+
"etag":"522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
|
279
|
+
"last_modified":"Sun, 21 Dec 2008 02:30:56 GMT",<br/>
|
280
|
+
"document":"{\"title\":\"foo\"}",<br/>
|
281
|
+
"uri":"\/notes\/abc"<br/>
|
282
|
+
},<br/>
|
283
|
+
{<br/>
|
284
|
+
"etag":"89487620-b134-012b-a2d8-0017f2c62348",<br/>
|
285
|
+
"last_modified":"Sun, 21 Dec 2008 02:25:19 GMT",<br/>
|
286
|
+
"document":"{\"title\":\"reminders\"}",<br/>
|
287
|
+
"uri":"\/notes\/abc\/versions\/89487620-b134-012b-a2d8-0017f2c62348"}<br/>
|
288
|
+
],<br/>
|
289
|
+
"total":2,<br/>
|
290
|
+
"offset":0<br/>
|
291
|
+
}
|
292
|
+
</div>
|
293
|
+
</p>
|
294
|
+
|
295
|
+
<p>
|
296
|
+
Notice the resolved response includes a Link header pointing back to its index.
|
297
|
+
</p>
|
298
|
+
|
299
|
+
<p>
|
300
|
+
We can use this same "resolved" technique on the main "notes" listing:
|
301
|
+
<div class="code">
|
302
|
+
$ curl -i http://localhost:9292/notes/_resolved<br/>
|
303
|
+
HTTP/1.1 200 OK<br/>
|
304
|
+
Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
|
305
|
+
ETag: "6628242625a7f71cce838a02deb27912"<br/>
|
306
|
+
Link: <http://localhost:9292/notes> rel="index"<br/>
|
307
|
+
Cache-Control: proxy-revalidate<br/>
|
308
|
+
Content-Type: application/json<br/>
|
309
|
+
Content-Length: 374<br/><br/>
|
310
|
+
|
311
|
+
{<br/>
|
312
|
+
"documents":[<br/>
|
313
|
+
{<br/>
|
314
|
+
"etag":"522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
|
315
|
+
"last_modified":"Sun, 21 Dec 2008 02:30:56 GMT",<br/>
|
316
|
+
"document":"{\"title\":\"foo\"}",<br/>
|
317
|
+
"uri":"\/notes\/abc"<br/>
|
318
|
+
},<br/>
|
319
|
+
{<br/>
|
320
|
+
"etag":"0dda0de0-b134-012b-a2d8-0017f2c62348",<br/>
|
321
|
+
"last_modified":"Sun, 21 Dec 2008 02:21:52 GMT",<br/>
|
322
|
+
"document":"{\"title\":\"projects\"}",<br/>
|
323
|
+
"uri":"\/notes\/0dda06f0-b134-012b-a2d8-0017f2c62348"<br/>
|
324
|
+
}<br/>
|
325
|
+
],<br/>
|
326
|
+
"total":2,<br/>
|
327
|
+
"offset":0<br/>
|
328
|
+
}
|
329
|
+
</div>
|
330
|
+
</p>
|
331
|
+
|
332
|
+
<p>
|
333
|
+
Next, let's delete the our most recent document:
|
334
|
+
<div class="code">
|
335
|
+
$ curl -i -XDELETE -H'If-Match:522be9f0-b135-012b-a2d8-0017f2c62348' http://localhost:9292/notes/abc<br/>
|
336
|
+
HTTP/1.1 200 OK<br/>
|
337
|
+
Cache-Control: no-cache<br/>
|
338
|
+
Content-Type: application/json<br/>
|
339
|
+
Content-Length: 174<br/><br/>
|
340
|
+
|
341
|
+
{<br/>
|
342
|
+
"uri":"\/notes\/abc\/versions\/522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
|
343
|
+
"ok":true,<br/>
|
344
|
+
"etag":"522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
|
345
|
+
"last_modified":"Sun, 21 Dec 2008 02:30:56 GMT"<br/>
|
346
|
+
}
|
347
|
+
</div>
|
348
|
+
</p>
|
349
|
+
|
350
|
+
<p>
|
351
|
+
Try to GET it again and notice the helpful <a href="http://tools.ietf.org/html/rfc2616#section-10.4.11">410</a>:
|
352
|
+
<div class="code">
|
353
|
+
$ curl -i http://localhost:9292/notes/abc<br/>
|
354
|
+
HTTP/1.1 410 Gone<br/>
|
355
|
+
Link: <http://localhost:9292/notes/abc/versions>; rel="http://joncrosby.me/cloudkit/1.0/rel/versions"<br/>
|
356
|
+
Cache-Control: no-cache<br/>
|
357
|
+
Content-Type: application/json<br/>
|
358
|
+
Content-Length: 37<br/><br/>
|
359
|
+
|
360
|
+
{"error":"entity previously deleted"}
|
361
|
+
</div>
|
362
|
+
</p>
|
363
|
+
|
364
|
+
<p>
|
365
|
+
Notice the history is preserved:
|
366
|
+
<div class="code">
|
367
|
+
$ curl -i http://localhost:9292/notes/abc/versions<br/>
|
368
|
+
HTTP/1.1 200 OK<br/>
|
369
|
+
Last-Modified: Sun, 21 Dec 2008 02:30:56 GMT<br/>
|
370
|
+
ETag: "2308ee33e953c9be41221ff7612e5217"<br/>
|
371
|
+
Link: <http://localhost:9292/notes/abc/versions/_resolved>; rel="http://joncrosby.me/cloudkit/1.0/rel/resolved"<br/>
|
372
|
+
Cache-Control: proxy-revalidate<br/>
|
373
|
+
Content-Type: application/json<br/>
|
374
|
+
Content-Length: 157<br/><br/>
|
375
|
+
|
376
|
+
{<br/>
|
377
|
+
"uris":[<br/>
|
378
|
+
"\/notes\/abc\/versions\/522be9f0-b135-012b-a2d8-0017f2c62348",<br/>
|
379
|
+
"\/notes\/abc\/versions\/89487620-b134-012b-a2d8-0017f2c62348"<br/>
|
380
|
+
],<br/>
|
381
|
+
"total":2,<br/>
|
382
|
+
"offset":0<br/>
|
383
|
+
}
|
384
|
+
</div>
|
385
|
+
</p>
|
386
|
+
</div>
|
387
|
+
</body>
|
388
|
+
</html>
|