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,3 @@
1
+ require 'riak'
2
+
3
+ warn Riak.t('deprecated.search', :backtrace => " "+caller.join("\n "))
@@ -0,0 +1,74 @@
1
+ module Riak
2
+ module Serializers
3
+ include Util::Translation
4
+ extend self
5
+
6
+ def [](content_type)
7
+ serializers[content_type]
8
+ end
9
+
10
+ def []=(content_type, serializer)
11
+ serializers[content_type] = serializer
12
+ end
13
+
14
+ def serialize(content_type, content)
15
+ serializer_for(content_type).dump(content)
16
+ end
17
+
18
+ def deserialize(content_type, content)
19
+ serializer_for(content_type).load(content)
20
+ end
21
+
22
+ private
23
+
24
+ def serializer_for(content_type)
25
+ serializers.fetch(content_type) do
26
+ raise NotImplementedError.new(t('serializer_not_implemented', :content_type => content_type.inspect))
27
+ end
28
+ end
29
+
30
+ def serializers
31
+ @serializers ||= {}
32
+ end
33
+
34
+ module TextPlain
35
+ extend self
36
+
37
+ def dump(object)
38
+ object.to_s
39
+ end
40
+
41
+ def load(string)
42
+ string
43
+ end
44
+ end
45
+
46
+ module ApplicationJSON
47
+ extend self
48
+
49
+ def dump(object)
50
+ object.to_json(Riak.json_options)
51
+ end
52
+
53
+ def load(string)
54
+ Riak::JSON.parse(string)
55
+ end
56
+ end
57
+
58
+ Serializers['text/plain'] = TextPlain
59
+ Serializers['application/json'] = ApplicationJSON
60
+ Serializers['application/x-ruby-marshal'] = ::Marshal
61
+
62
+ YAML_MIME_TYPES = %w[
63
+ text/yaml
64
+ text/x-yaml
65
+ application/yaml
66
+ application/x-yaml
67
+ ]
68
+
69
+ YAML_MIME_TYPES.each do |mime_type|
70
+ Serializers[mime_type] = ::YAML
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,77 @@
1
+ require 'riak/client'
2
+ require 'riak/util/translation'
3
+ require 'thread'
4
+
5
+ module Riak
6
+ # Implements a client-side form of monotonically-increasing k-sorted
7
+ # unique identifiers. These are useful for key generation if your
8
+ # data is time-sequential and needs to be sorted by key, perhaps in
9
+ # Riak Search. Inspired by Twitter's Snowflake project.
10
+ class Stamp
11
+ attr_reader :client
12
+
13
+ CLIENT_ID_MASK = (1 << 10) - 1
14
+ SEQUENCE_MASK = (1 << 12) - 1
15
+ TIMESTAMP_MASK = (1 << 41) - 1
16
+ SEQUENCE_SHIFT = 10
17
+ TIMESTAMP_SHIFT = 22
18
+
19
+ # @param [Client] client a {Riak::Client} which will be used for
20
+ # the "worker ID" component of the stamp.
21
+ # @see Client#stamp
22
+ def initialize(client)
23
+ @client = client
24
+ @mutex = Mutex.new
25
+ @timestamp = time_gen
26
+ @sequence = 0
27
+ end
28
+
29
+ # Generates a k-sorted unique ID for use as a key or other
30
+ # disambiguation purposes.
31
+ def next
32
+ @mutex.synchronize do
33
+ now = time_gen
34
+ if @timestamp == now
35
+ @sequence = (@sequence + 1) & SEQUENCE_MASK
36
+ now = wait_for_next_ms(@timestamp) if @sequence == 0
37
+ else
38
+ @sequence = 0
39
+ end
40
+
41
+ raise BackwardsClockError.new(@timestamp - now) if now < @timestamp
42
+
43
+ @timestamp = now
44
+ @timestamp << TIMESTAMP_SHIFT | @sequence << SEQUENCE_SHIFT | client_id
45
+ end
46
+ end
47
+
48
+ private
49
+ def client_id
50
+ case id = @client.client_id
51
+ when Integer
52
+ id & CLIENT_ID_MASK
53
+ else
54
+ id.hash & CLIENT_ID_MASK
55
+ end
56
+ end
57
+
58
+ def time_gen
59
+ (Time.now.to_f * 1000).floor & TIMESTAMP_MASK
60
+ end
61
+
62
+ def wait_for_next_ms(start)
63
+ now = time_gen
64
+ now = time_gen while now <= start
65
+ now
66
+ end
67
+ end
68
+
69
+ # Raised when calling {Stamp#next} and NTP or some other external
70
+ # event has moved the system clock backwards.
71
+ class BackwardsClockError < StandardError
72
+ include Util::Translation
73
+ def initialize(delay)
74
+ super t('backwards_clock', :delay => delay)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,252 @@
1
+
2
+ require 'tempfile'
3
+ require 'expect'
4
+
5
+ if ENV['DEBUG_RIAK_TEST_SERVER']
6
+ $expect_verbose = true
7
+ end
8
+
9
+ require 'open3'
10
+ require 'riak/util/tcp_socket_extensions'
11
+
12
+ module Riak
13
+ class TestServer
14
+ APP_CONFIG_DEFAULTS = {
15
+ :riak_core => {
16
+ :web_ip => "127.0.0.1",
17
+ :web_port => 9000,
18
+ :handoff_port => 9001,
19
+ :ring_creation_size => 64
20
+ },
21
+ :riak_kv => {
22
+ :storage_backend => :riak_kv_test_backend,
23
+ :pb_ip => "127.0.0.1",
24
+ :pb_port => 9002,
25
+ :js_vm_count => 8,
26
+ :js_max_vm_mem => 8,
27
+ :js_thread_stack => 16,
28
+ :riak_kv_stat => true,
29
+ # Turn off map caching
30
+ :map_cache_size => 0, # 0.14
31
+ :vnode_cache_entries => 0 # 0.13
32
+ },
33
+ :riak_search => {
34
+ :enabled => true,
35
+ :search_backend => :riak_search_test_backend
36
+ },
37
+ :luwak => {
38
+ :enabled => true
39
+ }
40
+ }
41
+ VM_ARGS_DEFAULTS = {
42
+ "-name" => "riaktest#{rand(1000000).to_s}@127.0.0.1",
43
+ "-setcookie" => "#{rand(1000000).to_s}_#{rand(1000000).to_s}",
44
+ "+K" => true,
45
+ "+A" => 64,
46
+ "-smp" => "enable",
47
+ "-env ERL_MAX_PORTS" => 4096,
48
+ "-env ERL_FULLSWEEP_AFTER" => 10,
49
+ "-pa" => File.expand_path("../../../erl_src", __FILE__)
50
+ }
51
+ DEFAULTS = {
52
+ :app_config => APP_CONFIG_DEFAULTS,
53
+ :vm_args => VM_ARGS_DEFAULTS,
54
+ :temp_dir => File.join(Dir.tmpdir,'riaktest'),
55
+ }
56
+ attr_accessor :temp_dir, :app_config, :vm_args, :cin, :cout, :cerr, :cpid
57
+
58
+ def initialize(options={})
59
+ options = deep_merge(DEFAULTS.dup, options)
60
+ @temp_dir = File.expand_path(options[:temp_dir])
61
+ @bin_dir = File.expand_path(options[:bin_dir])
62
+ options[:app_config][:riak_core][:ring_state_dir] ||= File.join(@temp_dir, "data", "ring")
63
+ @app_config = options[:app_config]
64
+ @vm_args = options[:vm_args]
65
+ # For synchronizing start/stop/recycle
66
+ @mutex = Mutex.new
67
+ cleanup # Should prevent some errors related to unclean startup
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, bin_filename)
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
+ @cin.flush
92
+ wait_for_erlang_prompt
93
+ @started = true
94
+ end
95
+ end
96
+ end
97
+
98
+ # Stops the test server if it is running.
99
+ def stop
100
+ if @started
101
+ @mutex.synchronize do
102
+ begin
103
+ @cin.puts "init:stop()."
104
+ @cin.flush
105
+ rescue Errno::EPIPE
106
+ ensure
107
+ register_stop
108
+ end
109
+ end
110
+ true
111
+ end
112
+ end
113
+
114
+ # Whether the server has been started.
115
+ def started?
116
+ @started
117
+ end
118
+
119
+ # Causes the entire contents of the Riak in-memory backends to be
120
+ # dumped by performing a soft restart.
121
+ def recycle
122
+ if @started
123
+ @mutex.synchronize do
124
+ begin
125
+ if @app_config[:riak_kv][:storage_backend] == :riak_kv_test_backend
126
+ @cin.puts "riak_kv_test_backend:reset()."
127
+ @cin.flush
128
+ wait_for_erlang_prompt
129
+ if search_enabled?
130
+ @cin.puts "riak_search_test_backend:reset()."
131
+ @cin.flush
132
+ wait_for_erlang_prompt
133
+ end
134
+ else
135
+ @cin.puts "init:restart()."
136
+ @cin.flush
137
+ wait_for_erlang_prompt
138
+ wait_for_startup
139
+ end
140
+ rescue Errno::EPIPE
141
+ warn "Broken pipe when recycling, is Riak alive?"
142
+ register_stop
143
+ return false
144
+ end
145
+ end
146
+ true
147
+ else
148
+ start
149
+ end
150
+ end
151
+
152
+ # Cleans up any files and directories generated by the test
153
+ # server.
154
+ def cleanup
155
+ stop if @started
156
+ FileUtils.rm_rf(@temp_dir)
157
+ @prepared = false
158
+ end
159
+
160
+ def riak_search?
161
+ File.exists?(File.join(@bin_dir, 'riaksearch'))
162
+ end
163
+
164
+ private
165
+ def create_temp_directories
166
+ %w{bin etc log data pipe}.each do |dir|
167
+ instance_variable_set("@temp_#{dir}", File.expand_path(File.join(@temp_dir, dir)))
168
+ FileUtils.mkdir_p(instance_variable_get("@temp_#{dir}"))
169
+ end
170
+ end
171
+
172
+ def write_riak_script
173
+ File.open(@riak_script, 'wb') do |f|
174
+ File.readlines(File.join(@bin_dir, bin_filename)).each do |line|
175
+ line.sub!(/(RUNNER_SCRIPT_DIR=)(.*)/, '\1' + @temp_bin)
176
+ line.sub!(/(RUNNER_ETC_DIR=)(.*)/, '\1' + @temp_etc)
177
+ line.sub!(/(RUNNER_USER=)(.*)/, '\1')
178
+ line.sub!(/(RUNNER_LOG_DIR=)(.*)/, '\1' + @temp_log)
179
+ line.sub!(/(PIPE_DIR=)(.*)/, '\1' + @temp_pipe)
180
+ if line.strip == "RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}"
181
+ line = "RUNNER_BASE_DIR=#{File.expand_path("..",@bin_dir)}\n"
182
+ end
183
+ f.write line
184
+ end
185
+ end
186
+ FileUtils.chmod(0755,@riak_script)
187
+ end
188
+
189
+ def bin_filename
190
+ riak_search? ? 'riaksearch' : 'riak'
191
+ end
192
+
193
+ def write_vm_args
194
+ File.open(File.join(@temp_etc, 'vm.args'), 'wb') do |f|
195
+ f.write @vm_args.map {|k,v| "#{k} #{v}" }.join("\n")
196
+ end
197
+ end
198
+
199
+ def write_app_config
200
+ File.open(File.join(@temp_etc, 'app.config'), 'wb') do |f|
201
+ f.write to_erlang_config(@app_config) + '.'
202
+ end
203
+ end
204
+
205
+ def deep_merge(source, target)
206
+ source.merge(target) do |key, old_val, new_val|
207
+ if Hash === old_val && Hash === new_val
208
+ deep_merge(old_val, new_val)
209
+ else
210
+ new_val
211
+ end
212
+ end
213
+ end
214
+
215
+ def to_erlang_config(hash, depth = 1)
216
+ padding = ' ' * depth
217
+ parent_padding = ' ' * (depth-1)
218
+ values = hash.map do |k,v|
219
+ printable = case v
220
+ when Hash
221
+ to_erlang_config(v, depth+1)
222
+ when String
223
+ "\"#{v}\""
224
+ else
225
+ v.to_s
226
+ end
227
+ "{#{k}, #{printable}}"
228
+ end.join(",\n#{padding}")
229
+ "[\n#{padding}#{values}\n#{parent_padding}]"
230
+ end
231
+
232
+ def wait_for_startup
233
+ TCPSocket.wait_for_service_with_timeout(:host => @app_config[:riak_core][:web_ip],
234
+ :port => @app_config[:riak_core][:web_port], :timeout => 10)
235
+ end
236
+
237
+ def wait_for_erlang_prompt
238
+ @cout.expect(/\(#{Regexp.escape(vm_args["-name"])}\)\d+>/)
239
+ end
240
+
241
+ def register_stop
242
+ %w{@cin @cout @cerr}.each {|io| if instance_variable_get(io); instance_variable_get(io).close; instance_variable_set(io, nil) end }
243
+ _cpid = @cpid; @cpid = nil
244
+ at_exit { _cpid.join if _cpid && _cpid.alive? }
245
+ @started = false
246
+ end
247
+
248
+ def search_enabled?
249
+ @app_config[:riak_search][:enabled]
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,45 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+
4
+ module Riak
5
+ class << self
6
+ # @see #escaper=
7
+ attr_reader :escaper
8
+
9
+ # Sets the class used for escaping URLs (buckets and keys) sent to
10
+ # Riak. Currently only supports URI and CGI, and defaults to URI.
11
+ # @param [Symbol,String,Class] esc A representation of which
12
+ # escaping class to use, either the Class itself or a String or
13
+ # Symbol name
14
+ # @see Riak::Util::Escape
15
+ def escaper=(esc)
16
+ case esc
17
+ when Symbol, String
18
+ @escaper = ::Object.const_get(esc.to_s.upcase.intern) if esc.to_s =~ /uri|cgi/i
19
+ when Class, Module
20
+ @escaper = esc
21
+ end
22
+ end
23
+ end
24
+
25
+ self.escaper = URI
26
+
27
+ module Util
28
+ # Methods for escaping URL segments.
29
+ module Escape
30
+ # CGI-escapes bucket or key names that may contain slashes for use in URLs.
31
+ # @param [String] bucket_or_key the bucket or key name
32
+ # @return [String] the escaped path segment
33
+ def escape(bucket_or_key)
34
+ Riak.escaper.escape(bucket_or_key.to_s).gsub("+", "%20").gsub('/', "%2F")
35
+ end
36
+
37
+ # CGI-unescapes bucket or key names
38
+ # @param [String] bucket_or_key the bucket or key name
39
+ # @return [String] the unescaped name
40
+ def unescape(bucket_or_key)
41
+ Riak.escaper.unescape(bucket_or_key.to_s)
42
+ end
43
+ end
44
+ end
45
+ end