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
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Rack::Builder" do
|
4
|
+
|
5
|
+
it "should expose services" do
|
6
|
+
app = Rack::Builder.new do
|
7
|
+
expose :items, :things
|
8
|
+
run lambda {|app| [200, {}, ['hello']]}
|
9
|
+
end
|
10
|
+
response = Rack::MockRequest.new(app).get('/items')
|
11
|
+
response.status.should == 200
|
12
|
+
documents = JSON.parse(response.body)['uris']
|
13
|
+
documents.should == []
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should expose services with auth using 'contain'" do
|
17
|
+
app = Rack::Builder.new do
|
18
|
+
contain :items, :things
|
19
|
+
run lambda {|app| [200, {}, ['hello']]}
|
20
|
+
end
|
21
|
+
response = Rack::MockRequest.new(app).get('/items')
|
22
|
+
response.status.should == 401
|
23
|
+
response = Rack::MockRequest.new(app).get('/things')
|
24
|
+
response.status.should == 401
|
25
|
+
response = Rack::MockRequest.new(app).get('/')
|
26
|
+
response.status.should == 200
|
27
|
+
response.body.should == 'hello'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should insert a default app if one does not exist" do
|
31
|
+
app = Rack::Builder.new { contain :items }
|
32
|
+
response = Rack::MockRequest.new(app).get('/items')
|
33
|
+
response.status.should == 401
|
34
|
+
response = Rack::MockRequest.new(app).get('/')
|
35
|
+
response.status.should == 200
|
36
|
+
response.body.match('CloudKit').should_not be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "A Request" do
|
4
|
+
|
5
|
+
it "should match requests with routes" do
|
6
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
7
|
+
'http://example.com')).match?('GET', '/').should be_true
|
8
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
9
|
+
'http://example.com/')).match?('GET', '/').should be_true
|
10
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
11
|
+
'http://example.com/')).match?('POST', '/').should_not be_true
|
12
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
13
|
+
'http://example.com/hello')).match?('GET', '/hello').should be_true
|
14
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
15
|
+
'http://example.com/hello')).match?('GET', '/hello').should be_true
|
16
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
17
|
+
'http://example.com/hello', :method => 'POST')).match?(
|
18
|
+
'POST', '/hello').should be_true
|
19
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
20
|
+
'http://example.com/hello?q=a', :method => 'POST')).match?(
|
21
|
+
'POST', '/hello', [{'q' => 'a'}]).should be_true
|
22
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
23
|
+
'http://example.com/hello?q=a', :method => 'POST')).match?(
|
24
|
+
'POST', '/hello', ['q']).should be_true
|
25
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
26
|
+
'http://example.com/hello?q=a', :method => 'POST')).match?(
|
27
|
+
'POST', '/hello', [{'q' => 'b'}]).should_not be_true
|
28
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
29
|
+
'http://example.com/hello?q', :method => 'POST')).match?(
|
30
|
+
'POST', '/hello', [{'q' => nil}]).should be_true
|
31
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
32
|
+
'http://example.com/hello?q=a', :method => 'POST')).match?(
|
33
|
+
'POST', '/hello', [{'q' => nil}]).should_not be_true
|
34
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
35
|
+
'http://example.com/hello?q=a', :method => 'POST')).match?(
|
36
|
+
'POST', '/hello', [{'q' => ''}]).should_not be_true
|
37
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
38
|
+
'http://example.com/hello?q&x=y', :method => 'PUT')).match?(
|
39
|
+
'PUT', '/hello', ['q', {'x' => 'y'}]).should be_true
|
40
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
41
|
+
'http://example.com/hello?q&x=y&z', :method => 'PUT')).match?(
|
42
|
+
'PUT', '/hello', ['q', {'x' => 'y'}]).should be_true
|
43
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
44
|
+
'http://example.com/hello?q&x=y', :method => 'PUT')).match?(
|
45
|
+
'PUT', '/hello', [{'q' => 'a'},{'x' => 'y'}]).should_not be_true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should treat a trailing :id as a wildcard for path matching" do
|
49
|
+
CloudKit::Request.new(Rack::MockRequest.env_for(
|
50
|
+
'http://example.com/hello/123')).match?('GET', '/hello/:id').should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should inject stack-internal via-style env vars" do
|
54
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/test'))
|
55
|
+
request.via.should == []
|
56
|
+
request.inject_via('a.b')
|
57
|
+
request.via.include?('a.b').should be_true
|
58
|
+
request.inject_via('c.d')
|
59
|
+
request.via.include?('a.b').should be_true
|
60
|
+
request.via.include?('c.d').should be_true
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should announce the use of auth middleware" do
|
64
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
65
|
+
request.announce_auth(CLOUDKIT_OAUTH_FILTER_KEY)
|
66
|
+
request.via.include?(CLOUDKIT_OAUTH_FILTER_KEY).should be_true
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should know if auth provided by upstream middleware" do
|
70
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
71
|
+
request.announce_auth(CLOUDKIT_OAUTH_FILTER_KEY)
|
72
|
+
request.using_auth?.should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should know the current user" do
|
76
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
77
|
+
request.current_user.should be_nil
|
78
|
+
request = CloudKit::Request.new(
|
79
|
+
Rack::MockRequest.env_for('/', CLOUDKIT_AUTH_KEY => 'cecil'))
|
80
|
+
request.current_user.should_not be_nil
|
81
|
+
request.current_user.should == 'cecil'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should set the current user" do
|
85
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
86
|
+
request.current_user = 'cecil'
|
87
|
+
request.current_user.should_not be_nil
|
88
|
+
request.current_user.should == 'cecil'
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should know the login url" do
|
92
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
93
|
+
request.login_url.should == '/login'
|
94
|
+
request = CloudKit::Request.new(
|
95
|
+
Rack::MockRequest.env_for('/', CLOUDKIT_LOGIN_URL => '/sessions'))
|
96
|
+
request.login_url.should == '/sessions'
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should set the login url" do
|
100
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
101
|
+
request.login_url = '/welcome'
|
102
|
+
request.login_url.should == '/welcome'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should know the logout url" do
|
106
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
107
|
+
request.logout_url.should == '/logout'
|
108
|
+
request = CloudKit::Request.new(
|
109
|
+
Rack::MockRequest.env_for('/', CLOUDKIT_LOGOUT_URL => '/sessions'))
|
110
|
+
request.logout_url.should == '/sessions'
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should set the logout url" do
|
114
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
115
|
+
request.logout_url = '/goodbye'
|
116
|
+
request.logout_url.should == '/goodbye'
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should get the session" do
|
120
|
+
request = CloudKit::Request.new(
|
121
|
+
Rack::MockRequest.env_for('/', 'rack.session' => 'this'))
|
122
|
+
request.session.should_not be_nil
|
123
|
+
request.session.should == 'this'
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should know the flash" do
|
127
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
128
|
+
'/', 'rack.session' => {}))
|
129
|
+
request.flash.is_a?(CloudKit::FlashSession).should be_true
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should parse if-match headers" do
|
133
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
134
|
+
'/items/123/versions'))
|
135
|
+
request.if_match.should be_nil
|
136
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
137
|
+
'/items/123/versions', 'HTTP_IF_MATCH' => '"a"'))
|
138
|
+
request.if_match.should == 'a'
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should treat a list of etags in an if-match header as a single etag" do
|
142
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
143
|
+
'/items/123/versions', 'HTTP_IF_MATCH' => '"a", "b"'))
|
144
|
+
# See CloudKit::Request#if_match for more info on this expectation
|
145
|
+
request.if_match.should == 'a", "b'
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should ignore if-match when set to *" do
|
149
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
150
|
+
'/items/123/versions', 'HTTP_IF_MATCH' => '*'))
|
151
|
+
request.if_match.should be_nil
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should understand header auth" do
|
155
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
156
|
+
'http://photos.example.net/photos?file=vacation.jpg&size=original',
|
157
|
+
'Authorization' =>
|
158
|
+
'OAuth realm="",' +
|
159
|
+
'oauth_version="1.0",' +
|
160
|
+
'oauth_consumer_key="dpf43f3p2l4k3l03",' +
|
161
|
+
'oauth_token="nnch734d00sl2jdk",' +
|
162
|
+
'oauth_timestamp="1191242096",' +
|
163
|
+
'oauth_nonce="kllo9940pd9333jh",' +
|
164
|
+
'oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",' +
|
165
|
+
'oauth_signature_method="HMAC-SHA1"'))
|
166
|
+
request['oauth_consumer_key'].should == 'dpf43f3p2l4k3l03'
|
167
|
+
request['oauth_token'].should == 'nnch734d00sl2jdk'
|
168
|
+
request['oauth_timestamp'].should == '1191242096'
|
169
|
+
request['oauth_nonce'].should == 'kllo9940pd9333jh'
|
170
|
+
request['oauth_signature'].should == 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='
|
171
|
+
request['oauth_signature_method'].should == 'HMAC-SHA1'
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should know the last path element" do
|
175
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/'))
|
176
|
+
request.last_path_element.should be_nil
|
177
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/abc'))
|
178
|
+
request.last_path_element.should == 'abc'
|
179
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/abc/'))
|
180
|
+
request.last_path_element.should == 'abc'
|
181
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for('/abc/def'))
|
182
|
+
request.last_path_element.should == 'def'
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should know its domain root" do
|
186
|
+
request = CloudKit::Request.new(Rack::MockRequest.env_for(
|
187
|
+
'/', 'HTTP_HOST' => 'example.com'))
|
188
|
+
request.domain_root.should == "http://example.com"
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "A Resource" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
CloudKit.setup_storage_adapter unless CloudKit.storage_adapter
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:each) do
|
10
|
+
CloudKit.storage_adapter.clear
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "on initialization" do
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
@resource = CloudKit::Resource.new(
|
17
|
+
CloudKit::URI.new('/items/123'),
|
18
|
+
JSON.generate({:foo => 'bar'}),
|
19
|
+
'http://eric.dolphy.info')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should know its uri" do
|
23
|
+
@resource.uri.string.should == CloudKit::URI.new('/items/123').string
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should know its json" do
|
27
|
+
@resource.json.should == "{\"foo\":\"bar\"}"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should know its remote user" do
|
31
|
+
@resource.remote_user.should == 'http://eric.dolphy.info'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should default its deleted status to false" do
|
35
|
+
@resource.should_not be_deleted
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should default its archived status to false" do
|
39
|
+
@resource.should_not be_archived
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should default its etag to nil" do
|
43
|
+
@resource.etag.should be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should default its last-modified date to nil" do
|
47
|
+
@resource.last_modified.should be_nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should know if it is current" do
|
51
|
+
@resource.should be_current
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "on save" do
|
57
|
+
|
58
|
+
before(:each) do
|
59
|
+
@resource = CloudKit::Resource.new(
|
60
|
+
CloudKit::URI.new('/items/123'),
|
61
|
+
JSON.generate({:foo => 'bar'}),
|
62
|
+
'http://eric.dolphy.info')
|
63
|
+
@resource.save
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should set its etag" do
|
67
|
+
@resource.etag.should_not be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should set its last modified date" do
|
71
|
+
@resource.last_modified.should_not be_nil
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should adjust its URI when adding to a resource collection" do
|
75
|
+
resource = CloudKit::Resource.new(
|
76
|
+
CloudKit::URI.new('/items'),
|
77
|
+
JSON.generate({:foo => 'bar'}),
|
78
|
+
'http://eric.dolphy.info')
|
79
|
+
resource.save
|
80
|
+
resource.uri.string.should_not == '/items'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should flatten its json structure for querying" do
|
84
|
+
hash = CloudKit.storage_adapter.query.first
|
85
|
+
hash.keys.include?('foo').should be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should know it is current" do
|
89
|
+
@resource.should be_current
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "on create" do
|
95
|
+
|
96
|
+
def store_json(hash)
|
97
|
+
CloudKit::Resource.create(
|
98
|
+
CloudKit::URI.new('/items/123'),
|
99
|
+
JSON.generate(hash),
|
100
|
+
'http://eric.dolphy.info')
|
101
|
+
CloudKit.storage_adapter.query { |q|
|
102
|
+
q.add_condition 'uri', :eql, '/items/123'
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should save the resource" do
|
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]}]}'
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "on update" do
|
133
|
+
|
134
|
+
before(:each) do
|
135
|
+
@resource = CloudKit::Resource.create(
|
136
|
+
CloudKit::URI.new('/items/123'),
|
137
|
+
JSON.generate({:foo => 'bar'}),
|
138
|
+
'http://eric.dolphy.info')
|
139
|
+
@original_resource = @resource.dup
|
140
|
+
now = Time.now
|
141
|
+
Time.stub!(:now).and_return(now+1)
|
142
|
+
@resource.update(JSON.generate({:foo => 'baz'}))
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should version the resource" do
|
146
|
+
@resource.versions.size.should == 2
|
147
|
+
@resource.versions[-1].should be_archived
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should set a new etag" do
|
151
|
+
@resource.etag.should_not == @original_resource.etag
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should set a new last modified date" do
|
155
|
+
@resource.last_modified.should_not == @original_resource.last_modified
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should fail on archived resource versions" do
|
159
|
+
lambda {
|
160
|
+
@resource.versions[-1].update({:foo => 'box'})
|
161
|
+
}.should raise_error(CloudKit::HistoricalIntegrityViolation)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should fail on deleted resource versions" do
|
165
|
+
lambda {
|
166
|
+
@resource.delete
|
167
|
+
@resource.update({:foo => 'box'})
|
168
|
+
}.should raise_error(CloudKit::HistoricalIntegrityViolation)
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "on delete" do
|
174
|
+
|
175
|
+
before(:each) do
|
176
|
+
@resource = CloudKit::Resource.create(
|
177
|
+
CloudKit::URI.new('/items/123'),
|
178
|
+
JSON.generate({:foo => 'bar'}),
|
179
|
+
'http://eric.dolphy.info')
|
180
|
+
now = Time.now
|
181
|
+
Time.stub!(:now).and_return(now+1)
|
182
|
+
@resource.delete
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should version the resource" do
|
186
|
+
@resource.versions.size.should == 2
|
187
|
+
@resource.versions[-1].should be_archived
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should set the etag on the main resource to nil" do
|
191
|
+
@resource.etag.should be_nil
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should know it has been deleted" do
|
195
|
+
@resource.deleted?.should be_true
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should fail on archived resource versions" do
|
199
|
+
lambda {
|
200
|
+
@resource.versions[-1].update({:foo => 'box'})
|
201
|
+
}.should raise_error(CloudKit::HistoricalIntegrityViolation)
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "with versions" do
|
207
|
+
|
208
|
+
before(:each) do
|
209
|
+
@resource = CloudKit::Resource.create(
|
210
|
+
CloudKit::URI.new('/items/123'),
|
211
|
+
JSON.generate({:foo => 'bar'}),
|
212
|
+
'http://eric.dolphy.info')
|
213
|
+
@resource_list = [@resource.dup]
|
214
|
+
|
215
|
+
2.times { |i|
|
216
|
+
now = Time.now
|
217
|
+
Time.stub!(:now).and_return(now+1)
|
218
|
+
@resource.update(JSON.generate({:foo => i}))
|
219
|
+
@resource_list << @resource.dup
|
220
|
+
}
|
221
|
+
@resource_list.reverse!
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should keep an ordered list of versions" do
|
225
|
+
@resource.versions.map { |version| version.last_modified }.
|
226
|
+
should == @resource_list.map { |version| version.last_modified }
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should include the current version in the version list" do
|
230
|
+
current_ts = @resource.last_modified
|
231
|
+
@resource_list.map { |version| version.last_modified }.should include(current_ts)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should know its previous version" do
|
235
|
+
@resource.previous_version.last_modified.should == @resource_list[1].last_modified
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should know its previous versions" do
|
239
|
+
expected_times = @resource_list[1..-1].map { |version| version.last_modified }
|
240
|
+
@resource.previous_versions.map { |version| version.last_modified }.should == expected_times
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "when finding" do
|
246
|
+
|
247
|
+
before(:each) do
|
248
|
+
['bar', 'baz'].each do |value|
|
249
|
+
CloudKit::Resource.create(
|
250
|
+
CloudKit::URI.new('/items'),
|
251
|
+
JSON.generate({:foo => value}),
|
252
|
+
"http://eric.dolphy.info/#{value}_user")
|
253
|
+
end
|
254
|
+
CloudKit::Resource.create(
|
255
|
+
CloudKit::URI.new('/items'),
|
256
|
+
JSON.generate({:foo => 'box'}),
|
257
|
+
"http://eric.dolphy.info/bar_user")
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "using #all" do
|
261
|
+
|
262
|
+
it "should find matching resources" do
|
263
|
+
result = CloudKit::Resource.all(
|
264
|
+
:remote_user => 'http://eric.dolphy.info/bar_user')
|
265
|
+
result.size.should == 2
|
266
|
+
result.map { |item| item.remote_user.should == 'http://eric.dolphy.info/bar_user' }
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should return all elements if no restrictions are given" do
|
270
|
+
CloudKit::Resource.all.size.should == 3
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should return an empty array if no resources are found" do
|
274
|
+
CloudKit::Resource.all(:uri => 'fail').should be_empty
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should find with query parameters referencing JSON elements" do
|
278
|
+
resources = CloudKit::Resource.all(
|
279
|
+
:collection_reference => '/items',
|
280
|
+
:foo => 'bar')
|
281
|
+
resources.size.should == 1
|
282
|
+
resources.first.json.should == "{\"foo\":\"bar\"}"
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "on #first" do
|
288
|
+
|
289
|
+
it "should find the first matching resource" do
|
290
|
+
result = CloudKit::Resource.first(:remote_user => 'http://eric.dolphy.info/bar_user')
|
291
|
+
result.should_not === Array
|
292
|
+
result.remote_user.should == 'http://eric.dolphy.info/bar_user'
|
293
|
+
result.parsed_json['foo'].should == 'box' # all listings are reverse ordered
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "on #current" do
|
299
|
+
|
300
|
+
it "should find only current matching resources" do
|
301
|
+
resource = CloudKit::Resource.first(:remote_user => 'http://eric.dolphy.info/bar_user')
|
302
|
+
resource.update(JSON.generate({:foo => 'x'}))
|
303
|
+
CloudKit::Resource.current(:collection_reference => '/items').size.should == 3
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|