jmoses-couchbase 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +15 -0
  2. data/.travis.yml +22 -0
  3. data/.yardopts +5 -0
  4. data/CONTRIBUTING.markdown +75 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +201 -0
  7. data/Makefile +3 -0
  8. data/README.markdown +665 -0
  9. data/RELEASE_NOTES.markdown +819 -0
  10. data/Rakefile +20 -0
  11. data/couchbase.gemspec +49 -0
  12. data/examples/chat-em/Gemfile +7 -0
  13. data/examples/chat-em/README.markdown +45 -0
  14. data/examples/chat-em/server.rb +82 -0
  15. data/examples/chat-goliath-grape/Gemfile +5 -0
  16. data/examples/chat-goliath-grape/README.markdown +50 -0
  17. data/examples/chat-goliath-grape/app.rb +67 -0
  18. data/examples/chat-goliath-grape/config/app.rb +20 -0
  19. data/examples/transcoders/Gemfile +3 -0
  20. data/examples/transcoders/README.markdown +59 -0
  21. data/examples/transcoders/cb-zcat +40 -0
  22. data/examples/transcoders/cb-zcp +45 -0
  23. data/examples/transcoders/gzip_transcoder.rb +49 -0
  24. data/examples/transcoders/options.rb +54 -0
  25. data/ext/couchbase_ext/.gitignore +4 -0
  26. data/ext/couchbase_ext/arguments.c +956 -0
  27. data/ext/couchbase_ext/arithmetic.c +316 -0
  28. data/ext/couchbase_ext/bucket.c +1373 -0
  29. data/ext/couchbase_ext/context.c +65 -0
  30. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  31. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  32. data/ext/couchbase_ext/delete.c +163 -0
  33. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  34. data/ext/couchbase_ext/extconf.rb +169 -0
  35. data/ext/couchbase_ext/get.c +316 -0
  36. data/ext/couchbase_ext/gethrtime.c +129 -0
  37. data/ext/couchbase_ext/http.c +432 -0
  38. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  39. data/ext/couchbase_ext/observe.c +171 -0
  40. data/ext/couchbase_ext/plugin_common.c +171 -0
  41. data/ext/couchbase_ext/result.c +129 -0
  42. data/ext/couchbase_ext/stats.c +163 -0
  43. data/ext/couchbase_ext/store.c +542 -0
  44. data/ext/couchbase_ext/timer.c +192 -0
  45. data/ext/couchbase_ext/touch.c +186 -0
  46. data/ext/couchbase_ext/unlock.c +176 -0
  47. data/ext/couchbase_ext/utils.c +551 -0
  48. data/ext/couchbase_ext/version.c +142 -0
  49. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  50. data/lib/active_support/cache/couchbase_store.rb +430 -0
  51. data/lib/couchbase.rb +155 -0
  52. data/lib/couchbase/bucket.rb +457 -0
  53. data/lib/couchbase/cluster.rb +119 -0
  54. data/lib/couchbase/connection_pool.rb +58 -0
  55. data/lib/couchbase/constants.rb +12 -0
  56. data/lib/couchbase/result.rb +26 -0
  57. data/lib/couchbase/transcoder.rb +120 -0
  58. data/lib/couchbase/utils.rb +62 -0
  59. data/lib/couchbase/version.rb +21 -0
  60. data/lib/couchbase/view.rb +506 -0
  61. data/lib/couchbase/view_row.rb +272 -0
  62. data/lib/ext/multi_json_fix.rb +56 -0
  63. data/lib/rack/session/couchbase.rb +108 -0
  64. data/tasks/benchmark.rake +6 -0
  65. data/tasks/compile.rake +160 -0
  66. data/tasks/test.rake +100 -0
  67. data/tasks/util.rake +21 -0
  68. data/test/profile/.gitignore +1 -0
  69. data/test/profile/Gemfile +6 -0
  70. data/test/profile/benchmark.rb +195 -0
  71. data/test/setup.rb +178 -0
  72. data/test/test_arithmetic.rb +185 -0
  73. data/test/test_async.rb +316 -0
  74. data/test/test_bucket.rb +276 -0
  75. data/test/test_cas.rb +235 -0
  76. data/test/test_couchbase.rb +77 -0
  77. data/test/test_couchbase_connection_pool.rb +77 -0
  78. data/test/test_couchbase_rails_cache_store.rb +361 -0
  79. data/test/test_delete.rb +120 -0
  80. data/test/test_errors.rb +82 -0
  81. data/test/test_eventmachine.rb +70 -0
  82. data/test/test_format.rb +164 -0
  83. data/test/test_get.rb +407 -0
  84. data/test/test_stats.rb +57 -0
  85. data/test/test_store.rb +216 -0
  86. data/test/test_timer.rb +42 -0
  87. data/test/test_touch.rb +97 -0
  88. data/test/test_unlock.rb +119 -0
  89. data/test/test_utils.rb +58 -0
  90. data/test/test_version.rb +52 -0
  91. metadata +353 -0
@@ -0,0 +1,169 @@
1
+ # encoding: UTF-8
2
+ # Author:: Couchbase <info@couchbase.com>
3
+ # Copyright:: 2011, 2012 Couchbase, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'rbconfig'
20
+ # This hack is more robust, because in bundler environment bundler touches
21
+ # all constants from rbconfig.rb before loading any scripts. This is why
22
+ # RC_ARCHS doesn't work under bundler on MacOS.
23
+ if RUBY_PLATFORM =~ /darwin/ && defined?(RbConfig::ARCHFLAGS)
24
+ [RbConfig::CONFIG, RbConfig::MAKEFILE_CONFIG].each do |cfg|
25
+ cfg["CFLAGS"].gsub!(RbConfig::ARCHFLAGS, '')
26
+ cfg["LDFLAGS"].gsub!(RbConfig::ARCHFLAGS, '')
27
+ cfg["LDSHARED"].gsub!(RbConfig::ARCHFLAGS, '')
28
+ cfg["LIBRUBY_LDSHARED"].gsub!(RbConfig::ARCHFLAGS, '')
29
+ cfg["configure_args"].gsub!(RbConfig::ARCHFLAGS, '')
30
+ end
31
+ end
32
+
33
+ # Unset RUBYOPT to avoid interferences
34
+ ENV['RUBYOPT'] = nil
35
+
36
+ require 'mkmf'
37
+
38
+ def define(macro, value = nil)
39
+ $defs.push("-D #{[macro.upcase, value].compact.join('=')}")
40
+ end
41
+
42
+ ($CFLAGS ||= "") << " #{ENV["CFLAGS"]}"
43
+ ($LDFLAGS ||= "") << " #{ENV["LDFLAGS"]}"
44
+ ($LIBS ||= "") << " #{ENV["LIBS"]}"
45
+
46
+ $CFLAGS << ' -std=c99 -Wall -Wextra '
47
+ if ENV['DEBUG']
48
+ $CFLAGS << ' -O0 -ggdb3 -pedantic '
49
+ else
50
+ $CFLAGS << ' -O2'
51
+ $LDFLAGS << ' -Wl,--strip-debug' if RbConfig::CONFIG['target_os'] =~ /mingw32/
52
+ end
53
+
54
+
55
+ if RbConfig::CONFIG['target_os'] =~ /mingw32/
56
+ $LDFLAGS << ' -static-libgcc'
57
+ dir_config("libcouchbase")
58
+ else
59
+ LIBDIR = RbConfig::CONFIG['libdir']
60
+ INCLUDEDIR = RbConfig::CONFIG['includedir']
61
+
62
+ HEADER_DIRS = [
63
+ # First search /opt/local for macports
64
+ '/opt/local/include',
65
+ # Then search /usr/local for people that installed from source
66
+ '/usr/local/include',
67
+ # Check the ruby install locations
68
+ INCLUDEDIR,
69
+ # Finally fall back to /usr
70
+ '/usr/include'
71
+ ]
72
+
73
+ LIB_DIRS = [
74
+ # First search /opt/local for macports
75
+ '/opt/local/lib',
76
+ # Then search /usr/local for people that installed from source
77
+ '/usr/local/lib',
78
+ # Check the ruby install locations
79
+ LIBDIR,
80
+ # Finally fall back to /usr
81
+ '/usr/lib'
82
+ ]
83
+
84
+ # For people using homebrew
85
+ brew_prefix = `brew --prefix libevent 2> /dev/null`.chomp
86
+ unless brew_prefix.empty?
87
+ LIB_DIRS.unshift File.join(brew_prefix, 'lib')
88
+ HEADER_DIRS.unshift File.join(brew_prefix, 'include')
89
+ end
90
+
91
+ HEADER_DIRS.delete_if{|d| !File.exists?(d)}
92
+ LIB_DIRS.delete_if{|d| !File.exists?(d)}
93
+
94
+ # it will find the libcouchbase likely. you can specify its path otherwise
95
+ #
96
+ # ruby extconf.rb [--with-libcouchbase-include=<dir>] [--with-libcouchbase-lib=<dir>]
97
+ #
98
+ # or
99
+ #
100
+ # ruby extconf.rb [--with-libcouchbase-dir=<dir>]
101
+ #
102
+ dir_config("libcouchbase", HEADER_DIRS, LIB_DIRS)
103
+ end
104
+
105
+
106
+ if COMMON_HEADERS !~ /"ruby\.h"/
107
+ (COMMON_HEADERS ||= "") << %(\n#include "ruby.h"\n)
108
+ end
109
+
110
+ if try_compile(<<-SRC)
111
+ #include <stdarg.h>
112
+ int foo(int x, ...) {
113
+ va_list va;
114
+ va_start(va, x);
115
+ va_arg(va, int);
116
+ va_arg(va, char *);
117
+ va_arg(va, double);
118
+ return 0;
119
+ }
120
+ int main() {
121
+ return foo(10, "", 3.14);
122
+ return 0;
123
+ }
124
+ SRC
125
+ define("HAVE_STDARG_PROTOTYPES")
126
+ end
127
+
128
+ def die(message)
129
+ STDERR.puts "\n#{"*" * 70}"
130
+ STDERR.puts "#{message.gsub(/^/, "* ")}"
131
+ STDERR.puts "#{"*" * 70}\n\n"
132
+ abort
133
+ end
134
+
135
+ install_notice = "You must install libcouchbase >= 2.1.3\nSee http://www.couchbase.com/communities/c/ for more details"
136
+
137
+ unless try_compile(<<-SRC)
138
+ #include <libcouchbase/couchbase.h>
139
+ #include <stdio.h>
140
+
141
+ int main() {
142
+ printf("User not found: %d\\n", LCB_INVALID_USERNAME);
143
+ return 0;
144
+ }
145
+ SRC
146
+ die(install_notice)
147
+ end
148
+
149
+ # just to add -lcouchbase properly
150
+ have_library("couchbase", "lcb_verify_compiler_setup", "libcouchbase/couchbase.h") or die(install_notice)
151
+ have_header("mach/mach_time.h")
152
+ have_header("stdint.h") or die("Failed to locate stdint.h")
153
+ have_header("sys/time.h")
154
+ have_header("fcntl.h")
155
+ have_type("st_index_t")
156
+ have_func("clock_gettime")
157
+ have_func("gettimeofday")
158
+ have_func("QueryPerformanceCounter")
159
+ have_func("gethrtime")
160
+ have_func("rb_hash_lookup2")
161
+ have_func("rb_thread_fd_select")
162
+ have_func("rb_thread_blocking_region")
163
+ have_func("rb_thread_call_without_gvl")
164
+ have_func("poll", "poll.h")
165
+ have_func("ppoll", "poll.h")
166
+ have_func("rb_fiber_yield")
167
+ define("_GNU_SOURCE")
168
+ create_header("couchbase_config.h")
169
+ create_makefile("couchbase_ext")
@@ -0,0 +1,316 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ cb_get_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp)
22
+ {
23
+ struct cb_context_st *ctx = (struct cb_context_st *)cookie;
24
+ struct cb_bucket_st *bucket = ctx->bucket;
25
+ VALUE key, val, flags, cas, exc = Qnil, res, raw;
26
+
27
+ ctx->nqueries--;
28
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
29
+ cb_strip_key_prefix(bucket, key);
30
+
31
+ if (error != LCB_KEY_ENOENT || !ctx->quiet) {
32
+ exc = cb_check_error(error, "failed to get value", key);
33
+ if (exc != Qnil) {
34
+ rb_ivar_set(exc, cb_id_iv_operation, cb_sym_get);
35
+ ctx->exception = exc;
36
+ }
37
+ }
38
+
39
+ if (error == LCB_SUCCESS) {
40
+ flags = ULONG2NUM(resp->v.v0.flags);
41
+ cas = ULL2NUM(resp->v.v0.cas);
42
+ raw = STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes);
43
+ val = cb_decode_value(ctx->transcoder, raw, resp->v.v0.flags, ctx->transcoder_opts);
44
+ if (rb_obj_is_kind_of(val, rb_eStandardError)) {
45
+ VALUE exc_str = rb_funcall(val, cb_id_to_s, 0);
46
+ VALUE msg = rb_funcall(rb_mKernel, cb_id_sprintf, 3,
47
+ rb_str_new2("unable to convert value for key \"%s\": %s"), key, exc_str);
48
+ ctx->exception = rb_exc_new3(cb_eValueFormatError, msg);
49
+ rb_ivar_set(ctx->exception, cb_id_iv_operation, cb_sym_get);
50
+ rb_ivar_set(ctx->exception, cb_id_iv_key, key);
51
+ rb_ivar_set(ctx->exception, cb_id_iv_inner_exception, val);
52
+ val = Qnil;
53
+ }
54
+ } else {
55
+ val = flags = cas = Qnil;
56
+ }
57
+ if (bucket->async) { /* asynchronous */
58
+ if (ctx->proc != Qnil) {
59
+ res = rb_class_new_instance(0, NULL, cb_cResult);
60
+ rb_ivar_set(res, cb_id_iv_error, exc);
61
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_get);
62
+ rb_ivar_set(res, cb_id_iv_key, key);
63
+ rb_ivar_set(res, cb_id_iv_value, val);
64
+ rb_ivar_set(res, cb_id_iv_flags, flags);
65
+ rb_ivar_set(res, cb_id_iv_cas, cas);
66
+ cb_proc_call(bucket, ctx->proc, 1, res);
67
+ }
68
+ } else { /* synchronous */
69
+ if (NIL_P(exc) && error != LCB_KEY_ENOENT) {
70
+ if (ctx->extended) {
71
+ val = rb_ary_new3(3, val, flags, cas);
72
+ }
73
+ if (ctx->all_replicas) {
74
+ VALUE ary = rb_hash_aref(ctx->rv, key);
75
+ if (NIL_P(ary)) {
76
+ ary = rb_ary_new();
77
+ rb_hash_aset(ctx->rv, key, ary);
78
+ }
79
+ rb_ary_push(ary, val);
80
+ } else {
81
+ rb_hash_aset(ctx->rv, key, val);
82
+ }
83
+ }
84
+ }
85
+
86
+ if (ctx->nqueries == 0) {
87
+ ctx->proc = Qnil;
88
+ if (bucket->async) {
89
+ cb_context_free(ctx);
90
+ }
91
+ }
92
+ (void)handle;
93
+ }
94
+
95
+ /*
96
+ * Obtain an object stored in Couchbase by given key.
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @see http://couchbase.com/docs/couchbase-manual-2.0/couchbase-architecture-apis-memcached-protocol-additions.html#couchbase-architecture-apis-memcached-protocol-additions-getl
101
+ *
102
+ * @overload get(*keys, options = {})
103
+ * @param keys [String, Symbol, Array] One or several keys to fetch
104
+ * @param options [Hash] Options for operation.
105
+ * @option options [true, false] :extended (false) If set to +true+, the
106
+ * operation will return a tuple +[value, flags, cas]+, otherwise (by
107
+ * default) it returns just the value.
108
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
109
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
110
+ * absolute times (from the epoch).
111
+ * @option options [true, false] :quiet (self.quiet) If set to +true+, the
112
+ * operation won't raise error for missing key, it will return +nil+.
113
+ * Otherwise it will raise error in synchronous mode. In asynchronous
114
+ * mode this option ignored.
115
+ * @option options [Symbol] :format (nil) Explicitly choose the decoder
116
+ * for this key (+:plain+, +:document+, +:marshal+). See
117
+ * {Bucket#default_format}.
118
+ * @option options [Fixnum, Boolean] :lock Lock the keys for time span.
119
+ * If this parameter is +true+ the key(s) will be locked for default
120
+ * timeout. Also you can use number to setup your own timeout in
121
+ * seconds. If it will be lower that zero or exceed the maximum, the
122
+ * server will use default value. You can determine actual default and
123
+ * maximum values calling {Bucket#stats} without arguments and
124
+ * inspecting keys "ep_getl_default_timeout" and "ep_getl_max_timeout"
125
+ * correspondingly. See overloaded hash syntax to specify custom timeout
126
+ * per each key.
127
+ * @option options [true, false] :assemble_hash (false) Assemble Hash for
128
+ * results. Hash assembled automatically if +:extended+ option is true
129
+ * or in case of "get and touch" multimple keys.
130
+ * @option options [true, false, :all, :first, Fixnum] :replica
131
+ * (false) Read key from replica node. Options +:ttl+ and +:lock+
132
+ * are not compatible with +:replica+. Value +true+ is a synonym to
133
+ * +:first+, which means sequentially iterate over all replicas
134
+ * and return first successful response, skipping all failures.
135
+ * It is also possible to query all replicas in parallel using
136
+ * the +:all+ option, or pass a replica index, starting from zero.
137
+ *
138
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
139
+ * (valid attributes: +error+, +operation+, +key+, +value+, +flags+,
140
+ * +cas+).
141
+ *
142
+ * @return [Object, Array, Hash] the value(s) (or tuples in extended mode)
143
+ * associated with the key.
144
+ *
145
+ * @raise [Couchbase::Error::NotFound] if the key is missing in the
146
+ * bucket.
147
+ *
148
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
149
+ *
150
+ * @raise [ArgumentError] when passing the block in synchronous mode
151
+ *
152
+ * @example Get single value in quiet mode (the default)
153
+ * c.get("foo") #=> the associated value or nil
154
+ *
155
+ * @example Use alternative hash-like syntax
156
+ * c["foo"] #=> the associated value or nil
157
+ *
158
+ * @example Get single value in verbose mode
159
+ * c.get("missing-foo", :quiet => false) #=> raises Couchbase::NotFound
160
+ * c.get("missing-foo", :quiet => true) #=> returns nil
161
+ *
162
+ * @example Get and touch single value. The key won't be accessible after 10 seconds
163
+ * c.get("foo", :ttl => 10)
164
+ *
165
+ * @example Extended get
166
+ * val, flags, cas = c.get("foo", :extended => true)
167
+ *
168
+ * @example Get multiple keys
169
+ * c.get("foo", "bar", "baz") #=> [val1, val2, val3]
170
+ *
171
+ * @example Get multiple keys with assembing result into the Hash
172
+ * c.get("foo", "bar", "baz", :assemble_hash => true)
173
+ * #=> {"foo" => val1, "bar" => val2, "baz" => val3}
174
+ *
175
+ * @example Extended get multiple keys
176
+ * c.get("foo", "bar", :extended => true)
177
+ * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
178
+ *
179
+ * @example Asynchronous get
180
+ * c.run do
181
+ * c.get("foo", "bar", "baz") do |res|
182
+ * ret.operation #=> :get
183
+ * ret.success? #=> true
184
+ * ret.key #=> "foo", "bar" or "baz" in separate calls
185
+ * ret.value
186
+ * ret.flags
187
+ * ret.cas
188
+ * end
189
+ * end
190
+ *
191
+ * @example Get and lock key using default timeout
192
+ * c.get("foo", :lock => true)
193
+ *
194
+ * @example Determine lock timeout parameters
195
+ * c.stats.values_at("ep_getl_default_timeout", "ep_getl_max_timeout")
196
+ * #=> [{"127.0.0.1:11210"=>"15"}, {"127.0.0.1:11210"=>"30"}]
197
+ *
198
+ * @example Get and lock key using custom timeout
199
+ * c.get("foo", :lock => 3)
200
+ *
201
+ * @example Get and lock multiple keys using custom timeout
202
+ * c.get("foo", "bar", :lock => 3)
203
+ *
204
+ * @overload get(keys, options = {})
205
+ * When the method receive hash map, it will behave like it receive list
206
+ * of keys (+keys.keys+), but also touch each key setting expiry time to
207
+ * the corresponding value. But unlike usual get this command always
208
+ * return hash map +{key => value}+ or +{key => [value, flags, cas]}+.
209
+ *
210
+ * @param keys [Hash] Map key-ttl
211
+ * @param options [Hash] Options for operation. (see options definition
212
+ * above)
213
+ *
214
+ * @return [Hash] the values (or tuples in extended mode) associated with
215
+ * the keys.
216
+ *
217
+ * @example Get and touch multiple keys
218
+ * c.get("foo" => 10, "bar" => 20) #=> {"foo" => val1, "bar" => val2}
219
+ *
220
+ * @example Extended get and touch multiple keys
221
+ * c.get({"foo" => 10, "bar" => 20}, :extended => true)
222
+ * #=> {"foo" => [val1, flags1, cas1], "bar" => [val2, flags2, cas2]}
223
+ *
224
+ * @example Get and lock multiple keys for chosen period in seconds
225
+ * c.get("foo" => 10, "bar" => 20, :lock => true)
226
+ * #=> {"foo" => val1, "bar" => val2}
227
+ */
228
+ VALUE
229
+ cb_bucket_get(int argc, VALUE *argv, VALUE self)
230
+ {
231
+ struct cb_bucket_st *bucket = DATA_PTR(self);
232
+ struct cb_context_st *ctx;
233
+ VALUE rv, proc, exc;
234
+ size_t ii;
235
+ lcb_error_t err = LCB_SUCCESS;
236
+ struct cb_params_st params;
237
+
238
+ if (!cb_bucket_connected_bang(bucket, cb_sym_get)) {
239
+ return Qnil;
240
+ }
241
+
242
+ memset(&params, 0, sizeof(struct cb_params_st));
243
+ rb_scan_args(argc, argv, "0*&", &params.args, &proc);
244
+ if (!bucket->async && proc != Qnil) {
245
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
246
+ }
247
+ params.type = cb_cmd_get;
248
+ params.bucket = bucket;
249
+ params.cmd.get.keys_ary = rb_ary_new();
250
+ cb_params_build(&params);
251
+ ctx = cb_context_alloc_common(bucket, proc, params.cmd.get.num);
252
+ ctx->extended = params.cmd.get.extended;
253
+ ctx->quiet = params.cmd.get.quiet;
254
+ ctx->transcoder = params.cmd.get.transcoder;
255
+ ctx->transcoder_opts = params.cmd.get.transcoder_opts;
256
+ if (RTEST(params.cmd.get.replica)) {
257
+ if (params.cmd.get.replica == cb_sym_all) {
258
+ ctx->nqueries = lcb_get_num_replicas(bucket->handle);
259
+ ctx->all_replicas = 1;
260
+ }
261
+ err = lcb_get_replica(bucket->handle, (const void *)ctx,
262
+ params.cmd.get.num, params.cmd.get.ptr_gr);
263
+ } else {
264
+ err = lcb_get(bucket->handle, (const void *)ctx,
265
+ params.cmd.get.num, params.cmd.get.ptr);
266
+ }
267
+ cb_params_destroy(&params);
268
+ exc = cb_check_error(err, "failed to schedule get request", Qnil);
269
+ if (exc != Qnil) {
270
+ cb_context_free(ctx);
271
+ rb_exc_raise(exc);
272
+ }
273
+ bucket->nbytes += params.npayload;
274
+ if (bucket->async) {
275
+ cb_maybe_do_loop(bucket);
276
+ return Qnil;
277
+ } else {
278
+ if (ctx->nqueries > 0) {
279
+ /* we have some operations pending */
280
+ lcb_wait(bucket->handle);
281
+ }
282
+ exc = ctx->exception;
283
+ rv = ctx->rv;
284
+ cb_context_free(ctx);
285
+ if (exc != Qnil) {
286
+ rb_exc_raise(exc);
287
+ }
288
+ exc = bucket->exception;
289
+ if (exc != Qnil) {
290
+ bucket->exception = Qnil;
291
+ rb_exc_raise(exc);
292
+ }
293
+ if (params.cmd.get.gat || params.cmd.get.assemble_hash ||
294
+ (params.cmd.get.extended && (params.cmd.get.num > 1 || params.cmd.get.array))) {
295
+ return rv; /* return as a hash {key => [value, flags, cas], ...} */
296
+ }
297
+ if (params.cmd.get.num > 1 || params.cmd.get.array) {
298
+ VALUE keys, ret;
299
+ ret = rb_ary_new();
300
+ /* make sure ret is guarded so not invisible in a register
301
+ * when stack scanning */
302
+ RB_GC_GUARD(ret);
303
+ keys = params.cmd.get.keys_ary;
304
+ for (ii = 0; ii < params.cmd.get.num; ++ii) {
305
+ rb_ary_push(ret, rb_hash_aref(rv, rb_ary_entry(keys, ii)));
306
+ }
307
+ return ret; /* return as an array [value1, value2, ...] */
308
+ } else {
309
+ VALUE vv = Qnil;
310
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
311
+ return vv;
312
+ }
313
+ }
314
+ }
315
+
316
+