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,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_arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_arithmetic_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 cas, key, val, exc, res;
26
+ ID o;
27
+
28
+ ctx->nqueries--;
29
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
30
+ cb_strip_key_prefix(bucket, key);
31
+
32
+ cas = resp->v.v0.cas > 0 ? ULL2NUM(resp->v.v0.cas) : Qnil;
33
+ o = ctx->arith > 0 ? cb_sym_increment : cb_sym_decrement;
34
+ exc = cb_check_error(error, "failed to perform arithmetic operation", key);
35
+ if (exc != Qnil) {
36
+ rb_ivar_set(exc, cb_id_iv_cas, cas);
37
+ rb_ivar_set(exc, cb_id_iv_operation, o);
38
+ ctx->exception = exc;
39
+ }
40
+ val = ULL2NUM(resp->v.v0.value);
41
+ if (bucket->async) { /* asynchronous */
42
+ if (ctx->proc != Qnil) {
43
+ res = rb_class_new_instance(0, NULL, cb_cResult);
44
+ rb_ivar_set(res, cb_id_iv_error, exc);
45
+ rb_ivar_set(res, cb_id_iv_operation, o);
46
+ rb_ivar_set(res, cb_id_iv_key, key);
47
+ rb_ivar_set(res, cb_id_iv_value, val);
48
+ rb_ivar_set(res, cb_id_iv_cas, cas);
49
+ cb_proc_call(bucket, ctx->proc, 1, res);
50
+ }
51
+ } else { /* synchronous */
52
+ if (NIL_P(exc)) {
53
+ if (ctx->extended) {
54
+ rb_hash_aset(ctx->rv, key, rb_ary_new3(2, val, cas));
55
+ } else {
56
+ rb_hash_aset(ctx->rv, key, val);
57
+ }
58
+ }
59
+ }
60
+ if (ctx->nqueries == 0) {
61
+ ctx->proc = Qnil;
62
+ if (bucket->async) {
63
+ cb_context_free(ctx);
64
+ }
65
+ }
66
+ (void)handle;
67
+ }
68
+
69
+ static inline VALUE
70
+ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
71
+ {
72
+ struct cb_bucket_st *bucket = DATA_PTR(self);
73
+ struct cb_context_st *ctx;
74
+ VALUE rv, proc, exc;
75
+ lcb_error_t err;
76
+ struct cb_params_st params;
77
+
78
+ if (!cb_bucket_connected_bang(bucket, sign > 0 ? cb_sym_increment : cb_sym_decrement)) {
79
+ return Qnil;
80
+ }
81
+
82
+ memset(&params, 0, sizeof(struct cb_params_st));
83
+ rb_scan_args(argc, argv, "0*&", &params.args, &proc);
84
+ if (!bucket->async && proc != Qnil) {
85
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
86
+ }
87
+ params.type = cb_cmd_arith;
88
+ params.bucket = bucket;
89
+ params.cmd.arith.sign = sign;
90
+ cb_params_build(&params);
91
+ ctx = cb_context_alloc_common(bucket, proc, params.cmd.arith.num);
92
+ ctx->extended = params.cmd.arith.extended;
93
+ err = lcb_arithmetic(bucket->handle, (const void *)ctx,
94
+ params.cmd.arith.num, params.cmd.arith.ptr);
95
+ cb_params_destroy(&params);
96
+ exc = cb_check_error(err, "failed to schedule arithmetic request", Qnil);
97
+ if (exc != Qnil) {
98
+ cb_context_free(ctx);
99
+ rb_exc_raise(exc);
100
+ }
101
+ bucket->nbytes += params.npayload;
102
+ if (bucket->async) {
103
+ cb_maybe_do_loop(bucket);
104
+ return Qnil;
105
+ } else {
106
+ if (ctx->nqueries > 0) {
107
+ /* we have some operations pending */
108
+ lcb_wait(bucket->handle);
109
+ }
110
+ exc = ctx->exception;
111
+ rv = ctx->rv;
112
+ cb_context_free(ctx);
113
+ if (exc != Qnil) {
114
+ rb_exc_raise(exc);
115
+ }
116
+ if (params.cmd.store.num > 1) {
117
+ return rv; /* return as a hash {key => cas, ...} */
118
+ } else {
119
+ VALUE vv = Qnil;
120
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
121
+ return vv;
122
+ }
123
+ return rv;
124
+ }
125
+ }
126
+
127
+ /*
128
+ * Increment the value of an existing numeric key
129
+ *
130
+ * @since 1.0.0
131
+ *
132
+ * The increment methods allow you to increase a given stored integer
133
+ * value. These are the incremental equivalent of the decrement operations
134
+ * and work on the same basis; updating the value of a key if it can be
135
+ * parsed to an integer. The update operation occurs on the server and is
136
+ * provided at the protocol level. This simplifies what would otherwise be a
137
+ * two-stage get and set operation.
138
+ *
139
+ * @note that server treats values as unsigned numbers, therefore if
140
+ * you try to store negative number and then increment or decrement it
141
+ * will cause overflow. (see "Integer overflow" example below)
142
+ *
143
+ * @overload incr(key, delta = 1, options = {})
144
+ * @param key [String, Symbol] Key used to reference the value.
145
+ * @param delta [Fixnum] Integer (up to 64 bits) value to increment
146
+ * @param options [Hash] Options for operation.
147
+ * @option options [true, false] :create (false) If set to +true+, it will
148
+ * initialize the key with zero value and zero flags (use +:initial+
149
+ * option to set another initial value). Note: it won't increment the
150
+ * missing value.
151
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
152
+ * missing key initialization. This option imply +:create+ option is
153
+ * +true+.
154
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
155
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
156
+ * absolute times (from the epoch). This option ignored for existent
157
+ * keys.
158
+ * @option options [true, false] :extended (false) If set to +true+, the
159
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
160
+ * returns just value.
161
+ *
162
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
163
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
164
+ *
165
+ * @return [Fixnum] the actual value of the key.
166
+ *
167
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
168
+ * option isn't +true+.
169
+ *
170
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
171
+ * value
172
+ *
173
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
174
+ *
175
+ * @raise [ArgumentError] when passing the block in synchronous mode
176
+ *
177
+ * @example Increment key by one
178
+ * c.incr("foo")
179
+ *
180
+ * @example Increment key by 50
181
+ * c.incr("foo", 50)
182
+ *
183
+ * @example Increment key by one <b>OR</b> initialize with zero
184
+ * c.incr("foo", :create => true) #=> will return old+1 or 0
185
+ *
186
+ * @example Increment key by one <b>OR</b> initialize with three
187
+ * c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
188
+ *
189
+ * @example Increment key and get its CAS value
190
+ * val, cas = c.incr("foo", :extended => true)
191
+ *
192
+ * @example Integer overflow
193
+ * c.set("foo", -100)
194
+ * c.get("foo") #=> -100
195
+ * c.incr("foo") #=> 18446744073709551517
196
+ * # but it might look like working
197
+ * c.set("foo", -2)
198
+ * c.get("foo") #=> -2
199
+ * c.incr("foo", 2) #=> 0
200
+ * # on server:
201
+ * # // UINT64_MAX is 18446744073709551615
202
+ * # uint64_t num = atoll("-2");
203
+ * # // num is 18446744073709551614
204
+ * # num += 2
205
+ * # // num is 0
206
+ *
207
+ * @example Asynchronous invocation
208
+ * c.run do
209
+ * c.incr("foo") do |ret|
210
+ * ret.operation #=> :increment
211
+ * ret.success? #=> true
212
+ * ret.key #=> "foo"
213
+ * ret.value
214
+ * ret.cas
215
+ * end
216
+ * end
217
+ *
218
+ */
219
+ VALUE
220
+ cb_bucket_incr(int argc, VALUE *argv, VALUE self)
221
+ {
222
+ return cb_bucket_arithmetic(+1, argc, argv, self);
223
+ }
224
+
225
+ /*
226
+ * Decrement the value of an existing numeric key
227
+ *
228
+ * @since 1.0.0
229
+ *
230
+ * The decrement methods reduce the value of a given key if the
231
+ * corresponding value can be parsed to an integer value. These operations
232
+ * are provided at a protocol level to eliminate the need to get, update,
233
+ * and reset a simple integer value in the database. It supports the use of
234
+ * an explicit offset value that will be used to reduce the stored value in
235
+ * the database.
236
+ *
237
+ * @note that server values stored and transmitted as unsigned numbers,
238
+ * therefore if you try to decrement negative or zero key, you will always
239
+ * get zero.
240
+ *
241
+ * @overload decr(key, delta = 1, options = {})
242
+ * @param key [String, Symbol] Key used to reference the value.
243
+ * @param delta [Fixnum] Integer (up to 64 bits) value to decrement
244
+ * @param options [Hash] Options for operation.
245
+ * @option options [true, false] :create (false) If set to +true+, it will
246
+ * initialize the key with zero value and zero flags (use +:initial+
247
+ * option to set another initial value). Note: it won't decrement the
248
+ * missing value.
249
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
250
+ * missing key initialization. This option imply +:create+ option is
251
+ * +true+.
252
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
253
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
254
+ * absolute times (from the epoch). This option ignored for existent
255
+ * keys.
256
+ * @option options [true, false] :extended (false) If set to +true+, the
257
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
258
+ * returns just value.
259
+ *
260
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
261
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
262
+ *
263
+ * @return [Fixnum] the actual value of the key.
264
+ *
265
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
266
+ * option isn't +true+.
267
+ *
268
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
269
+ * value
270
+ *
271
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
272
+ *
273
+ * @raise [ArgumentError] when passing the block in synchronous mode
274
+ *
275
+ * @example Decrement key by one
276
+ * c.decr("foo")
277
+ *
278
+ * @example Decrement key by 50
279
+ * c.decr("foo", 50)
280
+ *
281
+ * @example Decrement key by one <b>OR</b> initialize with zero
282
+ * c.decr("foo", :create => true) #=> will return old-1 or 0
283
+ *
284
+ * @example Decrement key by one <b>OR</b> initialize with three
285
+ * c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
286
+ *
287
+ * @example Decrement key and get its CAS value
288
+ * val, cas = c.decr("foo", :extended => true)
289
+ *
290
+ * @example Decrementing zero
291
+ * c.set("foo", 0)
292
+ * c.decrement("foo", 100500) #=> 0
293
+ *
294
+ * @example Decrementing negative value
295
+ * c.set("foo", -100)
296
+ * c.decrement("foo", 100500) #=> 0
297
+ *
298
+ * @example Asynchronous invocation
299
+ * c.run do
300
+ * c.decr("foo") do |ret|
301
+ * ret.operation #=> :decrement
302
+ * ret.success? #=> true
303
+ * ret.key #=> "foo"
304
+ * ret.value
305
+ * ret.cas
306
+ * end
307
+ * end
308
+ *
309
+ */
310
+ VALUE
311
+ cb_bucket_decr(int argc, VALUE *argv, VALUE self)
312
+ {
313
+ return cb_bucket_arithmetic(-1, argc, argv, self);
314
+ }
315
+
316
+
@@ -0,0 +1,1373 @@
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
+ static VALUE
21
+ trigger_on_connect_callback(VALUE self)
22
+ {
23
+ struct cb_bucket_st *bucket = DATA_PTR(self);
24
+ VALUE on_connect_proc = bucket->on_connect_proc;
25
+ if (RTEST(on_connect_proc)) {
26
+ VALUE res = rb_class_new_instance(0, NULL, cb_cResult);
27
+ rb_ivar_set(res, cb_id_iv_error, bucket->exception);
28
+ bucket->exception = Qnil;
29
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_connect);
30
+ rb_ivar_set(res, cb_id_iv_value, self);
31
+ return rb_funcall(on_connect_proc, cb_id_call, 1, res);
32
+ } else {
33
+ bucket->trigger_connect_cb_on_set = 1;
34
+ return Qnil;
35
+ }
36
+ }
37
+
38
+ static void
39
+ error_callback(lcb_t handle, lcb_error_t error, const char *errinfo)
40
+ {
41
+ struct cb_bucket_st *bucket = (struct cb_bucket_st *)lcb_get_cookie(handle);
42
+
43
+ lcb_breakout(handle);
44
+ bucket->exception = cb_check_error(error, errinfo, Qnil);
45
+ if (bucket->async && !bucket->connected) {
46
+ (void)trigger_on_connect_callback(bucket->self);
47
+ }
48
+ }
49
+
50
+ static void
51
+ configuration_callback(lcb_t handle, lcb_configuration_t config)
52
+ {
53
+ struct cb_bucket_st *bucket = (struct cb_bucket_st *)lcb_get_cookie(handle);
54
+
55
+ if (config == LCB_CONFIGURATION_NEW) {
56
+ bucket->connected = 1;
57
+ (void)trigger_on_connect_callback(bucket->self);
58
+ }
59
+ }
60
+
61
+ void
62
+ cb_bucket_free(void *ptr)
63
+ {
64
+ struct cb_bucket_st *bucket = ptr;
65
+
66
+ if (bucket) {
67
+ bucket->destroying = 1;
68
+ if (bucket->handle) {
69
+ lcb_destroy(bucket->handle);
70
+ lcb_destroy_io_ops(bucket->io);
71
+ }
72
+ if (bucket->object_space) {
73
+ st_free_table(bucket->object_space);
74
+ }
75
+ xfree(bucket);
76
+ }
77
+ }
78
+
79
+ static int
80
+ cb_bucket_mark_object_i(st_index_t key, st_data_t value, st_data_t arg)
81
+ {
82
+ ((mark_f)value)((void*)key, (struct cb_bucket_st*)arg);
83
+ return ST_CONTINUE;
84
+ }
85
+
86
+ void
87
+ cb_bucket_mark(void *ptr)
88
+ {
89
+ struct cb_bucket_st *bucket = ptr;
90
+
91
+ if (bucket) {
92
+ rb_gc_mark(bucket->authority);
93
+ rb_gc_mark(bucket->hostname);
94
+ rb_gc_mark(bucket->pool);
95
+ rb_gc_mark(bucket->bucket);
96
+ rb_gc_mark(bucket->username);
97
+ rb_gc_mark(bucket->password);
98
+ rb_gc_mark(bucket->exception);
99
+ rb_gc_mark(bucket->on_error_proc);
100
+ rb_gc_mark(bucket->on_connect_proc);
101
+ rb_gc_mark(bucket->key_prefix_val);
102
+ if (bucket->object_space) {
103
+ st_foreach(bucket->object_space, cb_bucket_mark_object_i, (st_data_t)bucket);
104
+ }
105
+ }
106
+ }
107
+
108
+ static void
109
+ do_scan_connection_options(struct cb_bucket_st *bucket, int argc, VALUE *argv)
110
+ {
111
+ VALUE uri, opts, arg;
112
+ char port_s[8];
113
+
114
+ if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
115
+ if (TYPE(uri) == T_HASH && argc == 1) {
116
+ opts = uri;
117
+ uri = Qnil;
118
+ }
119
+ if (uri != Qnil) {
120
+ const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
121
+ VALUE match, uri_obj, re;
122
+
123
+ Check_Type(uri, T_STRING);
124
+ uri_obj = rb_funcall(cb_mURI, cb_id_parse, 1, uri);
125
+
126
+ arg = rb_funcall(uri_obj, cb_id_scheme, 0);
127
+ if (arg == Qnil || rb_str_cmp(arg, STR_NEW_CSTR("http"))) {
128
+ rb_raise(rb_eArgError, "invalid URI: invalid scheme");
129
+ }
130
+
131
+ arg = rb_funcall(uri_obj, cb_id_user, 0);
132
+ if (arg != Qnil) {
133
+ bucket->username = rb_str_dup_frozen(StringValue(arg));
134
+ }
135
+
136
+ arg = rb_funcall(uri_obj, cb_id_password, 0);
137
+ if (arg != Qnil) {
138
+ bucket->password = rb_str_dup_frozen(StringValue(arg));
139
+ }
140
+ arg = rb_funcall(uri_obj, cb_id_host, 0);
141
+ if (arg != Qnil) {
142
+ bucket->hostname = rb_str_dup_frozen(StringValue(arg));
143
+ } else {
144
+ rb_raise(rb_eArgError, "invalid URI: missing hostname");
145
+ }
146
+
147
+ arg = rb_funcall(uri_obj, cb_id_port, 0);
148
+ bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
149
+
150
+ arg = rb_funcall(uri_obj, cb_id_path, 0);
151
+ re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
152
+ match = rb_funcall(re, cb_id_match, 1, arg);
153
+ arg = rb_reg_nth_match(2, match);
154
+ bucket->pool = NIL_P(arg) ? cb_vStrDefault : rb_str_dup_frozen(StringValue(arg));
155
+ rb_str_freeze(bucket->pool);
156
+ arg = rb_reg_nth_match(4, match);
157
+ bucket->bucket = NIL_P(arg) ? cb_vStrDefault : rb_str_dup_frozen(StringValue(arg));
158
+ rb_str_freeze(bucket->bucket);
159
+ }
160
+ if (TYPE(opts) == T_HASH) {
161
+ arg = rb_hash_aref(opts, cb_sym_type);
162
+ if (arg != Qnil) {
163
+ if (arg == cb_sym_cluster) {
164
+ bucket->type = LCB_TYPE_CLUSTER;
165
+ } else {
166
+ bucket->type = LCB_TYPE_BUCKET;
167
+ }
168
+ }
169
+ arg = rb_hash_aref(opts, cb_sym_node_list);
170
+ if (arg != Qnil) {
171
+ Check_Type(arg, T_ARRAY);
172
+ bucket->node_list = rb_ary_join(arg, STR_NEW_CSTR(";"));
173
+ rb_str_freeze(bucket->node_list);
174
+ }
175
+ arg = rb_hash_aref(opts, cb_sym_hostname);
176
+ if (arg != Qnil) {
177
+ bucket->hostname = rb_str_dup_frozen(StringValue(arg));
178
+ }
179
+ arg = rb_hash_aref(opts, cb_sym_pool);
180
+ if (arg != Qnil) {
181
+ bucket->pool = rb_str_dup_frozen(StringValue(arg));
182
+ }
183
+ arg = rb_hash_aref(opts, cb_sym_bucket);
184
+ if (arg != Qnil) {
185
+ bucket->bucket = rb_str_dup_frozen(StringValue(arg));
186
+ }
187
+ arg = rb_hash_aref(opts, cb_sym_username);
188
+ if (arg != Qnil) {
189
+ bucket->username = rb_str_dup_frozen(StringValue(arg));
190
+ }
191
+ arg = rb_hash_aref(opts, cb_sym_password);
192
+ if (arg != Qnil) {
193
+ bucket->password = rb_str_dup_frozen(StringValue(arg));
194
+ }
195
+ arg = rb_hash_aref(opts, cb_sym_port);
196
+ if (arg != Qnil) {
197
+ bucket->port = (uint16_t)NUM2UINT(arg);
198
+ }
199
+ arg = rb_hash_lookup2(opts, cb_sym_quiet, Qundef);
200
+ if (arg != Qundef) {
201
+ bucket->quiet = RTEST(arg);
202
+ }
203
+ arg = rb_hash_aref(opts, cb_sym_timeout);
204
+ if (arg != Qnil) {
205
+ bucket->timeout = (uint32_t)NUM2ULONG(arg);
206
+ }
207
+ arg = rb_hash_aref(opts, cb_sym_default_ttl);
208
+ if (arg != Qnil) {
209
+ bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
210
+ }
211
+ arg = rb_hash_aref(opts, cb_sym_default_observe_timeout);
212
+ if (arg != Qnil) {
213
+ bucket->default_observe_timeout = (uint32_t)NUM2ULONG(arg);
214
+ }
215
+ arg = rb_hash_aref(opts, cb_sym_default_flags);
216
+ if (arg != Qnil) {
217
+ bucket->default_flags = (uint32_t)NUM2ULONG(arg);
218
+ }
219
+ arg = rb_hash_aref(opts, cb_sym_default_format);
220
+ if (arg != Qnil) {
221
+ if (TYPE(arg) == T_FIXNUM) {
222
+ rb_warn("numeric argument to :default_format option is deprecated, use symbol");
223
+ switch (FIX2INT(arg)) {
224
+ case CB_FMT_DOCUMENT:
225
+ arg = cb_sym_document;
226
+ break;
227
+ case CB_FMT_MARSHAL:
228
+ arg = cb_sym_marshal;
229
+ break;
230
+ case CB_FMT_PLAIN:
231
+ arg = cb_sym_plain;
232
+ break;
233
+ }
234
+ }
235
+ if (arg == cb_sym_document) {
236
+ cb_bucket_transcoder_set(bucket->self, cb_mDocument);
237
+ } else if (arg == cb_sym_marshal) {
238
+ cb_bucket_transcoder_set(bucket->self, cb_mMarshal);
239
+ } else if (arg == cb_sym_plain) {
240
+ cb_bucket_transcoder_set(bucket->self, cb_mPlain);
241
+ }
242
+ }
243
+ arg = rb_hash_lookup2(opts, cb_sym_transcoder, Qundef);
244
+ if (arg != Qundef) {
245
+ cb_bucket_transcoder_set(bucket->self, arg);
246
+ }
247
+ arg = rb_hash_aref(opts, cb_sym_environment);
248
+ if (arg != Qnil) {
249
+ if (arg == cb_sym_production || arg == cb_sym_development) {
250
+ bucket->environment = arg;
251
+ }
252
+ }
253
+ arg = rb_hash_aref(opts, cb_sym_key_prefix);
254
+ if (arg != Qnil) {
255
+ bucket->key_prefix_val = rb_str_dup_frozen(StringValue(arg));
256
+ }
257
+ arg = rb_hash_aref(opts, cb_sym_default_arithmetic_init);
258
+ if (arg != Qnil) {
259
+ bucket->default_arith_create = RTEST(arg);
260
+ if (TYPE(arg) == T_FIXNUM) {
261
+ bucket->default_arith_init = NUM2ULL(arg);
262
+ }
263
+ }
264
+ arg = rb_hash_aref(opts, cb_sym_engine);
265
+ if (arg != Qnil) {
266
+ if (arg == cb_sym_default) {
267
+ bucket->engine = cb_sym_default;
268
+ } else if (arg == cb_sym_select) {
269
+ bucket->engine = cb_sym_select;
270
+ #ifdef _WIN32
271
+ } else if (arg == cb_sym_iocp) {
272
+ bucket->engine = cb_sym_iocp;
273
+ #else
274
+ } else if (arg == cb_sym_libev) {
275
+ bucket->engine = cb_sym_libev;
276
+ } else if (arg == cb_sym_libevent) {
277
+ bucket->engine = cb_sym_libevent;
278
+ #ifdef BUILD_EVENTMACHINE_PLUGIN
279
+ } else if (arg == cb_sym_eventmachine) {
280
+ bucket->engine = cb_sym_eventmachine;
281
+ #endif
282
+ #endif
283
+ } else {
284
+ VALUE ins = rb_funcall(arg, rb_intern("inspect"), 0);
285
+ rb_raise(rb_eArgError, "Couchbase: unknown engine %s", RSTRING_PTR(ins));
286
+ }
287
+ }
288
+ bucket->async = RTEST(rb_hash_aref(opts, cb_sym_async));
289
+ arg = rb_hash_aref(opts, cb_sym_transcoder);
290
+ if (arg != Qnil) {
291
+ bucket->default_arith_create = RTEST(arg);
292
+ if (TYPE(arg) == T_FIXNUM) {
293
+ bucket->default_arith_init = NUM2ULL(arg);
294
+ }
295
+ }
296
+ } else {
297
+ opts = Qnil;
298
+ }
299
+ }
300
+ if (RTEST(bucket->password) && !RTEST(bucket->username)) {
301
+ bucket->username = bucket->bucket;
302
+ }
303
+ if (bucket->default_observe_timeout < 2) {
304
+ rb_raise(rb_eArgError, "default_observe_timeout is too low");
305
+ }
306
+ snprintf(port_s, sizeof(port_s), ":%u", bucket->port);
307
+ bucket->authority = rb_str_dup(bucket->hostname);
308
+ rb_str_cat2(bucket->authority, port_s);
309
+ rb_str_freeze(bucket->authority);
310
+ }
311
+
312
+ static VALUE
313
+ em_disconnect_block(VALUE unused, VALUE self)
314
+ {
315
+ struct cb_bucket_st *bucket = DATA_PTR(self);
316
+ if (bucket->handle) {
317
+ return cb_bucket_disconnect(self);
318
+ }
319
+ (void)unused;
320
+ return Qnil;
321
+ }
322
+
323
+ static void
324
+ do_connect(struct cb_bucket_st *bucket)
325
+ {
326
+ lcb_error_t err;
327
+ struct lcb_create_st create_opts;
328
+
329
+ if (bucket->handle) {
330
+ cb_bucket_disconnect(bucket->self);
331
+ }
332
+
333
+ {
334
+ struct lcb_create_io_ops_st ciops;
335
+ memset(&ciops, 0, sizeof(ciops));
336
+ ciops.version = 0;
337
+
338
+ if (bucket->engine == cb_sym_libevent) {
339
+ ciops.v.v0.type = LCB_IO_OPS_LIBEVENT;
340
+ } else if (bucket->engine == cb_sym_select) {
341
+ ciops.v.v0.type = LCB_IO_OPS_SELECT;
342
+ #ifdef _WIN32
343
+ } else if (bucket->engine == cb_sym_iocp) {
344
+ ciops.v.v0.type = LCB_IO_OPS_WINIOCP;
345
+ #endif
346
+ } else if (bucket->engine == cb_sym_libev) {
347
+ ciops.v.v0.type = LCB_IO_OPS_LIBEV;
348
+ } else if (bucket->engine == cb_sym_eventmachine) {
349
+ ciops.version = 1;
350
+ ciops.v.v1.sofile = NULL;
351
+ ciops.v.v1.symbol = "cb_create_ruby_em_io_opts";
352
+ ciops.v.v1.cookie = bucket;
353
+ } else {
354
+ #ifdef _WIN32
355
+ ciops.v.v0.type = LCB_IO_OPS_DEFAULT;
356
+ #else
357
+ ciops.version = 1;
358
+ ciops.v.v1.sofile = NULL;
359
+ ciops.v.v1.symbol = "cb_create_ruby_mt_io_opts";
360
+ ciops.v.v1.cookie = NULL;
361
+ #endif
362
+ }
363
+ err = lcb_create_io_ops(&bucket->io, &ciops);
364
+ if (err != LCB_SUCCESS) {
365
+ rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
366
+ }
367
+ }
368
+
369
+ memset(&create_opts, 0, sizeof(struct lcb_create_st));
370
+ create_opts.version = 1;
371
+ create_opts.v.v1.type = bucket->type;
372
+ create_opts.v.v1.host = RTEST(bucket->node_list) ? RSTRING_PTR(bucket-> node_list) : RSTRING_PTR(bucket->authority);
373
+ create_opts.v.v1.user = RTEST(bucket->username) ? RSTRING_PTR(bucket->username) : NULL;
374
+ create_opts.v.v1.passwd = RTEST(bucket->password) ? RSTRING_PTR(bucket->password) : NULL;
375
+ create_opts.v.v1.bucket = RSTRING_PTR(bucket->bucket);
376
+ create_opts.v.v1.io = bucket->io;
377
+ err = lcb_create(&bucket->handle, &create_opts);
378
+ if (err != LCB_SUCCESS) {
379
+ bucket->handle = NULL;
380
+ rb_exc_raise(cb_check_error(err, "failed to create libcouchbase instance", Qnil));
381
+ }
382
+ lcb_set_cookie(bucket->handle, bucket);
383
+ (void)lcb_set_error_callback(bucket->handle, error_callback);
384
+ (void)lcb_set_store_callback(bucket->handle, cb_storage_callback);
385
+ (void)lcb_set_get_callback(bucket->handle, cb_get_callback);
386
+ (void)lcb_set_touch_callback(bucket->handle, cb_touch_callback);
387
+ (void)lcb_set_remove_callback(bucket->handle, cb_delete_callback);
388
+ (void)lcb_set_stat_callback(bucket->handle, cb_stat_callback);
389
+ (void)lcb_set_arithmetic_callback(bucket->handle, cb_arithmetic_callback);
390
+ (void)lcb_set_version_callback(bucket->handle, cb_version_callback);
391
+ (void)lcb_set_http_complete_callback(bucket->handle, cb_http_complete_callback);
392
+ (void)lcb_set_http_data_callback(bucket->handle, cb_http_data_callback);
393
+ (void)lcb_set_observe_callback(bucket->handle, cb_observe_callback);
394
+ (void)lcb_set_unlock_callback(bucket->handle, cb_unlock_callback);
395
+ (void)lcb_set_configuration_callback(bucket->handle, configuration_callback);
396
+
397
+ if (bucket->timeout > 0) {
398
+ lcb_set_timeout(bucket->handle, bucket->timeout);
399
+ } else {
400
+ bucket->timeout = lcb_get_timeout(bucket->handle);
401
+ }
402
+ err = lcb_connect(bucket->handle);
403
+ if (err != LCB_SUCCESS) {
404
+ cb_bucket_disconnect(bucket->self);
405
+ rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
406
+ }
407
+ bucket->exception = Qnil;
408
+ if (bucket->engine == cb_sym_eventmachine && !bucket->async_disconnect_hook_set) {
409
+ bucket->async_disconnect_hook_set = 1;
410
+ rb_block_call(em_m, cb_id_add_shutdown_hook, 0, NULL, em_disconnect_block, bucket->self);
411
+ }
412
+ if (!bucket->async) {
413
+ lcb_wait(bucket->handle);
414
+ if (bucket->exception != Qnil) {
415
+ cb_bucket_disconnect(bucket->self);
416
+ rb_exc_raise(bucket->exception);
417
+ }
418
+ }
419
+ }
420
+
421
+ VALUE
422
+ cb_bucket_alloc(VALUE klass)
423
+ {
424
+ VALUE obj;
425
+ struct cb_bucket_st *bucket;
426
+
427
+ /* allocate new bucket struct and set it to zero */
428
+ obj = Data_Make_Struct(klass, struct cb_bucket_st, cb_bucket_mark, cb_bucket_free,
429
+ bucket);
430
+ return obj;
431
+ }
432
+
433
+ /*
434
+ * Initialize new Bucket.
435
+ *
436
+ * @since 1.0.0
437
+ *
438
+ * @overload initialize(url, options = {})
439
+ * Initialize bucket using URI of the cluster and options. It is possible
440
+ * to override some parts of URI using the options keys (e.g. :host or
441
+ * :port)
442
+ *
443
+ * @param [String] url The full URL of management API of the cluster.
444
+ * @param [Hash] options The options for connection. See options definition
445
+ * below.
446
+ *
447
+ * @overload initialize(options = {})
448
+ * Initialize bucket using options only.
449
+ *
450
+ * @param [Hash] options The options for operation for connection
451
+ * @option options [Array] :node_list (nil) the list of nodes to connect
452
+ * to. If specified it takes precedence over +:host+ option. The list
453
+ * must be array of strings in form of host names or host names with
454
+ * ports (in first case port 8091 will be used, see examples).
455
+ * @option options [String] :host ("localhost") the hostname or IP address
456
+ * of the node
457
+ * @option options [Fixnum] :port (8091) the port of the managemenent API
458
+ * @option options [String] :pool ("default") the pool name
459
+ * @option options [String] :bucket ("default") the bucket name
460
+ * @option options [Fixnum] :default_ttl (0) the TTL used by default during
461
+ * storing key-value pairs.
462
+ * @option options [Fixnum] :default_flags (0) the default flags.
463
+ * @option options [Symbol] :default_format (:document) the format, which
464
+ * will be used for values by default. Note that changing format will
465
+ * amend flags. (see {Bucket#default_format})
466
+ * @option options [String] :username (nil) the user name to connect to the
467
+ * cluster. Used to authenticate on management API. The username could
468
+ * be skipped for protected buckets, the bucket name will be used
469
+ * instead.
470
+ * @option options [String] :password (nil) the password of the user.
471
+ * @option options [true, false] :quiet (false) the flag controlling if raising
472
+ * exception when the client executes operations on non-existent keys. If it
473
+ * is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
474
+ * default behaviour is to return +nil+ value silently (might be useful in
475
+ * Rails cache).
476
+ * @option options [Symbol] :environment (:production) the mode of the
477
+ * connection. Currently it influences only on design documents set. If
478
+ * the environment is +:development+, you will able to get design
479
+ * documents with 'dev_' prefix, otherwise (in +:production+ mode) the
480
+ * library will hide them from you.
481
+ * @option options [String] :key_prefix (nil) the prefix string which will
482
+ * be prepended to each key before sending out, and sripped before
483
+ * returning back to the application.
484
+ * @option options [Fixnum] :timeout (2500000) the timeout for IO
485
+ * operations (in microseconds)
486
+ * @option options [Fixnum, true] :default_arithmetic_init (0) the default
487
+ * initial value for arithmetic operations. Setting this option to any
488
+ * non positive number forces creation missing keys with given default
489
+ * value. Setting it to +true+ will use zero as initial value. (see
490
+ * {Bucket#incr} and {Bucket#decr}).
491
+ * @option options [Symbol] :engine (:default) the IO engine to use
492
+ * Currently following engines are supported:
493
+ * :default :: Built-in engine (multi-thread friendly)
494
+ * :select :: select(2) IO plugin from libcouchbase
495
+ * :iocp :: "I/O Completion Ports" plugin from libcouchbase (windows only)
496
+ * :libevent :: libevent IO plugin from libcouchbase (optional)
497
+ * :libev :: libev IO plugin from libcouchbase (optional)
498
+ * :eventmachine :: EventMachine plugin (builtin, but requires EM gem and ruby 1.9+)
499
+ * @option options [true, false] :async (false) If true, the
500
+ * connection instance will be considered always asynchronous and
501
+ * IO interaction will be occured only when {Couchbase::Bucket#run}
502
+ * called. See {Couchbase::Bucket#on_connect} to hook your code
503
+ * after the instance will be connected.
504
+ *
505
+ * @example Initialize connection using default options
506
+ * Couchbase.new
507
+ *
508
+ * @example Select custom bucket
509
+ * Couchbase.new(:bucket => 'foo')
510
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
511
+ *
512
+ * @example Connect to protected bucket
513
+ * Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
514
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
515
+ * :username => 'protected', :password => 'secret')
516
+ *
517
+ * @example Use list of nodes, in case some nodes might be dead
518
+ * Couchbase.new(:node_list => ['example.com:8091', 'example.org:8091', 'example.net'])
519
+ *
520
+ * @raise [Couchbase::Error::BucketNotFound] if there is no such bucket to
521
+ * connect to
522
+ *
523
+ * @raise [Couchbase::Error::Connect] if the socket wasn't accessible
524
+ * (doesn't accept connections or doesn't respond in time)
525
+ *
526
+ * @return [Bucket]
527
+ */
528
+ VALUE
529
+ cb_bucket_init(int argc, VALUE *argv, VALUE self)
530
+ {
531
+ struct cb_bucket_st *bucket = DATA_PTR(self);
532
+ bucket->self = self;
533
+ bucket->exception = Qnil;
534
+ bucket->type = LCB_TYPE_BUCKET;
535
+ bucket->hostname = cb_vStrLocalhost;
536
+ bucket->port = 8091;
537
+ bucket->pool = cb_vStrDefault;
538
+ bucket->bucket = cb_vStrDefault;
539
+ bucket->username = Qnil;
540
+ bucket->password = Qnil;
541
+ bucket->engine = cb_sym_default;
542
+ bucket->async = 0;
543
+ bucket->quiet = 0;
544
+ bucket->default_ttl = 0;
545
+ bucket->default_flags = 0;
546
+ cb_bucket_transcoder_set(self, cb_mDocument);
547
+ bucket->default_observe_timeout = 2500000;
548
+ bucket->on_error_proc = Qnil;
549
+ bucket->on_connect_proc = Qnil;
550
+ bucket->timeout = 0;
551
+ bucket->environment = cb_sym_production;
552
+ bucket->key_prefix_val = Qnil;
553
+ bucket->node_list = Qnil;
554
+ bucket->object_space = st_init_numtable();
555
+ bucket->destroying = 0;
556
+ bucket->connected = 0;
557
+ bucket->on_connect_proc = Qnil;
558
+ bucket->async_disconnect_hook_set = 0;
559
+
560
+ do_scan_connection_options(bucket, argc, argv);
561
+ do_connect(bucket);
562
+
563
+ return self;
564
+ }
565
+
566
+ /*
567
+ * Initialize copy
568
+ *
569
+ * Initializes copy of the object, used by {Couchbase::Bucket#dup}
570
+ *
571
+ * @param orig [Couchbase::Bucket] the source for copy
572
+ *
573
+ * @return [Couchbase::Bucket]
574
+ */
575
+ VALUE
576
+ cb_bucket_init_copy(VALUE copy, VALUE orig)
577
+ {
578
+ struct cb_bucket_st *copy_b;
579
+ struct cb_bucket_st *orig_b;
580
+
581
+ if (copy == orig)
582
+ return copy;
583
+
584
+ if (TYPE(orig) != T_DATA || TYPE(copy) != T_DATA ||
585
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)cb_bucket_free) {
586
+ rb_raise(rb_eTypeError, "wrong argument type");
587
+ }
588
+
589
+ copy_b = DATA_PTR(copy);
590
+ orig_b = DATA_PTR(orig);
591
+
592
+ copy_b->self = copy_b->self;
593
+ copy_b->port = orig_b->port;
594
+ copy_b->authority = orig_b->authority;
595
+ copy_b->hostname = orig_b->hostname;
596
+ copy_b->pool = orig_b->pool;
597
+ copy_b->bucket = orig_b->bucket;
598
+ copy_b->username = orig_b->username;
599
+ copy_b->password = orig_b->password;
600
+ copy_b->engine = orig_b->engine;
601
+ copy_b->async = orig_b->async;
602
+ copy_b->quiet = orig_b->quiet;
603
+ copy_b->transcoder = orig_b->transcoder;
604
+ copy_b->default_flags = orig_b->default_flags;
605
+ copy_b->default_ttl = orig_b->default_ttl;
606
+ copy_b->environment = orig_b->environment;
607
+ copy_b->timeout = orig_b->timeout;
608
+ copy_b->exception = Qnil;
609
+ copy_b->async_disconnect_hook_set = 0;
610
+ if (orig_b->on_error_proc != Qnil) {
611
+ copy_b->on_error_proc = rb_funcall(orig_b->on_error_proc, cb_id_dup, 0);
612
+ }
613
+ if (orig_b->on_connect_proc != Qnil) {
614
+ copy_b->on_connect_proc = rb_funcall(orig_b->on_connect_proc, cb_id_dup, 0);
615
+ }
616
+ copy_b->key_prefix_val = orig_b->key_prefix_val;
617
+ copy_b->object_space = st_init_numtable();
618
+ copy_b->destroying = 0;
619
+ copy_b->connected = 0;
620
+
621
+ do_connect(copy_b);
622
+
623
+ return copy;
624
+ }
625
+
626
+ /*
627
+ * Reconnect the bucket
628
+ *
629
+ * @since 1.1.0
630
+ *
631
+ * Reconnect the bucket using initial configuration with optional
632
+ * redefinition.
633
+ *
634
+ * @overload reconnect(url, options = {})
635
+ * see {Bucket#initialize Bucket#initialize(url, options)}
636
+ * @return [Couchbase::Bucket]
637
+ *
638
+ * @overload reconnect(options = {})
639
+ * see {Bucket#initialize Bucket#initialize(options)}
640
+ * @return [Couchbase::Bucket]
641
+ *
642
+ * @example reconnect with current parameters
643
+ * c.reconnect
644
+ *
645
+ * @example reconnect the instance to another bucket
646
+ * c.reconnect(:bucket => 'new')
647
+ */
648
+ VALUE
649
+ cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
650
+ {
651
+ struct cb_bucket_st *bucket = DATA_PTR(self);
652
+
653
+ do_scan_connection_options(bucket, argc, argv);
654
+ do_connect(bucket);
655
+
656
+ return self;
657
+ }
658
+
659
+ /* Document-method: connected?
660
+ * Check whether the instance connected to the cluster.
661
+ *
662
+ * @since 1.1.0
663
+ *
664
+ * @return [true, false] +true+ if the instance connected to the cluster
665
+ */
666
+ VALUE
667
+ cb_bucket_connected_p(VALUE self)
668
+ {
669
+ struct cb_bucket_st *bucket = DATA_PTR(self);
670
+ return (bucket->handle && bucket->connected) ? Qtrue : Qfalse;
671
+ }
672
+
673
+ /* Document-method: async?
674
+ * Check whether the connection asynchronous.
675
+ *
676
+ * @since 1.0.0
677
+ *
678
+ * By default all operations are synchronous and block waiting for
679
+ * results, but you can make them asynchronous and run event loop
680
+ * explicitly. (see {Bucket#run})
681
+ *
682
+ * @example Return value of #get operation depending on async flag
683
+ * connection = Connection.new
684
+ * connection.async? #=> false
685
+ *
686
+ * connection.run do |conn|
687
+ * conn.async? #=> true
688
+ * end
689
+ *
690
+ * @return [true, false] +true+ if the connection if asynchronous
691
+ *
692
+ * @see Bucket#run
693
+ */
694
+ VALUE
695
+ cb_bucket_async_p(VALUE self)
696
+ {
697
+ struct cb_bucket_st *bucket = DATA_PTR(self);
698
+ return bucket->async ? Qtrue : Qfalse;
699
+ }
700
+
701
+ VALUE
702
+ cb_bucket_quiet_get(VALUE self)
703
+ {
704
+ struct cb_bucket_st *bucket = DATA_PTR(self);
705
+ return bucket->quiet ? Qtrue : Qfalse;
706
+ }
707
+
708
+ VALUE
709
+ cb_bucket_quiet_set(VALUE self, VALUE val)
710
+ {
711
+ struct cb_bucket_st *bucket = DATA_PTR(self);
712
+ VALUE new;
713
+
714
+ bucket->quiet = RTEST(val);
715
+ new = bucket->quiet ? Qtrue : Qfalse;
716
+ return new;
717
+ }
718
+
719
+ VALUE
720
+ cb_bucket_default_flags_get(VALUE self)
721
+ {
722
+ struct cb_bucket_st *bucket = DATA_PTR(self);
723
+ return ULONG2NUM(bucket->default_flags);
724
+ }
725
+
726
+ VALUE
727
+ cb_bucket_default_flags_set(VALUE self, VALUE val)
728
+ {
729
+ struct cb_bucket_st *bucket = DATA_PTR(self);
730
+
731
+ bucket->default_flags = (uint32_t)NUM2ULONG(val);
732
+ return val;
733
+ }
734
+
735
+ VALUE
736
+ cb_bucket_transcoder_get(VALUE self)
737
+ {
738
+ struct cb_bucket_st *bucket = DATA_PTR(self);
739
+ return bucket->transcoder;
740
+ }
741
+
742
+ VALUE
743
+ cb_bucket_transcoder_set(VALUE self, VALUE val)
744
+ {
745
+ struct cb_bucket_st *bucket = DATA_PTR(self);
746
+
747
+ if (val != Qnil && !rb_respond_to(val, cb_id_dump) && !rb_respond_to(val, cb_id_load)) {
748
+ rb_raise(rb_eArgError, "transcoder must respond to dump and load methods");
749
+ }
750
+ bucket->transcoder = val;
751
+
752
+ return bucket->transcoder;
753
+ }
754
+
755
+ VALUE
756
+ cb_bucket_default_format_get(VALUE self)
757
+ {
758
+ struct cb_bucket_st *bucket = DATA_PTR(self);
759
+
760
+ if (bucket->transcoder == cb_mDocument) {
761
+ return cb_sym_document;
762
+ } else if (bucket->transcoder == cb_mMarshal) {
763
+ return cb_sym_marshal;
764
+ } else if (bucket->transcoder == cb_mPlain) {
765
+ return cb_sym_plain;
766
+ }
767
+ return Qnil;
768
+ }
769
+
770
+ VALUE
771
+ cb_bucket_default_format_set(VALUE self, VALUE val)
772
+ {
773
+ if (TYPE(val) == T_FIXNUM) {
774
+ rb_warn("numeric argument to #default_format option is deprecated, use symbol");
775
+ switch (FIX2INT(val)) {
776
+ case CB_FMT_DOCUMENT:
777
+ val = cb_sym_document;
778
+ break;
779
+ case CB_FMT_MARSHAL:
780
+ val = cb_sym_marshal;
781
+ break;
782
+ case CB_FMT_PLAIN:
783
+ val = cb_sym_plain;
784
+ break;
785
+ }
786
+ }
787
+ if (val == cb_sym_document) {
788
+ cb_bucket_transcoder_set(self, cb_mDocument);
789
+ } else if (val == cb_sym_marshal) {
790
+ cb_bucket_transcoder_set(self, cb_mMarshal);
791
+ } else if (val == cb_sym_plain) {
792
+ cb_bucket_transcoder_set(self, cb_mPlain);
793
+ } else {
794
+ rb_raise(rb_eArgError, "unknown format");
795
+ }
796
+
797
+ return val;
798
+ }
799
+
800
+ VALUE
801
+ cb_bucket_on_error_set(VALUE self, VALUE val)
802
+ {
803
+ struct cb_bucket_st *bucket = DATA_PTR(self);
804
+
805
+ if (rb_respond_to(val, cb_id_call)) {
806
+ bucket->on_error_proc = val;
807
+ } else {
808
+ bucket->on_error_proc = Qnil;
809
+ }
810
+
811
+ return bucket->on_error_proc;
812
+ }
813
+
814
+ VALUE
815
+ cb_bucket_on_error_get(VALUE self)
816
+ {
817
+ struct cb_bucket_st *bucket = DATA_PTR(self);
818
+
819
+ if (rb_block_given_p()) {
820
+ return cb_bucket_on_error_set(self, rb_block_proc());
821
+ } else {
822
+ return bucket->on_error_proc;
823
+ }
824
+ }
825
+
826
+ static
827
+ VALUE trigger_on_connect_callback_block(VALUE nil, VALUE self)
828
+ {
829
+ (void)nil;
830
+ return trigger_on_connect_callback(self);
831
+ }
832
+
833
+ VALUE
834
+ cb_bucket_on_connect_set(VALUE self, VALUE val)
835
+ {
836
+ struct cb_bucket_st *bucket = DATA_PTR(self);
837
+
838
+ if (rb_respond_to(val, cb_id_call)) {
839
+ bucket->on_connect_proc = val;
840
+ if (bucket->trigger_connect_cb_on_set) {
841
+ bucket->trigger_connect_cb_on_set = 0;
842
+ if (bucket->async) {
843
+ VALUE args[] = {INT2FIX(0)};
844
+ /* setup timer with zero interval to call on_connect
845
+ * callback on the next tick */
846
+ rb_block_call(bucket->self, cb_id_create_timer, 1,
847
+ args, trigger_on_connect_callback_block, bucket->self);
848
+ } else {
849
+ trigger_on_connect_callback(self);
850
+ }
851
+ }
852
+ } else {
853
+ bucket->on_connect_proc = Qnil;
854
+ }
855
+
856
+ return bucket->on_connect_proc;
857
+ }
858
+
859
+ VALUE
860
+ cb_bucket_on_connect_get(VALUE self)
861
+ {
862
+ struct cb_bucket_st *bucket = DATA_PTR(self);
863
+
864
+ if (rb_block_given_p()) {
865
+ return cb_bucket_on_connect_set(self, rb_block_proc());
866
+ } else {
867
+ return bucket->on_connect_proc;
868
+ }
869
+ }
870
+
871
+ VALUE
872
+ cb_bucket_timeout_get(VALUE self)
873
+ {
874
+ struct cb_bucket_st *bucket = DATA_PTR(self);
875
+ return ULONG2NUM(bucket->timeout);
876
+ }
877
+
878
+ VALUE
879
+ cb_bucket_timeout_set(VALUE self, VALUE val)
880
+ {
881
+ struct cb_bucket_st *bucket = DATA_PTR(self);
882
+ VALUE tmval;
883
+
884
+ bucket->timeout = (uint32_t)NUM2ULONG(val);
885
+ lcb_set_timeout(bucket->handle, bucket->timeout);
886
+ tmval = ULONG2NUM(bucket->timeout);
887
+
888
+ return tmval;
889
+ }
890
+
891
+ VALUE
892
+ cb_bucket_default_arithmetic_init_get(VALUE self)
893
+ {
894
+ struct cb_bucket_st *bucket = DATA_PTR(self);
895
+ return ULL2NUM(bucket->default_arith_init);
896
+ }
897
+
898
+ VALUE
899
+ cb_bucket_default_arithmetic_init_set(VALUE self, VALUE val)
900
+ {
901
+ struct cb_bucket_st *bucket = DATA_PTR(self);
902
+
903
+ bucket->default_arith_create = RTEST(val);
904
+ if (bucket->default_arith_create) {
905
+ bucket->default_arith_init = NUM2ULL(val);
906
+ } else {
907
+ bucket->default_arith_init = 0;
908
+ }
909
+ return ULL2NUM(bucket->default_arith_init);
910
+ }
911
+
912
+ VALUE
913
+ cb_bucket_key_prefix_get(VALUE self)
914
+ {
915
+ struct cb_bucket_st *bucket = DATA_PTR(self);
916
+ return bucket->key_prefix_val;
917
+ }
918
+
919
+ VALUE
920
+ cb_bucket_key_prefix_set(VALUE self, VALUE val)
921
+ {
922
+ struct cb_bucket_st *bucket = DATA_PTR(self);
923
+
924
+ bucket->key_prefix_val = rb_str_dup_frozen(StringValue(val));
925
+
926
+ return bucket->key_prefix_val;
927
+ }
928
+
929
+ /* Document-method: hostname
930
+ *
931
+ * @since 1.0.0
932
+ *
933
+ * @return [String] the host name of the management interface (default: "localhost")
934
+ */
935
+ VALUE
936
+ cb_bucket_hostname_get(VALUE self)
937
+ {
938
+ struct cb_bucket_st *bucket = DATA_PTR(self);
939
+
940
+ if (bucket->handle) {
941
+ const char * host = lcb_get_host(bucket->handle);
942
+ unsigned long len = RSTRING_LEN(bucket->hostname);
943
+ if (len != strlen(host) || strncmp(RSTRING_PTR(bucket->hostname), host, len) != 0) {
944
+ bucket->hostname = STR_NEW_CSTR(host);
945
+ rb_str_freeze(bucket->hostname);
946
+ }
947
+ }
948
+ return bucket->hostname;
949
+ }
950
+
951
+ /* Document-method: port
952
+ *
953
+ * @since 1.0.0
954
+ *
955
+ * @return [Fixnum] the port number of the management interface (default: 8091)
956
+ */
957
+ VALUE
958
+ cb_bucket_port_get(VALUE self)
959
+ {
960
+ struct cb_bucket_st *bucket = DATA_PTR(self);
961
+ if (bucket->handle) {
962
+ bucket->port = atoi(lcb_get_port(bucket->handle));
963
+ }
964
+ return UINT2NUM(bucket->port);
965
+ }
966
+
967
+ /* Document-method: authority
968
+ *
969
+ * @since 1.0.0
970
+ *
971
+ * @return [String] host with port
972
+ */
973
+ VALUE
974
+ cb_bucket_authority_get(VALUE self)
975
+ {
976
+ struct cb_bucket_st *bucket = DATA_PTR(self);
977
+ VALUE old_hostname = bucket->hostname;
978
+ uint16_t old_port = bucket->port;
979
+ VALUE hostname = cb_bucket_hostname_get(self);
980
+ cb_bucket_port_get(self);
981
+
982
+ if (hostname != old_hostname || bucket->port != old_port) {
983
+ char port_s[8];
984
+ snprintf(port_s, sizeof(port_s), ":%u", bucket->port);
985
+ bucket->authority = rb_str_dup(hostname);
986
+ rb_str_cat2(bucket->authority, port_s);
987
+ rb_str_freeze(bucket->authority);
988
+ }
989
+ return bucket->authority;
990
+ }
991
+
992
+ /* Document-method: bucket
993
+ *
994
+ * @since 1.0.0
995
+ *
996
+ * @return [String] the bucket name
997
+ */
998
+ VALUE
999
+ cb_bucket_bucket_get(VALUE self)
1000
+ {
1001
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1002
+ return bucket->bucket;
1003
+ }
1004
+
1005
+ /* Document-method: pool
1006
+ *
1007
+ * @since 1.0.0
1008
+ *
1009
+ * @return [String] the pool name (usually "default")
1010
+ */
1011
+ VALUE
1012
+ cb_bucket_pool_get(VALUE self)
1013
+ {
1014
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1015
+ return bucket->pool;
1016
+ }
1017
+
1018
+ /* Document-method: username
1019
+ *
1020
+ * @since 1.0.0
1021
+ *
1022
+ * @return [String] the username for protected buckets (usually matches
1023
+ * the bucket name)
1024
+ */
1025
+ VALUE
1026
+ cb_bucket_username_get(VALUE self)
1027
+ {
1028
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1029
+ return bucket->username;
1030
+ }
1031
+
1032
+ /* Document-method: password
1033
+ *
1034
+ * @since 1.0.0
1035
+ *
1036
+ * @return [String] the password for protected buckets
1037
+ */
1038
+ VALUE
1039
+ cb_bucket_password_get(VALUE self)
1040
+ {
1041
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1042
+ return bucket->password;
1043
+ }
1044
+
1045
+ /* Document-method: environment
1046
+ *
1047
+ * @since 1.2.0
1048
+ *
1049
+ * @see Bucket#initialize
1050
+ *
1051
+ * @return [Symbol] the environment (+:development+ or +:production+)
1052
+ */
1053
+ VALUE
1054
+ cb_bucket_environment_get(VALUE self)
1055
+ {
1056
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1057
+ return bucket->environment;
1058
+ }
1059
+ /* Document-method: num_replicas
1060
+ *
1061
+ * @since 1.2.0.dp6
1062
+ *
1063
+ * The numbers of the replicas for each node in the cluster
1064
+ *
1065
+ * @return [Fixnum]
1066
+ */
1067
+ VALUE
1068
+ cb_bucket_num_replicas_get(VALUE self)
1069
+ {
1070
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1071
+ int32_t nr = lcb_get_num_replicas(bucket->handle);
1072
+ if (nr < 0) {
1073
+ return Qnil;
1074
+ } else {
1075
+ return INT2FIX(nr);
1076
+ }
1077
+ }
1078
+ /* Document-method: default_observe_timeout
1079
+ *
1080
+ * @since 1.2.0.dp6
1081
+ *
1082
+ * Get default timeout value for {Bucket#observe_and_wait} operation in
1083
+ * microseconds
1084
+ *
1085
+ * @return [Fixnum]
1086
+ */
1087
+ VALUE
1088
+ cb_bucket_default_observe_timeout_get(VALUE self)
1089
+ {
1090
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1091
+ return INT2FIX(bucket->default_observe_timeout);
1092
+ }
1093
+
1094
+ /* Document-method: default_observe_timeout=
1095
+ *
1096
+ * @since 1.2.0.dp6
1097
+ *
1098
+ * Set default timeout value for {Bucket#observe_and_wait} operation in
1099
+ * microseconds
1100
+ *
1101
+ * @return [Fixnum]
1102
+ */
1103
+ VALUE
1104
+ cb_bucket_default_observe_timeout_set(VALUE self, VALUE val)
1105
+ {
1106
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1107
+ bucket->default_observe_timeout = FIX2INT(val);
1108
+ return val;
1109
+ }
1110
+ /* Document-method: url
1111
+ *
1112
+ * @since 1.0.0
1113
+ *
1114
+ * @return [String] the address of the cluster management interface
1115
+ */
1116
+ VALUE
1117
+ cb_bucket_url_get(VALUE self)
1118
+ {
1119
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1120
+ VALUE str;
1121
+
1122
+ (void)cb_bucket_authority_get(self);
1123
+ str = rb_str_buf_new2("http://");
1124
+ rb_str_append(str, bucket->authority);
1125
+ rb_str_buf_cat2(str, "/pools/");
1126
+ rb_str_append(str, bucket->pool);
1127
+ rb_str_buf_cat2(str, "/buckets/");
1128
+ rb_str_append(str, bucket->bucket);
1129
+ rb_str_buf_cat2(str, "/");
1130
+ return str;
1131
+ }
1132
+
1133
+ /*
1134
+ * Returns a string containing a human-readable representation of the
1135
+ * {Bucket}.
1136
+ *
1137
+ * @since 1.0.0
1138
+ *
1139
+ * @return [String]
1140
+ */
1141
+ VALUE
1142
+ cb_bucket_inspect(VALUE self)
1143
+ {
1144
+ VALUE str;
1145
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1146
+ char buf[200];
1147
+
1148
+ str = rb_str_buf_new2("#<");
1149
+ rb_str_buf_cat2(str, rb_obj_classname(self));
1150
+ snprintf(buf, 25, ":%p \"", (void *)self);
1151
+ (void)cb_bucket_authority_get(self);
1152
+ rb_str_buf_cat2(str, buf);
1153
+ rb_str_buf_cat2(str, "http://");
1154
+ rb_str_append(str, bucket->authority);
1155
+ rb_str_buf_cat2(str, "/pools/");
1156
+ rb_str_append(str, bucket->pool);
1157
+ rb_str_buf_cat2(str, "/buckets/");
1158
+ rb_str_append(str, bucket->bucket);
1159
+ rb_str_buf_cat2(str, "/\" transcoder=");
1160
+ rb_str_append(str, rb_inspect(bucket->transcoder));
1161
+ snprintf(buf, 150, ", default_flags=0x%x, quiet=%s, connected=%s, timeout=%u",
1162
+ bucket->default_flags,
1163
+ bucket->quiet ? "true" : "false",
1164
+ (bucket->handle && bucket->connected) ? "true" : "false",
1165
+ bucket->timeout);
1166
+ rb_str_buf_cat2(str, buf);
1167
+ if (RTEST(bucket->key_prefix_val)) {
1168
+ rb_str_buf_cat2(str, ", key_prefix=");
1169
+ rb_str_append(str, rb_inspect(bucket->key_prefix_val));
1170
+ }
1171
+ rb_str_buf_cat2(str, ">");
1172
+
1173
+ return str;
1174
+ }
1175
+
1176
+ static void
1177
+ do_loop(struct cb_bucket_st *bucket)
1178
+ {
1179
+ lcb_wait(bucket->handle);
1180
+ bucket->nbytes = 0;
1181
+ }
1182
+
1183
+ void
1184
+ cb_maybe_do_loop(struct cb_bucket_st *bucket)
1185
+ {
1186
+ if (bucket->threshold != 0 && bucket->nbytes > bucket->threshold) {
1187
+ do_loop(bucket);
1188
+ }
1189
+ }
1190
+
1191
+ static VALUE
1192
+ do_run(VALUE *args)
1193
+ {
1194
+ VALUE self = args[0], opts = args[1], proc = args[2], exc;
1195
+ VALUE was_async = args[3];
1196
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1197
+
1198
+ if (bucket->handle == NULL) {
1199
+ rb_raise(cb_eConnectError, "closed connection");
1200
+ }
1201
+
1202
+ if (bucket->running) {
1203
+ rb_raise(cb_eInvalidError, "nested #run");
1204
+ }
1205
+ bucket->threshold = 0;
1206
+ if (opts != Qnil) {
1207
+ VALUE arg;
1208
+ Check_Type(opts, T_HASH);
1209
+ arg = rb_hash_aref(opts, cb_sym_send_threshold);
1210
+ if (arg != Qnil) {
1211
+ bucket->threshold = (uint32_t)NUM2ULONG(arg);
1212
+ }
1213
+ }
1214
+ bucket->async = 1;
1215
+ bucket->running = 1;
1216
+ if (proc != Qnil) {
1217
+ cb_proc_call(bucket, proc, 1, self);
1218
+ }
1219
+ if (bucket->exception != Qnil) {
1220
+ exc = bucket->exception;
1221
+ bucket->exception = Qnil;
1222
+ if (was_async) {
1223
+ cb_async_error_notify(bucket, exc);
1224
+ /* XXX return here? */
1225
+ } else {
1226
+ rb_exc_raise(exc);
1227
+ }
1228
+ }
1229
+ do_loop(bucket);
1230
+ if (bucket->exception != Qnil) {
1231
+ exc = bucket->exception;
1232
+ bucket->exception = Qnil;
1233
+ if (!was_async) {
1234
+ rb_exc_raise(exc);
1235
+ }
1236
+ /* async connections notified immediately from the callbacks
1237
+ * via cb_async_error_notify() */
1238
+ }
1239
+ return Qnil;
1240
+ }
1241
+
1242
+ static VALUE
1243
+ ensure_run(VALUE *args)
1244
+ {
1245
+ VALUE self = args[0];
1246
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1247
+
1248
+ bucket->running = 0;
1249
+ bucket->async = args[3];
1250
+ bucket->running = args[4];
1251
+ return Qnil;
1252
+ }
1253
+
1254
+ /*
1255
+ * Run the event loop.
1256
+ *
1257
+ * @since 1.0.0
1258
+ *
1259
+ * @param [Hash] options The options for operation for connection
1260
+ * @option options [Fixnum] :send_threshold (0) if the internal command
1261
+ * buffer will exceeds this value, then the library will start network
1262
+ * interaction and block the current thread until all scheduled commands
1263
+ * will be completed.
1264
+ *
1265
+ * @yieldparam [Bucket] bucket the bucket instance
1266
+ *
1267
+ * @example Use block to run the loop
1268
+ * c = Couchbase.new
1269
+ * c.run do
1270
+ * c.get("foo") {|ret| puts ret.value}
1271
+ * end
1272
+ *
1273
+ * @example Use lambda to run the loop
1274
+ * c = Couchbase.new
1275
+ * operations = lambda do |c|
1276
+ * c.get("foo") {|ret| puts ret.value}
1277
+ * end
1278
+ * c.run(&operations)
1279
+ *
1280
+ * @example Use threshold to send out commands automatically
1281
+ * c = Couchbase.connect
1282
+ * sent = 0
1283
+ * c.run(:send_threshold => 8192) do # 8Kb
1284
+ * c.set("foo1", "x" * 100) {|r| sent += 1}
1285
+ * # 128 bytes buffered, sent is 0 now
1286
+ * c.set("foo2", "x" * 10000) {|r| sent += 1}
1287
+ * # 10028 bytes added, sent is 2 now
1288
+ * c.set("foo3", "x" * 100) {|r| sent += 1}
1289
+ * end
1290
+ * # all commands were executed and sent is 3 now
1291
+ *
1292
+ * @example Use {Couchbase::Bucket#run} without block for async connection
1293
+ * c = Couchbase.new(:async => true)
1294
+ * c.run # ensure that instance connected
1295
+ * c.set("foo", "bar"){|r| puts r.cas}
1296
+ * c.run
1297
+ *
1298
+ * @return [nil]
1299
+ *
1300
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1301
+ */
1302
+ VALUE
1303
+ cb_bucket_run(int argc, VALUE *argv, VALUE self)
1304
+ {
1305
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1306
+ VALUE args[5];
1307
+
1308
+ /* it is allowed to omit block for async connections */
1309
+ if (!bucket->async) {
1310
+ rb_need_block();
1311
+ }
1312
+ args[0] = self;
1313
+ rb_scan_args(argc, argv, "01&", &args[1], &args[2]);
1314
+ args[3] = bucket->async;
1315
+ args[4] = bucket->running;
1316
+ rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
1317
+ return Qnil;
1318
+ }
1319
+
1320
+ /*
1321
+ * Stop the event loop.
1322
+ *
1323
+ * @since 1.2.0
1324
+ *
1325
+ * @example Breakout the event loop when 5th request is completed
1326
+ * c = Couchbase.connect
1327
+ * c.run do
1328
+ * 10.times do |ii|
1329
+ * c.get("foo") do |ret|
1330
+ * puts ii
1331
+ * c.stop if ii == 5
1332
+ * end
1333
+ * end
1334
+ * end
1335
+ *
1336
+ * @return [nil]
1337
+ */
1338
+ VALUE
1339
+ cb_bucket_stop(VALUE self)
1340
+ {
1341
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1342
+ lcb_breakout(bucket->handle);
1343
+ return Qnil;
1344
+ }
1345
+
1346
+ /*
1347
+ * Close the connection to the cluster
1348
+ *
1349
+ * @since 1.1.0
1350
+ *
1351
+ * @return [true]
1352
+ *
1353
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1354
+ */
1355
+ VALUE
1356
+ cb_bucket_disconnect(VALUE self)
1357
+ {
1358
+ struct cb_bucket_st *bucket = DATA_PTR(self);
1359
+
1360
+ if (bucket->handle) {
1361
+ lcb_destroy(bucket->handle);
1362
+ lcb_destroy_io_ops(bucket->io);
1363
+ bucket->handle = NULL;
1364
+ bucket->io = NULL;
1365
+ bucket->connected = 0;
1366
+ return Qtrue;
1367
+ } else {
1368
+ rb_raise(cb_eConnectError, "closed connection");
1369
+ return Qfalse;
1370
+ }
1371
+ }
1372
+
1373
+