cloudkit-jruby 0.11.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|