ripple 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +61 -48
- data/lib/ripple.rb +5 -1
- data/lib/ripple/core_ext/casting.rb +3 -0
- data/lib/ripple/document.rb +6 -2
- data/lib/ripple/document/associations.rb +154 -0
- data/lib/ripple/document/{persistence/callbacks.rb → associations/embedded.rb} +20 -24
- data/lib/ripple/document/associations/instantiators.rb +41 -0
- data/lib/{riak/util/translation.rb → ripple/document/associations/linked.rb} +14 -11
- data/lib/ripple/document/associations/many.rb +52 -0
- data/lib/ripple/document/associations/many_embedded_proxy.rb +49 -0
- data/lib/{riak/i18n.rb → ripple/document/associations/one.rb} +18 -2
- data/lib/ripple/document/associations/one_embedded_proxy.rb +41 -0
- data/lib/ripple/document/associations/proxy.rb +125 -0
- data/lib/ripple/document/attribute_methods.rb +8 -1
- data/lib/ripple/document/attribute_methods/read.rb +4 -0
- data/lib/ripple/document/attribute_methods/write.rb +4 -0
- data/lib/ripple/document/bucket_access.rb +1 -1
- data/lib/ripple/document/callbacks.rb +75 -0
- data/lib/ripple/document/finders.rb +50 -3
- data/lib/ripple/document/persistence.rb +14 -6
- data/lib/ripple/document/validations.rb +35 -7
- data/lib/ripple/document/validations/associated_validator.rb +37 -0
- data/lib/ripple/embedded_document.rb +8 -2
- data/lib/{riak/map_reduce_error.rb → ripple/embedded_document/conversion.rb} +19 -5
- data/lib/{riak/invalid_response.rb → ripple/embedded_document/finders.rb} +17 -8
- data/lib/ripple/embedded_document/persistence.rb +75 -13
- data/lib/ripple/locale/en.yml +7 -1
- data/{spec/riak/net_http_backend_spec.rb → lib/ripple/railtie.rb} +17 -13
- data/spec/fixtures/config.yml +3 -0
- data/spec/integration/ripple/associations_spec.rb +81 -0
- data/spec/integration/ripple/persistence_spec.rb +54 -0
- data/spec/ripple/associations/many_embedded_proxy_spec.rb +124 -0
- data/spec/ripple/associations/one_embedded_proxy_spec.rb +130 -0
- data/spec/ripple/associations/proxy_spec.rb +78 -0
- data/spec/ripple/associations_spec.rb +111 -0
- data/spec/ripple/attribute_methods_spec.rb +37 -16
- data/spec/ripple/bucket_access_spec.rb +3 -14
- data/spec/ripple/callbacks_spec.rb +53 -9
- data/spec/ripple/document_spec.rb +22 -6
- data/spec/ripple/embedded_document/conversion_spec.rb +35 -0
- data/spec/{riak/headers_spec.rb → ripple/embedded_document/finders_spec.rb} +17 -14
- data/spec/ripple/embedded_document/persistence_spec.rb +86 -0
- data/spec/ripple/embedded_document_spec.rb +1 -26
- data/spec/ripple/finders_spec.rb +66 -30
- data/spec/ripple/persistence_spec.rb +33 -21
- data/spec/ripple/properties_spec.rb +1 -7
- data/spec/ripple/ripple_spec.rb +10 -0
- data/spec/ripple/timestamps_spec.rb +12 -19
- data/spec/ripple/validations_spec.rb +48 -6
- data/spec/spec_helper.rb +4 -10
- data/spec/support/associations/proxies.rb +16 -0
- data/spec/support/integration.rb +4 -0
- data/spec/support/mocks.rb +3 -0
- data/spec/support/models/address.rb +8 -0
- data/spec/support/models/box.rb +6 -0
- data/spec/support/models/cardboard_box.rb +3 -0
- data/spec/support/models/clock.rb +6 -0
- data/spec/support/models/customer.rb +4 -0
- data/spec/support/models/email.rb +4 -0
- data/spec/support/models/family.rb +14 -0
- data/spec/support/models/favorite.rb +4 -0
- data/spec/support/models/invoice.rb +6 -0
- data/spec/support/models/late_invoice.rb +3 -0
- data/spec/support/models/note.rb +4 -0
- data/spec/support/models/page.rb +4 -0
- data/spec/support/models/paid_invoice.rb +4 -0
- data/spec/support/models/tree.rb +3 -0
- data/spec/support/models/user.rb +6 -0
- data/spec/support/models/widget.rb +6 -0
- metadata +111 -138
- data/.document +0 -5
- data/.gitignore +0 -26
- data/CONTRIBUTORS.textile +0 -5
- data/LICENSE +0 -13
- data/README.textile +0 -128
- data/RELEASE_NOTES.textile +0 -68
- data/VERSION +0 -1
- data/lib/riak.rb +0 -46
- data/lib/riak/bucket.rb +0 -157
- data/lib/riak/client.rb +0 -139
- data/lib/riak/client/curb_backend.rb +0 -82
- data/lib/riak/client/http_backend.rb +0 -209
- data/lib/riak/client/net_http_backend.rb +0 -49
- data/lib/riak/failed_request.rb +0 -37
- data/lib/riak/link.rb +0 -73
- data/lib/riak/locale/en.yml +0 -37
- data/lib/riak/map_reduce.rb +0 -248
- data/lib/riak/robject.rb +0 -258
- data/lib/riak/util/escape.rb +0 -12
- data/lib/riak/util/fiber1.8.rb +0 -48
- data/lib/riak/util/headers.rb +0 -44
- data/lib/riak/util/multipart.rb +0 -52
- data/lib/riak/walk_spec.rb +0 -117
- data/ripple.gemspec +0 -169
- data/spec/fixtures/cat.jpg +0 -0
- data/spec/fixtures/multipart-blank.txt +0 -7
- data/spec/fixtures/multipart-with-body.txt +0 -16
- data/spec/riak/bucket_spec.rb +0 -230
- data/spec/riak/client_spec.rb +0 -174
- data/spec/riak/curb_backend_spec.rb +0 -50
- data/spec/riak/escape_spec.rb +0 -17
- data/spec/riak/http_backend_spec.rb +0 -131
- data/spec/riak/link_spec.rb +0 -82
- data/spec/riak/map_reduce_spec.rb +0 -352
- data/spec/riak/multipart_spec.rb +0 -36
- data/spec/riak/object_spec.rb +0 -532
- data/spec/riak/walk_spec_spec.rb +0 -208
- data/spec/spec.opts +0 -1
- data/spec/support/http_backend_implementation_examples.rb +0 -215
- data/spec/support/mock_server.rb +0 -58
data/lib/riak/failed_request.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
require 'riak'
|
15
|
-
|
16
|
-
module Riak
|
17
|
-
# Exception raised when the expected response code from Riak
|
18
|
-
# fails to match the actual response code.
|
19
|
-
class FailedRequest < StandardError
|
20
|
-
include Util::Translation
|
21
|
-
# @return [Symbol] the HTTP method, one of :head, :get, :post, :put, :delete
|
22
|
-
attr_reader :method
|
23
|
-
# @return [Fixnum] the expected response code
|
24
|
-
attr_reader :expected
|
25
|
-
# @return [Fixnum] the received response code
|
26
|
-
attr_reader :code
|
27
|
-
# @return [Hash] the response headers
|
28
|
-
attr_reader :headers
|
29
|
-
# @return [String] the response body, if present
|
30
|
-
attr_reader :body
|
31
|
-
|
32
|
-
def initialize(method, expected_code, received_code, headers, body)
|
33
|
-
@method, @expected, @code, @headers, @body = method, expected_code, received_code, headers, body
|
34
|
-
super t("failed_request", :expected => @expected.inspect, :code => @code, :body => @body)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/riak/link.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
require 'riak'
|
15
|
-
|
16
|
-
module Riak
|
17
|
-
# Represents a link from one object to another in Riak
|
18
|
-
class Link
|
19
|
-
include Util::Translation
|
20
|
-
# @return [String] the URL (relative or absolute) of the related resource
|
21
|
-
attr_accessor :url
|
22
|
-
|
23
|
-
# @return [String] the relationship ("rel") of the other resource to this one
|
24
|
-
attr_accessor :rel
|
25
|
-
alias :tag :rel
|
26
|
-
alias :tag= :rel=
|
27
|
-
|
28
|
-
# @param [String] header_string the string value of the Link: HTTP header from a Riak response
|
29
|
-
# @return [Array<Link>] an array of Riak::Link structs parsed from the header
|
30
|
-
def self.parse(header_string)
|
31
|
-
header_string.scan(%r{<([^>]+)>\s*;\s*(?:rel|riaktag)=\"([^\"]+)\"}).map do |match|
|
32
|
-
new(match[0], match[1])
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize(url, rel)
|
37
|
-
@url, @rel = url, rel
|
38
|
-
end
|
39
|
-
|
40
|
-
# @return [String] bucket_name, if the Link url is a known Riak link ("/riak/<bucket>/<key>")
|
41
|
-
def bucket
|
42
|
-
URI.unescape($1) if url =~ %r{^/[^/]+/([^/]+)/?}
|
43
|
-
end
|
44
|
-
|
45
|
-
# @return [String] key, if the Link url is a known Riak link ("/riak/<bucket>/<key>")
|
46
|
-
def key
|
47
|
-
URI.unescape($1) if url =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
|
48
|
-
end
|
49
|
-
|
50
|
-
def inspect; to_s; end
|
51
|
-
|
52
|
-
def to_s
|
53
|
-
%Q[<#{@url}>; riaktag="#{@rel}"]
|
54
|
-
end
|
55
|
-
|
56
|
-
def hash
|
57
|
-
self.to_s.hash
|
58
|
-
end
|
59
|
-
|
60
|
-
def eql?(other)
|
61
|
-
self == other
|
62
|
-
end
|
63
|
-
|
64
|
-
def ==(other)
|
65
|
-
other.is_a?(Link) && url == other.url && rel == other.rel
|
66
|
-
end
|
67
|
-
|
68
|
-
def to_walk_spec
|
69
|
-
raise t("bucket_link_conversion") if @rel == "up" || key.nil?
|
70
|
-
WalkSpec.new(:bucket => bucket, :tag => @rel)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
data/lib/riak/locale/en.yml
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
en:
|
15
|
-
riak:
|
16
|
-
client_type: "invalid argument {{client}} is not a Riak::Client"
|
17
|
-
string_type: "invalid_argument {{string}} is not a String"
|
18
|
-
loading_bucket: "while loading bucket '{{name}}'"
|
19
|
-
failed_request: "Expected {{expected}} from Riak but received {{code}}. {{body}}"
|
20
|
-
hash_type: "invalid argument {{hash}} is not a Hash"
|
21
|
-
path_and_body_required: "You must supply both a resource path and a body."
|
22
|
-
request_body_type: "Request body must be a string or IO."
|
23
|
-
resource_path_short: "Resource path too short"
|
24
|
-
missing_host_and_port: "You must specify a host and port, or use the defaults of 127.0.0.1:8098"
|
25
|
-
invalid_client_id: "Invalid client ID, must be a string or between 0 and {{max_id}}"
|
26
|
-
hostname_invalid: "host must be a valid hostname"
|
27
|
-
port_invalid: "port must be an integer between 0 and 65535"
|
28
|
-
install_curb: "curb library not found! Please `gem install curb` for better performance."
|
29
|
-
bucket_link_conversion: "Can't convert a bucket link to a walk spec"
|
30
|
-
invalid_phase_type: "type must be :map, :reduce, or :link"
|
31
|
-
module_function_pair_required: "function must have two elements when an array"
|
32
|
-
stored_function_invalid: "function must have :bucket and :key when a hash"
|
33
|
-
walk_spec_invalid_unless_link: "WalkSpec is only valid for a function when the type is :link"
|
34
|
-
invalid_function_value: "invalid value for function: {{value}}"
|
35
|
-
content_type_undefined: "content_type is not defined!"
|
36
|
-
too_few_arguments: "too few arguments: {{params}}"
|
37
|
-
wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
|
data/lib/riak/map_reduce.rb
DELETED
@@ -1,248 +0,0 @@
|
|
1
|
-
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
require 'riak'
|
15
|
-
|
16
|
-
module Riak
|
17
|
-
# Class for invoking map-reduce jobs using the HTTP interface.
|
18
|
-
class MapReduce
|
19
|
-
include Util::Translation
|
20
|
-
# @return [Array<[bucket,key]>,String] The bucket/keys for input to the job, or the bucket (all keys).
|
21
|
-
# @see #add
|
22
|
-
attr_accessor :inputs
|
23
|
-
|
24
|
-
# @return [Array<Phase>] The map and reduce phases that will be executed
|
25
|
-
# @see #map
|
26
|
-
# @see #reduce
|
27
|
-
# @see #link
|
28
|
-
attr_accessor :query
|
29
|
-
|
30
|
-
# Creates a new map-reduce job.
|
31
|
-
# @param [Client] client the Riak::Client interface
|
32
|
-
# @yield [self] helpful for initializing the job
|
33
|
-
def initialize(client)
|
34
|
-
@client, @inputs, @query = client, [], []
|
35
|
-
yield self if block_given?
|
36
|
-
end
|
37
|
-
|
38
|
-
# Add or replace inputs for the job.
|
39
|
-
# @overload add(bucket)
|
40
|
-
# Run the job across all keys in the bucket. This will replace any other inputs previously added.
|
41
|
-
# @param [String, Bucket] bucket the bucket to run the job on
|
42
|
-
# @overload add(bucket,key)
|
43
|
-
# Add a bucket/key pair to the job.
|
44
|
-
# @param [String,Bucket] bucket the bucket of the object
|
45
|
-
# @param [String] key the key of the object
|
46
|
-
# @overload add(object)
|
47
|
-
# Add an object to the job (by its bucket/key)
|
48
|
-
# @param [RObject] object the object to add to the inputs
|
49
|
-
# @overload add(bucket, key, keydata)
|
50
|
-
# @param [String,Bucket] bucket the bucket of the object
|
51
|
-
# @param [String] key the key of the object
|
52
|
-
# @param [String] keydata extra data to pass along with the object to the job
|
53
|
-
# @return [MapReduce] self
|
54
|
-
def add(*params)
|
55
|
-
params = params.dup.flatten
|
56
|
-
case params.size
|
57
|
-
when 1
|
58
|
-
p = params.first
|
59
|
-
case p
|
60
|
-
when Bucket
|
61
|
-
@inputs = p.name
|
62
|
-
when RObject
|
63
|
-
@inputs << [p.bucket.name, p.key]
|
64
|
-
when String
|
65
|
-
@inputs = p
|
66
|
-
end
|
67
|
-
when 2..3
|
68
|
-
bucket = params.shift
|
69
|
-
bucket = bucket.name if Bucket === bucket
|
70
|
-
@inputs << params.unshift(bucket)
|
71
|
-
end
|
72
|
-
self
|
73
|
-
end
|
74
|
-
alias :<< :add
|
75
|
-
alias :include :add
|
76
|
-
|
77
|
-
# Add a map phase to the job.
|
78
|
-
# @overload map(function)
|
79
|
-
# @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module,function] pair
|
80
|
-
# @overload map(function?, options)
|
81
|
-
# @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module, function] pair
|
82
|
-
# @param [Hash] options extra options for the phase (see {Phase#initialize})
|
83
|
-
# @return [MapReduce] self
|
84
|
-
# @see Phase#initialize
|
85
|
-
def map(*params)
|
86
|
-
options = params.extract_options!
|
87
|
-
@query << Phase.new({:type => :map, :function => params.shift}.merge(options))
|
88
|
-
self
|
89
|
-
end
|
90
|
-
|
91
|
-
# Add a reduce phase to the job.
|
92
|
-
# @overload reduce(function)
|
93
|
-
# @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module,function] pair
|
94
|
-
# @overload reduce(function?, options)
|
95
|
-
# @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module, function] pair
|
96
|
-
# @param [Hash] options extra options for the phase (see {Phase#initialize})
|
97
|
-
# @return [MapReduce] self
|
98
|
-
# @see Phase#initialize
|
99
|
-
def reduce(*params)
|
100
|
-
options = params.extract_options!
|
101
|
-
@query << Phase.new({:type => :reduce, :function => params.shift}.merge(options))
|
102
|
-
self
|
103
|
-
end
|
104
|
-
|
105
|
-
# Add a link phase to the job. Link phases follow links attached to objects automatically (a special case of map).
|
106
|
-
# @overload link(walk_spec, options={})
|
107
|
-
# @param [WalkSpec] walk_spec a WalkSpec that represents the types of links to follow
|
108
|
-
# @param [Hash] options extra options for the phase (see {Phase#initialize})
|
109
|
-
# @overload link(bucket, tag, keep, options={})
|
110
|
-
# @param [String, nil] bucket the bucket to limit links to
|
111
|
-
# @param [String, nil] tag the tag to limit links to
|
112
|
-
# @param [Boolean] keep whether to keep results of this phase (overrides the phase options)
|
113
|
-
# @param [Hash] options extra options for the phase (see {Phase#initialize})
|
114
|
-
# @overload link(options)
|
115
|
-
# @param [Hash] options options for both the walk spec and link phase
|
116
|
-
# @see WalkSpec#initialize
|
117
|
-
# @return [MapReduce] self
|
118
|
-
# @see Phase#initialize
|
119
|
-
def link(*params)
|
120
|
-
options = params.extract_options!
|
121
|
-
walk_spec_options = options.slice!(:type, :function, :language, :arg) unless params.first
|
122
|
-
walk_spec = WalkSpec.normalize(params.shift || walk_spec_options).first
|
123
|
-
@query << Phase.new({:type => :link, :function => walk_spec}.merge(options))
|
124
|
-
self
|
125
|
-
end
|
126
|
-
|
127
|
-
# Sets the timeout for the map-reduce job.
|
128
|
-
# @param [Fixnum] value the job timeout, in milliseconds
|
129
|
-
def timeout(value)
|
130
|
-
@timeout = value
|
131
|
-
end
|
132
|
-
|
133
|
-
# Convert the job to JSON for submission over the HTTP interface.
|
134
|
-
# @return [String] the JSON representation
|
135
|
-
def to_json(options={})
|
136
|
-
hash = {"inputs" => inputs, "query" => query.map(&:as_json)}
|
137
|
-
hash['timeout'] = @timeout.to_i if @timeout
|
138
|
-
ActiveSupport::JSON.encode(hash, options)
|
139
|
-
end
|
140
|
-
|
141
|
-
# Executes this map-reduce job.
|
142
|
-
# @return [Array<Array>] similar to link-walking, each element is an array of results from a phase where "keep" is true. If there is only one "keep" phase, only the results from that phase will be returned.
|
143
|
-
def run
|
144
|
-
response = @client.http.post(200, @client.mapred, to_json, {"Content-Type" => "application/json", "Accept" => "application/json"})
|
145
|
-
if response.try(:[], :headers).try(:[],'content-type').include?("application/json")
|
146
|
-
ActiveSupport::JSON.decode(response[:body])
|
147
|
-
else
|
148
|
-
response
|
149
|
-
end
|
150
|
-
rescue FailedRequest => fr
|
151
|
-
if fr.code == 500 && fr.headers['content-type'].include?("application/json")
|
152
|
-
raise MapReduceError.new(fr.body)
|
153
|
-
else
|
154
|
-
raise fr
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# Represents an individual phase in a map-reduce pipeline. Generally you'll want to call
|
159
|
-
# methods of {MapReduce} instead of using this directly.
|
160
|
-
class Phase
|
161
|
-
include Util::Translation
|
162
|
-
# @return [Symbol] the type of phase - :map, :reduce, or :link
|
163
|
-
attr_accessor :type
|
164
|
-
|
165
|
-
# @return [String, Array<String, String>, Hash, WalkSpec] For :map and :reduce types, the Javascript function to run (as a string or hash with bucket/key), or the module + function in Erlang to run. For a :link type, a {Riak::WalkSpec} or an equivalent hash.
|
166
|
-
attr_accessor :function
|
167
|
-
|
168
|
-
# @return [String] the language of the phase's function - "javascript" or "erlang". Meaningless for :link type phases.
|
169
|
-
attr_accessor :language
|
170
|
-
|
171
|
-
# @return [Boolean] whether results of this phase will be returned
|
172
|
-
attr_accessor :keep
|
173
|
-
|
174
|
-
# @return [Array] any extra static arguments to pass to the phase
|
175
|
-
attr_accessor :arg
|
176
|
-
|
177
|
-
# Creates a phase in the map-reduce pipeline
|
178
|
-
# @param [Hash] options options for the phase
|
179
|
-
# @option options [Symbol] :type one of :map, :reduce, :link
|
180
|
-
# @option options [String] :language ("javascript") "erlang" or "javascript"
|
181
|
-
# @option options [String, Array, Hash] :function In the case of Javascript, a literal function in a string, or a hash with :bucket and :key. In the case of Erlang, an Array of [module, function]. For a :link phase, a hash including any of :bucket, :tag or a WalkSpec.
|
182
|
-
# @option options [Boolean] :keep (false) whether to return the results of this phase
|
183
|
-
# @option options [Array] :arg (nil) any extra static arguments to pass to the phase
|
184
|
-
def initialize(options={})
|
185
|
-
self.type = options[:type]
|
186
|
-
self.language = options[:language] || "javascript"
|
187
|
-
self.function = options[:function]
|
188
|
-
self.keep = options[:keep] || false
|
189
|
-
self.arg = options[:arg]
|
190
|
-
end
|
191
|
-
|
192
|
-
def type=(value)
|
193
|
-
raise ArgumentError, t("invalid_phase_type") unless value.to_s =~ /^(map|reduce|link)$/i
|
194
|
-
@type = value.to_s.downcase.to_sym
|
195
|
-
end
|
196
|
-
|
197
|
-
def function=(value)
|
198
|
-
case value
|
199
|
-
when Array
|
200
|
-
raise ArgumentError, t("module_function_pair_required") unless value.size == 2
|
201
|
-
@language = "erlang"
|
202
|
-
when Hash
|
203
|
-
raise ArgumentError, t("stored_function_invalid") unless type == :link || value.has_key?(:bucket) && value.has_key?(:key)
|
204
|
-
@language = "javascript"
|
205
|
-
when String
|
206
|
-
@language = "javascript"
|
207
|
-
when WalkSpec
|
208
|
-
raise ArgumentError, t("walk_spec_invalid_unless_link") unless type == :link
|
209
|
-
else
|
210
|
-
raise ArgumentError, t("invalid_function_value", :value => value.inspect)
|
211
|
-
end
|
212
|
-
@function = value
|
213
|
-
end
|
214
|
-
|
215
|
-
# Converts the phase to JSON for use while invoking a job.
|
216
|
-
# @return [String] a JSON representation of the phase
|
217
|
-
def to_json(options=nil)
|
218
|
-
ActiveSupport::JSON.encode(as_json, options)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Converts the phase to its JSON-compatible representation for job invocation.
|
222
|
-
# @return [Hash] a Hash-equivalent of the phase
|
223
|
-
def as_json(options=nil)
|
224
|
-
obj = case type
|
225
|
-
when :map, :reduce
|
226
|
-
defaults = {"language" => language, "keep" => keep}
|
227
|
-
case function
|
228
|
-
when Hash
|
229
|
-
defaults.merge(function)
|
230
|
-
when String
|
231
|
-
if function =~ /\s*function/
|
232
|
-
defaults.merge("source" => function)
|
233
|
-
else
|
234
|
-
defaults.merge("name" => function)
|
235
|
-
end
|
236
|
-
when Array
|
237
|
-
defaults.merge("module" => function[0], "function" => function[1])
|
238
|
-
end
|
239
|
-
when :link
|
240
|
-
spec = WalkSpec.normalize(function).first
|
241
|
-
{"bucket" => spec.bucket, "tag" => spec.tag, "keep" => spec.keep || keep}
|
242
|
-
end
|
243
|
-
obj["arg"] = arg if arg
|
244
|
-
{ type => obj }
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
data/lib/riak/robject.rb
DELETED
@@ -1,258 +0,0 @@
|
|
1
|
-
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
require 'riak'
|
15
|
-
require 'set'
|
16
|
-
|
17
|
-
module Riak
|
18
|
-
# Parent class of all object types supported by ripple. {Riak::RObject} represents
|
19
|
-
# the data and metadata stored in a bucket/key pair in the Riak database.
|
20
|
-
class RObject
|
21
|
-
include Util
|
22
|
-
include Util::Translation
|
23
|
-
include Util::Escape
|
24
|
-
|
25
|
-
# @return [Bucket] the bucket in which this object is contained
|
26
|
-
attr_accessor :bucket
|
27
|
-
|
28
|
-
# @return [String] the key of this object within its bucket
|
29
|
-
attr_accessor :key
|
30
|
-
|
31
|
-
# @return [String] the MIME content type of the object
|
32
|
-
attr_accessor :content_type
|
33
|
-
|
34
|
-
# @return [String] the Riak vector clock for the object
|
35
|
-
attr_accessor :vclock
|
36
|
-
alias_attribute :vector_clock, :vclock
|
37
|
-
|
38
|
-
# @return [Object] the data stored in Riak at this object's key. Varies in format by content-type, defaulting to String from the response body.
|
39
|
-
attr_accessor :data
|
40
|
-
|
41
|
-
# @return [Set<Link>] an Set of {Riak::Link} objects for relationships between this object and other resources
|
42
|
-
attr_accessor :links
|
43
|
-
|
44
|
-
# @return [String] the ETag header from the most recent HTTP response, useful for caching and reloading
|
45
|
-
attr_accessor :etag
|
46
|
-
|
47
|
-
# @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading
|
48
|
-
attr_accessor :last_modified
|
49
|
-
|
50
|
-
# @return [Hash] a hash of any X-Riak-Meta-* headers that were in the HTTP response, keyed on the trailing portion
|
51
|
-
attr_accessor :meta
|
52
|
-
|
53
|
-
# Create a new object manually
|
54
|
-
# @param [Bucket] bucket the bucket in which the object exists
|
55
|
-
# @param [String] key the key at which the object resides. If nil, a key will be assigned when the object is saved.
|
56
|
-
# @see Bucket#get
|
57
|
-
def initialize(bucket, key=nil)
|
58
|
-
@bucket, @key = bucket, key
|
59
|
-
@links, @meta = Set.new, {}
|
60
|
-
yield self if block_given?
|
61
|
-
end
|
62
|
-
|
63
|
-
# Load object data from an HTTP response
|
64
|
-
# @param [Hash] response a response from {Riak::Client::HTTPBackend}
|
65
|
-
def load(response)
|
66
|
-
extract_header(response, "location", :key) {|v| URI.unescape(v.split("/").last) }
|
67
|
-
extract_header(response, "content-type", :content_type)
|
68
|
-
extract_header(response, "x-riak-vclock", :vclock)
|
69
|
-
extract_header(response, "link", :links) {|v| Set.new(Link.parse(v)) }
|
70
|
-
extract_header(response, "etag", :etag)
|
71
|
-
extract_header(response, "last-modified", :last_modified) {|v| Time.httpdate(v) }
|
72
|
-
@meta = response[:headers].inject({}) do |h,(k,v)|
|
73
|
-
if k =~ /x-riak-meta-(.*)/
|
74
|
-
h[$1] = v
|
75
|
-
end
|
76
|
-
h
|
77
|
-
end
|
78
|
-
@conflict = response[:code].try(:to_i) == 300 && content_type =~ /multipart\/mixed/
|
79
|
-
@siblings = nil
|
80
|
-
@data = deserialize(response[:body]) if response[:body].present?
|
81
|
-
self
|
82
|
-
end
|
83
|
-
|
84
|
-
# HTTP header hash that will be sent along when storing the object
|
85
|
-
# @return [Hash] hash of HTTP Headers
|
86
|
-
def store_headers
|
87
|
-
{}.tap do |hash|
|
88
|
-
hash["Content-Type"] = @content_type
|
89
|
-
hash["X-Riak-Vclock"] = @vclock if @vclock
|
90
|
-
unless @links.blank?
|
91
|
-
hash["Link"] = @links.reject {|l| l.rel == "up" }.map(&:to_s).join(", ")
|
92
|
-
end
|
93
|
-
unless @meta.blank?
|
94
|
-
@meta.each do |k,v|
|
95
|
-
hash["X-Riak-Meta-#{k}"] = v.to_s
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# HTTP header hash that will be sent along when reloading the object
|
102
|
-
# @return [Hash] hash of HTTP headers
|
103
|
-
def reload_headers
|
104
|
-
{}.tap do |h|
|
105
|
-
h['If-None-Match'] = @etag if @etag.present?
|
106
|
-
h['If-Modified-Since'] = @last_modified.httpdate if @last_modified.present?
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# Store the object in Riak
|
111
|
-
# @param [Hash] options query parameters
|
112
|
-
# @option options [Fixnum] :r the "r" parameter (Read quorum for the implicit read performed when validating the store operation)
|
113
|
-
# @option options [Fixnum] :w the "w" parameter (Write quorum)
|
114
|
-
# @option options [Fixnum] :dw the "dw" parameter (Durable-write quorum)
|
115
|
-
# @option options [Boolean] :returnbody (true) whether to return the result of a successful write in the body of the response. Set to false for fire-and-forget updates, set to true to immediately have access to the object's stored representation.
|
116
|
-
# @return [Riak::RObject] self
|
117
|
-
# @raise [ArgumentError] if the content_type is not defined
|
118
|
-
def store(options={})
|
119
|
-
raise ArgumentError, t("content_type_undefined") unless @content_type.present?
|
120
|
-
params = {:returnbody => true}.merge(options)
|
121
|
-
method, codes, path = @key.present? ? [:put, [200,204,300], "#{escape(@bucket.name)}/#{escape(@key)}"] : [:post, 201, escape(@bucket.name)]
|
122
|
-
response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, serialize(data), store_headers)
|
123
|
-
load(response)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Reload the object from Riak. Will use conditional GETs when possible.
|
127
|
-
# @param [Hash] options query parameters
|
128
|
-
# @option options [Fixnum] :r the "r" parameter (Read quorum)
|
129
|
-
# @option options [Boolean] :force will force a reload request if the vclock is not present, useful for reloading the object after a store (not passed in the query params)
|
130
|
-
# @return [Riak::RObject] self
|
131
|
-
def reload(options={})
|
132
|
-
force = options.delete(:force)
|
133
|
-
return self unless @key && (@vclock || force)
|
134
|
-
codes = @bucket.allow_mult ? [200,300,304] : [200,304]
|
135
|
-
response = @bucket.client.http.get(codes, @bucket.client.prefix, escape(@bucket.name), escape(@key), options, reload_headers)
|
136
|
-
load(response) unless response[:code] == 304
|
137
|
-
self
|
138
|
-
end
|
139
|
-
|
140
|
-
alias :fetch :reload
|
141
|
-
|
142
|
-
# Delete the object from Riak and freeze this instance. Will work whether or not the object actually
|
143
|
-
# exists in the Riak database.
|
144
|
-
def delete
|
145
|
-
return if key.blank?
|
146
|
-
@bucket.client.http.delete([204,404], @bucket.client.prefix, escape(@bucket.name), escape(@key))
|
147
|
-
freeze
|
148
|
-
end
|
149
|
-
|
150
|
-
# Returns sibling objects when in conflict.
|
151
|
-
# @return [Array<RObject>] an array of conflicting sibling objects for this key
|
152
|
-
# @return [self] this object when not in conflict
|
153
|
-
def siblings
|
154
|
-
return self unless conflict?
|
155
|
-
@siblings ||= Multipart.parse(data, Multipart.extract_boundary(content_type)).map do |part|
|
156
|
-
RObject.new(self.bucket, self.key) do |sibling|
|
157
|
-
sibling.load(part)
|
158
|
-
sibling.vclock = vclock
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# @return [true,false] Whether this object has conflicting sibling objects (divergent vclocks)
|
164
|
-
def conflict?
|
165
|
-
@conflict.present?
|
166
|
-
end
|
167
|
-
|
168
|
-
# Serializes the internal object data for sending to Riak. Differs based on the content-type.
|
169
|
-
# This method is called internally when storing the object.
|
170
|
-
# Automatically serialized formats:
|
171
|
-
# * JSON (application/json)
|
172
|
-
# * YAML (text/yaml)
|
173
|
-
# * Marshal (application/octet-stream if meta['ruby-serialization'] == "Marshal")
|
174
|
-
# @param [Object] payload the data to serialize
|
175
|
-
def serialize(payload)
|
176
|
-
return payload if IO === payload
|
177
|
-
case @content_type
|
178
|
-
when /json/
|
179
|
-
ActiveSupport::JSON.encode(payload)
|
180
|
-
when /yaml/
|
181
|
-
YAML.dump(payload)
|
182
|
-
when "application/octet-stream"
|
183
|
-
if @meta['ruby-serialization'] == "Marshal"
|
184
|
-
Marshal.dump(payload)
|
185
|
-
else
|
186
|
-
payload.to_s
|
187
|
-
end
|
188
|
-
else
|
189
|
-
payload.to_s
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# Deserializes the internal object data from a Riak response. Differs based on the content-type.
|
194
|
-
# This method is called internally when loading the object.
|
195
|
-
# Automatically deserialized formats:
|
196
|
-
# * JSON (application/json)
|
197
|
-
# * YAML (text/yaml)
|
198
|
-
# * Marshal (application/octet-stream if meta['ruby-serialization'] == "Marshal")
|
199
|
-
# @param [String] body the serialized response body
|
200
|
-
def deserialize(body)
|
201
|
-
case @content_type
|
202
|
-
when /json/
|
203
|
-
ActiveSupport::JSON.decode(body)
|
204
|
-
when /yaml/
|
205
|
-
YAML.load(body)
|
206
|
-
when "application/octet-stream"
|
207
|
-
if @meta['ruby-serialization'] == "Marshal"
|
208
|
-
Marshal.load(body)
|
209
|
-
else
|
210
|
-
body
|
211
|
-
end
|
212
|
-
else
|
213
|
-
body
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
# @return [String] A representation suitable for IRB and debugging output
|
218
|
-
def inspect
|
219
|
-
"#<#{self.class.name} #{@bucket.client.http.path(@bucket.client.prefix, escape(@bucket.name), escape(@key)).to_s} [#{@content_type}]:#{@data.inspect}>"
|
220
|
-
end
|
221
|
-
|
222
|
-
# Walks links from this object to other objects in Riak.
|
223
|
-
def walk(*params)
|
224
|
-
specs = WalkSpec.normalize(*params)
|
225
|
-
response = @bucket.client.http.get(200, @bucket.client.prefix, escape(@bucket.name), escape(@key), specs.join("/"))
|
226
|
-
if boundary = Multipart.extract_boundary(response[:headers]['content-type'].first)
|
227
|
-
Multipart.parse(response[:body], boundary).map do |group|
|
228
|
-
map_walk_group(group)
|
229
|
-
end
|
230
|
-
else
|
231
|
-
[]
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
# Converts the object to a link suitable for linking other objects to it
|
236
|
-
def to_link(tag=nil)
|
237
|
-
Link.new(@bucket.client.http.path(@bucket.client.prefix, escape(@bucket.name), escape(@key)).path, tag)
|
238
|
-
end
|
239
|
-
|
240
|
-
private
|
241
|
-
def extract_header(response, name, attribute=nil)
|
242
|
-
if response[:headers][name].present?
|
243
|
-
value = response[:headers][name].try(:first)
|
244
|
-
value = yield value if block_given?
|
245
|
-
send("#{attribute}=", value) if attribute
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
def map_walk_group(group)
|
250
|
-
group.map do |obj|
|
251
|
-
if obj[:headers] && obj[:body] && obj[:headers]['location']
|
252
|
-
bucket, key = $1, $2 if obj[:headers]['location'].first =~ %r{/.*/(.*)/(.*)$}
|
253
|
-
RObject.new(@bucket.client.bucket(bucket, :keys => false), key).load(obj)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|