ripple 0.6.0 → 0.6.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/RELEASE_NOTES.textile +13 -0
- data/VERSION +1 -1
- data/lib/riak.rb +1 -0
- data/lib/riak/bucket.rb +9 -7
- data/lib/riak/client.rb +2 -1
- data/lib/riak/client/http_backend.rb +1 -1
- data/lib/riak/link.rb +12 -2
- data/lib/riak/robject.rb +7 -6
- data/lib/riak/util/escape.rb +12 -0
- data/lib/riak/walk_spec.rb +5 -1
- data/lib/ripple/core_ext/casting.rb +33 -0
- data/lib/ripple/document/attribute_methods/read.rb +1 -2
- data/ripple.gemspec +8 -3
- data/spec/riak/bucket_spec.rb +18 -0
- data/spec/riak/client_spec.rb +5 -0
- data/spec/riak/escape_spec.rb +17 -0
- data/spec/riak/http_backend_spec.rb +0 -5
- data/spec/riak/link_spec.rb +14 -0
- data/spec/riak/object_spec.rb +55 -5
- data/spec/ripple/attribute_methods_spec.rb +5 -0
- data/spec/ripple/core_ext_spec.rb +23 -0
- data/spec/ripple/properties_spec.rb +24 -0
- metadata +71 -33
data/RELEASE_NOTES.textile
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
h1. Ripple Release Notes
|
2
2
|
|
3
|
+
h2. 0.6.1 Patch Release - 2010-03-17
|
4
|
+
|
5
|
+
This is a minor release with fixes for a few issues:
|
6
|
+
|
7
|
+
* Riak::Link objects will now be unique when added to RObject#links
|
8
|
+
Set. [John Lynch]
|
9
|
+
* Attributes on Ripple::Document classes are no longer clone, which had
|
10
|
+
prevented non-scalar properties from being modified directly (e.g. Array).
|
11
|
+
* Buckets, keys, and walk specs are properly escaped in URLs
|
12
|
+
(including slashes).
|
13
|
+
* Time-related properties properly convert to string formats in JSON that
|
14
|
+
they will work in the context of MapReduce jobs.
|
15
|
+
|
3
16
|
h2. 0.6.0 Feature Release - 2010-03-05
|
4
17
|
|
5
18
|
This release contains enhancements and bugfixes in preparation for the
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.1
|
data/lib/riak.rb
CHANGED
data/lib/riak/bucket.rb
CHANGED
@@ -18,6 +18,8 @@ module Riak
|
|
18
18
|
# using {Client#bucket}, or create it manually and retrieve its meta-information later.
|
19
19
|
class Bucket
|
20
20
|
include Util::Translation
|
21
|
+
include Util::Escape
|
22
|
+
|
21
23
|
# @return [Riak::Client] the associated client
|
22
24
|
attr_reader :client
|
23
25
|
|
@@ -62,12 +64,12 @@ module Riak
|
|
62
64
|
# @return [Array<String>] Keys in this bucket
|
63
65
|
def keys(options={})
|
64
66
|
if block_given?
|
65
|
-
@client.http.get(200, @client.prefix, name, {:props => false}, {}) do |chunk|
|
67
|
+
@client.http.get(200, @client.prefix, escape(name), {:props => false}, {}) do |chunk|
|
66
68
|
obj = ActiveSupport::JSON.decode(chunk) rescue {}
|
67
69
|
yield obj['keys'].map {|k| URI.unescape(k) } if obj['keys']
|
68
70
|
end
|
69
71
|
elsif @keys.nil? || options[:reload]
|
70
|
-
response = @client.http.get(200, @client.prefix, name, {:props => false}, {})
|
72
|
+
response = @client.http.get(200, @client.prefix, escape(name), {:props => false}, {})
|
71
73
|
load(response)
|
72
74
|
end
|
73
75
|
@keys
|
@@ -81,7 +83,7 @@ module Riak
|
|
81
83
|
def props=(properties)
|
82
84
|
raise ArgumentError, t("hash_type", :hash => properties.inspect) unless Hash === properties
|
83
85
|
body = {'props' => properties}.to_json
|
84
|
-
@client.http.put(204, @client.prefix, name, body, {"Content-Type" => "application/json"})
|
86
|
+
@client.http.put(204, @client.prefix, escape(name), body, {"Content-Type" => "application/json"})
|
85
87
|
@props = properties
|
86
88
|
end
|
87
89
|
|
@@ -93,7 +95,7 @@ module Riak
|
|
93
95
|
# @raise [FailedRequest] if the object is not found or some other error occurs
|
94
96
|
def get(key, options={})
|
95
97
|
code = allow_mult ? [200,300] : 200
|
96
|
-
response = @client.http.get(code, @client.prefix, name, key, options, {})
|
98
|
+
response = @client.http.get(code, @client.prefix, escape(name), escape(key), options, {})
|
97
99
|
RObject.new(self, key).load(response)
|
98
100
|
end
|
99
101
|
alias :[] :get
|
@@ -126,7 +128,7 @@ module Riak
|
|
126
128
|
def allow_mult
|
127
129
|
props['allow_mult']
|
128
130
|
end
|
129
|
-
|
131
|
+
|
130
132
|
# Set the allow_mult property. *NOTE* This will result in a PUT request to Riak.
|
131
133
|
# @param [true, false] value whether the bucket should allow siblings
|
132
134
|
def allow_mult=(value)
|
@@ -146,10 +148,10 @@ module Riak
|
|
146
148
|
self.props = props.merge('n_val' => value)
|
147
149
|
value
|
148
150
|
end
|
149
|
-
|
151
|
+
|
150
152
|
# @return [String] a representation suitable for IRB and debugging output
|
151
153
|
def inspect
|
152
|
-
"#<Riak::Bucket #{client.http.path(client.prefix, name).to_s}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
|
154
|
+
"#<Riak::Bucket #{client.http.path(client.prefix, escape(name)).to_s}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
|
153
155
|
end
|
154
156
|
end
|
155
157
|
end
|
data/lib/riak/client.rb
CHANGED
@@ -17,6 +17,7 @@ module Riak
|
|
17
17
|
# A client connection to Riak.
|
18
18
|
class Client
|
19
19
|
include Util::Translation
|
20
|
+
include Util::Escape
|
20
21
|
|
21
22
|
autoload :HTTPBackend, "riak/client/http_backend"
|
22
23
|
autoload :NetHTTPBackend, "riak/client/net_http_backend"
|
@@ -116,7 +117,7 @@ module Riak
|
|
116
117
|
# @return [Bucket] the requested bucket
|
117
118
|
def bucket(name, options={})
|
118
119
|
options.assert_valid_keys(:keys, :props)
|
119
|
-
response = http.get(200, prefix, name, options, {})
|
120
|
+
response = http.get(200, prefix, escape(name), options, {})
|
120
121
|
Bucket.new(self, name).load(response)
|
121
122
|
end
|
122
123
|
alias :[] :bucket
|
@@ -143,7 +143,7 @@ module Riak
|
|
143
143
|
# @return [URI] an absolute URI for the resource
|
144
144
|
def path(*segments)
|
145
145
|
query = segments.extract_options!.to_param
|
146
|
-
root_uri.merge(
|
146
|
+
root_uri.merge(segments.join("/").gsub(/\/+/, "/").sub(/^\//, '')).tap do |uri|
|
147
147
|
uri.query = query if query.present?
|
148
148
|
end
|
149
149
|
end
|
data/lib/riak/link.rb
CHANGED
@@ -22,6 +22,8 @@ module Riak
|
|
22
22
|
|
23
23
|
# @return [String] the relationship ("rel") of the other resource to this one
|
24
24
|
attr_accessor :rel
|
25
|
+
alias :tag :rel
|
26
|
+
alias :tag= :rel=
|
25
27
|
|
26
28
|
# @param [String] header_string the string value of the Link: HTTP header from a Riak response
|
27
29
|
# @return [Array<Link>] an array of Riak::Link structs parsed from the header
|
@@ -37,12 +39,12 @@ module Riak
|
|
37
39
|
|
38
40
|
# @return [String] bucket_name, if the Link url is a known Riak link ("/riak/<bucket>/<key>")
|
39
41
|
def bucket
|
40
|
-
$1 if url =~ %r{
|
42
|
+
URI.unescape($1) if url =~ %r{^/[^/]+/([^/]+)/?}
|
41
43
|
end
|
42
44
|
|
43
45
|
# @return [String] key, if the Link url is a known Riak link ("/riak/<bucket>/<key>")
|
44
46
|
def key
|
45
|
-
$1 if url =~ %r{
|
47
|
+
URI.unescape($1) if url =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
|
46
48
|
end
|
47
49
|
|
48
50
|
def inspect; to_s; end
|
@@ -51,6 +53,14 @@ module Riak
|
|
51
53
|
%Q[<#{@url}>; riaktag="#{@rel}"]
|
52
54
|
end
|
53
55
|
|
56
|
+
def hash
|
57
|
+
self.to_s.hash
|
58
|
+
end
|
59
|
+
|
60
|
+
def eql?(other)
|
61
|
+
self == other
|
62
|
+
end
|
63
|
+
|
54
64
|
def ==(other)
|
55
65
|
other.is_a?(Link) && url == other.url && rel == other.rel
|
56
66
|
end
|
data/lib/riak/robject.rb
CHANGED
@@ -20,6 +20,7 @@ module Riak
|
|
20
20
|
class RObject
|
21
21
|
include Util
|
22
22
|
include Util::Translation
|
23
|
+
include Util::Escape
|
23
24
|
|
24
25
|
# @return [Bucket] the bucket in which this object is contained
|
25
26
|
attr_accessor :bucket
|
@@ -117,7 +118,7 @@ module Riak
|
|
117
118
|
def store(options={})
|
118
119
|
raise ArgumentError, t("content_type_undefined") unless @content_type.present?
|
119
120
|
params = {:returnbody => true}.merge(options)
|
120
|
-
method, codes, path = @key.present? ? [:put, [200,204], "#{@bucket.name}/#{@key}"] : [:post, 201, @bucket.name]
|
121
|
+
method, codes, path = @key.present? ? [:put, [200,204,300], "#{escape(@bucket.name)}/#{escape(@key)}"] : [:post, 201, escape(@bucket.name)]
|
121
122
|
response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, serialize(data), store_headers)
|
122
123
|
load(response)
|
123
124
|
end
|
@@ -131,7 +132,7 @@ module Riak
|
|
131
132
|
force = options.delete(:force)
|
132
133
|
return self unless @key && (@vclock || force)
|
133
134
|
codes = @bucket.allow_mult ? [200,300,304] : [200,304]
|
134
|
-
response = @bucket.client.http.get(codes, @bucket.client.prefix, @bucket.name, @key, options, reload_headers)
|
135
|
+
response = @bucket.client.http.get(codes, @bucket.client.prefix, escape(@bucket.name), escape(@key), options, reload_headers)
|
135
136
|
load(response) unless response[:code] == 304
|
136
137
|
self
|
137
138
|
end
|
@@ -142,7 +143,7 @@ module Riak
|
|
142
143
|
# exists in the Riak database.
|
143
144
|
def delete
|
144
145
|
return if key.blank?
|
145
|
-
@bucket.client.http.delete([204,404], @bucket.client.prefix, @bucket.name, key)
|
146
|
+
@bucket.client.http.delete([204,404], @bucket.client.prefix, escape(@bucket.name), escape(@key))
|
146
147
|
freeze
|
147
148
|
end
|
148
149
|
|
@@ -215,13 +216,13 @@ module Riak
|
|
215
216
|
|
216
217
|
# @return [String] A representation suitable for IRB and debugging output
|
217
218
|
def inspect
|
218
|
-
"#<#{self.class.name} #{@bucket.client.http.path(@bucket.client.prefix, @bucket.name, @key).to_s} [#{@content_type}]:#{@data.inspect}>"
|
219
|
+
"#<#{self.class.name} #{@bucket.client.http.path(@bucket.client.prefix, escape(@bucket.name), escape(@key)).to_s} [#{@content_type}]:#{@data.inspect}>"
|
219
220
|
end
|
220
221
|
|
221
222
|
# Walks links from this object to other objects in Riak.
|
222
223
|
def walk(*params)
|
223
224
|
specs = WalkSpec.normalize(*params)
|
224
|
-
response = @bucket.client.http.get(200, @bucket.client.prefix, @bucket.name, @key, specs.join("/"))
|
225
|
+
response = @bucket.client.http.get(200, @bucket.client.prefix, escape(@bucket.name), escape(@key), specs.join("/"))
|
225
226
|
if boundary = Multipart.extract_boundary(response[:headers]['content-type'].first)
|
226
227
|
Multipart.parse(response[:body], boundary).map do |group|
|
227
228
|
map_walk_group(group)
|
@@ -233,7 +234,7 @@ module Riak
|
|
233
234
|
|
234
235
|
# Converts the object to a link suitable for linking other objects to it
|
235
236
|
def to_link(tag=nil)
|
236
|
-
Link.new(@bucket.client.http.path(@bucket.client.prefix, @bucket.name, @key).path, tag)
|
237
|
+
Link.new(@bucket.client.http.path(@bucket.client.prefix, escape(@bucket.name), escape(@key)).path, tag)
|
237
238
|
end
|
238
239
|
|
239
240
|
private
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Riak
|
2
|
+
module Util
|
3
|
+
module Escape
|
4
|
+
# URI-escapes bucket or key names that may contain slashes for use in URLs.
|
5
|
+
# @param [String] bucket_or_key the bucket or key name
|
6
|
+
# @return [String] the escaped path segment
|
7
|
+
def escape(bucket_or_key)
|
8
|
+
URI.escape(bucket_or_key).gsub("/", "%2F")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/riak/walk_spec.rb
CHANGED
@@ -24,6 +24,8 @@ module Riak
|
|
24
24
|
# Riak::WalkSpec.new({:bucket => 'tracks', :result => true})
|
25
25
|
class WalkSpec
|
26
26
|
include Util::Translation
|
27
|
+
include Util::Escape
|
28
|
+
|
27
29
|
# @return [String] The bucket followed links should be restricted to. "_" represents all buckets.
|
28
30
|
attr_accessor :bucket
|
29
31
|
|
@@ -84,7 +86,9 @@ module Riak
|
|
84
86
|
|
85
87
|
# Converts the walk-spec into the form required by the link-walker resource URL
|
86
88
|
def to_s
|
87
|
-
|
89
|
+
b = @bucket && escape(@bucket) || '_'
|
90
|
+
t = @tag && escape(@tag) || '_'
|
91
|
+
"#{b},#{t},#{@keep ? '1' : '_'}"
|
88
92
|
end
|
89
93
|
|
90
94
|
def ==(other)
|
@@ -94,3 +94,36 @@ class FalseClass
|
|
94
94
|
Boolean.ripple_cast(value)
|
95
95
|
end
|
96
96
|
end
|
97
|
+
|
98
|
+
# @private
|
99
|
+
class Time
|
100
|
+
def as_json(options={})
|
101
|
+
self.utc.rfc822
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.ripple_cast(value)
|
105
|
+
value.respond_to?(:to_time) && value.to_time or raise Ripple::PropertyTypeMismatch.new(self, value)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @private
|
110
|
+
class Date
|
111
|
+
def as_json(options={})
|
112
|
+
self.to_s(:rfc822)
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.ripple_cast(value)
|
116
|
+
value.respond_to?(:to_date) && value.to_date or raise Ripple::PropertyTypeMismatch.new(self, value)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# @private
|
121
|
+
class DateTime
|
122
|
+
def as_json(options={})
|
123
|
+
self.utc.to_s(:rfc822)
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.ripple_cast(value)
|
127
|
+
value.respond_to?(:to_datetime) && value.to_datetime or raise Ripple::PropertyTypeMismatch.new(self, value)
|
128
|
+
end
|
129
|
+
end
|
data/ripple.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ripple}
|
8
|
-
s.version = "0.6.
|
8
|
+
s.version = "0.6.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Sean Cribbs"]
|
12
|
-
s.date = %q{2010-03-
|
12
|
+
s.date = %q{2010-03-17}
|
13
13
|
s.description = %q{ripple is a rich Ruby client for Riak, Basho's distributed database. It includes all the basics of accessing and manipulating Riak buckets and objects, and an object mapper library for building a rich domain on top of Riak.}
|
14
14
|
s.email = %q{seancribbs@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -39,6 +39,7 @@ Gem::Specification.new do |s|
|
|
39
39
|
"lib/riak/map_reduce.rb",
|
40
40
|
"lib/riak/map_reduce_error.rb",
|
41
41
|
"lib/riak/robject.rb",
|
42
|
+
"lib/riak/util/escape.rb",
|
42
43
|
"lib/riak/util/fiber1.8.rb",
|
43
44
|
"lib/riak/util/headers.rb",
|
44
45
|
"lib/riak/util/multipart.rb",
|
@@ -72,6 +73,7 @@ Gem::Specification.new do |s|
|
|
72
73
|
"spec/riak/bucket_spec.rb",
|
73
74
|
"spec/riak/client_spec.rb",
|
74
75
|
"spec/riak/curb_backend_spec.rb",
|
76
|
+
"spec/riak/escape_spec.rb",
|
75
77
|
"spec/riak/headers_spec.rb",
|
76
78
|
"spec/riak/http_backend_spec.rb",
|
77
79
|
"spec/riak/link_spec.rb",
|
@@ -83,6 +85,7 @@ Gem::Specification.new do |s|
|
|
83
85
|
"spec/ripple/attribute_methods_spec.rb",
|
84
86
|
"spec/ripple/bucket_access_spec.rb",
|
85
87
|
"spec/ripple/callbacks_spec.rb",
|
88
|
+
"spec/ripple/core_ext_spec.rb",
|
86
89
|
"spec/ripple/document_spec.rb",
|
87
90
|
"spec/ripple/embedded_document_spec.rb",
|
88
91
|
"spec/ripple/finders_spec.rb",
|
@@ -100,12 +103,13 @@ Gem::Specification.new do |s|
|
|
100
103
|
s.rdoc_options = ["--charset=UTF-8"]
|
101
104
|
s.require_paths = ["lib"]
|
102
105
|
s.requirements = ["`gem install curb` for better HTTP performance"]
|
103
|
-
s.rubygems_version = %q{1.3.
|
106
|
+
s.rubygems_version = %q{1.3.6}
|
104
107
|
s.summary = %q{ripple is a rich Ruby client for Riak, Basho's distributed database.}
|
105
108
|
s.test_files = [
|
106
109
|
"spec/riak/bucket_spec.rb",
|
107
110
|
"spec/riak/client_spec.rb",
|
108
111
|
"spec/riak/curb_backend_spec.rb",
|
112
|
+
"spec/riak/escape_spec.rb",
|
109
113
|
"spec/riak/headers_spec.rb",
|
110
114
|
"spec/riak/http_backend_spec.rb",
|
111
115
|
"spec/riak/link_spec.rb",
|
@@ -117,6 +121,7 @@ Gem::Specification.new do |s|
|
|
117
121
|
"spec/ripple/attribute_methods_spec.rb",
|
118
122
|
"spec/ripple/bucket_access_spec.rb",
|
119
123
|
"spec/ripple/callbacks_spec.rb",
|
124
|
+
"spec/ripple/core_ext_spec.rb",
|
120
125
|
"spec/ripple/document_spec.rb",
|
121
126
|
"spec/ripple/embedded_document_spec.rb",
|
122
127
|
"spec/ripple/finders_spec.rb",
|
data/spec/riak/bucket_spec.rb
CHANGED
@@ -104,6 +104,12 @@ describe Riak::Bucket do
|
|
104
104
|
@http.should_receive(:get).with(200, "/riak/","foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
|
105
105
|
@bucket.keys.should == ["bar baz"]
|
106
106
|
end
|
107
|
+
|
108
|
+
it "should escape the bucket name" do
|
109
|
+
@bucket.instance_variable_set :@name, "unescaped "
|
110
|
+
@http.should_receive(:get).with(200, "/riak/","unescaped%20", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
|
111
|
+
@bucket.keys.should == ["bar"]
|
112
|
+
end
|
107
113
|
end
|
108
114
|
|
109
115
|
describe "setting the bucket properties" do
|
@@ -120,6 +126,12 @@ describe Riak::Bucket do
|
|
120
126
|
it "should raise an error if an invalid property is given" do
|
121
127
|
lambda { @bucket.props = "blah" }.should raise_error(ArgumentError)
|
122
128
|
end
|
129
|
+
|
130
|
+
it "should escape the bucket name" do
|
131
|
+
@bucket.instance_variable_set :@name, "foo bar"
|
132
|
+
@http.should_receive(:put).with(204, "/riak/","foo%20bar", '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
|
133
|
+
@bucket.props = {:n_val => 2}
|
134
|
+
end
|
123
135
|
end
|
124
136
|
|
125
137
|
describe "fetching an object" do
|
@@ -143,6 +155,12 @@ describe Riak::Bucket do
|
|
143
155
|
@http.should_receive(:get).with([200,300], "/riak/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
|
144
156
|
@bucket.get('db')
|
145
157
|
end
|
158
|
+
|
159
|
+
it "should escape the bucket and key names" do
|
160
|
+
@bucket.instance_variable_set(:@name, "foo ")
|
161
|
+
@http.should_receive(:get).with(200, "/riak/","foo%20", "%20bar", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
|
162
|
+
@bucket.get(' bar')
|
163
|
+
end
|
146
164
|
end
|
147
165
|
|
148
166
|
describe "creating a new blank object" do
|
data/spec/riak/client_spec.rb
CHANGED
@@ -165,5 +165,10 @@ describe Riak::Client do
|
|
165
165
|
@http.should_receive(:get).with(200, "/riak/", "foo", {:keys => false}, {}).and_return(@payload)
|
166
166
|
@client.bucket("foo", :keys => false)
|
167
167
|
end
|
168
|
+
|
169
|
+
it "should escape bucket names with invalid characters" do
|
170
|
+
@http.should_receive(:get).with(200, "/riak/", "foo%2Fbar%20", {:keys => false}, {}).and_return(@payload)
|
171
|
+
@client.bucket("foo/bar ", :keys => false)
|
172
|
+
end
|
168
173
|
end
|
169
174
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Riak::Util::Escape do
|
4
|
+
before :each do
|
5
|
+
@object = Object.new
|
6
|
+
@object.extend(Riak::Util::Escape)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should escape standard non-safe characters" do
|
10
|
+
@object.escape("some string").should == "some%20string"
|
11
|
+
@object.escape("another^one").should == "another%5Eone"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should escape slashes" do
|
15
|
+
@object.escape("some/inner/path").should == "some%2Finner%2Fpath"
|
16
|
+
end
|
17
|
+
end
|
@@ -45,11 +45,6 @@ describe Riak::Client::HTTPBackend do
|
|
45
45
|
@backend.path("/foo/bar").to_s.should == "http://127.0.0.1:8098/foo/bar"
|
46
46
|
end
|
47
47
|
|
48
|
-
it "should escape appropriate characters in a relative resource path" do
|
49
|
-
@backend.path("foo bar").to_s.should == "http://127.0.0.1:8098/foo%20bar"
|
50
|
-
@backend.path("foo", "bar", {"param" => "a string"}).to_s.should == "http://127.0.0.1:8098/foo/bar?param=a+string"
|
51
|
-
end
|
52
|
-
|
53
48
|
it "should compute a URI from a relative resource path with a hash of query parameters" do
|
54
49
|
@backend.path("baz", :r => 2).to_s.should == "http://127.0.0.1:8098/baz?r=2"
|
55
50
|
end
|
data/spec/riak/link_spec.rb
CHANGED
@@ -65,4 +65,18 @@ describe Riak::Link do
|
|
65
65
|
[one].should include(two)
|
66
66
|
[two].should include(one)
|
67
67
|
end
|
68
|
+
|
69
|
+
it "should unescape the bucket name" do
|
70
|
+
Riak::Link.new("/riak/bucket%20spaces/key", "foo").bucket.should == "bucket spaces"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should unescape the key name" do
|
74
|
+
Riak::Link.new("/riak/bucket/key%2Fname", "foo").key.should == "key/name"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should not rely on the prefix to equal /riak/ when extracting the bucket and key" do
|
78
|
+
link = Riak::Link.new("/raw/bucket/key", "foo")
|
79
|
+
link.bucket.should == "bucket"
|
80
|
+
link.key.should == "key"
|
81
|
+
end
|
68
82
|
end
|
data/spec/riak/object_spec.rb
CHANGED
@@ -30,7 +30,7 @@ describe Riak::RObject do
|
|
30
30
|
@object.key.should == "bar"
|
31
31
|
end
|
32
32
|
|
33
|
-
it "should initialize the links to an empty
|
33
|
+
it "should initialize the links to an empty set" do
|
34
34
|
@object = Riak::RObject.new(@bucket, "bar")
|
35
35
|
@object.links.should == Set.new
|
36
36
|
end
|
@@ -202,6 +202,11 @@ describe Riak::RObject do
|
|
202
202
|
@object.load({:headers => {"content-type" => ["multipart/mixed; boundary=foo"]}, :code => 300 })
|
203
203
|
@object.should be_conflict
|
204
204
|
end
|
205
|
+
|
206
|
+
it "should unescape the key given in the location header" do
|
207
|
+
@object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/baz%20"]}})
|
208
|
+
@object.key.should == "baz "
|
209
|
+
end
|
205
210
|
end
|
206
211
|
|
207
212
|
describe "extracting siblings" do
|
@@ -247,7 +252,7 @@ describe Riak::RObject do
|
|
247
252
|
|
248
253
|
describe "when links are defined" do
|
249
254
|
before :each do
|
250
|
-
@object.links
|
255
|
+
@object.links << Riak::Link.new("/riak/foo/baz", "next")
|
251
256
|
end
|
252
257
|
|
253
258
|
it "should include a Link header with references to other objects" do
|
@@ -260,10 +265,15 @@ describe Riak::RObject do
|
|
260
265
|
@object.store_headers.should have_key("Link")
|
261
266
|
@object.store_headers["Link"].should_not include('riaktag="up"')
|
262
267
|
end
|
268
|
+
|
269
|
+
it "should not allow duplicate links" do
|
270
|
+
@object.links << Riak::Link.new("/riak/foo/baz", "next")
|
271
|
+
@object.links.length.should == 1
|
272
|
+
end
|
263
273
|
end
|
264
274
|
|
265
275
|
it "should exclude the Link header when no links are present" do
|
266
|
-
@object.links =
|
276
|
+
@object.links = Set.new
|
267
277
|
@object.store_headers.should_not have_key("Link")
|
268
278
|
end
|
269
279
|
|
@@ -339,6 +349,12 @@ describe Riak::RObject do
|
|
339
349
|
@http.should_receive(:post).with(201, "/riak/", "foo", {:dw => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
|
340
350
|
@object.store(:dw => 2)
|
341
351
|
end
|
352
|
+
|
353
|
+
it "should escape the bucket name" do
|
354
|
+
@bucket.should_receive(:name).and_return("foo ")
|
355
|
+
@http.should_receive(:post).with(201, "/riak/", "foo%20", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 201})
|
356
|
+
@object.store
|
357
|
+
end
|
342
358
|
end
|
343
359
|
|
344
360
|
describe "when the object has a key" do
|
@@ -347,16 +363,23 @@ describe Riak::RObject do
|
|
347
363
|
end
|
348
364
|
|
349
365
|
it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
|
350
|
-
@http.should_receive(:put).with([200,204], "/riak/", "foo/bar", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
|
366
|
+
@http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
|
351
367
|
@object.store
|
352
368
|
@object.key.should == "somereallylongstring"
|
353
369
|
@object.vclock.should == "areallylonghashvalue"
|
354
370
|
end
|
355
371
|
|
356
372
|
it "should include persistence-tuning parameters in the query string" do
|
357
|
-
@http.should_receive(:put).with([200,204], "/riak/", "foo/bar", {:dw => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
|
373
|
+
@http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:dw => 2, :returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
|
358
374
|
@object.store(:dw => 2)
|
359
375
|
end
|
376
|
+
|
377
|
+
it "should escape the bucket and key names" do
|
378
|
+
@http.should_receive(:put).with([200,204,300], "/riak/", "foo%20/bar%2Fbaz", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
|
379
|
+
@bucket.instance_variable_set(:@name, "foo ")
|
380
|
+
@object.key = "bar/baz"
|
381
|
+
@object.store
|
382
|
+
end
|
360
383
|
end
|
361
384
|
end
|
362
385
|
|
@@ -410,6 +433,13 @@ describe Riak::RObject do
|
|
410
433
|
@http.should_receive(:get).with([200,300,304], "/riak/", "foo", "bar", {}, {}).and_return({:code => 304})
|
411
434
|
@object.reload
|
412
435
|
end
|
436
|
+
|
437
|
+
it "should escape the bucket and key names" do
|
438
|
+
@bucket.should_receive(:name).and_return("some/deep/path")
|
439
|
+
@object.key = "another/deep/path"
|
440
|
+
@http.should_receive(:get).with([200,304], "/riak/", "some%2Fdeep%2Fpath", "another%2Fdeep%2Fpath", {}, {}).and_return({:code => 304})
|
441
|
+
@object.reload
|
442
|
+
end
|
413
443
|
end
|
414
444
|
|
415
445
|
describe "walking from the object to linked objects" do
|
@@ -443,6 +473,13 @@ describe Riak::RObject do
|
|
443
473
|
@client.should_receive(:bucket).with("foo", :keys => false).and_return(@bucket)
|
444
474
|
@object.walk(nil,"next",true)
|
445
475
|
end
|
476
|
+
|
477
|
+
it "should escape the bucket, key and link specs" do
|
478
|
+
@object.key = "bar/baz"
|
479
|
+
@bucket.should_receive(:name).and_return("quin/quux")
|
480
|
+
@http.should_receive(:get).with(200, "/riak/", "quin%2Fquux", "bar%2Fbaz", "_,next%2F2,1").and_return(:headers => {"content-type" => ["multipart/mixed; boundary=12345"]}, :body => "\n--12345\nContent-Type: multipart/mixed; boundary=09876\n\n--09876--\n\n--12345--\n")
|
481
|
+
@object.walk(:tag => "next/2", :keep => true)
|
482
|
+
end
|
446
483
|
end
|
447
484
|
|
448
485
|
describe "when deleting" do
|
@@ -468,6 +505,13 @@ describe Riak::RObject do
|
|
468
505
|
@http.should_receive(:delete).and_raise(Riak::FailedRequest.new(:delete, [204,404], 500, {}, ""))
|
469
506
|
lambda { @object.delete }.should raise_error(Riak::FailedRequest)
|
470
507
|
end
|
508
|
+
|
509
|
+
it "should escape the bucket and key names" do
|
510
|
+
@object.key = "deep/path"
|
511
|
+
@bucket.should_receive(:name).and_return("bucket spaces")
|
512
|
+
@http.should_receive(:delete).with([204,404], "/riak/", "bucket%20spaces", "deep%2Fpath").and_return({:code => 204, :headers => {}})
|
513
|
+
@object.delete
|
514
|
+
end
|
471
515
|
end
|
472
516
|
|
473
517
|
it "should convert to a link having the same url and an empty tag" do
|
@@ -479,4 +523,10 @@ describe Riak::RObject do
|
|
479
523
|
@object = Riak::RObject.new(@bucket, "bar")
|
480
524
|
@object.to_link("next").should == Riak::Link.new("/riak/foo/bar", "next")
|
481
525
|
end
|
526
|
+
|
527
|
+
it "should escape the bucket and key when converting to a link" do
|
528
|
+
@object = Riak::RObject.new(@bucket, "deep/path")
|
529
|
+
@bucket.should_receive(:name).and_return("bucket spaces")
|
530
|
+
@object.to_link("bar").url.should == "/riak/bucket%20spaces/deep%2Fpath"
|
531
|
+
end
|
482
532
|
end
|
@@ -60,6 +60,11 @@ describe Ripple::Document::AttributeMethods do
|
|
60
60
|
it "should return the property default if defined and not set" do
|
61
61
|
@widget.name.should == "widget"
|
62
62
|
end
|
63
|
+
|
64
|
+
it "should expose the property directly" do
|
65
|
+
@widget.name.gsub!("w","f")
|
66
|
+
@widget.name.should == "fidget"
|
67
|
+
end
|
63
68
|
end
|
64
69
|
|
65
70
|
describe "mutators" do
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Time do
|
4
|
+
it "serializes to JSON in UTC, RFC 822 format" do
|
5
|
+
Time.utc(2010,3,16,12).as_json.should == "Tue, 16 Mar 2010 12:00:00 -0000"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Date do
|
10
|
+
it "serializes to JSON RFC 822 format" do
|
11
|
+
Date.civil(2010,3,16).as_json.should == "16 Mar 2010"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe DateTime do
|
16
|
+
before :each do
|
17
|
+
Time.zone = :utc
|
18
|
+
end
|
19
|
+
|
20
|
+
it "serializes to JSON in UTC, RFC 822 format" do
|
21
|
+
DateTime.civil(2010,3,16,12).as_json.should == "Tue, 16 Mar 2010 12:00:00 +0000"
|
22
|
+
end
|
23
|
+
end
|
@@ -195,5 +195,29 @@ describe Ripple::Document::Property do
|
|
195
195
|
end
|
196
196
|
end
|
197
197
|
end
|
198
|
+
|
199
|
+
describe "when type is a Time type" do
|
200
|
+
before :each do
|
201
|
+
@prop = Ripple::Document::Property.new(:foo, Time)
|
202
|
+
end
|
203
|
+
|
204
|
+
["Tue, 16 Mar 2010 12:00:00 -0000","2010/03/16 12:00:00 GMT", Time.utc(2010,03,16,12)].each do |v|
|
205
|
+
it "should cast #{v.inspect} to #{Time.utc(2010,03,16,12).inspect}" do
|
206
|
+
@prop.type_cast(v).should == Time.utc(2010,03,16,12)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "when type is a Date type" do
|
212
|
+
before :each do
|
213
|
+
@prop = Ripple::Document::Property.new(:foo, Date)
|
214
|
+
end
|
215
|
+
|
216
|
+
["Tue, 16 Mar 2010 00:00:00 -0000", "2010/03/16 12:00:00 GMT", Time.utc(2010,03,16,12), "2010/03/16"].each do |v|
|
217
|
+
it "should cast #{v.inspect} to 2010/03/16" do
|
218
|
+
@prop.type_cast(v).should == Date.civil(2010,3,16)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
198
222
|
end
|
199
223
|
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ripple
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 6
|
8
|
+
- 1
|
9
|
+
version: 0.6.1
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Sean Cribbs
|
@@ -9,79 +14,105 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-17 00:00:00 -04:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rspec
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 3
|
23
30
|
version: "1.3"
|
24
|
-
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
25
33
|
- !ruby/object:Gem::Dependency
|
26
34
|
name: fakeweb
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
37
|
requirements:
|
31
38
|
- - ">="
|
32
39
|
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 1
|
42
|
+
- 2
|
33
43
|
version: "1.2"
|
34
|
-
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
35
46
|
- !ruby/object:Gem::Dependency
|
36
47
|
name: rack
|
37
|
-
|
38
|
-
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
50
|
requirements:
|
41
51
|
- - ">="
|
42
52
|
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 1
|
55
|
+
- 0
|
43
56
|
version: "1.0"
|
44
|
-
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id003
|
45
59
|
- !ruby/object:Gem::Dependency
|
46
60
|
name: yard
|
47
|
-
|
48
|
-
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
50
63
|
requirements:
|
51
64
|
- - ">="
|
52
65
|
- !ruby/object:Gem::Version
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
- 5
|
69
|
+
- 2
|
53
70
|
version: 0.5.2
|
54
|
-
|
71
|
+
type: :development
|
72
|
+
version_requirements: *id004
|
55
73
|
- !ruby/object:Gem::Dependency
|
56
74
|
name: curb
|
57
|
-
|
58
|
-
|
59
|
-
version_requirements: !ruby/object:Gem::Requirement
|
75
|
+
prerelease: false
|
76
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
60
77
|
requirements:
|
61
78
|
- - ">="
|
62
79
|
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
- 6
|
63
83
|
version: "0.6"
|
64
|
-
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id005
|
65
86
|
- !ruby/object:Gem::Dependency
|
66
87
|
name: activesupport
|
67
|
-
|
68
|
-
|
69
|
-
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
prerelease: false
|
89
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
70
90
|
requirements:
|
71
91
|
- - ~>
|
72
92
|
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 3
|
95
|
+
- 0
|
96
|
+
- 0
|
97
|
+
- beta
|
73
98
|
version: 3.0.0.beta
|
74
|
-
|
99
|
+
type: :runtime
|
100
|
+
version_requirements: *id006
|
75
101
|
- !ruby/object:Gem::Dependency
|
76
102
|
name: activemodel
|
77
|
-
|
78
|
-
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
103
|
+
prerelease: false
|
104
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
80
105
|
requirements:
|
81
106
|
- - ~>
|
82
107
|
- !ruby/object:Gem::Version
|
108
|
+
segments:
|
109
|
+
- 3
|
110
|
+
- 0
|
111
|
+
- 0
|
112
|
+
- beta
|
83
113
|
version: 3.0.0.beta
|
84
|
-
|
114
|
+
type: :runtime
|
115
|
+
version_requirements: *id007
|
85
116
|
description: ripple is a rich Ruby client for Riak, Basho's distributed database. It includes all the basics of accessing and manipulating Riak buckets and objects, and an object mapper library for building a rich domain on top of Riak.
|
86
117
|
email: seancribbs@gmail.com
|
87
118
|
executables: []
|
@@ -114,6 +145,7 @@ files:
|
|
114
145
|
- lib/riak/map_reduce.rb
|
115
146
|
- lib/riak/map_reduce_error.rb
|
116
147
|
- lib/riak/robject.rb
|
148
|
+
- lib/riak/util/escape.rb
|
117
149
|
- lib/riak/util/fiber1.8.rb
|
118
150
|
- lib/riak/util/headers.rb
|
119
151
|
- lib/riak/util/multipart.rb
|
@@ -147,6 +179,7 @@ files:
|
|
147
179
|
- spec/riak/bucket_spec.rb
|
148
180
|
- spec/riak/client_spec.rb
|
149
181
|
- spec/riak/curb_backend_spec.rb
|
182
|
+
- spec/riak/escape_spec.rb
|
150
183
|
- spec/riak/headers_spec.rb
|
151
184
|
- spec/riak/http_backend_spec.rb
|
152
185
|
- spec/riak/link_spec.rb
|
@@ -158,6 +191,7 @@ files:
|
|
158
191
|
- spec/ripple/attribute_methods_spec.rb
|
159
192
|
- spec/ripple/bucket_access_spec.rb
|
160
193
|
- spec/ripple/callbacks_spec.rb
|
194
|
+
- spec/ripple/core_ext_spec.rb
|
161
195
|
- spec/ripple/document_spec.rb
|
162
196
|
- spec/ripple/embedded_document_spec.rb
|
163
197
|
- spec/ripple/finders_spec.rb
|
@@ -183,18 +217,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
183
217
|
requirements:
|
184
218
|
- - ">="
|
185
219
|
- !ruby/object:Gem::Version
|
220
|
+
segments:
|
221
|
+
- 0
|
186
222
|
version: "0"
|
187
|
-
version:
|
188
223
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
224
|
requirements:
|
190
225
|
- - ">="
|
191
226
|
- !ruby/object:Gem::Version
|
227
|
+
segments:
|
228
|
+
- 0
|
192
229
|
version: "0"
|
193
|
-
version:
|
194
230
|
requirements:
|
195
231
|
- `gem install curb` for better HTTP performance
|
196
232
|
rubyforge_project:
|
197
|
-
rubygems_version: 1.3.
|
233
|
+
rubygems_version: 1.3.6
|
198
234
|
signing_key:
|
199
235
|
specification_version: 3
|
200
236
|
summary: ripple is a rich Ruby client for Riak, Basho's distributed database.
|
@@ -202,6 +238,7 @@ test_files:
|
|
202
238
|
- spec/riak/bucket_spec.rb
|
203
239
|
- spec/riak/client_spec.rb
|
204
240
|
- spec/riak/curb_backend_spec.rb
|
241
|
+
- spec/riak/escape_spec.rb
|
205
242
|
- spec/riak/headers_spec.rb
|
206
243
|
- spec/riak/http_backend_spec.rb
|
207
244
|
- spec/riak/link_spec.rb
|
@@ -213,6 +250,7 @@ test_files:
|
|
213
250
|
- spec/ripple/attribute_methods_spec.rb
|
214
251
|
- spec/ripple/bucket_access_spec.rb
|
215
252
|
- spec/ripple/callbacks_spec.rb
|
253
|
+
- spec/ripple/core_ext_spec.rb
|
216
254
|
- spec/ripple/document_spec.rb
|
217
255
|
- spec/ripple/embedded_document_spec.rb
|
218
256
|
- spec/ripple/finders_spec.rb
|