cloudkit 0.9.0 → 0.9.1

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 CHANGED
@@ -1,2 +1,9 @@
1
+ 0.9.1
2
+ - Fixed Rack::Lint/rackup errors related to Content-Type headers
3
+ - Patched Rack to support StringIO#string in Rack::Lint::InputWrapper
4
+ - Fixed server_url encoding in OpenIDStore
5
+ - Added sqlite3-ruby dependency in gemspec
6
+ - Updated documentation
7
+
1
8
  0.9.0
2
9
  - First public gem release
data/README CHANGED
@@ -6,6 +6,10 @@ More specifically, CloudKit provides RESTful JSON storage with optional OpenID a
6
6
 
7
7
  CloudKit is Rack middleware and as such can be used on its own or alongside other Rack-based applications or middleware components like Rails, Merb or Sinatra.
8
8
 
9
+ ===Install
10
+
11
+ gem install cloudkit
12
+
9
13
  ===Examples
10
14
 
11
15
  In a rackup file called config.ru:
@@ -29,10 +33,34 @@ An explicit version of example 2, minus the default developer landing page:
29
33
  use CloudKit::OAuthFilter
30
34
  use CloudKit::OpenIDFilter
31
35
  use CloudKit::Service, :collections => [:notes, :todos]
32
- run lambda {|env| [200, {}, ['HELLO']]}
36
+ run lambda {|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
37
+
38
+ The same as above, using MySQL:
39
+
40
+ require 'cloudkit'
41
+ use Rack::Config { |env|
42
+ env['cloudkit.storage.uri'] = 'mysql://user:pass@localhost/cloudkit_example'
43
+ }
44
+ use Rack::Pool::Session
45
+ use CloudKit::OAuthFilter
46
+ use CloudKit::OpenIDFilter
47
+ use CloudKit::Service, :collections => [:notes, :todos]
48
+ run lambda {|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
33
49
 
34
50
  See the examples directory for more.
35
51
 
52
+ ===Online
53
+
54
+ Main Site: http://getcloudkit.com
55
+
56
+ Blog: http://blog.joncrosby.me
57
+
58
+ Google Group: http://groups.google.com/group/cloudkit
59
+
60
+ Source: http://github.com/jcrosby/cloudkit
61
+
62
+ ===License
63
+
36
64
  Copyright (c) 2008 Jon Crosby http://joncrosby.me
37
65
 
38
66
  Permission is hereby granted, free of charge, to any person obtaining
@@ -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.9.0"
6
- s.date = "2008-12-22"
5
+ s.version = "0.9.1"
6
+ s.date = "2008-12-31"
7
7
  s.summary = "An Open Web JSON Appliance."
8
8
  s.description = "An Open Web JSON Appliance."
9
9
  s.authors = ["Jon Crosby"]
@@ -11,11 +11,11 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "http://getcloudkit.com"
12
12
  s.files = %w[
13
13
  CHANGES
14
- cloudkit.gemspec
15
14
  COPYING
16
15
  README
17
16
  Rakefile
18
17
  TODO
18
+ cloudkit.gemspec
19
19
  doc/curl.html
20
20
  doc/images/example-code.gif
21
21
  doc/images/json-title.gif
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
38
38
  lib/cloudkit/openid_filter.rb
39
39
  lib/cloudkit/openid_store.rb
40
40
  lib/cloudkit/rack/builder.rb
41
+ lib/cloudkit/rack/lint.rb
41
42
  lib/cloudkit/rack/router.rb
42
43
  lib/cloudkit/request.rb
43
44
  lib/cloudkit/service.rb
@@ -79,4 +80,5 @@ Gem::Specification.new do |s|
79
80
  s.add_dependency 'oauth', '>= 0.2.7'
80
81
  s.add_dependency 'ruby-openid', '= 2.1.2'
81
82
  s.add_dependency 'json', '= 1.1.3'
83
+ s.add_dependency 'sqlite3-ruby', '= 1.2.4'
82
84
  end
@@ -74,6 +74,7 @@ See what we can do with these note resources:
74
74
  $ curl -i -XOPTIONS http://localhost:3000/notes<br/>
75
75
  HTTP/1.1 200 OK<br/>
76
76
  Content-Length: 0<br/>
77
+ Content-Type: application/json<br/>
77
78
  Allow: GET, HEAD, POST, OPTIONS<br/>
78
79
  Connection: keep-alive<br/>
79
80
  </div>
@@ -94,10 +95,6 @@ List the currently available notes:
94
95
  </div>
95
96
  </p>
96
97
 
97
- <p>
98
- The Link header will be explained momentarily.
99
- </p>
100
-
101
98
  <p>
102
99
  Create a note using <a href="http://tools.ietf.org/html/rfc2616#section-9.5">POST</a>:
103
100
  <div class="code">
@@ -179,9 +176,7 @@ Along with the usual metadata, individual resources also provide discovery
179
176
  information via
180
177
  <a href="http://www.mnot.net/drafts/draft-nottingham-http-link-header-03.txt">Link Headers</a>
181
178
  as shown above. These links allow user agents to find related resources such as
182
- the complete history of a document and its
183
- <a href="http://tools.ietf.org/html/rfc2616#section-14.19">ETags</a>.
184
- The utility of these items will be demonstrated momentarily.
179
+ the complete history of a document.
185
180
  </p>
186
181
 
187
182
  <p>
@@ -218,8 +213,9 @@ Succeed in updating by being specific:
218
213
  </p>
219
214
 
220
215
  <p>
221
- (Note: Your ETag will likely be different so substitute the one that curl
222
- provided when you created your own "abc" resource.)
216
+ (Note: Your <a href="http://tools.ietf.org/html/rfc2616#section-14.19">ETag</a>
217
+ will likely be different so substitute the one that curl provided when you
218
+ created your own "abc" resource.)
223
219
  </p>
224
220
 
225
221
  <p>
@@ -22,6 +22,11 @@
22
22
  <div class="subtitle">An Open Web JSON Appliance</div>
23
23
  </div>
24
24
  </div>
25
+ <div class="meta">
26
+ <p class="wrapper">
27
+ Version 0.9.1 released. Install with <em>gem install cloudkit</em>.
28
+ </p>
29
+ </div>
25
30
  <div class="wrapper intro-row">
26
31
  <div class="column-one">
27
32
  <div class="block-header">
@@ -118,7 +118,7 @@ a {
118
118
  font-family: 'Courier New', monospace;
119
119
  color: #00ff00;
120
120
  background-color: black;
121
- font-size: 13px;
121
+ font-size: 12px;
122
122
  width: 100%;
123
123
  margin-left: 20px;
124
124
  margin-bottom: 30px;
@@ -3,4 +3,4 @@ require 'cloudkit'
3
3
  use Rack::Session::Pool
4
4
  use CloudKit::OpenIDFilter
5
5
  use CloudKit::Service, :collections => [:notes]
6
- run lambda{|env| [200, {}, ['HELLO']]}
6
+ run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
@@ -2,4 +2,4 @@ $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
2
  require 'cloudkit'
3
3
  use CloudKit::OAuthFilter
4
4
  use CloudKit::Service, :collections => [:notes]
5
- run lambda{|env| [200, {}, ['HELLO']]}
5
+ run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
@@ -7,4 +7,4 @@ use Rack::Session::Pool
7
7
  use CloudKit::OAuthFilter
8
8
  use CloudKit::OpenIDFilter
9
9
  use CloudKit::Service, :collections => [:notes]
10
- run lambda{|env| [200, {}, ['HELLO']]}
10
+ run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
@@ -1,10 +1,10 @@
1
1
  $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
2
  require 'cloudkit'
3
3
  use Rack::Config do |env|
4
- env['cloudkit.storage.uri'] = 'mysql://root:@localhost/cloudkit_example'
4
+ env['cloudkit.storage.uri'] = 'mysql://user:pass@localhost/cloudkit_example'
5
5
  end
6
6
  use Rack::Session::Pool
7
7
  use CloudKit::OAuthFilter
8
8
  use CloudKit::OpenIDFilter
9
9
  use CloudKit::Service, :collections => [:notes]
10
- run lambda{|env| [200, {}, ['HELLO']]}
10
+ run lambda{|env| [200, {'Content-Type' => 'text/html'}, ['HELLO']]}
@@ -27,6 +27,7 @@ require 'cloudkit/oauth_store'
27
27
  require 'cloudkit/openid_filter'
28
28
  require 'cloudkit/openid_store'
29
29
  require 'cloudkit/rack/builder'
30
+ require 'cloudkit/rack/lint'
30
31
  require 'cloudkit/rack/router'
31
32
  require 'cloudkit/request'
32
33
  require 'cloudkit/service'
@@ -99,7 +99,7 @@ module CloudKit
99
99
  @@store.put(
100
100
  "/cloudkit_oauth_request_tokens/#{token_id}",
101
101
  :json => request_token)
102
- [201, {}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
102
+ [201, {'Content-Type' => 'text/html'}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
103
103
  end
104
104
 
105
105
  def request_authorization(request)
@@ -186,7 +186,7 @@ module CloudKit
186
186
  @@store.delete(
187
187
  "/cloudkit_oauth_request_tokens/#{request[:oauth_token]}",
188
188
  :etag => request_token_response.etag)
189
- [201, {}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
189
+ [201, {'Content-Type' => 'text/html'}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
190
190
  end
191
191
 
192
192
  def inject_user_or_challenge(request)
@@ -232,7 +232,8 @@ module CloudKit
232
232
  def challenge_headers(request)
233
233
  {
234
234
  'WWW-Authenticate' => "OAuth realm=\"http://#{request.env['HTTP_HOST']}\"",
235
- 'Link' => discovery_link(request)
235
+ 'Link' => discovery_link(request),
236
+ 'Content-Type' => 'application/json'
236
237
  }
237
238
  end
238
239
 
@@ -242,7 +243,7 @@ module CloudKit
242
243
 
243
244
  def login_redirect(request)
244
245
  request.session['return_to'] = request.url if request.session
245
- [302, {'Location' => request.login_url}, []]
246
+ [302, {'Location' => request.login_url, 'Content-Type' => 'text/html'}, []]
246
247
  end
247
248
 
248
249
  def load_user_from_session(request)
@@ -11,7 +11,7 @@ module CloudKit
11
11
  class OpenIDFilter
12
12
  include Util
13
13
 
14
- @@lock = Mutex.new
14
+ @@lock = Mutex.new
15
15
  @@store = nil
16
16
 
17
17
  def initialize(app, options={})
@@ -21,7 +21,7 @@ module CloudKit
21
21
  def call(env)
22
22
  @@lock.synchronize do
23
23
  @@store = OpenIDStore.new(env[storage_uri_key])
24
- @users = UserStore.new(env[storage_uri_key])
24
+ @users = UserStore.new(env[storage_uri_key])
25
25
  @@store.get_association('x') rescue nil # refresh sqlite3
26
26
  end unless @@store
27
27
 
@@ -39,7 +39,11 @@ module CloudKit
39
39
  else
40
40
  if request.env[challenge_key]
41
41
  store_location(request)
42
- erb(request, :openid_login, request.env[challenge_key], 401)
42
+ erb(
43
+ request,
44
+ :openid_login,
45
+ request.env[challenge_key].merge('Content-Type' => 'text/html'),
46
+ 401)
43
47
  elsif !request.via.include?(oauth_filter_key)
44
48
  store_location(request)
45
49
  login_redirect(request)
@@ -61,7 +65,10 @@ module CloudKit
61
65
 
62
66
  request.env[auth_key] = nil
63
67
  request.flash['info'] = 'You have been logged out.'
64
- response = Rack::Response.new([], 302, {'Location' => request.login_url})
68
+ response = Rack::Response.new(
69
+ [],
70
+ 302,
71
+ {'Location' => request.login_url, 'Content-Type' => 'text/html'})
65
72
  response.delete_cookie('remember_me')
66
73
  response.finish
67
74
  end
@@ -72,14 +79,14 @@ module CloudKit
72
79
 
73
80
  def begin_openid_login(request)
74
81
  begin
75
- response = openid_consumer(request).begin request[:openid_url]
82
+ response = openid_consumer(request).begin(request[:openid_url])
76
83
  rescue => e
77
84
  request.flash[:error] = e
78
85
  return login_redirect(request)
79
86
  end
80
87
 
81
88
  redirect_url = response.redirect_url(base_url(request), full_url(request))
82
- [302, {'Location' => redirect_url}, []]
89
+ [302, {'Location' => redirect_url, 'Content-Type' => 'text/html'}, []]
83
90
  end
84
91
 
85
92
  def complete_openid_login(request)
@@ -116,10 +123,13 @@ module CloudKit
116
123
  user['remember_me_token'] = Base64.encode64(
117
124
  OpenSSL::Random.random_bytes(32)).gsub(/\W/,'')
118
125
  url = request.session.delete('return_to')
119
- response = Rack::Response.new([], 302, {'Location' => (url || '/')})
126
+ response = Rack::Response.new(
127
+ [],
128
+ 302,
129
+ {'Location' => (url || '/'), 'Content-Type' => 'text/html'})
120
130
  response.set_cookie(
121
131
  'remember_me', {
122
- :value => user['remember_me_token'],
132
+ :value => user['remember_me_token'],
123
133
  :expires => Time.at(user['remember_me_expiration']).utc})
124
134
  json = JSON.generate(user)
125
135
  @users.put(user_uri, :etag => user_result.etag, :json => json)
@@ -132,7 +142,7 @@ module CloudKit
132
142
  end
133
143
 
134
144
  def login_redirect(request)
135
- [302, {'Location' => request.login_url}, []]
145
+ [302, {'Location' => request.login_url, 'Content-Type' => 'text/html'}, []]
136
146
  end
137
147
 
138
148
  def base_url(request)
@@ -72,9 +72,11 @@ module CloudKit
72
72
  def use_nonce(server_url, timestamp, salt) #:nodoc:
73
73
  return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
74
74
 
75
- fragment = URI.escape([server_url, timestamp, salt].join('-'))
76
- uri = "/cloudkit_openid_nonces/#{fragment}"
77
- result = @@store.put(uri, :json => '{}')
75
+ fragment = URI.escape(
76
+ [server_url, timestamp, salt].join('-'),
77
+ Regexp.union(URI::REGEXP::UNSAFE, '/', ':'))
78
+ uri = "/cloudkit_openid_nonces/#{fragment}"
79
+ result = @@store.put(uri, :json => '{}')
78
80
  return (result.status == 201)
79
81
  end
80
82
 
@@ -10,7 +10,7 @@ module Rack #:nodoc:
10
10
  if (env['PATH_INFO'] == '/')
11
11
  [200, {'Content-Type' => 'text/html'}, [welcome]]
12
12
  else
13
- [404, {}, []]
13
+ [404, {'Content-Type' => 'text/html'}, []]
14
14
  end
15
15
  end
16
16
  @ins << default_app if @last_cloudkit_id == @ins.last.object_id
@@ -0,0 +1,12 @@
1
+ module Rack #:nodoc:
2
+ class Lint #:nodoc:
3
+ class InputWrapper
4
+
5
+ # Rack::Lint wraps StringIO but does not pass #string to the underlying
6
+ # object. This patch fixes the issue.
7
+ def string
8
+ @input.string
9
+ end
10
+ end
11
+ end
12
+ end
@@ -39,7 +39,9 @@ module CloudKit::ResponseHelpers
39
39
  end
40
40
 
41
41
  def allow(methods)
42
- CloudKit::Response.new(200, {'Allow' => methods.join(', ')})
42
+ CloudKit::Response.new(
43
+ 200,
44
+ {'Allow' => methods.join(', '), 'Content-Type' => 'application/json'})
43
45
  end
44
46
 
45
47
  def response(status, content='', etag=nil, last_modified=nil, options={})
@@ -21,6 +21,7 @@ end
21
21
 
22
22
  def plain_service
23
23
  Rack::Builder.new do
24
+ use Rack::Lint
24
25
  use Rack::Config do |env|
25
26
  env['cloudkit.storage.uri'] = 'sqlite://service.db'
26
27
  end
@@ -31,6 +32,7 @@ end
31
32
 
32
33
  def authed_service
33
34
  Rack::Builder.new do
35
+ use Rack::Lint
34
36
  use Rack::Config do |env|
35
37
  env['cloudkit.storage.uri'] = 'sqlite://service.db'
36
38
  r = CloudKit::Request.new(env)
@@ -43,6 +45,7 @@ end
43
45
 
44
46
  def openid_app
45
47
  Rack::Builder.new do
48
+ use Rack::Lint
46
49
  use Rack::Session::Pool
47
50
  use CloudKit::OpenIDFilter
48
51
  run echo_env(auth_key)
@@ -14,7 +14,17 @@ class ServiceTest < Test::Unit::TestCase
14
14
  should "return a 501 for unimplemented methods" do
15
15
  response = @request.request('TRACE', '/items')
16
16
  assert_equal 501, response.status
17
- response = @request.request('REJUXTAPOSE', '/items')
17
+
18
+ # disable Rack::Lint so that an invalid HTTP method
19
+ # can be tested
20
+ app = Rack::Builder.new {
21
+ use Rack::Config do |env|
22
+ env['cloudkit.storage.uri'] = 'sqlite://service.db'
23
+ end
24
+ use CloudKit::Service, :collections => [:items, :things]
25
+ run echo_text('nothing')
26
+ }
27
+ response = Rack::MockRequest.new(app).request('REJUXTAPOSE', '/items')
18
28
  assert_equal 501, response.status
19
29
  end
20
30
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Crosby
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-22 00:00:00 -08:00
12
+ date: 2008-12-31 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -82,6 +82,16 @@ dependencies:
82
82
  - !ruby/object:Gem::Version
83
83
  version: 1.1.3
84
84
  version:
85
+ - !ruby/object:Gem::Dependency
86
+ name: sqlite3-ruby
87
+ type: :runtime
88
+ version_requirement:
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.2.4
94
+ version:
85
95
  description: An Open Web JSON Appliance.
86
96
  email: jon@joncrosby.me
87
97
  executables: []
@@ -92,11 +102,11 @@ extra_rdoc_files: []
92
102
 
93
103
  files:
94
104
  - CHANGES
95
- - cloudkit.gemspec
96
105
  - COPYING
97
106
  - README
98
107
  - Rakefile
99
108
  - TODO
109
+ - cloudkit.gemspec
100
110
  - doc/curl.html
101
111
  - doc/images/example-code.gif
102
112
  - doc/images/json-title.gif
@@ -119,6 +129,7 @@ files:
119
129
  - lib/cloudkit/openid_filter.rb
120
130
  - lib/cloudkit/openid_store.rb
121
131
  - lib/cloudkit/rack/builder.rb
132
+ - lib/cloudkit/rack/lint.rb
122
133
  - lib/cloudkit/rack/router.rb
123
134
  - lib/cloudkit/request.rb
124
135
  - lib/cloudkit/service.rb