seomoz-riak-client 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/Gemfile +27 -0
  2. data/Guardfile +14 -0
  3. data/Rakefile +76 -0
  4. data/erl_src/riak_kv_test_backend.beam +0 -0
  5. data/erl_src/riak_kv_test_backend.erl +174 -0
  6. data/erl_src/riak_search_test_backend.beam +0 -0
  7. data/erl_src/riak_search_test_backend.erl +175 -0
  8. data/lib/active_support/cache/riak_store.rb +2 -0
  9. data/lib/riak.rb +21 -0
  10. data/lib/riak/bucket.rb +215 -0
  11. data/lib/riak/cache_store.rb +84 -0
  12. data/lib/riak/client.rb +415 -0
  13. data/lib/riak/client/beefcake/messages.rb +147 -0
  14. data/lib/riak/client/beefcake/object_methods.rb +92 -0
  15. data/lib/riak/client/beefcake_protobuffs_backend.rb +176 -0
  16. data/lib/riak/client/excon_backend.rb +65 -0
  17. data/lib/riak/client/http_backend.rb +203 -0
  18. data/lib/riak/client/http_backend/configuration.rb +46 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +94 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +218 -0
  23. data/lib/riak/client/net_http_backend.rb +79 -0
  24. data/lib/riak/client/protobuffs_backend.rb +97 -0
  25. data/lib/riak/client/pump.rb +30 -0
  26. data/lib/riak/client/search.rb +94 -0
  27. data/lib/riak/core_ext.rb +6 -0
  28. data/lib/riak/core_ext/blank.rb +53 -0
  29. data/lib/riak/core_ext/extract_options.rb +7 -0
  30. data/lib/riak/core_ext/json.rb +15 -0
  31. data/lib/riak/core_ext/slice.rb +18 -0
  32. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  33. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  34. data/lib/riak/core_ext/to_param.rb +31 -0
  35. data/lib/riak/encoding.rb +6 -0
  36. data/lib/riak/failed_request.rb +81 -0
  37. data/lib/riak/i18n.rb +3 -0
  38. data/lib/riak/json.rb +28 -0
  39. data/lib/riak/link.rb +85 -0
  40. data/lib/riak/locale/en.yml +48 -0
  41. data/lib/riak/map_reduce.rb +206 -0
  42. data/lib/riak/map_reduce/filter_builder.rb +94 -0
  43. data/lib/riak/map_reduce/phase.rb +98 -0
  44. data/lib/riak/map_reduce_error.rb +7 -0
  45. data/lib/riak/robject.rb +290 -0
  46. data/lib/riak/search.rb +3 -0
  47. data/lib/riak/serializers.rb +74 -0
  48. data/lib/riak/stamp.rb +77 -0
  49. data/lib/riak/test_server.rb +252 -0
  50. data/lib/riak/util/escape.rb +45 -0
  51. data/lib/riak/util/fiber1.8.rb +48 -0
  52. data/lib/riak/util/headers.rb +53 -0
  53. data/lib/riak/util/multipart.rb +52 -0
  54. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  55. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  56. data/lib/riak/util/translation.rb +19 -0
  57. data/lib/riak/walk_spec.rb +105 -0
  58. data/riak-client.gemspec +55 -0
  59. data/seomoz-riak-client.gemspec +55 -0
  60. data/spec/fixtures/cat.jpg +0 -0
  61. data/spec/fixtures/multipart-blank.txt +7 -0
  62. data/spec/fixtures/multipart-mapreduce.txt +10 -0
  63. data/spec/fixtures/multipart-with-body.txt +16 -0
  64. data/spec/fixtures/server.cert.crt +15 -0
  65. data/spec/fixtures/server.cert.key +15 -0
  66. data/spec/fixtures/test.pem +1 -0
  67. data/spec/integration/riak/cache_store_spec.rb +154 -0
  68. data/spec/integration/riak/http_backends_spec.rb +58 -0
  69. data/spec/integration/riak/protobuffs_backends_spec.rb +32 -0
  70. data/spec/integration/riak/test_server_spec.rb +161 -0
  71. data/spec/riak/beefcake_protobuffs_backend_spec.rb +59 -0
  72. data/spec/riak/bucket_spec.rb +205 -0
  73. data/spec/riak/client_spec.rb +517 -0
  74. data/spec/riak/core_ext/to_param_spec.rb +15 -0
  75. data/spec/riak/escape_spec.rb +69 -0
  76. data/spec/riak/excon_backend_spec.rb +64 -0
  77. data/spec/riak/headers_spec.rb +38 -0
  78. data/spec/riak/http_backend/configuration_spec.rb +51 -0
  79. data/spec/riak/http_backend/object_methods_spec.rb +217 -0
  80. data/spec/riak/http_backend/transport_methods_spec.rb +117 -0
  81. data/spec/riak/http_backend_spec.rb +269 -0
  82. data/spec/riak/link_spec.rb +71 -0
  83. data/spec/riak/map_reduce/filter_builder_spec.rb +32 -0
  84. data/spec/riak/map_reduce/phase_spec.rb +136 -0
  85. data/spec/riak/map_reduce_spec.rb +310 -0
  86. data/spec/riak/multipart_spec.rb +23 -0
  87. data/spec/riak/net_http_backend_spec.rb +16 -0
  88. data/spec/riak/robject_spec.rb +427 -0
  89. data/spec/riak/search_spec.rb +178 -0
  90. data/spec/riak/serializers_spec.rb +93 -0
  91. data/spec/riak/stamp_spec.rb +54 -0
  92. data/spec/riak/stream_parser_spec.rb +53 -0
  93. data/spec/riak/walk_spec_spec.rb +195 -0
  94. data/spec/spec_helper.rb +39 -0
  95. data/spec/support/drb_mock_server.rb +39 -0
  96. data/spec/support/http_backend_implementation_examples.rb +266 -0
  97. data/spec/support/integration_setup.rb +10 -0
  98. data/spec/support/mock_server.rb +81 -0
  99. data/spec/support/mocks.rb +4 -0
  100. data/spec/support/test_server.yml.example +2 -0
  101. data/spec/support/unified_backend_examples.rb +255 -0
  102. metadata +271 -0
@@ -0,0 +1,6 @@
1
+ if defined? Encoding
2
+ Encoding.default_internal = "UTF-8" if Encoding.default_internal.nil? ||
3
+ !Encoding.default_internal.ascii_compatible?
4
+ else
5
+ $KCODE = "U"
6
+ end
@@ -0,0 +1,81 @@
1
+
2
+ require 'riak/util/translation'
3
+ require 'riak/json'
4
+
5
+ module Riak
6
+ # Exception raised when receiving an unexpected client response from
7
+ # Riak.
8
+ class FailedRequest < StandardError
9
+ include Util::Translation
10
+
11
+ def initialize(message)
12
+ super(message || t('failed_request'))
13
+ end
14
+ end
15
+
16
+ # Exception raised when the expected HTTP response code from Riak
17
+ # fails to match the actual response code.
18
+ class HTTPFailedRequest < FailedRequest
19
+ # @return [Symbol] the HTTP method, one of :head, :get, :post, :put, :delete
20
+ attr_reader :method
21
+ # @return [Fixnum] the expected response code
22
+ attr_reader :expected
23
+ # @return [Fixnum] the received response code
24
+ attr_reader :code
25
+ # @return [Hash] the response headers
26
+ attr_reader :headers
27
+ # @return [String] the response body, if present
28
+ attr_reader :body
29
+
30
+ def initialize(method, expected_code, received_code, headers, body)
31
+ @method, @expected, @code, @headers, @body = method, expected_code, received_code, headers, body
32
+ super t("http_failed_request", :expected => @expected.inspect, :code => @code, :body => @body)
33
+ end
34
+
35
+ def is_json?
36
+ headers['content-type'].include?('application/json')
37
+ end
38
+
39
+ # @return [true,false] whether the error represents a "not found" response
40
+ def not_found?
41
+ @code.to_i == 404
42
+ end
43
+
44
+ # @return [true,false] whether the error represents an internal
45
+ # server error
46
+ def server_error?
47
+ @code.to_i == 500
48
+ end
49
+ end
50
+
51
+ # Exception raised when receiving an unexpected Protocol Buffers response from Riak
52
+ class ProtobuffsFailedRequest < FailedRequest
53
+ def initialize(code, message)
54
+ super t('protobuffs_failed_request', :code => code, :body => message)
55
+ @original_message = message
56
+ @not_found = code == :not_found
57
+ @server_error = code == :server_error
58
+ end
59
+
60
+ # @return [true, false] whether the error response is in JSON
61
+ def is_json?
62
+ begin
63
+ JSON.parse(original_message)
64
+ true
65
+ rescue
66
+ false
67
+ end
68
+ end
69
+
70
+ # @return [true,false] whether the error represents a "not found" response
71
+ def not_found?
72
+ @not_found
73
+ end
74
+
75
+ # @return [true,false] whether the error represents an internal
76
+ # server error
77
+ def server_error?
78
+ @server_error
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,3 @@
1
+ require 'i18n'
2
+
3
+ I18n.load_path << File.expand_path("../locale/en.yml", __FILE__)
@@ -0,0 +1,28 @@
1
+ require 'multi_json'
2
+ MultiJson.engine # Force loading of an engine
3
+ require 'riak/core_ext/json'
4
+
5
+ module Riak
6
+ class << self
7
+ # Options that will be passed to the JSON parser and encoder.
8
+ # Defaults to {:max_nesting => 20}
9
+ attr_accessor :json_options
10
+ end
11
+ self.json_options = {:max_nesting => 20}
12
+
13
+ # JSON module for internal use inside riak-client
14
+ module JSON
15
+ class << self
16
+ # Parse a JSON string
17
+ def parse(str)
18
+ MultiJson.decode(str, Riak.json_options)
19
+ end
20
+
21
+ # Generate a JSON string
22
+ def encode(obj)
23
+ MultiJson.encode(obj)
24
+ end
25
+ alias :dump :encode
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+
2
+ require 'riak/util/translation'
3
+ require 'riak/util/escape'
4
+ require 'riak/walk_spec'
5
+
6
+ module Riak
7
+ # Represents a link from one object to another in Riak
8
+ class Link
9
+ include Util::Translation
10
+ include Util::Escape
11
+
12
+ # @return [String] the relationship tag (or "rel") of the other resource to this one
13
+ attr_accessor :tag
14
+ alias_method :rel, :tag
15
+ alias_method :rel=, :tag=
16
+
17
+ # @return [String] the bucket of the related resource
18
+ attr_accessor :bucket
19
+
20
+ # @return [String] the key of the related resource
21
+ attr_accessor :key
22
+
23
+ %w{bucket key}.each do |m|
24
+ class_eval %{ def #{m}=(value); @url = nil; @#{m} = value; end }
25
+ end
26
+
27
+ # @param [String] header_string the string value of the Link: HTTP header from a Riak response
28
+ # @return [Array<Link>] an array of Riak::Link structs parsed from the header
29
+ def self.parse(header_string)
30
+ header_string.scan(%r{<([^>]+)>\s*;\s*(?:rel|riaktag)=\"([^\"]+)\"}).map do |match|
31
+ new(match[0], match[1])
32
+ end
33
+ end
34
+
35
+ # @overload initialize(url, tag)
36
+ # @param [String] url the url of the related resource
37
+ # @param [String] tag the tag for the related resource
38
+ # @overload initialize(bucket, key, tag)
39
+ # @param [String] bucket the bucket of the related resource
40
+ # @param [String] key the key of the related resource
41
+ # @param [String] tag the tag for the related resource
42
+ def initialize(*args)
43
+ raise ArgumentError unless (2..3).include?(args.size)
44
+ if args.size == 2
45
+ self.url, @tag = args
46
+ else
47
+ @bucket, @key, @tag = args
48
+ end
49
+ end
50
+
51
+ # @return [String] the URL (relative or absolute) of the related resource
52
+ def url
53
+ @url ||= "/riak/#{escape(bucket)}" + (key.blank? ? "" : "/#{escape(key)}")
54
+ end
55
+
56
+ def url=(value)
57
+ @url = value
58
+ @bucket = unescape($1) if value =~ %r{^/[^/]+/([^/]+)/?}
59
+ @key = unescape($1) if value =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
60
+ end
61
+
62
+ def inspect; to_s; end
63
+
64
+ def to_s
65
+ %Q[<#{url}>; riaktag="#{tag}"]
66
+ end
67
+
68
+ def hash
69
+ self.to_s.hash
70
+ end
71
+
72
+ def eql?(other)
73
+ self == other
74
+ end
75
+
76
+ def ==(other)
77
+ other.is_a?(Link) && url == other.url && tag == other.tag
78
+ end
79
+
80
+ def to_walk_spec
81
+ raise t("bucket_link_conversion") if tag == "up" || key.nil?
82
+ WalkSpec.new(:bucket => bucket, :tag => tag)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,48 @@
1
+ en:
2
+ riak:
3
+ backwards_clock: "System clock moved backwards, ID generation will fail for %{delay} more milliseconds."
4
+ bucket_link_conversion: "Can't convert a bucket link to a walk spec"
5
+ client_type: "invalid argument %{client} is not a Riak::Client"
6
+ conflict_resolver_invalid: "The given resolver (%{resolver}) did not respond to :call"
7
+ content_type_undefined: "content_type is not defined!"
8
+ deprecated:
9
+ port: "DEPRECATION: Riak::Client#port has been deprecated, use #http_port or #pb_port for the appropriate protocol.\n%{backtrace}"
10
+ search: "DEPRECATION: Riak Search features are included in the main client, you no longer need to require 'riak/search'.\n%{backtrace}"
11
+ empty_map_reduce_query: "Specify one or more query phases to your MapReduce."
12
+ failed_request: "Client request failed."
13
+ filter_needs_block: "Filter %{filter} expects a block."
14
+ filter_arity_mismatch: "Filter %{filter} expects %{expected} arguments but %{received} were given."
15
+ full_bucket_mapred: "Full-bucket MapReduce, including key filters, invokes list-keys which is an expensive operation that should not be used in production.\n %{backtrace}"
16
+ hash_type: "invalid argument %{hash} is not a Hash"
17
+ http_configuration: "The %{backend} HTTP backend cannot be used. Please check its requirements."
18
+ http_failed_request: "Expected %{expected} from Riak but received %{code}. %{body}"
19
+ hostname_invalid: "host must be a valid hostname"
20
+ protocol_invalid: "'%{invalid}' is not a valid protocol, valid values are %{valid}"
21
+ invalid_basic_auth: "basic auth must be set using 'user:pass'"
22
+ invalid_client_id: "Invalid client ID, must be a string or between 0 and %{max_id}"
23
+ invalid_io_object: "Invalid IO-like object assigned to RObject#data. It should be assigned to raw_data instead."
24
+ invalid_function_value: "invalid value for function: %{value}"
25
+ invalid_options: "Invalid configuration options given."
26
+ invalid_phase_type: "type must be :map, :reduce, or :link"
27
+ invalid_ssl_verify_mode: "%{invalid} is not a valid :verify_mode option for SSL. Valid options are 'peer' and 'none'."
28
+ loading_bucket: "while loading bucket '%{name}'"
29
+ list_buckets: "Riak::Client#buckets is an expensive operation that should not be used in production.\n %{backtrace}"
30
+ list_keys: "Riak::Bucket#keys is an expensive operation that should not be used in production.\n %{backtrace}"
31
+ missing_block: "A block must be given."
32
+ missing_host_and_port: "You must specify a host and port, or use the defaults of 127.0.0.1:8098"
33
+ module_function_pair_required: "function must have two elements when an array"
34
+ not_found: "The requested object was not found."
35
+ path_and_body_required: "You must supply both a resource path and a body."
36
+ port_invalid: "port must be an integer between 0 and 65535"
37
+ protobuffs_failed_request: "Expected success from Riak but received %{code}. %{body}"
38
+ request_body_type: "Request body must be a String or respond to :read."
39
+ resource_path_short: "Resource path too short"
40
+ search_docs_require_id: "Search index documents must include the 'id' field."
41
+ search_remove_requires_id_or_query: "Search index documents to be removed must have 'id' or 'query' keys."
42
+ serializer_not_implemented: "No serializer has been registered for content type %{content_type}"
43
+ stale_write_prevented: "Stale write prevented by client."
44
+ stored_function_invalid: "function must have :bucket and :key when a hash"
45
+ string_type: "invalid_argument %{string} is not a String"
46
+ too_few_arguments: "too few arguments: %{params}"
47
+ walk_spec_invalid_unless_link: "WalkSpec is only valid for a function when the type is :link"
48
+ wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
@@ -0,0 +1,206 @@
1
+
2
+ require 'riak/util/translation'
3
+ require 'riak/util/escape'
4
+ require 'riak/json'
5
+ require 'riak/client'
6
+ require 'riak/bucket'
7
+ require 'riak/robject'
8
+ require 'riak/walk_spec'
9
+ require 'riak/failed_request'
10
+ require 'riak/map_reduce_error'
11
+ require 'riak/map_reduce/phase'
12
+ require 'riak/map_reduce/filter_builder'
13
+
14
+ module Riak
15
+ # Class for invoking map-reduce jobs using the HTTP interface.
16
+ class MapReduce
17
+ include Util::Translation
18
+ include Util::Escape
19
+
20
+ # @return [Array<[bucket,key]>,String,Hash<:bucket,:filters>] The
21
+ # bucket/keys for input to the job, or the bucket (all
22
+ # keys), or a hash containing the bucket and key-filters.
23
+ # @see #add
24
+ attr_accessor :inputs
25
+
26
+ # @return [Array<Phase>] The map and reduce phases that will be executed
27
+ # @see #map
28
+ # @see #reduce
29
+ # @see #link
30
+ attr_accessor :query
31
+
32
+ # Creates a new map-reduce job.
33
+ # @param [Client] client the Riak::Client interface
34
+ # @yield [self] helpful for initializing the job
35
+ def initialize(client)
36
+ @client, @inputs, @query = client, [], []
37
+ yield self if block_given?
38
+ end
39
+
40
+ # Add or replace inputs for the job.
41
+ # @overload add(bucket)
42
+ # Run the job across all keys in the bucket. This will replace any other inputs previously added.
43
+ # @param [String, Bucket] bucket the bucket to run the job on
44
+ # @overload add(bucket,key)
45
+ # Add a bucket/key pair to the job.
46
+ # @param [String,Bucket] bucket the bucket of the object
47
+ # @param [String] key the key of the object
48
+ # @overload add(object)
49
+ # Add an object to the job (by its bucket/key)
50
+ # @param [RObject] object the object to add to the inputs
51
+ # @overload add(bucket, key, keydata)
52
+ # @param [String,Bucket] bucket the bucket of the object
53
+ # @param [String] key the key of the object
54
+ # @param [String] keydata extra data to pass along with the object to the job
55
+ # @overload add(bucket, filters)
56
+ # Run the job across all keys in the bucket, with the given
57
+ # key-filters. This will replace any other inputs previously
58
+ # added. (Requires Riak 0.14)
59
+ # @param [String,Bucket] bucket the bucket to filter keys from
60
+ # @param [Array<Array>] filters a list of key-filters to apply
61
+ # to the key list
62
+ # @return [MapReduce] self
63
+ def add(*params)
64
+ params = params.dup
65
+ params = params.first if Array === params.first
66
+ case params.size
67
+ when 1
68
+ p = params.first
69
+ case p
70
+ when Bucket
71
+ warn(t('full_bucket_mapred', :backtrace => caller.join("\n "))) unless Riak.disable_list_keys_warnings
72
+ @inputs = escape(p.name)
73
+ when RObject
74
+ @inputs << [escape(p.bucket.name), escape(p.key)]
75
+ when String
76
+ warn(t('full_bucket_mapred', :backtrace => caller.join("\n "))) unless Riak.disable_list_keys_warnings
77
+ @inputs = escape(p)
78
+ end
79
+ when 2..3
80
+ bucket = params.shift
81
+ bucket = bucket.name if Bucket === bucket
82
+ if Array === params.first
83
+ warn(t('full_bucket_mapred', :backtrace => caller.join("\n "))) unless Riak.disable_list_keys_warnings
84
+ @inputs = {:bucket => escape(bucket), :key_filters => params.first }
85
+ else
86
+ key = params.shift
87
+ @inputs << params.unshift(escape(key)).unshift(escape(bucket))
88
+ end
89
+ end
90
+ self
91
+ end
92
+ alias :<< :add
93
+ alias :include :add
94
+
95
+ # Adds a bucket and key-filters built by the given
96
+ # block. Equivalent to #add with a list of filters.
97
+ # @param [String] bucket the bucket to apply key-filters to
98
+ # @yield [] builder block - instance_eval'ed into a FilterBuilder
99
+ # @return [MapReduce] self
100
+ # @see MapReduce#add
101
+ def filter(bucket, &block)
102
+ add(bucket, FilterBuilder.new(&block).to_a)
103
+ end
104
+
105
+ # (Riak Search) Use a search query to start a map/reduce job.
106
+ # @param [String, Bucket] bucket the bucket/index to search
107
+ # @param [String] query the query to run
108
+ # @return [MapReduce] self
109
+ def search(bucket, query)
110
+ bucket = bucket.name if bucket.respond_to?(:name)
111
+ @inputs = {:module => "riak_search", :function => "mapred_search", :arg => [bucket, query]}
112
+ self
113
+ end
114
+
115
+ # Add a map phase to the job.
116
+ # @overload map(function)
117
+ # @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module,function] pair
118
+ # @overload map(function?, options)
119
+ # @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module, function] pair
120
+ # @param [Hash] options extra options for the phase (see {Phase#initialize})
121
+ # @return [MapReduce] self
122
+ # @see Phase#initialize
123
+ def map(*params)
124
+ options = params.extract_options!
125
+ @query << Phase.new({:type => :map, :function => params.shift}.merge(options))
126
+ self
127
+ end
128
+
129
+ # Add a reduce phase to the job.
130
+ # @overload reduce(function)
131
+ # @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module,function] pair
132
+ # @overload reduce(function?, options)
133
+ # @param [String, Array] function a Javascript function that represents the phase, or an Erlang [module, function] pair
134
+ # @param [Hash] options extra options for the phase (see {Phase#initialize})
135
+ # @return [MapReduce] self
136
+ # @see Phase#initialize
137
+ def reduce(*params)
138
+ options = params.extract_options!
139
+ @query << Phase.new({:type => :reduce, :function => params.shift}.merge(options))
140
+ self
141
+ end
142
+
143
+ # Add a link phase to the job. Link phases follow links attached to objects automatically (a special case of map).
144
+ # @overload link(walk_spec, options={})
145
+ # @param [WalkSpec] walk_spec a WalkSpec that represents the types of links to follow
146
+ # @param [Hash] options extra options for the phase (see {Phase#initialize})
147
+ # @overload link(bucket, tag, keep, options={})
148
+ # @param [String, nil] bucket the bucket to limit links to
149
+ # @param [String, nil] tag the tag to limit links to
150
+ # @param [Boolean] keep whether to keep results of this phase (overrides the phase options)
151
+ # @param [Hash] options extra options for the phase (see {Phase#initialize})
152
+ # @overload link(options)
153
+ # @param [Hash] options options for both the walk spec and link phase
154
+ # @see WalkSpec#initialize
155
+ # @return [MapReduce] self
156
+ # @see Phase#initialize
157
+ def link(*params)
158
+ options = params.extract_options!
159
+ walk_spec_options = options.slice!(:type, :function, :language, :arg) unless params.first
160
+ walk_spec = WalkSpec.normalize(params.shift || walk_spec_options).first
161
+ @query << Phase.new({:type => :link, :function => walk_spec}.merge(options))
162
+ self
163
+ end
164
+
165
+ # Sets the timeout for the map-reduce job.
166
+ # @param [Fixnum] value the job timeout, in milliseconds
167
+ def timeout(value)
168
+ @timeout = value
169
+ return self
170
+ end
171
+ alias :timeout= :timeout
172
+
173
+ # Convert the job to JSON for submission over the HTTP interface.
174
+ # @return [String] the JSON representation
175
+ def to_json(*a)
176
+ hash = {"inputs" => inputs, "query" => query.map(&:as_json)}
177
+ hash['timeout'] = @timeout.to_i if @timeout
178
+ hash.to_json(*a)
179
+ end
180
+
181
+ # Executes this map-reduce job.
182
+ # @overload run
183
+ # Return the entire collection of results.
184
+ # @return [Array<Array>] similar to link-walking, each element is
185
+ # an array of results from a phase where "keep" is true. If there
186
+ # is only one "keep" phase, only the results from that phase will
187
+ # be returned.
188
+ # @overload run
189
+ # Stream the results through the given block without accumulating.
190
+ # @yield [phase, data] A block to stream results through
191
+ # @yieldparam [Fixnum] phase the phase from which the results were
192
+ # generated
193
+ # @yieldparam [Array] data a list of results from the phase
194
+ # @return [nil] nothing
195
+ def run(&block)
196
+ raise MapReduceError.new(t("empty_map_reduce_query")) if @query.empty?
197
+ @client.backend.mapred(self, &block)
198
+ rescue FailedRequest => fr
199
+ if fr.server_error? && fr.is_json?
200
+ raise MapReduceError.new(fr.body)
201
+ else
202
+ raise fr
203
+ end
204
+ end
205
+ end
206
+ end