riak-client 0.9.8 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.gitignore +32 -0
  2. data/Gemfile +17 -11
  3. data/Guardfile +14 -0
  4. data/Rakefile +18 -44
  5. data/erl_src/riak_kv_test_backend.beam +0 -0
  6. data/erl_src/riak_kv_test_backend.erl +461 -128
  7. data/erl_src/riak_search_test_backend.beam +0 -0
  8. data/erl_src/riak_search_test_backend.erl +175 -0
  9. data/lib/active_support/cache/riak_store.rb +0 -13
  10. data/lib/riak.rb +11 -16
  11. data/lib/riak/bucket.rb +59 -41
  12. data/lib/riak/cache_store.rb +1 -14
  13. data/lib/riak/client.rb +145 -73
  14. data/lib/riak/client/beefcake/messages.rb +36 -31
  15. data/lib/riak/client/beefcake/object_methods.rb +27 -19
  16. data/lib/riak/client/beefcake_protobuffs_backend.rb +27 -33
  17. data/lib/riak/client/excon_backend.rb +0 -13
  18. data/lib/riak/client/http_backend.rb +95 -60
  19. data/lib/riak/client/http_backend/configuration.rb +144 -19
  20. data/lib/riak/client/http_backend/key_streamer.rb +1 -14
  21. data/lib/riak/client/http_backend/object_methods.rb +16 -16
  22. data/lib/riak/client/http_backend/request_headers.rb +0 -13
  23. data/lib/riak/client/http_backend/transport_methods.rb +26 -56
  24. data/lib/riak/client/net_http_backend.rb +11 -13
  25. data/lib/riak/client/protobuffs_backend.rb +21 -19
  26. data/lib/riak/client/pump.rb +1 -15
  27. data/lib/riak/client/search.rb +85 -0
  28. data/lib/riak/cluster.rb +151 -0
  29. data/lib/riak/core_ext.rb +1 -0
  30. data/lib/riak/core_ext/deep_dup.rb +13 -0
  31. data/lib/riak/core_ext/json.rb +15 -0
  32. data/lib/riak/core_ext/stringify_keys.rb +1 -1
  33. data/lib/riak/core_ext/symbolize_keys.rb +1 -1
  34. data/lib/riak/encoding.rb +6 -0
  35. data/lib/riak/failed_request.rb +2 -15
  36. data/lib/riak/i18n.rb +0 -13
  37. data/lib/riak/json.rb +19 -8
  38. data/lib/riak/link.rb +18 -20
  39. data/lib/riak/locale/en.yml +13 -16
  40. data/lib/riak/map_reduce.rb +40 -20
  41. data/lib/riak/map_reduce/filter_builder.rb +14 -18
  42. data/lib/riak/map_reduce/phase.rb +0 -13
  43. data/lib/riak/map_reduce_error.rb +0 -13
  44. data/lib/riak/node.rb +38 -0
  45. data/lib/riak/node/configuration.rb +286 -0
  46. data/lib/riak/node/console.rb +139 -0
  47. data/lib/riak/node/control.rb +207 -0
  48. data/lib/riak/node/defaults.rb +70 -0
  49. data/lib/riak/node/generation.rb +99 -0
  50. data/lib/riak/node/log.rb +34 -0
  51. data/lib/riak/node/version.rb +37 -0
  52. data/lib/riak/robject.rb +45 -41
  53. data/lib/riak/search.rb +2 -161
  54. data/lib/riak/serializers.rb +74 -0
  55. data/lib/riak/stamp.rb +77 -0
  56. data/lib/riak/test_server.rb +56 -220
  57. data/lib/riak/util/escape.rb +58 -17
  58. data/lib/riak/util/headers.rb +2 -15
  59. data/lib/riak/util/multipart.rb +0 -13
  60. data/lib/riak/util/multipart/stream_parser.rb +0 -13
  61. data/lib/riak/util/tcp_socket_extensions.rb +1 -14
  62. data/lib/riak/util/translation.rb +0 -13
  63. data/lib/riak/version.rb +3 -0
  64. data/lib/riak/walk_spec.rb +0 -13
  65. data/riak-client.gemspec +27 -47
  66. data/spec/fixtures/multipart-with-marked-tombstones.txt +17 -0
  67. data/spec/fixtures/multipart-with-unmarked-tombstone.txt +16 -0
  68. data/spec/integration/riak/cache_store_spec.rb +2 -40
  69. data/spec/integration/riak/cluster_spec.rb +88 -0
  70. data/spec/integration/riak/http_backends_spec.rb +6 -30
  71. data/spec/integration/riak/node_spec.rb +184 -0
  72. data/spec/integration/riak/protobuffs_backends_spec.rb +2 -26
  73. data/spec/integration/riak/test_server_spec.rb +31 -167
  74. data/spec/riak/beefcake_protobuffs_backend_spec.rb +5 -4
  75. data/spec/riak/bucket_spec.rb +26 -36
  76. data/spec/riak/client_spec.rb +44 -38
  77. data/spec/riak/escape_spec.rb +56 -30
  78. data/spec/riak/excon_backend_spec.rb +4 -17
  79. data/spec/riak/headers_spec.rb +1 -14
  80. data/spec/riak/http_backend/configuration_spec.rb +211 -34
  81. data/spec/riak/http_backend/object_methods_spec.rb +52 -18
  82. data/spec/riak/http_backend/transport_methods_spec.rb +5 -38
  83. data/spec/riak/http_backend_spec.rb +84 -78
  84. data/spec/riak/link_spec.rb +19 -18
  85. data/spec/riak/map_reduce/filter_builder_spec.rb +1 -14
  86. data/spec/riak/map_reduce/phase_spec.rb +1 -14
  87. data/spec/riak/map_reduce_spec.rb +141 -43
  88. data/spec/riak/multipart_spec.rb +1 -14
  89. data/spec/riak/net_http_backend_spec.rb +2 -15
  90. data/spec/riak/robject_spec.rb +129 -97
  91. data/spec/riak/search_spec.rb +45 -62
  92. data/spec/riak/serializers_spec.rb +93 -0
  93. data/spec/riak/stamp_spec.rb +54 -0
  94. data/spec/riak/stream_parser_spec.rb +3 -16
  95. data/spec/riak/walk_spec_spec.rb +1 -14
  96. data/spec/spec_helper.rb +22 -27
  97. data/spec/support/http_backend_implementation_examples.rb +49 -79
  98. data/spec/support/integration_setup.rb +10 -0
  99. data/spec/support/mock_server.rb +0 -14
  100. data/spec/support/mocks.rb +0 -13
  101. data/spec/support/test_server.rb +30 -0
  102. data/spec/support/test_server.yml.example +14 -2
  103. data/spec/support/unified_backend_examples.rb +36 -27
  104. metadata +100 -31
  105. data/lib/riak/client/curb_backend.rb +0 -89
  106. data/spec/riak/curb_backend_spec.rb +0 -76
@@ -0,0 +1,175 @@
1
+ %% -------------------------------------------------------------------
2
+ %%
3
+ %% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
4
+ %%
5
+ %% -------------------------------------------------------------------
6
+
7
+ -module(riak_search_test_backend).
8
+ -behavior(riak_search_backend).
9
+
10
+ -export([
11
+ reset/0,
12
+ start/2,
13
+ stop/1,
14
+ index/2,
15
+ delete/2,
16
+ stream/6,
17
+ range/8,
18
+ info/5,
19
+ fold/3,
20
+ is_empty/1,
21
+ drop/1
22
+ ]).
23
+ -export([
24
+ stream_results/3
25
+ ]).
26
+
27
+ -include_lib("riak_search/include/riak_search.hrl").
28
+
29
+ -record(state, {partition, table}).
30
+
31
+ reset() ->
32
+ {ok, Ring} = riak_core_ring_manager:get_my_ring(),
33
+ [ ets:delete_all_objects(list_to_atom("rs" ++ integer_to_list(P))) ||
34
+ P <- riak_core_ring:my_indices(Ring) ],
35
+ riak_search_config:clear(),
36
+ ok.
37
+
38
+ start(Partition, _Config) ->
39
+ Table = ets:new(list_to_atom("rs" ++ integer_to_list(Partition)),
40
+ [named_table, public, ordered_set]),
41
+ {ok, #state{partition=Partition, table=Table}}.
42
+
43
+ stop(State) ->
44
+ maybe_delete(State).
45
+
46
+ index(IFTVPKList, #state{table=Table}=State) ->
47
+ lists:foreach(
48
+ fun({I, F, T, V, P, K}) ->
49
+ Key = {b(I), b(F), b(T), b(V)},
50
+ case ets:lookup(Table, Key) of
51
+ [{_, _, ExistingKeyClock}] ->
52
+ if ExistingKeyClock > K ->
53
+ %% stored data is newer
54
+ ok;
55
+ true ->
56
+ %% stored data is older
57
+ ets:update_element(Table, Key,
58
+ [{2, P},{3, K}])
59
+ end;
60
+ [] ->
61
+ ets:insert(Table, {Key, P, K})
62
+ end
63
+ end,
64
+ IFTVPKList),
65
+ {reply, {indexed, node()}, State}.
66
+
67
+ delete(IFTVKList, State) ->
68
+ Table = State#state.table,
69
+ lists:foreach(fun(IFTVK) -> delete_fun(IFTVK, Table) end, IFTVKList),
70
+ {reply, {deleted, node()}, State}.
71
+
72
+ delete_fun({I, F, T, V, K}, Table) ->
73
+ Key = {b(I), b(F), b(T), b(V)},
74
+ case ets:lookup(Table, Key) of
75
+ [{Key, _Props, ExistingKeyClock}] ->
76
+ if ExistingKeyClock > K ->
77
+ %% stored data is newer
78
+ ok;
79
+ true ->
80
+ %% stored data is older
81
+ ets:delete(Table, Key)
82
+ end;
83
+ [] ->
84
+ ok
85
+ end;
86
+ delete_fun({I, F, T, V, _P, K}, Table) ->
87
+ %% copied idea from merge_index_backend
88
+ %% other operations include Props, though delete shouldn't
89
+ delete_fun({I, F, T, V, K}, Table).
90
+
91
+ info(Index, Field, Term, Sender, State) ->
92
+ Count = ets:select_count(State#state.table,
93
+ [{{{b(Index), b(Field), b(Term), '_'},
94
+ '_', '_'},
95
+ [],[true]}]),
96
+ riak_search_backend:info_response(Sender, [{Term, node(), Count}]),
97
+ noreply.
98
+
99
+ -define(STREAM_SIZE, 100).
100
+
101
+ range(Index, Field, StartTerm, EndTerm, _Size, FilterFun, Sender, State) ->
102
+ ST = b(StartTerm),
103
+ ET = b(EndTerm),
104
+ spawn(riak_search_ets_backend, stream_results,
105
+ [Sender,
106
+ FilterFun,
107
+ ets:select(State#state.table,
108
+ [{{{b(Index), b(Field), '$1', '$2'}, '$3', '_'},
109
+ [{'>=', '$1', ST}, {'=<', '$1', ET}],
110
+ [{{'$2', '$3'}}]}],
111
+ ?STREAM_SIZE)]),
112
+ noreply.
113
+
114
+ stream(Index, Field, Term, FilterFun, Sender, State) ->
115
+ spawn(riak_search_ets_backend, stream_results,
116
+ [Sender,
117
+ FilterFun,
118
+ ets:select(State#state.table,
119
+ [{{{b(Index), b(Field), b(Term), '$1'}, '$2', '_'},
120
+ [], [{{'$1', '$2'}}]}],
121
+ ?STREAM_SIZE)]),
122
+ noreply.
123
+
124
+ stream_results(Sender, FilterFun, {Results0, Continuation}) ->
125
+ case lists:filter(fun({V,P}) -> FilterFun(V, P) end, Results0) of
126
+ [] ->
127
+ ok;
128
+ Results ->
129
+ riak_search_backend:response_results(Sender, Results)
130
+ end,
131
+ stream_results(Sender, FilterFun, ets:select(Continuation));
132
+ stream_results(Sender, _, '$end_of_table') ->
133
+ riak_search_backend:response_done(Sender).
134
+
135
+ fold(FoldFun, Acc, State) ->
136
+ Fun = fun({{I,F,T,V},P,K}, {OuterAcc, {{I,{F,T}},InnerAcc}}) ->
137
+ %% same IFT, just accumulate doc/props/clock
138
+ {OuterAcc, {{I,{F,T}},[{V,P,K}|InnerAcc]}};
139
+ ({{I,F,T,V},P,K}, {OuterAcc, {FoldKey, VPKList}}) ->
140
+ %% finished a string of IFT, send it off
141
+ %% (sorted order is assumed)
142
+ NewOuterAcc = FoldFun(FoldKey, VPKList, OuterAcc),
143
+ {NewOuterAcc, {{I,{F,T}},[{V,P,K}]}};
144
+ ({{I,F,T,V},P,K}, {OuterAcc, undefined}) ->
145
+ %% first round through the fold - just start building
146
+ {OuterAcc, {{I,{F,T}},[{V,P,K}]}}
147
+ end,
148
+ {OuterAcc0, Final} = ets:foldl(Fun, {Acc, undefined}, State#state.table),
149
+ OuterAcc = case Final of
150
+ {FoldKey, VPKList} ->
151
+ %% one last IFT to send off
152
+ FoldFun(FoldKey, VPKList, OuterAcc0);
153
+ undefined ->
154
+ %% this partition was empty
155
+ OuterAcc0
156
+ end,
157
+ {reply, OuterAcc, State}.
158
+
159
+ is_empty(State) ->
160
+ 0 == ets:info(State#state.table, size).
161
+
162
+ drop(State) ->
163
+ maybe_delete(State).
164
+
165
+ maybe_delete(State) ->
166
+ case lists:member(State#state.table, ets:all()) of
167
+ true ->
168
+ ets:delete(State#state.table),
169
+ ok;
170
+ false ->
171
+ ok
172
+ end.
173
+
174
+ b(Binary) when is_binary(Binary) -> Binary;
175
+ b(List) when is_list(List) -> iolist_to_binary(List).
@@ -1,15 +1,2 @@
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
1
  require 'riak'
15
2
  require 'riak/cache_store'
@@ -1,26 +1,21 @@
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
- $KCODE = "UTF8" if RUBY_VERSION < "1.9"
15
-
1
+ require 'riak/encoding'
16
2
  require 'riak/core_ext'
17
3
  require 'riak/client'
18
4
  require 'riak/map_reduce'
5
+ require 'riak/util/translation'
19
6
 
20
7
  # The Riak module contains all aspects of the client interface to
21
8
  # Riak.
22
9
  module Riak
23
10
  # Utility classes and mixins
24
- module Util
11
+ module Util; end
12
+ extend Util::Translation
13
+
14
+ class << self
15
+ # Only change this if you really know what you're doing. Better to
16
+ # err on the side of caution and assume you don't.
17
+ # @private
18
+ attr_accessor :disable_list_keys_warnings
25
19
  end
20
+ self.disable_list_keys_warnings = false
26
21
  end
@@ -1,19 +1,4 @@
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
-
15
1
  require 'riak/util/translation'
16
- require 'riak/util/escape'
17
2
  require 'riak/client'
18
3
  require 'riak/robject'
19
4
  require 'riak/failed_request'
@@ -23,7 +8,9 @@ module Riak
23
8
  # using {Client#bucket}, or create it manually and retrieve its meta-information later.
24
9
  class Bucket
25
10
  include Util::Translation
26
- include Util::Escape
11
+
12
+ # (Riak Search) The precommit specification for kv/search integration
13
+ SEARCH_PRECOMMIT_HOOK = {"mod" => "riak_search_kv_hook", "fun" => "precommit"}
27
14
 
28
15
  # @return [Riak::Client] the associated client
29
16
  attr_reader :client
@@ -40,22 +27,21 @@ module Riak
40
27
  @client, @name = client, name
41
28
  end
42
29
 
43
- # Accesses or retrieves a list of keys in this bucket.
30
+ # Retrieves a list of keys in this bucket.
44
31
  # If a block is given, keys will be streamed through
45
32
  # the block (useful for large buckets). When streaming,
46
- # results of the operation will not be retained in the local Bucket object.
47
- # @param [Hash] options extra options
33
+ # results of the operation will not be returned to the caller.
48
34
  # @yield [Array<String>] a list of keys from the current chunk
49
- # @option options [Boolean] :reload (false) If present, will force reloading of the bucket's keys from Riak
50
35
  # @return [Array<String>] Keys in this bucket
51
- def keys(options={}, &block)
36
+ # @note This operation has serious performance implications and
37
+ # should not be used in production applications.
38
+ def keys(&block)
39
+ warn(t('list_keys', :backtrace => caller.join("\n "))) unless Riak.disable_list_keys_warnings
52
40
  if block_given?
53
- @client.backend.list_keys(self, &block)
54
- @keys = nil
55
- elsif @keys.nil? || options[:reload]
56
- @keys = @client.backend.list_keys(self)
41
+ @client.list_keys(self, &block)
42
+ else
43
+ @client.list_keys(self)
57
44
  end
58
- @keys
59
45
  end
60
46
 
61
47
  # Sets internal properties on the bucket
@@ -80,7 +66,7 @@ module Riak
80
66
  def props=(properties)
81
67
  raise ArgumentError, t("hash_type", :hash => properties.inspect) unless Hash === properties
82
68
  props.merge!(properties)
83
- @client.backend.set_bucket_props(self, properties)
69
+ @client.set_bucket_props(self, properties)
84
70
  props
85
71
  end
86
72
  alias :'properties=' :'props='
@@ -88,7 +74,7 @@ module Riak
88
74
  # @return [Hash] Internal Riak bucket properties.
89
75
  # @see #props=
90
76
  def props
91
- @props ||= @client.backend.get_bucket_props(self)
77
+ @props ||= @client.get_bucket_props(self)
92
78
  end
93
79
  alias :properties :props
94
80
 
@@ -99,7 +85,7 @@ module Riak
99
85
  # @return [Riak::RObject] the object
100
86
  # @raise [FailedRequest] if the object is not found or some other error occurs
101
87
  def get(key, options={})
102
- @client.backend.fetch_object(self, key, options[:r])
88
+ @client.get_object(self, key, options)
103
89
  end
104
90
  alias :[] :get
105
91
 
@@ -145,9 +131,23 @@ module Riak
145
131
  # Deletes a key from the bucket
146
132
  # @param [String] key the key to delete
147
133
  # @param [Hash] options quorum options
148
- # @option options [Fixnum] :rw - the read/write quorum for the delete
134
+ # @option options [Fixnum] :rw - the read/write quorum for the
135
+ # delete
136
+ # @option options [String] :vclock - the vector clock of the
137
+ # object being deleted
149
138
  def delete(key, options={})
150
- client.backend.delete_object(self, key, options[:rw])
139
+ client.delete_object(self, key, options)
140
+ end
141
+
142
+ # Queries a secondary index on the bucket.
143
+ # @note This will only work if your Riak installation supports 2I.
144
+ # @param [String] index the name of the index
145
+ # @param [String,Integer,Range] query the value of the index, or a
146
+ # Range of values to query
147
+ # @return [Array<String>] a list of keys that match the index
148
+ # query
149
+ def get_index(index, query)
150
+ client.get_index(self, index, query)
151
151
  end
152
152
 
153
153
  # @return [true, false] whether the bucket allows divergent siblings
@@ -177,17 +177,35 @@ module Riak
177
177
  end
178
178
  alias :'n_val=' :'n_value='
179
179
 
180
- [:r,:w,:dw,:rw].each do |q|
181
- class_eval <<-CODE
182
- def #{q}
183
- props["#{q}"]
184
- end
180
+ %w(r w dw rw).each do |q|
181
+ define_method(q) { props[q] }
182
+ define_method("#{q}=") { |value|
183
+ self.props = { q => value }
184
+ value
185
+ }
186
+ end
185
187
 
186
- def #{q}=(value)
187
- self.props = {"#{q}" => value}
188
- value
189
- end
190
- CODE
188
+ # (Riak Search) Installs a precommit hook that automatically indexes objects
189
+ # into riak_search.
190
+ def enable_index!
191
+ unless is_indexed?
192
+ self.props = {"precommit" => (props['precommit'] + [SEARCH_PRECOMMIT_HOOK]), "search" => true}
193
+ end
194
+ end
195
+
196
+ # (Riak Search) Removes the precommit hook that automatically indexes objects
197
+ # into riak_search.
198
+ def disable_index!
199
+ if is_indexed?
200
+ self.props = {"precommit" => (props['precommit'] - [SEARCH_PRECOMMIT_HOOK]), "search" => false}
201
+ end
202
+ end
203
+
204
+ # (Riak Search) Detects whether the bucket is automatically indexed into
205
+ # riak_search.
206
+ # @return [true,false] whether the bucket includes the search indexing hook
207
+ def is_indexed?
208
+ props['search'] == true || props['precommit'].include?(SEARCH_PRECOMMIT_HOOK)
191
209
  end
192
210
 
193
211
  # @return [String] a representation suitable for IRB and debugging output
@@ -1,16 +1,3 @@
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
1
 
15
2
  require 'yaml'
16
3
  require 'riak/client'
@@ -45,7 +32,7 @@ module Riak
45
32
  end
46
33
 
47
34
  def bucket
48
- @bucket ||= @client.bucket(@bucket_name, :keys => false)
35
+ @bucket ||= @client.bucket(@bucket_name)
49
36
  end
50
37
 
51
38
  def delete_matched(matcher, options={})
@@ -1,30 +1,17 @@
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
-
15
1
  require 'tempfile'
16
2
  require 'delegate'
17
3
  require 'riak'
18
4
  require 'riak/util/translation'
19
5
  require 'riak/util/escape'
20
6
  require 'riak/failed_request'
7
+ require 'riak/client/search'
21
8
  require 'riak/client/http_backend'
22
9
  require 'riak/client/net_http_backend'
23
- require 'riak/client/curb_backend'
24
10
  require 'riak/client/excon_backend'
25
11
  require 'riak/client/protobuffs_backend'
26
12
  require 'riak/client/beefcake_protobuffs_backend'
27
13
  require 'riak/bucket'
14
+ require 'riak/stamp'
28
15
 
29
16
  module Riak
30
17
  # A client connection to Riak.
@@ -41,7 +28,7 @@ module Riak
41
28
  # Regexp for validating hostnames, lifted from uri.rb in Ruby 1.8.6
42
29
  HOST_REGEX = /^(?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\])$/n
43
30
 
44
- VALID_OPTIONS = [:protocol, :host, :port, :http_port, :pb_port, :prefix, :client_id, :mapred, :luwak, :http_backend, :protobuffs_backend, :ssl, :basic_auth]
31
+ VALID_OPTIONS = [:protocol, :host, :port, :http_port, :pb_port, :prefix, :client_id, :mapred, :luwak, :solr, :http_paths, :http_backend, :protobuffs_backend, :ssl, :basic_auth]
45
32
 
46
33
  # @return [String] The protocol to use for the Riak endpoint
47
34
  attr_reader :protocol
@@ -68,15 +55,9 @@ module Riak
68
55
  # from the provided config
69
56
  attr_writer :ssl
70
57
 
71
- # @return [String] The URL path prefix to the "raw" HTTP endpoint
72
- attr_accessor :prefix
73
-
74
- # @return [String] The URL path to the map-reduce HTTP endpoint
75
- attr_accessor :mapred
76
-
77
- # @return [String] The URL path to the luwak HTTP endpoint
78
- attr_accessor :luwak
79
-
58
+ # @return [Hash] HTTP path configuration.
59
+ attr_accessor :http_paths
60
+
80
61
  # @return [Symbol] The HTTP backend/client to use
81
62
  attr_accessor :http_backend
82
63
 
@@ -86,7 +67,8 @@ module Riak
86
67
  # Creates a client connection to Riak
87
68
  # @param [Hash] options configuration options for the client
88
69
  # @option options [String] :host ('127.0.0.1') The host or IP address for the Riak endpoint
89
- # @option options [Fixnum] :port (8098) The port of the Riak HTTP endpoint
70
+ # @option options [Fixnum] :http_port (8098) The port of the Riak HTTP endpoint
71
+ # @option options [Fixnum] :pb_port (8087) The port of the Riak Protocol Buffers endpoint
90
72
  # @option options [String] :prefix ('/riak/') The URL path prefix to the main HTTP endpoint
91
73
  # @option options [String] :mapred ('/mapred') The path to the map-reduce HTTP endpoint
92
74
  # @option options [Fixnum, String] :client_id (rand(MAX_CLIENT_ID)) The internal client ID used by Riak to route responses
@@ -103,9 +85,12 @@ module Riak
103
85
  self.http_port = options[:http_port] || 8098
104
86
  self.pb_port = options[:pb_port] || 8087
105
87
  self.port = options[:port] if options[:port]
106
- self.prefix = options[:prefix] || "/riak/"
107
- self.mapred = options[:mapred] || "/mapred"
108
- self.luwak = options[:luwak] || "/luwak"
88
+ self.http_paths = {
89
+ prefix: options[:prefix] || "/riak/",
90
+ mapred: options[:mapred] || "/mapred",
91
+ luwak: options[:luwak] || "/luwak",
92
+ solr: options[:solr] || "/solr" # Unused?
93
+ }.merge(options[:http_paths] || {})
109
94
  self.http_backend = options[:http_backend] || :NetHTTP
110
95
  self.protobuffs_backend = options[:protobuffs_backend] || :Beefcake
111
96
  self.basic_auth = options[:basic_auth] if options[:basic_auth]
@@ -279,26 +264,18 @@ module Riak
279
264
  end
280
265
  end
281
266
 
282
- # Pings the Riak server to check for liveness.
283
- # @return [true,false] whether the Riak server is alive and reachable
284
- def ping
285
- backend.ping
286
- end
287
-
288
267
  # Retrieves a bucket from Riak.
289
268
  # @param [String] bucket the bucket to retrieve
290
269
  # @param [Hash] options options for retrieving the bucket
291
- # @option options [Boolean] :keys (false whether to retrieve the bucket keys
292
270
  # @option options [Boolean] :props (false) whether to retreive the bucket properties
293
271
  # @return [Bucket] the requested bucket
294
272
  def bucket(name, options={})
295
- unless (options.keys - [:keys, :props]).empty?
273
+ unless (options.keys - [:props]).empty?
296
274
  raise ArgumentError, "invalid options"
297
275
  end
298
276
  @bucket_cache ||= {}
299
277
  (@bucket_cache[name] ||= Bucket.new(self, name)).tap do |b|
300
278
  b.props if options[:props]
301
- b.keys if options[:keys]
302
279
  end
303
280
  end
304
281
  alias :[] :bucket
@@ -308,30 +285,35 @@ module Riak
308
285
  # in development.
309
286
  # @return [Array<Bucket>] a list of buckets
310
287
  def buckets
288
+ warn(t('list_buckets', :backtrace => caller.join("\n "))) unless Riak.disable_list_keys_warnings
311
289
  backend.list_buckets.map {|name| Bucket.new(self, name) }
312
290
  end
313
291
  alias :list_buckets :buckets
314
292
 
315
- # Stores a large file/IO-like object in Riak via the "Luwak" interface.
316
- # @overload store_file(filename, content_type, data)
317
- # Stores the file at the given key/filename
318
- # @param [String] filename the key/filename for the object
319
- # @param [String] content_type the MIME Content-Type for the data
320
- # @param [IO, String] data the contents of the file
321
- # @overload store_file(content_type, data)
322
- # Stores the file with a server-determined key/filename
323
- # @param [String] content_type the MIME Content-Type for the data
324
- # @param [String, #read] data the contents of the file
325
- # @return [String] the key/filename where the object was stored
326
- def store_file(*args)
327
- data, content_type, filename = args.reverse
328
- if filename
329
- http.put(204, luwak, escape(filename), data, {"Content-Type" => content_type})
330
- filename
331
- else
332
- response = http.post(201, luwak, data, {"Content-Type" => content_type})
333
- response[:headers]["location"].first.split("/").last
334
- end
293
+ # Deletes a file stored via the "Luwak" interface
294
+ # @param [String] filename the key/filename to delete
295
+ def delete_file(filename)
296
+ http.delete([204,404], http_paths[:luwak], escape(filename))
297
+ true
298
+ end
299
+
300
+ # Delete an object. See Bucket#delete
301
+ def delete_object(bucket, key, options = {})
302
+ backend.delete_object(bucket, key, options)
303
+ end
304
+
305
+ # Checks whether a file exists in "Luwak".
306
+ # @param [String] key the key to check
307
+ # @return [true, false] whether the key exists in "Luwak"
308
+ def file_exists?(key)
309
+ result = http.head([200,404], http_paths[:luwak], escape(key))
310
+ result[:code] == 200
311
+ end
312
+ alias :file_exist? :file_exists?
313
+
314
+ # Bucket properties. See Bucket#props
315
+ def get_bucket_props(bucket)
316
+ backend.get_bucket_props bucket
335
317
  end
336
318
 
337
319
  # Retrieves a large file/IO object from Riak via the "Luwak"
@@ -347,12 +329,12 @@ module Riak
347
329
  # @yieldparam [String] chunk a single chunk of the object's data
348
330
  def get_file(filename, &block)
349
331
  if block_given?
350
- http.get(200, luwak, escape(filename), &block)
332
+ http.get(200, http_paths[:luwak], escape(filename), &block)
351
333
  nil
352
334
  else
353
335
  tmpfile = LuwakFile.new(escape(filename))
354
336
  begin
355
- response = http.get(200, luwak, escape(filename)) do |chunk|
337
+ response = http.get(200, http_paths[:luwak], escape(filename)) do |chunk|
356
338
  tmpfile.write chunk
357
339
  end
358
340
  tmpfile.content_type = response[:headers]['content-type'].first
@@ -363,27 +345,117 @@ module Riak
363
345
  end
364
346
  end
365
347
 
366
- # Deletes a file stored via the "Luwak" interface
367
- # @param [String] filename the key/filename to delete
368
- def delete_file(filename)
369
- http.delete([204,404], luwak, escape(filename))
370
- true
348
+ # Queries a secondary index on a bucket. See Bucket#get_index
349
+ def get_index(bucket, index, query)
350
+ backend.get_index bucket, index, query
371
351
  end
372
-
373
- # Checks whether a file exists in "Luwak".
374
- # @param [String] key the key to check
375
- # @return [true, false] whether the key exists in "Luwak"
376
- def file_exists?(key)
377
- result = http.head([200,404], luwak, escape(key))
378
- result[:code] == 200
352
+
353
+ # Get an object. See Bucket#get
354
+ def get_object(bucket, key, options = {})
355
+ backend.fetch_object(bucket, key, options)
379
356
  end
380
- alias :file_exist? :file_exists?
381
357
 
382
358
  # @return [String] A representation suitable for IRB and debugging output.
383
359
  def inspect
384
360
  "#<Riak::Client #{protocol}://#{host}:#{protocol == 'pbc' ? pb_port : http_port}>"
385
361
  end
386
362
 
363
+ # Retrieves a list of keys in the given bucket. See Bucket#keys
364
+ def list_keys(bucket, &block)
365
+ if block_given?
366
+ backend.list_keys bucket, &block
367
+ else
368
+ backend.list_keys bucket
369
+ end
370
+ end
371
+
372
+ # Deprecated accessor for http_paths[:luwak]
373
+ def luwak
374
+ http_paths[:luwak]
375
+ end
376
+
377
+ # Deprecated accessor for http_paths[:luwak]
378
+ def luwak=(luwak)
379
+ http_paths[:luwak] = luwak
380
+ end
381
+
382
+ # Executes a mapreduce request. See MapReduce#run
383
+ def mapred(mr, &block)
384
+ backend.mapred(mr, &block)
385
+ end
386
+
387
+ # Pings the Riak server to check for liveness.
388
+ # @return [true,false] whether the Riak server is alive and reachable
389
+ def ping
390
+ backend.ping
391
+ end
392
+
393
+ # Deprecated accessor for http_paths[:prefix]
394
+ def prefix
395
+ http_paths[:prefix]
396
+ end
397
+
398
+ # Deprecated accessor http_paths[:prefix]
399
+ def prefix=(prefix)
400
+ http_paths[:prefix] = prefix
401
+ end
402
+
403
+ # Reloads the object from Riak.
404
+ def reload_object(object, options = {})
405
+ backend.reload_object(object, options)
406
+ end
407
+
408
+ # Deprecated accessor for http_paths[:solr]
409
+ def solr
410
+ http_paths[:solr]
411
+ end
412
+
413
+ # Deprecated accessor for http_paths[:solr]
414
+ def solr=(solr)
415
+ http_paths[:solr] = solr
416
+ end
417
+
418
+ # Sets the properties on a bucket. See Bucket#props=
419
+ def set_bucket_props(bucket, properties)
420
+ backend.set_bucket_props(bucket, properties)
421
+ end
422
+
423
+ # Exposes a {Stamp} object for use in generating unique
424
+ # identifiers.
425
+ # @return [Stamp] an ID generator
426
+ # @see Stamp#next
427
+ def stamp
428
+ @stamp ||= Riak::Stamp.new(self)
429
+ end
430
+
431
+ # Stores a large file/IO-like object in Riak via the "Luwak" interface.
432
+ # @overload store_file(filename, content_type, data)
433
+ # Stores the file at the given key/filename
434
+ # @param [String] filename the key/filename for the object
435
+ # @param [String] content_type the MIME Content-Type for the data
436
+ # @param [IO, String] data the contents of the file
437
+ # @overload store_file(content_type, data)
438
+ # Stores the file with a server-determined key/filename
439
+ # @param [String] content_type the MIME Content-Type for the data
440
+ # @param [String, #read] data the contents of the file
441
+ # @return [String] the key/filename where the object was stored
442
+ def store_file(*args)
443
+ data, content_type, filename = args.reverse
444
+ if filename
445
+ http.put(204, http_paths[:luwak], escape(filename), data, {"Content-Type" => content_type})
446
+ filename
447
+ else
448
+ response = http.post(201, http_paths[:luwak], data, {"Content-Type" => content_type})
449
+ response[:headers]["location"].first.split("/").last
450
+ end
451
+ end
452
+
453
+ # Stores an object in Riak.
454
+ def store_object(object, options = {})
455
+ params = {:returnbody => true}.merge(options)
456
+ backend.store_object(object, params)
457
+ end
458
+
387
459
  private
388
460
  def make_client_id
389
461
  rand(MAX_CLIENT_ID)