riak-client 0.8.0 → 0.8.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/Gemfile CHANGED
@@ -1,10 +1,10 @@
1
1
  # A sample Gemfile
2
2
  source :gemcutter
3
3
 
4
- gem 'activesupport', '~>2.3.5' # '3.0.0'
4
+ gem 'activesupport', '~>2.3.5' #'~>3.0.0'
5
5
  gem 'i18n'
6
6
 
7
- gem 'rspec', "~>2.0.0.beta.18"
7
+ gem 'rspec', "~>2.0.0"
8
8
  gem 'fakeweb', ">=1.2"
9
9
  gem 'rack', '>=1.0'
10
10
  gem 'curb', '>=0.6'
@@ -1,22 +1,24 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activesupport (2.3.8)
4
+ activesupport (2.3.9)
5
5
  curb (0.7.8)
6
6
  diff-lcs (1.1.2)
7
7
  fakeweb (1.3.0)
8
8
  i18n (0.4.1)
9
9
  rack (1.2.1)
10
10
  rake (0.8.7)
11
- rspec (2.0.0.beta.20)
12
- rspec-core (= 2.0.0.beta.20)
13
- rspec-expectations (= 2.0.0.beta.20)
14
- rspec-mocks (= 2.0.0.beta.20)
15
- rspec-core (2.0.0.beta.20)
16
- rspec-expectations (2.0.0.beta.20)
11
+ rspec (2.0.0)
12
+ rspec-core (= 2.0.0)
13
+ rspec-expectations (= 2.0.0)
14
+ rspec-mocks (= 2.0.0)
15
+ rspec-core (2.0.0)
16
+ rspec-expectations (2.0.0)
17
17
  diff-lcs (>= 1.1.2)
18
- rspec-mocks (2.0.0.beta.20)
19
- yajl-ruby (0.7.7)
18
+ rspec-mocks (2.0.0)
19
+ rspec-core (= 2.0.0)
20
+ rspec-expectations (= 2.0.0)
21
+ yajl-ruby (0.7.8)
20
22
 
21
23
  PLATFORMS
22
24
  ruby
@@ -29,5 +31,5 @@ DEPENDENCIES
29
31
  i18n
30
32
  rack (>= 1.0)
31
33
  rake
32
- rspec (~> 2.0.0.beta.18)
34
+ rspec (~> 2.0.0)
33
35
  yajl-ruby
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'htmldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('diff-lcs', 'htmldiff')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('diff-lcs', 'ldiff')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rackup' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('rack', 'rackup')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('rake', 'rake')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,173 @@
1
+ %% -------------------------------------------------------------------
2
+ %%
3
+ %% riak_kv_test_backend: storage engine based on ETS tables
4
+ %%
5
+ %% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
6
+ %%
7
+ %% This file is provided to you under the Apache License,
8
+ %% Version 2.0 (the "License"); you may not use this file
9
+ %% except in compliance with the License. You may obtain
10
+ %% a copy of the License at
11
+ %%
12
+ %% http://www.apache.org/licenses/LICENSE-2.0
13
+ %%
14
+ %% Unless required by applicable law or agreed to in writing,
15
+ %% software distributed under the License is distributed on an
16
+ %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ %% KIND, either express or implied. See the License for the
18
+ %% specific language governing permissions and limitations
19
+ %% under the License.
20
+ %%
21
+ %% -------------------------------------------------------------------
22
+
23
+ % @doc riak_kv_test_backend is a Riak storage backend using ets that exposes a reset function for efficiently clearing stored data.
24
+
25
+ -module(riak_kv_test_backend).
26
+ -behavior(riak_kv_backend).
27
+ -behavior(gen_server).
28
+ -ifdef(TEST).
29
+ -include_lib("eunit/include/eunit.hrl").
30
+ -endif.
31
+ -export([start/2,stop/1,get/2,put/3,list/1,list_bucket/2,delete/2,
32
+ is_empty/1, drop/1, fold/3, callback/3, reset/0]).
33
+
34
+ -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
35
+ terminate/2, code_change/3]).
36
+
37
+ % @type state() = term().
38
+ -record(state, {t}).
39
+
40
+ % @spec start(Partition :: integer(), Config :: proplist()) ->
41
+ % {ok, state()} | {{error, Reason :: term()}, state()}
42
+ start(Partition, _Config) ->
43
+ gen_server:start_link(?MODULE, [Partition], []).
44
+
45
+ % @spec reset() -> ok
46
+ reset() ->
47
+ Pids = lists:filter(fun(Item) ->
48
+ lists:prefix("test_backend", atom_to_list(Item))
49
+ end, registered()),
50
+ [gen_server:call(Pid, reset) || Pid <- Pids],
51
+ ok.
52
+
53
+ %% @private
54
+ init([Partition]) ->
55
+ PName = list_to_atom("test_backend" ++ integer_to_list(Partition)),
56
+ register(PName, self()),
57
+ {ok, #state{t=ets:new(list_to_atom(integer_to_list(Partition)),[])}}.
58
+
59
+ %% @private
60
+ handle_cast(_, State) -> {noreply, State}.
61
+
62
+ %% @private
63
+ handle_call(stop,_From,State) -> {reply, srv_stop(State), State};
64
+ handle_call({get,BKey},_From,State) -> {reply, srv_get(State,BKey), State};
65
+ handle_call({put,BKey,Val},_From,State) ->
66
+ {reply, srv_put(State,BKey,Val),State};
67
+ handle_call({delete,BKey},_From,State) -> {reply, srv_delete(State,BKey),State};
68
+ handle_call(list,_From,State) -> {reply, srv_list(State), State};
69
+ handle_call({list_bucket,Bucket},_From,State) ->
70
+ {reply, srv_list_bucket(State, Bucket), State};
71
+ handle_call(is_empty, _From, State) ->
72
+ {reply, ets:info(State#state.t, size) =:= 0, State};
73
+ handle_call(drop, _From, State) ->
74
+ ets:delete(State#state.t),
75
+ {reply, ok, State};
76
+ handle_call({fold, Fun0, Acc}, _From, State) ->
77
+ Fun = fun({{B,K}, V}, AccIn) -> Fun0({B,K}, V, AccIn) end,
78
+ Reply = ets:foldl(Fun, Acc, State#state.t),
79
+ {reply, Reply, State};
80
+ handle_call(reset, _From, State) ->
81
+ ets:delete_all_objects(State#state.t),
82
+ {reply, ok, State}.
83
+
84
+ % @spec stop(state()) -> ok | {error, Reason :: term()}
85
+ stop(SrvRef) -> gen_server:call(SrvRef,stop).
86
+ srv_stop(State) ->
87
+ true = ets:delete(State#state.t),
88
+ ok.
89
+
90
+ % get(state(), riak_object:bkey()) ->
91
+ % {ok, Val :: binary()} | {error, Reason :: term()}
92
+ % key must be 160b
93
+ get(SrvRef, BKey) -> gen_server:call(SrvRef,{get,BKey}).
94
+ srv_get(State, BKey) ->
95
+ case ets:lookup(State#state.t,BKey) of
96
+ [] -> {error, notfound};
97
+ [{BKey,Val}] -> {ok, Val};
98
+ Err -> {error, Err}
99
+ end.
100
+
101
+ % put(state(), riak_object:bkey(), Val :: binary()) ->
102
+ % ok | {error, Reason :: term()}
103
+ % key must be 160b
104
+ put(SrvRef, BKey, Val) -> gen_server:call(SrvRef,{put,BKey,Val}).
105
+ srv_put(State,BKey,Val) ->
106
+ true = ets:insert(State#state.t, {BKey,Val}),
107
+ ok.
108
+
109
+ % delete(state(), riak_object:bkey()) ->
110
+ % ok | {error, Reason :: term()}
111
+ % key must be 160b
112
+ delete(SrvRef, BKey) -> gen_server:call(SrvRef,{delete,BKey}).
113
+ srv_delete(State, BKey) ->
114
+ true = ets:delete(State#state.t, BKey),
115
+ ok.
116
+
117
+ % list(state()) -> [riak_object:bkey()]
118
+ list(SrvRef) -> gen_server:call(SrvRef,list).
119
+ srv_list(State) ->
120
+ MList = ets:match(State#state.t,{'$1','_'}),
121
+ list(MList,[]).
122
+ list([],Acc) -> Acc;
123
+ list([[K]|Rest],Acc) -> list(Rest,[K|Acc]).
124
+
125
+ % list_bucket(term(), Bucket :: riak_object:bucket()) -> [Key :: binary()]
126
+ list_bucket(SrvRef, Bucket) ->
127
+ gen_server:call(SrvRef,{list_bucket, Bucket}).
128
+ srv_list_bucket(State, {filter, Bucket, Fun}) ->
129
+ MList = lists:filter(Fun, ets:match(State#state.t,{{Bucket,'$1'},'_'})),
130
+ list(MList,[]);
131
+ srv_list_bucket(State, Bucket) ->
132
+ case Bucket of
133
+ '_' -> MatchSpec = {{'$1','_'},'_'};
134
+ _ -> MatchSpec = {{Bucket,'$1'},'_'}
135
+ end,
136
+ MList = ets:match(State#state.t,MatchSpec),
137
+ list(MList,[]).
138
+
139
+ is_empty(SrvRef) -> gen_server:call(SrvRef, is_empty).
140
+
141
+ drop(SrvRef) -> gen_server:call(SrvRef, drop).
142
+
143
+ fold(SrvRef, Fun, Acc0) -> gen_server:call(SrvRef, {fold, Fun, Acc0}, infinity).
144
+
145
+ %% Ignore callbacks for other backends so multi backend works
146
+ callback(_State, _Ref, _Msg) ->
147
+ ok.
148
+
149
+ %% @private
150
+ handle_info(_Msg, State) -> {noreply, State}.
151
+
152
+ %% @private
153
+ terminate(_Reason, _State) -> ok.
154
+
155
+ %% @private
156
+ code_change(_OldVsn, State, _Extra) -> {ok, State}.
157
+
158
+ %%
159
+ %% Test
160
+ %%
161
+ -ifdef(TEST).
162
+
163
+ % @private
164
+ simple_test() ->
165
+ riak_kv_backend:standard_test(?MODULE, []).
166
+
167
+ -ifdef(EQC).
168
+ %% @private
169
+ eqc_test() ->
170
+ ?assertEqual(true, backend_eqc:test(?MODULE, true)).
171
+
172
+ -endif. % EQC
173
+ -endif. % TEST
@@ -44,6 +44,9 @@ module Riak
44
44
  autoload :InvalidResponse, "riak/invalid_response"
45
45
  autoload :MapReduceError, "riak/map_reduce_error"
46
46
 
47
+ # Test server
48
+ autoload :TestServer, "riak/test_server"
49
+
47
50
  # Utility classes and mixins
48
51
  module Util
49
52
  autoload :Escape, "riak/util/escape"
@@ -35,9 +35,6 @@ module Riak
35
35
  attr_accessor :vclock
36
36
  alias_attribute :vector_clock, :vclock
37
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
38
  # @return [Set<Link>] a Set of {Riak::Link} objects for relationships between this object and other resources
42
39
  attr_accessor :links
43
40
 
@@ -50,6 +47,10 @@ module Riak
50
47
  # @return [Hash] a hash of any X-Riak-Meta-* headers that were in the HTTP response, keyed on the trailing portion
51
48
  attr_accessor :meta
52
49
 
50
+ # @return [Boolean] whether to attempt to prevent stale writes using conditional PUT semantics, If-None-Match: * or If-Match: {#etag}
51
+ # @see http://wiki.basho.com/display/RIAK/REST+API#RESTAPI-Storeaneworexistingobjectwithakey Riak Rest API Docs
52
+ attr_accessor :prevent_stale_writes
53
+
53
54
  # Loads a list of RObjects that were emitted from a MapReduce
54
55
  # query.
55
56
  # @param [Client] client A Riak::Client with which the results will be associated
@@ -89,7 +90,7 @@ module Riak
89
90
  end
90
91
  @conflict = response[:code].try(:to_i) == 300 && content_type =~ /multipart\/mixed/
91
92
  @siblings = nil
92
- @data = deserialize(response[:body]) if response[:body].present?
93
+ self.raw_data = response[:body] if response[:body].present?
93
94
  self
94
95
  end
95
96
 
@@ -115,12 +116,49 @@ module Riak
115
116
  self
116
117
  end
117
118
 
119
+ # @return [Object] the unmarshaled form of {#raw_data} stored in riak at this object's key
120
+ def data
121
+ if @raw_data && !@data
122
+ @data = deserialize(@raw_data)
123
+ @raw_data = nil
124
+ end
125
+ @data
126
+ end
127
+
128
+ # @param [Object] unmarshaled form of the data to be stored in riak. Object will be serialized using {#serialize} if a known content_type is used. Setting this overrides values stored with {#raw_data=}
129
+ # @return [Object] the object stored
130
+ def data=(new_data)
131
+ @raw_data = nil
132
+ @data = new_data
133
+ end
134
+
135
+ # @return [String] raw data stored in riak for this object's key
136
+ def raw_data
137
+ if @data && !@raw_data
138
+ @raw_data = serialize(@data)
139
+ @data = nil
140
+ end
141
+ @raw_data
142
+ end
143
+
144
+ # @param [String, IO] the raw data to be stored in riak at this key, will not be marshaled or manipulated prior to storage. Overrides any data stored by {#data=}
145
+ # @return [String] the data stored
146
+ def raw_data=(new_raw_data)
147
+ @data = nil
148
+ @raw_data = new_raw_data
149
+ end
150
+
118
151
  # HTTP header hash that will be sent along when storing the object
119
152
  # @return [Hash] hash of HTTP Headers
120
153
  def store_headers
121
154
  {}.tap do |hash|
122
155
  hash["Content-Type"] = @content_type
123
156
  hash["X-Riak-Vclock"] = @vclock if @vclock
157
+ if @prevent_stale_writes && @etag.present?
158
+ hash["If-Match"] = @etag
159
+ elsif @prevent_stale_writes
160
+ hash["If-None-Match"] = "*"
161
+ end
124
162
  unless @links.blank?
125
163
  hash["Link"] = @links.reject {|l| l.rel == "up" }.map(&:to_s).join(", ")
126
164
  end
@@ -153,7 +191,7 @@ module Riak
153
191
  raise ArgumentError, t("content_type_undefined") unless @content_type.present?
154
192
  params = {:returnbody => true}.merge(options)
155
193
  method, codes, path = @key.present? ? [:put, [200,204,300], "#{escape(@bucket.name)}/#{escape(@key)}"] : [:post, 201, escape(@bucket.name)]
156
- response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, serialize(data), store_headers)
194
+ response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, raw_data, store_headers)
157
195
  load(response)
158
196
  end
159
197
 
@@ -0,0 +1,231 @@
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 'tempfile'
16
+ require 'expect'
17
+ require 'open3'
18
+ require 'riak/util/tcp_socket_extensions'
19
+
20
+ module Riak
21
+ class TestServer
22
+ APP_CONFIG_DEFAULTS = {
23
+ :riak_core => {
24
+ :web_ip => "127.0.0.1",
25
+ :web_port => 9000,
26
+ :handoff_port => 9001,
27
+ :ring_creation_size => 64
28
+ },
29
+ :riak_kv => {
30
+ :storage_backend => :riak_kv_test_backend,
31
+ :pb_ip => "127.0.0.1",
32
+ :pb_port => 9002,
33
+ :js_vm_count => 8,
34
+ :js_max_vm_mem => 8,
35
+ :js_thread_stack => 16,
36
+ :riak_kv_stat => true
37
+ },
38
+ :luwak => {
39
+ :enabled => false
40
+ }
41
+ }
42
+ VM_ARGS_DEFAULTS = {
43
+ "-name" => "riaktest#{rand(1000000).to_s}@127.0.0.1",
44
+ "-setcookie" => "#{rand(1000000).to_s}_#{rand(1000000).to_s}",
45
+ "+K" => true,
46
+ "+A" => 64,
47
+ "-smp" => "enable",
48
+ "-env ERL_MAX_PORTS" => 4096,
49
+ "-env ERL_FULLSWEEP_AFTER" => 10,
50
+ "-pa" => File.expand_path("../../../erl_src", __FILE__)
51
+ }
52
+ DEFAULTS = {
53
+ :app_config => APP_CONFIG_DEFAULTS,
54
+ :vm_args => VM_ARGS_DEFAULTS,
55
+ :temp_dir => File.join(Dir.tmpdir,'riaktest'),
56
+ }
57
+ attr_accessor :temp_dir, :app_config, :vm_args, :cin, :cout, :cerr, :cpid
58
+
59
+ def initialize(options={})
60
+ options = deep_merge(DEFAULTS.dup, options)
61
+ @temp_dir = File.expand_path(options[:temp_dir])
62
+ @bin_dir = File.expand_path(options[:bin_dir])
63
+ options[:app_config][:riak_core][:ring_state_dir] ||= File.join(@temp_dir, "data", "ring")
64
+ @app_config = options[:app_config]
65
+ @vm_args = options[:vm_args]
66
+ # For synchronizing start/stop/recycle
67
+ @mutex = Mutex.new
68
+ end
69
+
70
+ # Sets up the proper scripts, configuration and directories for
71
+ # the test server. Call at the top of your test suite (not in a
72
+ # setup method).
73
+ def prepare!
74
+ unless @prepared
75
+ create_temp_directories
76
+ @riak_script = File.join(@temp_bin, 'riak')
77
+ write_riak_script
78
+ write_vm_args
79
+ write_app_config
80
+ @prepared = true
81
+ end
82
+ end
83
+
84
+ # Starts the test server if it is not already running, and waits
85
+ # for it to respond to pings.
86
+ def start
87
+ if @prepared && !@started
88
+ @mutex.synchronize do
89
+ @cin, @cout, @cerr, @cpid = Open3.popen3("#{@riak_script} console")
90
+ @cin.puts
91
+ wait_for_erlang_prompt
92
+ @started = true
93
+ end
94
+ end
95
+ end
96
+
97
+ # Stops the test server if it is running.
98
+ def stop
99
+ if @started
100
+ @mutex.synchronize do
101
+ begin
102
+ @cin.puts "init:stop()."
103
+ rescue Errno::EPIPE
104
+ ensure
105
+ register_stop
106
+ end
107
+ end
108
+ true
109
+ end
110
+ end
111
+
112
+ # Whether the server has been started.
113
+ def started?
114
+ @started
115
+ end
116
+
117
+ # Causes the entire contents of the Riak in-memory backends to be
118
+ # dumped by performing a soft restart.
119
+ def recycle
120
+ if @started
121
+ @mutex.synchronize do
122
+ begin
123
+ if @app_config[:riak_kv][:storage_backend] == :riak_kv_test_backend
124
+ @cin.puts "riak_kv_test_backend:reset()."
125
+ wait_for_erlang_prompt
126
+ else
127
+ @cin.puts "init:restart()."
128
+ wait_for_erlang_prompt
129
+ wait_for_startup
130
+ end
131
+ rescue Errno::EPIPE
132
+ warn "Broken pipe when recycling, is Riak alive?"
133
+ register_stop
134
+ return false
135
+ end
136
+ end
137
+ true
138
+ else
139
+ start
140
+ end
141
+ end
142
+
143
+ # Cleans up any files and directories generated by the test
144
+ # server.
145
+ def cleanup
146
+ stop if @started
147
+ FileUtils.rm_rf(@temp_dir)
148
+ @prepared = false
149
+ end
150
+
151
+ private
152
+ def create_temp_directories
153
+ %w{bin etc log data pipe}.each do |dir|
154
+ instance_variable_set("@temp_#{dir}", File.expand_path(File.join(@temp_dir, dir)))
155
+ FileUtils.mkdir_p(instance_variable_get("@temp_#{dir}"))
156
+ end
157
+ end
158
+
159
+ def write_riak_script
160
+ File.open(@riak_script, 'wb') do |f|
161
+ File.readlines(File.join(@bin_dir, 'riak')).each do |line|
162
+ line.sub!(/(RUNNER_SCRIPT_DIR=)(.*)/, '\1' + @temp_bin)
163
+ line.sub!(/(RUNNER_ETC_DIR=)(.*)/, '\1' + @temp_etc)
164
+ line.sub!(/(RUNNER_USER=)(.*)/, '\1')
165
+ line.sub!(/(RUNNER_LOG_DIR=)(.*)/, '\1' + @temp_log)
166
+ line.sub!(/(PIPE_DIR=)(.*)/, '\1' + @temp_pipe)
167
+ if line.strip == "RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}"
168
+ line = "RUNNER_BASE_DIR=#{File.expand_path("..",@bin_dir)}\n"
169
+ end
170
+ f.write line
171
+ end
172
+ end
173
+ FileUtils.chmod(0755,@riak_script)
174
+ end
175
+
176
+ def write_vm_args
177
+ File.open(File.join(@temp_etc, 'vm.args'), 'wb') do |f|
178
+ f.write @vm_args.map {|k,v| "#{k} #{v}" }.join("\n")
179
+ end
180
+ end
181
+
182
+ def write_app_config
183
+ File.open(File.join(@temp_etc, 'app.config'), 'wb') do |f|
184
+ f.write to_erlang_config(@app_config) + '.'
185
+ end
186
+ end
187
+
188
+ def deep_merge(source, target)
189
+ source.merge(target) do |key, old_val, new_val|
190
+ if Hash === old_val && Hash === new_val
191
+ deep_merge(old_val, new_val)
192
+ else
193
+ new_val
194
+ end
195
+ end
196
+ end
197
+
198
+ def to_erlang_config(hash, depth = 1)
199
+ padding = ' ' * depth
200
+ parent_padding = ' ' * (depth-1)
201
+ values = hash.map do |k,v|
202
+ printable = case v
203
+ when Hash
204
+ to_erlang_config(v, depth+1)
205
+ when String
206
+ "\"#{v}\""
207
+ else
208
+ v.to_s
209
+ end
210
+ "{#{k}, #{printable}}"
211
+ end.join(",\n#{padding}")
212
+ "[\n#{padding}#{values}\n#{parent_padding}]"
213
+ end
214
+
215
+ def wait_for_startup
216
+ TCPSocket.wait_for_service_with_timeout(:host => @app_config[:riak_core][:web_ip],
217
+ :port => @app_config[:riak_core][:web_port], :timeout => 10)
218
+ end
219
+
220
+ def wait_for_erlang_prompt
221
+ @cout.expect(/\(#{Regexp.escape(vm_args["-name"])}\)\d+>/)
222
+ end
223
+
224
+ def register_stop
225
+ %w{@cin @cout @cerr}.each {|io| if instance_variable_get(io); instance_variable_get(io).close; instance_variable_set(io, nil) end }
226
+ _cpid = @cpid; @cpid = nil
227
+ at_exit { _cpid.join if _cpid.alive? }
228
+ @started = false
229
+ end
230
+ end
231
+ end
@@ -21,7 +21,7 @@ module Net::HTTPHeader
21
21
  respond_to?(:enum_for) and (block_given? or return enum_for(__method__))
22
22
  @header.each do |k,v|
23
23
  base_length = "#{k}: \r\n".length
24
- values = v.map {|i| i.split(", ") }.flatten
24
+ values = v.map {|i| i.to_s.split(", ") }.flatten
25
25
  while !values.empty?
26
26
  current_line = ""
27
27
  while values.first && current_line.length + base_length + values.first.length + 2 < 8192
@@ -0,0 +1,70 @@
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 'timeout'
15
+ require 'socket'
16
+
17
+ # Borrowed from Webrat and Selenium client, watches for TCP port
18
+ # liveness of the spawned server.
19
+ # @private
20
+ class TCPSocket
21
+ def self.wait_for_service(options)
22
+ verbose_wait until listening_service?(options)
23
+ end
24
+
25
+ def self.wait_for_service_termination(options)
26
+ verbose_wait while listening_service?(options)
27
+ end
28
+
29
+ def self.listening_service?(options)
30
+ Timeout::timeout(options[:timeout] || 20) do
31
+ begin
32
+ socket = TCPSocket.new(options[:host], options[:port])
33
+ socket.close unless socket.nil?
34
+ true
35
+ rescue Errno::ECONNREFUSED,
36
+ Errno::EBADF # Windows
37
+ false
38
+ end
39
+ end
40
+ end
41
+
42
+ def self.verbose_wait
43
+ # Removed the puts call so as not to clutter up test output.
44
+ sleep 2
45
+ end
46
+
47
+ def self.wait_for_service_with_timeout(options)
48
+ start_time = Time.now
49
+
50
+ until listening_service?(options)
51
+ verbose_wait
52
+
53
+ if options[:timeout] && (Time.now > start_time + options[:timeout])
54
+ raise SocketError.new("Socket did not open within #{options[:timeout]} seconds")
55
+ end
56
+ end
57
+ end
58
+
59
+ def self.wait_for_service_termination_with_timeout(options)
60
+ start_time = Time.now
61
+
62
+ while listening_service?(options)
63
+ verbose_wait
64
+
65
+ if options[:timeout] && (Time.now > start_time + options[:timeout])
66
+ raise SocketError.new("Socket did not terminate within #{options[:timeout]} seconds")
67
+ end
68
+ end
69
+ end
70
+ end
@@ -14,15 +14,30 @@
14
14
  require File.expand_path("../../spec_helper", File.dirname(__FILE__))
15
15
 
16
16
  describe Riak::CacheStore do
17
+ before :all do
18
+ if $test_server
19
+ @web_port = 9000
20
+ $test_server.start
21
+ end
22
+ end
23
+
17
24
  before do
18
- @cache = ActiveSupport::Cache.lookup_store(:riak_store)
25
+ @web_port ||= 8098
26
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :port => @web_port)
19
27
  @cleanup = true
20
28
  end
21
29
 
22
30
  after do
23
- @cache.bucket.keys(:force => true).each do |k|
24
- @cache.bucket.delete(k, :rw => 1) unless k.blank?
25
- end if @cleanup
31
+ if @cleanup
32
+ if $test_server
33
+ $test_server.recycle
34
+ Thread.current[:curl_easy_handle] = nil
35
+ else
36
+ @cache.bucket.keys(:force => true).each do |k|
37
+ @cache.bucket.delete(k, :rw => 1) unless k.blank?
38
+ end
39
+ end
40
+ end
26
41
  end
27
42
 
28
43
  describe "Riak integration" do
@@ -45,7 +60,7 @@ describe Riak::CacheStore do
45
60
  end
46
61
 
47
62
  it "should choose the bucket according to the initializer option" do
48
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :bucket => "foobar")
63
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :bucket => "foobar", :port => @web_port)
49
64
  @cache.bucket.name.should == "foobar"
50
65
  end
51
66
 
@@ -54,7 +69,7 @@ describe Riak::CacheStore do
54
69
  end
55
70
 
56
71
  it "should set the N value to the specified value" do
57
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :n_value => 1)
72
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :n_value => 1, :port => @web_port)
58
73
  @cache.bucket.n_value.should == 1
59
74
  end
60
75
 
@@ -63,7 +78,7 @@ describe Riak::CacheStore do
63
78
  end
64
79
 
65
80
  it "should set the bucket R default to the specified value" do
66
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :r => "quorum")
81
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :r => "quorum", :port => @web_port)
67
82
  @cache.bucket.r.should == "quorum"
68
83
  end
69
84
 
@@ -72,7 +87,7 @@ describe Riak::CacheStore do
72
87
  end
73
88
 
74
89
  it "should set the bucket W default to the specified value" do
75
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :w => "all")
90
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :w => "all", :port => @web_port)
76
91
  @cache.bucket.w.should == "all"
77
92
  end
78
93
 
@@ -81,7 +96,7 @@ describe Riak::CacheStore do
81
96
  end
82
97
 
83
98
  it "should set the bucket DW default to the specified value" do
84
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :dw => "quorum")
99
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :dw => "quorum", :port => @web_port)
85
100
  @cache.bucket.dw.should == "quorum"
86
101
  end
87
102
 
@@ -90,7 +105,7 @@ describe Riak::CacheStore do
90
105
  end
91
106
 
92
107
  it "should set the bucket RW default to the specified value" do
93
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :rw => "all")
108
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :rw => "all", :port => @web_port)
94
109
  @cache.bucket.rw.should == "all"
95
110
  end
96
111
  end
@@ -0,0 +1,174 @@
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 File.expand_path("../../spec_helper", File.dirname(__FILE__))
15
+
16
+ if $test_server
17
+ describe Riak::TestServer do
18
+ before do
19
+ @server = $test_server
20
+ end
21
+
22
+ after do
23
+ @server.stop
24
+ @server.cleanup
25
+ end
26
+
27
+ describe "isolation from and modification of the existing install" do
28
+ before do
29
+ @server.prepare!
30
+ @riak_bin = "#{@server.temp_dir}/bin/riak"
31
+ @vm_args = "#{@server.temp_dir}/etc/vm.args"
32
+ @app_config = "#{@server.temp_dir}/etc/app.config"
33
+ end
34
+
35
+ describe "for app.config" do
36
+ it "should create the app.config file in the temporary directory" do
37
+ File.should be_exist(File.expand_path(@app_config))
38
+ end
39
+
40
+ it "should be a correct Erlang config" do
41
+ config = File.read(@app_config)
42
+ config[-2..-1].should == '].'
43
+ config[0..0].should == '['
44
+ end
45
+
46
+ it "should set the backend to use the cache backend" do
47
+ File.readlines(@app_config).should be_any do |line|
48
+ line =~ /\{storage_backend\s*,\s*(.*)\}/ && $1 == "riak_kv_cache_backend"
49
+ end
50
+ end
51
+
52
+ it "should set the default ports to 9000-9002" do
53
+ config = File.readlines(@app_config)
54
+ config.should be_any do |line|
55
+ line =~ /\{web_port\s*,\s*(.*)\}/ && $1 == "9000"
56
+ end
57
+ config.should be_any do |line|
58
+ line =~ /\{handoff_port\s*,\s*(.*)\}/ && $1 == "9001"
59
+ end
60
+ config.should be_any do |line|
61
+ line =~ /\{pb_port\s*,\s*(.*)\}/ && $1 == "9002"
62
+ end
63
+ end
64
+
65
+ it "should set the ring directory to point to the temporary directory" do
66
+ config = File.readlines(@app_config)
67
+ config.should be_any do |line|
68
+ line =~ /\{ring_state_dir\s*,\s*(.*)\}/ && $1 == File.join(@server.temp_dir, "data", "ring")
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "for vm.args" do
74
+ it "should create the vm.args file in the temporary directory" do
75
+ File.should be_exist(File.expand_path(@vm_args))
76
+ end
77
+
78
+ it "should set a quasi-random node name" do
79
+ File.readlines(@vm_args).should be_any do |line|
80
+ line =~ /^-name (.*)/ && $1 =~ /riaktest\d+@/
81
+ end
82
+ end
83
+
84
+ it "should set a quasi-random cookie" do
85
+ File.readlines(@vm_args).should be_any do |line|
86
+ line =~ /^-setcookie (.*)/ && $1 != "riak"
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "for the riak script" do
92
+ it "should create the script in the temporary directory" do
93
+ File.should be_exist(File.expand_path(@riak_bin))
94
+ end
95
+
96
+ it "should modify the RUNNER_SCRIPT_DIR to point to the temporary directory" do
97
+ File.readlines(@riak_bin).should be_any do |line|
98
+ line =~ /RUNNER_SCRIPT_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/bin")
99
+
100
+ end
101
+ end
102
+
103
+ it "should modify the RUNNER_ETC_DIR to point to the temporary directory" do
104
+ File.readlines(@riak_bin).should be_any do |line|
105
+ line =~ /RUNNER_ETC_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/etc")
106
+ end
107
+ end
108
+
109
+ it "should modify the RUNNER_USER to point to the current user" do
110
+ File.readlines(@riak_bin).should be_any do |line|
111
+ line =~ /RUNNER_USER=(.*)/ && $1 == (ENV['USER'] || `whoami`)
112
+ end
113
+ end
114
+
115
+ it "should modify the RUNNER_LOG_DIR to point to the temporary directory" do
116
+ File.readlines(@riak_bin).should be_any do |line|
117
+ line =~ /RUNNER_LOG_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/log")
118
+ end
119
+ end
120
+
121
+ it "should modify the RUNNER_BASE_DIR so that it is not relative" do
122
+ File.readlines(@riak_bin).should be_any do |line|
123
+ line =~ /RUNNER_BASE_DIR=(.*)/ && $1.strip != "${RUNNER_SCRIPT_DIR%/*}" && File.directory?($1)
124
+ end
125
+ end
126
+
127
+ it "should modify the PIPE_DIR to point to the temporary directory" do
128
+ File.readlines(@riak_bin).should be_any do |line|
129
+ line =~ /PIPE_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/pipe") && File.directory?($1)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ it "should cleanup the existing config" do
136
+ @server.prepare!
137
+ @server.cleanup
138
+ File.should_not be_directory(@server.temp_dir)
139
+ end
140
+
141
+ it "should start Riak in the background" do
142
+ @server.prepare!
143
+ @server.start.should be_true
144
+ @server.should be_started
145
+ end
146
+
147
+ it "should stop a started test server" do
148
+ @server.prepare!
149
+ @server.start.should be_true
150
+ @server.stop
151
+ @server.should_not be_started
152
+ end
153
+
154
+ it "should recycle the server contents" do
155
+ begin
156
+ @server.prepare!
157
+ @server.start.should be_true
158
+
159
+ client = Riak::Client.new(:port => 9000)
160
+ obj = client['test_bucket'].new("test_item")
161
+ obj.data = {"data" => "testing"}
162
+ obj.store rescue nil
163
+
164
+ @server.recycle
165
+ @server.should be_started
166
+ lambda do
167
+ client['test_bucket']['test_item']
168
+ end.should raise_error(Riak::FailedRequest)
169
+ ensure
170
+ @server.stop
171
+ end
172
+ end
173
+ end
174
+ end
@@ -118,6 +118,64 @@ describe Riak::RObject do
118
118
  end
119
119
  end
120
120
 
121
+ describe "data access methods" do
122
+ before :each do
123
+ @object = Riak::RObject.new(@bucket, "bar")
124
+ @object.content_type = "application/json"
125
+ end
126
+
127
+ describe "for raw data" do
128
+ describe "when unserialized data was already provided" do
129
+ before do
130
+ @object.data = { :some => :data }
131
+ end
132
+
133
+ it "should reset unserialized forms when stored" do
134
+ @object.raw_data = value = '{ "raw": "json" }'
135
+
136
+ @object.raw_data.should == value
137
+ @object.data.should == { "raw" => "json" }
138
+ end
139
+
140
+ it "should lazily serialize when read" do
141
+ @object.raw_data.should == '{"some":"data"}'
142
+ end
143
+ end
144
+
145
+ it "should not unnecessarily marshal/demarshal" do
146
+ @object.should_not_receive(:serialize)
147
+ @object.should_not_receive(:deserialize)
148
+ @object.raw_data = value = "{not even valid json!}}"
149
+ @object.raw_data.should == value
150
+ end
151
+ end
152
+
153
+ describe "for unserialized data" do
154
+ describe "when raw data was already provided" do
155
+ before do
156
+ @object.raw_data = '{"some":"data"}'
157
+ end
158
+
159
+ it "should reset previously stored raw data" do
160
+ @object.data = value = { "new" => "data" }
161
+ @object.raw_data.should == '{"new":"data"}'
162
+ @object.data.should == value
163
+ end
164
+
165
+ it "should lazily deserialize when read" do
166
+ @object.data.should == { "some" => "data" }
167
+ end
168
+ end
169
+
170
+ it "should not unnecessarily marshal/demarshal" do
171
+ @object.should_not_receive(:serialize)
172
+ @object.should_not_receive(:deserialize)
173
+ @object.data = value = { "some" => "data" }
174
+ @object.data.should == value
175
+ end
176
+ end
177
+ end
178
+
121
179
  describe "loading data from the response" do
122
180
  before :each do
123
181
  @object = Riak::RObject.new(@bucket, "bar")
@@ -130,9 +188,16 @@ describe Riak::RObject do
130
188
 
131
189
  it "should load the body data" do
132
190
  @object.load({:headers => {"content-type" => ["application/json"]}, :body => '{"foo":"bar"}'})
191
+ @object.raw_data.should be_present
133
192
  @object.data.should be_present
134
193
  end
135
194
 
195
+ it "should handle raw data properly" do
196
+ @object.should_not_receive(:deserialize) # optimize for the raw_data case, don't penalize people for using raw_data
197
+ @object.load({:headers => {"content-type" => ["application/json"]}, :body => body = '{"foo":"bar"}'})
198
+ @object.raw_data.should == body
199
+ end
200
+
136
201
  it "should deserialize the body data" do
137
202
  @object.should_receive(:deserialize).with("{}").and_return({})
138
203
  @object.load({:headers => {"content-type" => ["application/json"]}, :body => "{}"})
@@ -335,6 +400,23 @@ describe Riak::RObject do
335
400
  @object.store_headers.should_not have_key("X-Riak-Vclock")
336
401
  end
337
402
 
403
+ describe "when conditional PUTs are requested" do
404
+ before :each do
405
+ @object.prevent_stale_writes = true
406
+ end
407
+
408
+ it "should include an If-None-Match: * header" do
409
+ @object.store_headers.should have_key("If-None-Match")
410
+ @object.store_headers["If-None-Match"].should == "*"
411
+ end
412
+
413
+ it "should include an If-Match header with the etag when an etag is present" do
414
+ @object.etag = "foobar"
415
+ @object.store_headers.should have_key("If-Match")
416
+ @object.store_headers["If-Match"].should == @object.etag
417
+ end
418
+ end
419
+
338
420
  describe "when links are defined" do
339
421
  before :each do
340
422
  @object.links << Riak::Link.new("/riak/foo/baz", "next")
@@ -447,6 +529,22 @@ describe Riak::RObject do
447
529
  @object.key = "bar"
448
530
  end
449
531
 
532
+ describe "when the content is of a known serializable type" do
533
+ before :each do
534
+ @object.content_type = "application/json"
535
+ @headers = @object.store_headers
536
+ end
537
+
538
+ it "should not serialize content if #raw_data is used" do
539
+ storing = @object.raw_data = "{this is probably invalid json}}"
540
+ @http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, storing, @headers).and_return({:headers => {"x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
541
+ @object.should_not_receive(:serialize)
542
+ @object.should_not_receive(:deserialize)
543
+ @object.store
544
+ @object.raw_data.should == storing
545
+ end
546
+ end
547
+
450
548
  it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
451
549
  @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})
452
550
  @object.store
@@ -16,14 +16,26 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
16
 
17
17
  require 'rubygems' # Use the gems path only for the spec suite
18
18
  require 'riak'
19
- require 'rspec/autorun'
19
+ require 'rspec'
20
20
  require 'fakeweb'
21
21
 
22
+ begin
23
+ require 'yaml'
24
+ config = YAML.load_file("spec/support/test_server.yml")
25
+ $test_server = Riak::TestServer.new(config.symbolize_keys)
26
+ $test_server.prepare!
27
+ $test_server.start
28
+ at_exit { $test_server.cleanup }
29
+ rescue => e
30
+ warn "Can't run Riak::TestServer specs. Specify the location of your Riak installation in spec/support/test_server.yml. See Riak::TestServer docs for more info."
31
+ warn e.inspect
32
+ end
33
+
22
34
  Dir[File.join(File.dirname(__FILE__), "support", "*.rb")].each {|f| require f }
23
35
 
24
36
  Rspec.configure do |config|
25
37
  config.mock_with :rspec
26
-
38
+
27
39
  config.before(:each) do
28
40
  FakeWeb.clean_registry
29
41
  end
@@ -0,0 +1,2 @@
1
+ bin_dir: /Users/sean/Development/riak/rel/riak/bin
2
+ temp_dir: /Users/sean/Development/ripple/.riaktest
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 8
8
- - 0
9
- version: 0.8.0
8
+ - 1
9
+ version: 0.8.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Sean Cribbs
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-31 00:00:00 -04:00
17
+ date: 2010-10-11 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -115,6 +115,13 @@ extensions: []
115
115
  extra_rdoc_files: []
116
116
 
117
117
  files:
118
+ - bin/htmldiff
119
+ - bin/ldiff
120
+ - bin/rackup
121
+ - bin/rake
122
+ - bin/rspec
123
+ - erl_src/riak_kv_test_backend.beam
124
+ - erl_src/riak_kv_test_backend.erl
118
125
  - Gemfile
119
126
  - Gemfile.lock
120
127
  - lib/active_support/cache/riak_store.rb
@@ -132,10 +139,12 @@ files:
132
139
  - lib/riak/map_reduce.rb
133
140
  - lib/riak/map_reduce_error.rb
134
141
  - lib/riak/robject.rb
142
+ - lib/riak/test_server.rb
135
143
  - lib/riak/util/escape.rb
136
144
  - lib/riak/util/fiber1.8.rb
137
145
  - lib/riak/util/headers.rb
138
146
  - lib/riak/util/multipart.rb
147
+ - lib/riak/util/tcp_socket_extensions.rb
139
148
  - lib/riak/util/translation.rb
140
149
  - lib/riak/walk_spec.rb
141
150
  - lib/riak.rb
@@ -144,6 +153,7 @@ files:
144
153
  - spec/fixtures/multipart-blank.txt
145
154
  - spec/fixtures/multipart-with-body.txt
146
155
  - spec/integration/riak/cache_store_spec.rb
156
+ - spec/integration/riak/test_server_spec.rb
147
157
  - spec/riak/bucket_spec.rb
148
158
  - spec/riak/client_spec.rb
149
159
  - spec/riak/curb_backend_spec.rb
@@ -160,6 +170,7 @@ files:
160
170
  - spec/support/http_backend_implementation_examples.rb
161
171
  - spec/support/mock_server.rb
162
172
  - spec/support/mocks.rb
173
+ - spec/support/test_server.yml
163
174
  has_rdoc: true
164
175
  homepage: http://seancribbs.github.com/ripple
165
176
  licenses: []
@@ -194,6 +205,7 @@ specification_version: 3
194
205
  summary: riak-client is a rich client for Riak, the distributed database by Basho.
195
206
  test_files:
196
207
  - spec/integration/riak/cache_store_spec.rb
208
+ - spec/integration/riak/test_server_spec.rb
197
209
  - spec/riak/bucket_spec.rb
198
210
  - spec/riak/client_spec.rb
199
211
  - spec/riak/curb_backend_spec.rb