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,19 @@
|
|
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>OAuth Request Token Authorized</title>
|
7
|
+
<style type="text/css">
|
8
|
+
body {
|
9
|
+
font-family: 'Helvetica', 'Arial', san-serif;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<p>
|
15
|
+
Your OAuth Request Token has been authorized. Please return to the application
|
16
|
+
that initiated this request to complete the setup.
|
17
|
+
</p>
|
18
|
+
</body>
|
19
|
+
</html>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<XRDS xmlns="xri://$xrds">
|
3
|
+
<XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0">
|
4
|
+
<Type>xri://$xrds*simple</Type>
|
5
|
+
<Expires><%= Time.at(Time.now.to_i + 2592000).utc.xmlschema %></Expires>
|
6
|
+
<Service priority="10">
|
7
|
+
<Type>http://oauth.net/core/1.0/endpoint/request</Type>
|
8
|
+
<Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
|
9
|
+
<Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
|
10
|
+
<Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
|
11
|
+
<URI><%= request.scheme %>://<%= request.env['HTTP_HOST'] %>/oauth/request_tokens</URI>
|
12
|
+
</Service>
|
13
|
+
<Service priority="10">
|
14
|
+
<Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
|
15
|
+
<Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
|
16
|
+
<URI><%= request.scheme %>://<%= request.env['HTTP_HOST'] %>/oauth/authorization</URI>
|
17
|
+
</Service>
|
18
|
+
<Service priority="10">
|
19
|
+
<Type>http://oauth.net/core/1.0/endpoint/access</Type>
|
20
|
+
<Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
|
21
|
+
<Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
|
22
|
+
<Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
|
23
|
+
<URI><%= request.scheme %>://<%= request.env['HTTP_HOST'] %>/oauth/access_tokens</URI>
|
24
|
+
</Service>
|
25
|
+
<Service priority="10">
|
26
|
+
<Type>http://oauth.net/core/1.0/endpoint/resource</Type>
|
27
|
+
<Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
|
28
|
+
<Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
|
29
|
+
<Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
|
30
|
+
</Service>
|
31
|
+
<Service priority="10">
|
32
|
+
<Type>http://oauth.net/discovery/1.0/consumer-identity/static</Type>
|
33
|
+
<LocalID>cloudkitconsumer</LocalID>
|
34
|
+
</Service>
|
35
|
+
</XRD>
|
36
|
+
<XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
|
37
|
+
<Type>xri://$xrds*simple</Type>
|
38
|
+
<Service priority="10">
|
39
|
+
<Type>http://oauth.net/discovery/1.0</Type>
|
40
|
+
<URI>#oauth</URI>
|
41
|
+
</Service>
|
42
|
+
</XRD>
|
43
|
+
</XRDS>
|
@@ -0,0 +1,31 @@
|
|
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>Sign Up or Sign In</title>
|
7
|
+
<style type="text/css">
|
8
|
+
body {
|
9
|
+
font-family: 'Helvetica', 'Arial', san-serif;
|
10
|
+
}
|
11
|
+
#openid_url {
|
12
|
+
background: url(data:image/gif;base64,R0lGODlhEAAQANUAAPr6+t/f37Gxsby8vJycnKWlpaampq+vr/zUpfz8/Lm5ufLy8r6+vp2dncvLy/muVsjIyLW1tcTExKGhof7+/sDAwMHBwbS0tNnZ2erq6qioqPn5+bi4uLu7u+np6e3t7evr66mpqeXl5b29vaqqqtfX18zMzO7u7u/v756enveTHv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAQABAAAAZlwJVwKEQQj0eECslcqZbN4zMqhTY/EoFzKbCAVoDNKjFIERTbFaeRcpwWCU3qgBJOVxVDhOEpH4hTFBkBIyQiKSkBgFZEiBiLTBApBgBDd0gXKROKaU0mZiEFl0wAJR0FD1SqQ0EAOw%3D%3D) no-repeat #FFF 5px;
|
13
|
+
padding-left: 25px;
|
14
|
+
width: 300px;
|
15
|
+
}
|
16
|
+
#submit {
|
17
|
+
display: block;
|
18
|
+
margin-top: 10px;
|
19
|
+
}
|
20
|
+
</style>
|
21
|
+
</head>
|
22
|
+
<body>
|
23
|
+
<%= request.flash[:error] %>
|
24
|
+
<%= request.flash[:info] %>
|
25
|
+
<p>Sign Up or Sign In with OpenID</p>
|
26
|
+
<form action="/login" method="POST">
|
27
|
+
<input type="text" id="openid_url" name="openid_url"/>
|
28
|
+
<input id="submit" type="submit" value="Sign In"/>
|
29
|
+
</form>
|
30
|
+
</body>
|
31
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
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>OAuth Authorization Requested</title>
|
7
|
+
<style type="text/css">
|
8
|
+
body {
|
9
|
+
font-family: 'Helvetica', 'Arial', san-serif;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<p>
|
15
|
+
Another application or website has requested access to your data.
|
16
|
+
</p>
|
17
|
+
<form action="/oauth/authorized_request_tokens/<%= request['oauth_token'] %>" method="POST">
|
18
|
+
<input type="hidden" name="_method" value="PUT"/>
|
19
|
+
<input type="submit" value="Approve"/>
|
20
|
+
<input type="submit" value="Deny"/>
|
21
|
+
</form>
|
22
|
+
</body>
|
23
|
+
</html>
|
@@ -0,0 +1,18 @@
|
|
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>OAuth Request Token Authorization Denied</title>
|
7
|
+
<style type="text/css">
|
8
|
+
body {
|
9
|
+
font-family: 'Helvetica', 'Arial', san-serif;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<p>
|
15
|
+
Your OAuth Request Token was not denied and has been removed.
|
16
|
+
</p>
|
17
|
+
</body>
|
18
|
+
</html>
|
data/lib/cloudkit/uri.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module CloudKit
|
2
|
+
|
3
|
+
# A CloudKit::URI wraps a URI string, adding methods useful for routing
|
4
|
+
# in CloudKit as well as caching URI components for future comparisons.
|
5
|
+
class URI
|
6
|
+
|
7
|
+
# The string form of a URI.
|
8
|
+
attr_reader :string
|
9
|
+
|
10
|
+
# Create a new URI with the given string.
|
11
|
+
def initialize(string)
|
12
|
+
@string = string
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return the resource collection URI fragment.
|
16
|
+
# Example: URI.new('/foos/123').collection_uri_fragment => '/foos
|
17
|
+
def collection_uri_fragment
|
18
|
+
"/#{components[0]}" rescue nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Splits a URI into its components
|
22
|
+
def components
|
23
|
+
@components ||= @string.split('/').reject{|x| x == '' || x == nil} rescue []
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return the resource collection referenced by a URI.
|
27
|
+
# Example: URI.new('/foos/123').collection_type => :foos
|
28
|
+
def collection_type
|
29
|
+
components[0].to_sym rescue nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the URI for the current version of a resource.
|
33
|
+
# Example: URI.new('/foos/123/versions/abc').current_resource_uri => '/foos/123'
|
34
|
+
def current_resource_uri
|
35
|
+
"/#{components[0..1].join('/')}" rescue nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns true if URI matches /cloudkit-meta
|
39
|
+
def meta_uri?
|
40
|
+
return components.size == 1 && components[0] == 'cloudkit-meta'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns true if URI matches /{collection}
|
44
|
+
def resource_collection_uri?
|
45
|
+
return components.size == 1 && components[0] != 'cloudkit-meta'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns true if URI matches /{collection}/_resolved
|
49
|
+
def resolved_resource_collection_uri?
|
50
|
+
return components.size == 2 && components[1] == '_resolved'
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns true if URI matches /{collection}/{uuid}
|
54
|
+
def resource_uri?
|
55
|
+
return components.size == 2 && components[1] != '_resolved'
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns true if URI matches /{collection}/{uuid}/versions
|
59
|
+
def version_collection_uri?
|
60
|
+
return components.size == 3 && components[2] == 'versions'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns true if URI matches /{collection}/{uuid}/versions/_resolved
|
64
|
+
def resolved_version_collection_uri?
|
65
|
+
return components.size == 4 && components[2] == 'versions' && components[3] == '_resolved'
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns true if URI matches /{collection}/{uuid}/versions/{etag}
|
69
|
+
def resource_version_uri?
|
70
|
+
return components.size == 4 && components[2] == 'versions' && components[3] != '_resolved'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns a cannonical URI for a given URI/URI fragment, generating it if
|
74
|
+
# required.
|
75
|
+
# Example: URI.new('/items/123').cannoncal_uri_string => /items/123
|
76
|
+
#
|
77
|
+
# Example: URI.new('/items').cannonical_uri_string => /items/some-new-uuid
|
78
|
+
def cannonical_uri_string
|
79
|
+
@cannonical_uri_string ||= if resource_collection_uri?
|
80
|
+
"#{@string}/#{UUID.generate}"
|
81
|
+
elsif resource_uri?
|
82
|
+
@string
|
83
|
+
else
|
84
|
+
raise CloudKit::InvalidURIFormat
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CloudKit
|
2
|
+
|
3
|
+
# A thin layer on top of CloudKit::Store providing consistent URIs and
|
4
|
+
# automatic upgrades if required for future releases.
|
5
|
+
class UserStore
|
6
|
+
@@store = nil
|
7
|
+
|
8
|
+
def initialize(uri=nil)
|
9
|
+
unless @@store
|
10
|
+
@@store = Store.new(:collections => [:cloudkit_users])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(uri, options={}) #:nodoc:
|
15
|
+
@@store.get(CloudKit::URI.new(uri), options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def post(uri, options={}) #:nodoc:
|
19
|
+
@@store.post(CloudKit::URI.new(uri), options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def put(uri, options={}) #:nodoc:
|
23
|
+
@@store.put(CloudKit::URI.new(uri), options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(uri, options={}) #:nodoc:
|
27
|
+
@@store.delete(CloudKit::URI.new(uri), options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def resolve_uris(uris) #:nodoc:
|
31
|
+
@@store.resolve_uris(uris.map { |uri| CloudKit::URI.new(uri) })
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return the version for this UserStore
|
35
|
+
def version; 1; end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CloudKit
|
2
|
+
module Util
|
3
|
+
|
4
|
+
# Render ERB content
|
5
|
+
def erb(request, template, headers={'Content-Type' => 'text/html'}, status=200)
|
6
|
+
template_file = open(
|
7
|
+
File.join(File.dirname(__FILE__),
|
8
|
+
'templates',
|
9
|
+
"#{template.to_s}.erb"))
|
10
|
+
template = ERB.new(template_file.read)
|
11
|
+
result = template.result(binding)
|
12
|
+
Rack::Response.new(result, status, headers).finish
|
13
|
+
end
|
14
|
+
|
15
|
+
# Build a Rack::Router instance
|
16
|
+
def r(method, path, params=[])
|
17
|
+
Rack::Router.new(method, path, params)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Remove the outer double quotes from a given string.
|
21
|
+
def unquote(text)
|
22
|
+
(text =~ /^\".*\"$/) ? text[1..-2] : text
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/ext_spec.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "A Hash" do
|
4
|
+
|
5
|
+
it "should re-key an entry if it exists" do
|
6
|
+
x = {:a => 1, :b => 2}
|
7
|
+
x.rekey!(:b, :c)
|
8
|
+
x.should == {:a => 1, :c => 2}
|
9
|
+
x.rekey!(:d, :b)
|
10
|
+
x.should == {:a => 1, :c => 2}
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should re-key false and nil values" do
|
14
|
+
x = {:a => false, :b => nil}
|
15
|
+
x.rekey!(:b, :c)
|
16
|
+
x.should == {:a => false, :c => nil}
|
17
|
+
x.rekey!(:d, :b)
|
18
|
+
x.should == {:a => false, :c => nil}
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should merge conditionally" do
|
22
|
+
x = {:a => 1}
|
23
|
+
y = {:b => 2}
|
24
|
+
x.filter_merge!(:c => y[:c])
|
25
|
+
x.should == {:a => 1}
|
26
|
+
x.filter_merge!(:c => y[:b])
|
27
|
+
x.should == {:a => 1, :c => 2}
|
28
|
+
x = {}.filter_merge!(:a => 1)
|
29
|
+
x.should == {:a => 1}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should merge false values correctly" do
|
33
|
+
x = {:a => 1}
|
34
|
+
y = {:b => 2}
|
35
|
+
x.filter_merge!(:c => false)
|
36
|
+
x.should == {:a => 1, :c => false}
|
37
|
+
x.filter_merge!(:c => y[:b])
|
38
|
+
x.should == {:a => 1, :c => 2}
|
39
|
+
x = {}.filter_merge!(:a => false)
|
40
|
+
x.should == {:a => false}
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should exclude pairs using a single key" do
|
44
|
+
x = {:a => 1, :b => 2}
|
45
|
+
y = x.excluding(:b)
|
46
|
+
y.should == {:a => 1}
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should exclude pairs using a list of keys" do
|
50
|
+
x = {:a => 1, :b => 2, :c => 3}
|
51
|
+
y = x.excluding(:b, :c)
|
52
|
+
y.should == {:a => 1}
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "An Array" do
|
58
|
+
|
59
|
+
it "should exclude elements" do
|
60
|
+
x = [0, 1, 2, 3]
|
61
|
+
y = x.excluding(1, 3)
|
62
|
+
y.should == [0, 2]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "An Object" do
|
68
|
+
|
69
|
+
it "should try" do
|
70
|
+
x = {:a => 'a'}
|
71
|
+
result = x[:a].try(:upcase)
|
72
|
+
result.should == 'A'
|
73
|
+
lambda { x[:b].try(:upcase) }.should_not raise_error
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "A FlashSession" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@flash = CloudKit::FlashSession.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should accept a value for a key" do
|
10
|
+
@flash['greeting'] = 'hello'
|
11
|
+
@flash['greeting'].should == 'hello'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should erase a key/value pair after access" do
|
15
|
+
@flash['greeting'] = 'hello'
|
16
|
+
x = @flash['greeting']
|
17
|
+
@flash['greeting'].should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "A MemoryTable" do
|
4
|
+
before(:each) do
|
5
|
+
@table = CloudKit::MemoryTable.new
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:each) do
|
9
|
+
@table.clear
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should reject non-hash records" do
|
13
|
+
@table['a'] = 1
|
14
|
+
@table['a'].should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should reject non-string record keys" do
|
18
|
+
@table['a'] = {:foo => 'bar'}
|
19
|
+
@table['a'].should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should reject non-string record values" do
|
23
|
+
@table['a'] = {'foo' => 1}
|
24
|
+
@table['a'].should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should get and set values for table keys like a hash" do
|
28
|
+
@table['a'] = {'foo' => 'bar'}
|
29
|
+
@table['a'].should == {'foo' => 'bar'}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should clear its contents" do
|
33
|
+
@table['a'] = {'foo' => 'bar'}
|
34
|
+
@table['b'] = {'foo' => 'baz'}
|
35
|
+
@table.clear
|
36
|
+
@table['a'].should be_nil
|
37
|
+
@table['b'].should be_nil
|
38
|
+
@table.keys.should be_empty
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should keep an ordered set of keys" do
|
42
|
+
@table['b'] = {'foo' => 'bar'}
|
43
|
+
@table['a'] = {'foo' => 'baz'}
|
44
|
+
@table.keys.should == ['b', 'a']
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should generate incrementing ids" do
|
48
|
+
ids = []
|
49
|
+
4.times { ids << @table.generate_unique_id }
|
50
|
+
ids.should == [1, 2, 3, 4]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should query using a block supporting :eql comparisons" do
|
54
|
+
# For this release, only :eql comparisons are required
|
55
|
+
@table['a'] = {'foo' => 'bar', 'color' => 'blue'}
|
56
|
+
@table['b'] = {'foo' => 'baz', 'color' => 'blue'}
|
57
|
+
@table.query { |q|
|
58
|
+
q.add_condition('foo', :eql, 'bar')
|
59
|
+
}.should == [{'foo' => 'bar', 'color' => 'blue', :pk => 'a'}]
|
60
|
+
@table.query { |q|
|
61
|
+
q.add_condition('foo', :eql, 'baz')
|
62
|
+
}.should == [{'foo' => 'baz', 'color' => 'blue', :pk => 'b'}]
|
63
|
+
@table.query { |q|
|
64
|
+
q.add_condition('color', :eql, 'blue')
|
65
|
+
}.should == [
|
66
|
+
{'foo' => 'bar', 'color' => 'blue', :pk => 'a'},
|
67
|
+
{'foo' => 'baz', 'color' => 'blue', :pk => 'b'}]
|
68
|
+
@table.query { |q|
|
69
|
+
q.add_condition('foo', :eql, 'bar')
|
70
|
+
q.add_condition('color', :eql, 'blue')
|
71
|
+
}.should == [{'foo' => 'bar', 'color' => 'blue', :pk => 'a'}]
|
72
|
+
@table.query { |q|
|
73
|
+
q.add_condition('foo', :eql, 'bar')
|
74
|
+
q.add_condition('color', :eql, 'red')
|
75
|
+
}.should == []
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should query without a block" do
|
79
|
+
@table['a'] = {'foo' => 'bar', 'color' => 'blue'}
|
80
|
+
@table['b'] = {'foo' => 'baz', 'color' => 'blue'}
|
81
|
+
@table.query.should == [
|
82
|
+
{'foo' => 'bar', 'color' => 'blue', :pk => 'a'},
|
83
|
+
{'foo' => 'baz', 'color' => 'blue', :pk => 'b'}]
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|