cloudkit 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +7 -0
- data/cloudkit.gemspec +3 -2
- data/doc/curl.html +3 -3
- data/doc/index.html +1 -1
- data/examples/6.ru +11 -0
- data/examples/TOC +3 -1
- data/lib/cloudkit.rb +2 -2
- data/lib/cloudkit/openid_filter.rb +5 -4
- data/lib/cloudkit/request.rb +3 -1
- data/lib/cloudkit/service.rb +1 -1
- data/lib/cloudkit/store/memory_table.rb +0 -6
- data/lib/cloudkit/store/resource.rb +15 -2
- data/spec/openid_filter_spec.rb +15 -0
- data/spec/resource_spec.rb +25 -6
- data/spec/service_spec.rb +39 -0
- metadata +3 -2
data/CHANGES
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
0.11.1
|
2
|
+
- Added a block option for configuring OpenID bypassed routes (Devlin Daley)
|
3
|
+
- Added write locks for Tokyo Tyrant Tables
|
4
|
+
- Added Tokyo Tyrant Table example
|
5
|
+
- Fixed POST method tunneling bug (Saimon Moore)
|
6
|
+
- Fixed escaping of nested JSON Objects and Arrays
|
7
|
+
|
1
8
|
0.11.0
|
2
9
|
- Added Tokyo Cabinet storage
|
3
10
|
- Added MemoryTable development-time storage
|
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.11.
|
6
|
-
s.date = "2008-03-
|
5
|
+
s.version = "0.11.1"
|
6
|
+
s.date = "2008-03-24"
|
7
7
|
s.summary = "An Open Web JSON Appliance."
|
8
8
|
s.description = "An Open Web JSON Appliance."
|
9
9
|
s.authors = ["Jon Crosby"]
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
examples/3.ru
|
30
30
|
examples/4.ru
|
31
31
|
examples/5.ru
|
32
|
+
examples/6.ru
|
32
33
|
examples/TOC
|
33
34
|
lib/cloudkit.rb
|
34
35
|
lib/cloudkit/constants.rb
|
data/doc/curl.html
CHANGED
@@ -38,13 +38,13 @@ If you haven't already installed the gem:
|
|
38
38
|
</p>
|
39
39
|
|
40
40
|
<p>
|
41
|
-
If you already have the gem, make sure you're running the latest version (0.11.
|
41
|
+
If you already have the gem, make sure you're running the latest version (0.11.1):
|
42
42
|
<div class="code">
|
43
43
|
$ gem list cloudkit<br/>
|
44
|
-
cloudkit (0.
|
44
|
+
cloudkit (0.10.0) <-- need to upgrade<br/>
|
45
45
|
$ gem update cloudkit<br/>
|
46
46
|
$ gem list cloudkit<br/>
|
47
|
-
cloudkit (0.11.
|
47
|
+
cloudkit (0.11.1, 0.10.0) <-- 0.11.1 is now in the list
|
48
48
|
</div>
|
49
49
|
</p>
|
50
50
|
|
data/doc/index.html
CHANGED
@@ -24,7 +24,7 @@
|
|
24
24
|
</div>
|
25
25
|
<div class="meta">
|
26
26
|
<p class="wrapper">
|
27
|
-
Version 0.11 released with Tokyo Cabinet support. Install with <em>gem install cloudkit</em>.
|
27
|
+
Version 0.11.1 released with Tokyo Cabinet support. Install with <em>gem install cloudkit</em>.
|
28
28
|
</p>
|
29
29
|
</div>
|
30
30
|
<div class="wrapper intro-row">
|
data/examples/6.ru
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
+
require 'cloudkit'
|
3
|
+
require 'rufus/tokyo/tyrant' # gem install rufus-tokyo
|
4
|
+
# start Tokyo Tyrant with a table store...
|
5
|
+
# ttserver data.tct
|
6
|
+
CloudKit.setup_storage_adapter(Rufus::Tokyo::TyrantTable.new('127.0.0.1', 1978))
|
7
|
+
use Rack::Session::Pool
|
8
|
+
use CloudKit::OAuthFilter
|
9
|
+
use CloudKit::OpenIDFilter
|
10
|
+
use CloudKit::Service, :collections => [:notes]
|
11
|
+
run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
data/examples/TOC
CHANGED
@@ -4,7 +4,7 @@ Index of Examples
|
|
4
4
|
When using the gem version of CloudKit, the first line of each example can be
|
5
5
|
removed.
|
6
6
|
|
7
|
-
1. Expose Notes - Mount a JSON "notes" API. Uses in-memory
|
7
|
+
1. Expose Notes - Mount a JSON "notes" API. Uses in-memory store.
|
8
8
|
|
9
9
|
2. Contain Notes - Same as #1, adding OpenID and OAuth.
|
10
10
|
|
@@ -13,3 +13,5 @@ removed.
|
|
13
13
|
4. Notes with OAuth - Same as #1 using only OAuth.
|
14
14
|
|
15
15
|
5. Tokyo Notes - Same as #2 with a Tokyo Cabinet Table store.
|
16
|
+
|
17
|
+
6. Tyrant Notes - Same as #2 with a Tokyo Tyrant Table store.
|
data/lib/cloudkit.rb
CHANGED
@@ -34,11 +34,11 @@ require 'cloudkit/user_store'
|
|
34
34
|
include CloudKit::Constants
|
35
35
|
|
36
36
|
module CloudKit
|
37
|
-
VERSION = '0.11.
|
37
|
+
VERSION = '0.11.1'
|
38
38
|
|
39
39
|
# Sets up the storage adapter. Defaults to development-time
|
40
40
|
# CloudKit::MemoryTable. Also supports Rufus Tokyo Table instances. See the
|
41
|
-
# examples directory for
|
41
|
+
# examples directory for Cabinet and Tyrant Table examples.
|
42
42
|
def self.setup_storage_adapter(adapter_instance=nil)
|
43
43
|
@storage_adapter = adapter_instance || CloudKit::MemoryTable.new
|
44
44
|
end
|
@@ -19,9 +19,10 @@ module CloudKit
|
|
19
19
|
@@lock = Mutex.new
|
20
20
|
@@store = nil
|
21
21
|
|
22
|
-
def initialize(app, options={})
|
22
|
+
def initialize(app, options={}, &bypass_route_callback)
|
23
23
|
@app = app
|
24
24
|
@options = options
|
25
|
+
@bypass_route_callback = bypass_route_callback || Proc.new {|url| url == '/'}
|
25
26
|
end
|
26
27
|
|
27
28
|
def call(env)
|
@@ -213,12 +214,12 @@ module CloudKit
|
|
213
214
|
end
|
214
215
|
|
215
216
|
def allow?(uri)
|
216
|
-
@
|
217
|
+
@bypass_route_callback.call(uri) ||
|
218
|
+
@options[:allow] && @options[:allow].include?(uri)
|
217
219
|
end
|
218
220
|
|
219
221
|
def bypass?(request)
|
220
|
-
|
221
|
-
allow?(request.path_info) ||
|
222
|
+
allow?(request.path_info) ||
|
222
223
|
valid_auth_key?(request) ||
|
223
224
|
logged_in?(request)
|
224
225
|
end
|
data/lib/cloudkit/request.rb
CHANGED
@@ -17,7 +17,9 @@ module CloudKit
|
|
17
17
|
# Return the JSON content from the request body
|
18
18
|
def json
|
19
19
|
self.body.rewind
|
20
|
-
self.body.read
|
20
|
+
raw = self.body.read
|
21
|
+
# extract the json from the body to avoid tunneled _method param from being parsed as json
|
22
|
+
(matches = raw.match(/(\{.*\})/)) ? matches[1] : raw
|
21
23
|
end
|
22
24
|
|
23
25
|
# Return a CloudKit::URI instance representing the rack request's path info.
|
data/lib/cloudkit/service.rb
CHANGED
@@ -49,7 +49,7 @@ module CloudKit
|
|
49
49
|
# modify resources that are not current.
|
50
50
|
def update(json, remote_user=nil)
|
51
51
|
raise HistoricalIntegrityViolation unless current?
|
52
|
-
|
52
|
+
transaction do
|
53
53
|
record = CloudKit.storage_adapter[@id]
|
54
54
|
record['uri'] = "#{@uri.string}/versions/#{@etag}"
|
55
55
|
record['archived'] = escape(true)
|
@@ -65,7 +65,7 @@ module CloudKit
|
|
65
65
|
# are not current.
|
66
66
|
def delete
|
67
67
|
raise HistoricalIntegrityViolation unless current?
|
68
|
-
|
68
|
+
transaction do
|
69
69
|
original_uri = @uri
|
70
70
|
record = CloudKit.storage_adapter[@id]
|
71
71
|
record['uri'] = "#{@uri.string}/versions/#{@etag}"
|
@@ -223,6 +223,8 @@ module CloudKit
|
|
223
223
|
"null"
|
224
224
|
when Fixnum, Bignum, Float
|
225
225
|
value.to_s
|
226
|
+
when Array, Hash
|
227
|
+
JSON.generate(value) # temporary bug fix prior to JSONQuery support
|
226
228
|
else
|
227
229
|
value
|
228
230
|
end
|
@@ -252,5 +254,16 @@ module CloudKit
|
|
252
254
|
def escape_values(hash)
|
253
255
|
hash.inject({}) { |memo, pair| memo.merge({pair[0] => escape(pair[1])}) }
|
254
256
|
end
|
257
|
+
|
258
|
+
def transaction
|
259
|
+
open('.lock', 'w+') do |f|
|
260
|
+
f.flock(File::LOCK_EX)
|
261
|
+
begin
|
262
|
+
yield
|
263
|
+
ensure
|
264
|
+
f.flock(File::LOCK_UN)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
255
268
|
end
|
256
269
|
end
|
data/spec/openid_filter_spec.rb
CHANGED
@@ -22,6 +22,20 @@ describe "An OpenIDFilter" do
|
|
22
22
|
response.status.should == 200
|
23
23
|
end
|
24
24
|
|
25
|
+
it "should allow pass through of URIs defined in bypass route callback" do
|
26
|
+
openid_app = Rack::Builder.new {
|
27
|
+
use Rack::Lint
|
28
|
+
use Rack::Session::Pool
|
29
|
+
use CloudKit::OpenIDFilter, :allow => ['/foo'] do |url|
|
30
|
+
['/bar'].include? url
|
31
|
+
end
|
32
|
+
run echo_env(CLOUDKIT_AUTH_KEY)
|
33
|
+
}
|
34
|
+
request = Rack::MockRequest.new(openid_app)
|
35
|
+
response = request.get('/bar')
|
36
|
+
response.status.should == 200
|
37
|
+
end
|
38
|
+
|
25
39
|
it "should redirect to the login page if authorization is required" do
|
26
40
|
response = @request.get('/protected')
|
27
41
|
response.status.should == 302
|
@@ -61,4 +75,5 @@ describe "An OpenIDFilter" do
|
|
61
75
|
end
|
62
76
|
|
63
77
|
end
|
78
|
+
|
64
79
|
end
|
data/spec/resource_spec.rb
CHANGED
@@ -93,19 +93,38 @@ describe "A Resource" do
|
|
93
93
|
|
94
94
|
describe "on create" do
|
95
95
|
|
96
|
-
|
97
|
-
|
96
|
+
def store_json(hash)
|
97
|
+
CloudKit::Resource.create(
|
98
98
|
CloudKit::URI.new('/items/123'),
|
99
|
-
JSON.generate(
|
99
|
+
JSON.generate(hash),
|
100
100
|
'http://eric.dolphy.info')
|
101
|
-
|
101
|
+
CloudKit.storage_adapter.query { |q|
|
102
102
|
q.add_condition 'uri', :eql, '/items/123'
|
103
103
|
}
|
104
104
|
end
|
105
105
|
|
106
106
|
it "should save the resource" do
|
107
|
-
|
108
|
-
|
107
|
+
result = store_json({:foo => 'bar'})
|
108
|
+
result.size.should == 1
|
109
|
+
result.first['json'].should == "{\"foo\":\"bar\"}"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should accept nested array values" do
|
113
|
+
result = store_json({:foo => [1,2]})
|
114
|
+
result.size.should == 1
|
115
|
+
result.first['json'].should == '{"foo":[1,2]}'
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should accept nested hash values" do
|
119
|
+
result = store_json({:foo => {:bar => 'baz'}})
|
120
|
+
result.size.should == 1
|
121
|
+
result.first['json'].should == '{"foo":{"bar":"baz"}}'
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should accept recursively nested array/hash values" do
|
125
|
+
result = store_json({:foo => [1,{:bar => [2,3]}]})
|
126
|
+
result.size.should == 1
|
127
|
+
result.first['json'].should == '{"foo":[1,{"bar":[2,3]}]}'
|
109
128
|
end
|
110
129
|
|
111
130
|
end
|
data/spec/service_spec.rb
CHANGED
@@ -736,6 +736,21 @@ describe "A CloudKit::Service" do
|
|
736
736
|
new_etag.should_not == etag
|
737
737
|
end
|
738
738
|
|
739
|
+
describe "using POST method tunneling" do
|
740
|
+
|
741
|
+
it "should behave like a PUT" do
|
742
|
+
json = JSON.generate(:this => 'thing')
|
743
|
+
response = @request.post(
|
744
|
+
'/items/xyz?_method=PUT',
|
745
|
+
{:input => json}.merge(VALID_TEST_AUTH))
|
746
|
+
response.status.should == 201
|
747
|
+
result = @request.get('/items/xyz', VALID_TEST_AUTH)
|
748
|
+
result.status.should == 200
|
749
|
+
JSON.parse(result.body)['this'].should == 'thing'
|
750
|
+
end
|
751
|
+
|
752
|
+
end
|
753
|
+
|
739
754
|
end
|
740
755
|
|
741
756
|
describe "on DELETE /:collection/:id" do
|
@@ -848,6 +863,20 @@ describe "A CloudKit::Service" do
|
|
848
863
|
json['total'].should == 1
|
849
864
|
end
|
850
865
|
|
866
|
+
describe "using POST method tunneling" do
|
867
|
+
|
868
|
+
it "should behave like a DELETE" do
|
869
|
+
response = @request.post(
|
870
|
+
'/items/abc?_method=DELETE',
|
871
|
+
'HTTP_IF_MATCH' => @etag,
|
872
|
+
CLOUDKIT_AUTH_KEY => TEST_REMOTE_USER)
|
873
|
+
response.status.should == 200
|
874
|
+
result = @request.get('/items/abc', VALID_TEST_AUTH)
|
875
|
+
result.status.should == 410
|
876
|
+
end
|
877
|
+
|
878
|
+
end
|
879
|
+
|
851
880
|
end
|
852
881
|
|
853
882
|
describe "on OPTIONS /:collection" do
|
@@ -866,6 +895,16 @@ describe "A CloudKit::Service" do
|
|
866
895
|
methods.sort.should == ['GET', 'HEAD', 'OPTIONS', 'POST']
|
867
896
|
end
|
868
897
|
|
898
|
+
describe "using POST method tunneling" do
|
899
|
+
|
900
|
+
it "should behave like an OPTIONS request" do
|
901
|
+
response = @request.post('/items?_method=OPTIONS', VALID_TEST_AUTH)
|
902
|
+
response['Allow'].should_not be_nil
|
903
|
+
methods = response['Allow'].split(', ')
|
904
|
+
methods.sort.should == ['GET', 'HEAD', 'OPTIONS', 'POST']
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
869
908
|
end
|
870
909
|
|
871
910
|
describe "on OPTIONS /:collection/_resolved" do
|
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.11.
|
4
|
+
version: 0.11.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-03-
|
12
|
+
date: 2008-03-24 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- examples/3.ru
|
91
91
|
- examples/4.ru
|
92
92
|
- examples/5.ru
|
93
|
+
- examples/6.ru
|
93
94
|
- examples/TOC
|
94
95
|
- lib/cloudkit.rb
|
95
96
|
- lib/cloudkit/constants.rb
|