jmoses-couchbase 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+