couchbase 1.1.5 → 1.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +12 -1
  3. data/HISTORY.markdown +112 -1
  4. data/README.markdown +149 -6
  5. data/couchbase.gemspec +5 -1
  6. data/ext/couchbase_ext/.gitignore +4 -0
  7. data/ext/couchbase_ext/arguments.c +973 -0
  8. data/ext/couchbase_ext/arithmetic.c +322 -0
  9. data/ext/couchbase_ext/bucket.c +1092 -0
  10. data/ext/couchbase_ext/couchbase_ext.c +618 -3247
  11. data/ext/couchbase_ext/couchbase_ext.h +519 -0
  12. data/ext/couchbase_ext/delete.c +167 -0
  13. data/ext/couchbase_ext/extconf.rb +24 -5
  14. data/ext/couchbase_ext/get.c +301 -0
  15. data/ext/couchbase_ext/gethrtime.c +124 -0
  16. data/ext/couchbase_ext/http.c +402 -0
  17. data/ext/couchbase_ext/observe.c +174 -0
  18. data/ext/couchbase_ext/result.c +126 -0
  19. data/ext/couchbase_ext/stats.c +169 -0
  20. data/ext/couchbase_ext/store.c +522 -0
  21. data/ext/couchbase_ext/timer.c +192 -0
  22. data/ext/couchbase_ext/touch.c +190 -0
  23. data/ext/couchbase_ext/unlock.c +180 -0
  24. data/ext/couchbase_ext/utils.c +471 -0
  25. data/ext/couchbase_ext/version.c +147 -0
  26. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  27. data/lib/active_support/cache/couchbase_store.rb +356 -0
  28. data/lib/couchbase.rb +24 -3
  29. data/lib/couchbase/bucket.rb +372 -9
  30. data/lib/couchbase/result.rb +26 -0
  31. data/lib/couchbase/utils.rb +59 -0
  32. data/lib/couchbase/version.rb +1 -1
  33. data/lib/couchbase/view.rb +305 -0
  34. data/lib/couchbase/view_row.rb +230 -0
  35. data/lib/ext/multi_json_fix.rb +47 -0
  36. data/lib/rack/session/couchbase.rb +104 -0
  37. data/tasks/compile.rake +5 -14
  38. data/test/setup.rb +6 -2
  39. data/test/test_arithmetic.rb +32 -2
  40. data/test/test_async.rb +18 -4
  41. data/test/test_bucket.rb +11 -1
  42. data/test/test_cas.rb +13 -3
  43. data/test/test_couchbase_rails_cache_store.rb +294 -0
  44. data/test/test_delete.rb +60 -3
  45. data/test/test_format.rb +28 -17
  46. data/test/test_get.rb +91 -14
  47. data/test/test_store.rb +31 -1
  48. data/test/{test_flush.rb → test_timer.rb} +11 -18
  49. data/test/test_touch.rb +33 -5
  50. data/test/test_unlock.rb +120 -0
  51. data/test/test_utils.rb +26 -0
  52. metadata +101 -8
@@ -0,0 +1,322 @@
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
+ arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *resp)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ struct bucket_st *bucket = ctx->bucket;
25
+ VALUE cas, key, val, *rv = ctx->rv, exc, res;
26
+ ID o;
27
+
28
+ ctx->nqueries--;
29
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
30
+ strip_key_prefix(bucket, key);
31
+
32
+ cas = resp->v.v0.cas > 0 ? ULL2NUM(resp->v.v0.cas) : Qnil;
33
+ o = ctx->arith > 0 ? sym_increment : sym_decrement;
34
+ exc = cb_check_error(error, "failed to perform arithmetic operation", key);
35
+ if (exc != Qnil) {
36
+ rb_ivar_set(exc, id_iv_cas, cas);
37
+ rb_ivar_set(exc, id_iv_operation, o);
38
+ if (bucket->async) {
39
+ if (bucket->on_error_proc != Qnil) {
40
+ cb_proc_call(bucket->on_error_proc, 3, o, key, exc);
41
+ } else {
42
+ if (NIL_P(bucket->exception)) {
43
+ bucket->exception = exc;
44
+ }
45
+ }
46
+ }
47
+ if (NIL_P(ctx->exception)) {
48
+ ctx->exception = cb_gc_protect(bucket, exc);
49
+ }
50
+ }
51
+ val = ULL2NUM(resp->v.v0.value);
52
+ if (bucket->async) { /* asynchronous */
53
+ if (ctx->proc != Qnil) {
54
+ res = rb_class_new_instance(0, NULL, cResult);
55
+ rb_ivar_set(res, id_iv_error, exc);
56
+ rb_ivar_set(res, id_iv_operation, o);
57
+ rb_ivar_set(res, id_iv_key, key);
58
+ rb_ivar_set(res, id_iv_value, val);
59
+ rb_ivar_set(res, id_iv_cas, cas);
60
+ cb_proc_call(ctx->proc, 1, res);
61
+ }
62
+ } else { /* synchronous */
63
+ if (NIL_P(exc)) {
64
+ if (ctx->extended) {
65
+ rb_hash_aset(*rv, key, rb_ary_new3(2, val, cas));
66
+ } else {
67
+ rb_hash_aset(*rv, key, val);
68
+ }
69
+ }
70
+ }
71
+ if (ctx->nqueries == 0) {
72
+ cb_gc_unprotect(bucket, ctx->proc);
73
+ }
74
+ (void)handle;
75
+ }
76
+
77
+ static inline VALUE
78
+ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
79
+ {
80
+ struct bucket_st *bucket = DATA_PTR(self);
81
+ struct context_st *ctx;
82
+ VALUE args, rv, proc, exc;
83
+ lcb_error_t err;
84
+ struct params_st params;
85
+
86
+ if (bucket->handle == NULL) {
87
+ rb_raise(eConnectError, "closed connection");
88
+ }
89
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
90
+ if (!bucket->async && proc != Qnil) {
91
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
92
+ }
93
+ memset(&params, 0, sizeof(struct params_st));
94
+ params.type = cmd_arith;
95
+ params.bucket = bucket;
96
+ params.cmd.arith.sign = sign;
97
+ cb_params_build(&params, RARRAY_LEN(args), args);
98
+ ctx = xcalloc(1, sizeof(struct context_st));
99
+ if (ctx == NULL) {
100
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
101
+ }
102
+ rv = rb_hash_new();
103
+ ctx->rv = &rv;
104
+ ctx->bucket = bucket;
105
+ ctx->proc = cb_gc_protect(bucket, proc);
106
+ ctx->exception = Qnil;
107
+ ctx->nqueries = params.cmd.arith.num;
108
+ err = lcb_arithmetic(bucket->handle, (const void *)ctx,
109
+ params.cmd.arith.num, params.cmd.arith.ptr);
110
+ cb_params_destroy(&params);
111
+ exc = cb_check_error(err, "failed to schedule arithmetic request", Qnil);
112
+ if (exc != Qnil) {
113
+ xfree(ctx);
114
+ rb_exc_raise(exc);
115
+ }
116
+ bucket->nbytes += params.npayload;
117
+ if (bucket->async) {
118
+ maybe_do_loop(bucket);
119
+ return Qnil;
120
+ } else {
121
+ if (ctx->nqueries > 0) {
122
+ /* we have some operations pending */
123
+ lcb_wait(bucket->handle);
124
+ }
125
+ exc = ctx->exception;
126
+ xfree(ctx);
127
+ if (exc != Qnil) {
128
+ cb_gc_unprotect(bucket, exc);
129
+ rb_exc_raise(exc);
130
+ }
131
+ if (params.cmd.store.num > 1) {
132
+ return rv; /* return as a hash {key => cas, ...} */
133
+ } else {
134
+ VALUE vv = Qnil;
135
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
136
+ return vv;
137
+ }
138
+ return rv;
139
+ }
140
+ }
141
+
142
+ /*
143
+ * Increment the value of an existing numeric key
144
+ *
145
+ * @since 1.0.0
146
+ *
147
+ * The increment methods allow you to increase a given stored integer
148
+ * value. These are the incremental equivalent of the decrement operations
149
+ * and work on the same basis; updating the value of a key if it can be
150
+ * parsed to an integer. The update operation occurs on the server and is
151
+ * provided at the protocol level. This simplifies what would otherwise be a
152
+ * two-stage get and set operation.
153
+ *
154
+ * @note that server values stored and transmitted as unsigned numbers,
155
+ * therefore if you try to store negative number and then increment or
156
+ * decrement it will cause overflow. (see "Integer overflow" example
157
+ * below)
158
+ *
159
+ * @overload incr(key, delta = 1, options = {})
160
+ * @param key [String, Symbol] Key used to reference the value.
161
+ * @param delta [Fixnum] Integer (up to 64 bits) value to increment
162
+ * @param options [Hash] Options for operation.
163
+ * @option options [true, false] :create (false) If set to +true+, it will
164
+ * initialize the key with zero value and zero flags (use +:initial+
165
+ * option to set another initial value). Note: it won't increment the
166
+ * missing value.
167
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
168
+ * missing key initialization. This option imply +:create+ option is
169
+ * +true+.
170
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
171
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
172
+ * absolute times (from the epoch). This option ignored for existent
173
+ * keys.
174
+ * @option options [true, false] :extended (false) If set to +true+, the
175
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
176
+ * returns just value.
177
+ *
178
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
179
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
180
+ *
181
+ * @return [Fixnum] the actual value of the key.
182
+ *
183
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
184
+ * option isn't +true+.
185
+ *
186
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
187
+ * value
188
+ *
189
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
190
+ *
191
+ * @raise [ArgumentError] when passing the block in synchronous mode
192
+ *
193
+ * @example Increment key by one
194
+ * c.incr("foo")
195
+ *
196
+ * @example Increment key by 50
197
+ * c.incr("foo", 50)
198
+ *
199
+ * @example Increment key by one <b>OR</b> initialize with zero
200
+ * c.incr("foo", :create => true) #=> will return old+1 or 0
201
+ *
202
+ * @example Increment key by one <b>OR</b> initialize with three
203
+ * c.incr("foo", 50, :initial => 3) #=> will return old+50 or 3
204
+ *
205
+ * @example Increment key and get its CAS value
206
+ * val, cas = c.incr("foo", :extended => true)
207
+ *
208
+ * @example Integer overflow
209
+ * c.set("foo", -100)
210
+ * c.get("foo") #=> -100
211
+ * c.incr("foo") #=> 18446744073709551517
212
+ *
213
+ * @example Asynchronous invocation
214
+ * c.run do
215
+ * c.incr("foo") do |ret|
216
+ * ret.operation #=> :increment
217
+ * ret.success? #=> true
218
+ * ret.key #=> "foo"
219
+ * ret.value
220
+ * ret.cas
221
+ * end
222
+ * end
223
+ *
224
+ */
225
+ VALUE
226
+ cb_bucket_incr(int argc, VALUE *argv, VALUE self)
227
+ {
228
+ return cb_bucket_arithmetic(+1, argc, argv, self);
229
+ }
230
+
231
+ /*
232
+ * Decrement the value of an existing numeric key
233
+ *
234
+ * @since 1.0.0
235
+ *
236
+ * The decrement methods reduce the value of a given key if the
237
+ * corresponding value can be parsed to an integer value. These operations
238
+ * are provided at a protocol level to eliminate the need to get, update,
239
+ * and reset a simple integer value in the database. It supports the use of
240
+ * an explicit offset value that will be used to reduce the stored value in
241
+ * the database.
242
+ *
243
+ * @note that server values stored and transmitted as unsigned numbers,
244
+ * therefore if you try to decrement negative or zero key, you will always
245
+ * get zero.
246
+ *
247
+ * @overload decr(key, delta = 1, options = {})
248
+ * @param key [String, Symbol] Key used to reference the value.
249
+ * @param delta [Fixnum] Integer (up to 64 bits) value to decrement
250
+ * @param options [Hash] Options for operation.
251
+ * @option options [true, false] :create (false) If set to +true+, it will
252
+ * initialize the key with zero value and zero flags (use +:initial+
253
+ * option to set another initial value). Note: it won't decrement the
254
+ * missing value.
255
+ * @option options [Fixnum] :initial (0) Integer (up to 64 bits) value for
256
+ * missing key initialization. This option imply +:create+ option is
257
+ * +true+.
258
+ * @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
259
+ * Values larger than 30*24*60*60 seconds (30 days) are interpreted as
260
+ * absolute times (from the epoch). This option ignored for existent
261
+ * keys.
262
+ * @option options [true, false] :extended (false) If set to +true+, the
263
+ * operation will return tuple +[value, cas]+, otherwise (by default) it
264
+ * returns just value.
265
+ *
266
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
267
+ * (valid attributes: +error+, +operation+, +key+, +value+, +cas+).
268
+ *
269
+ * @return [Fixnum] the actual value of the key.
270
+ *
271
+ * @raise [Couchbase::Error::NotFound] if key is missing and +:create+
272
+ * option isn't +true+.
273
+ *
274
+ * @raise [Couchbase::Error::DeltaBadval] if the key contains non-numeric
275
+ * value
276
+ *
277
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
278
+ *
279
+ * @raise [ArgumentError] when passing the block in synchronous mode
280
+ *
281
+ * @example Decrement key by one
282
+ * c.decr("foo")
283
+ *
284
+ * @example Decrement key by 50
285
+ * c.decr("foo", 50)
286
+ *
287
+ * @example Decrement key by one <b>OR</b> initialize with zero
288
+ * c.decr("foo", :create => true) #=> will return old-1 or 0
289
+ *
290
+ * @example Decrement key by one <b>OR</b> initialize with three
291
+ * c.decr("foo", 50, :initial => 3) #=> will return old-50 or 3
292
+ *
293
+ * @example Decrement key and get its CAS value
294
+ * val, cas = c.decr("foo", :extended => true)
295
+ *
296
+ * @example Decrementing zero
297
+ * c.set("foo", 0)
298
+ * c.decrement("foo", 100500) #=> 0
299
+ *
300
+ * @example Decrementing negative value
301
+ * c.set("foo", -100)
302
+ * c.decrement("foo", 100500) #=> 0
303
+ *
304
+ * @example Asynchronous invocation
305
+ * c.run do
306
+ * c.decr("foo") do |ret|
307
+ * ret.operation #=> :decrement
308
+ * ret.success? #=> true
309
+ * ret.key #=> "foo"
310
+ * ret.value
311
+ * ret.cas
312
+ * end
313
+ * end
314
+ *
315
+ */
316
+ VALUE
317
+ cb_bucket_decr(int argc, VALUE *argv, VALUE self)
318
+ {
319
+ return cb_bucket_arithmetic(-1, argc, argv, self);
320
+ }
321
+
322
+
@@ -0,0 +1,1092 @@
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 void
21
+ error_callback(lcb_t handle, lcb_error_t error, const char *errinfo)
22
+ {
23
+ struct bucket_st *bucket = (struct bucket_st *)lcb_get_cookie(handle);
24
+
25
+ bucket->io->stop_event_loop(bucket->io);
26
+ bucket->exception = cb_check_error(error, errinfo, Qnil);
27
+ }
28
+
29
+ void
30
+ cb_bucket_free(void *ptr)
31
+ {
32
+ struct bucket_st *bucket = ptr;
33
+
34
+ if (bucket) {
35
+ if (bucket->handle) {
36
+ lcb_destroy(bucket->handle);
37
+ }
38
+ xfree(bucket->authority);
39
+ xfree(bucket->hostname);
40
+ xfree(bucket->pool);
41
+ xfree(bucket->bucket);
42
+ xfree(bucket->username);
43
+ xfree(bucket->password);
44
+ xfree(bucket->key_prefix);
45
+ xfree(bucket);
46
+ }
47
+ }
48
+
49
+ void
50
+ cb_bucket_mark(void *ptr)
51
+ {
52
+ struct bucket_st *bucket = ptr;
53
+
54
+ if (bucket) {
55
+ rb_gc_mark(bucket->exception);
56
+ rb_gc_mark(bucket->on_error_proc);
57
+ rb_gc_mark(bucket->key_prefix_val);
58
+ rb_gc_mark(bucket->object_space);
59
+ }
60
+ }
61
+
62
+ static void
63
+ do_scan_connection_options(struct bucket_st *bucket, int argc, VALUE *argv)
64
+ {
65
+ VALUE uri, opts, arg;
66
+ size_t len;
67
+
68
+ if (rb_scan_args(argc, argv, "02", &uri, &opts) > 0) {
69
+ if (TYPE(uri) == T_HASH && argc == 1) {
70
+ opts = uri;
71
+ uri = Qnil;
72
+ }
73
+ if (uri != Qnil) {
74
+ const char path_re[] = "^(/pools/([A-Za-z0-9_.-]+)(/buckets/([A-Za-z0-9_.-]+))?)?";
75
+ VALUE match, uri_obj, re;
76
+
77
+ Check_Type(uri, T_STRING);
78
+ uri_obj = rb_funcall(mURI, id_parse, 1, uri);
79
+
80
+ arg = rb_funcall(uri_obj, id_scheme, 0);
81
+ if (arg == Qnil || rb_str_cmp(arg, STR_NEW_CSTR("http"))) {
82
+ rb_raise(rb_eArgError, "invalid URI: invalid scheme");
83
+ }
84
+
85
+ arg = rb_funcall(uri_obj, id_user, 0);
86
+ if (arg != Qnil) {
87
+ xfree(bucket->username);
88
+ bucket->username = strdup(RSTRING_PTR(arg));
89
+ if (bucket->username == NULL) {
90
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
91
+ }
92
+ }
93
+
94
+ arg = rb_funcall(uri_obj, id_password, 0);
95
+ if (arg != Qnil) {
96
+ xfree(bucket->password);
97
+ bucket->password = strdup(RSTRING_PTR(arg));
98
+ if (bucket->password == NULL) {
99
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
100
+ }
101
+ }
102
+ arg = rb_funcall(uri_obj, id_host, 0);
103
+ if (arg != Qnil) {
104
+ xfree(bucket->hostname);
105
+ bucket->hostname = strdup(RSTRING_PTR(arg));
106
+ if (bucket->hostname == NULL) {
107
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
108
+ }
109
+ } else {
110
+ rb_raise(rb_eArgError, "invalid URI: missing hostname");
111
+ }
112
+
113
+ arg = rb_funcall(uri_obj, id_port, 0);
114
+ bucket->port = NIL_P(arg) ? 8091 : (uint16_t)NUM2UINT(arg);
115
+
116
+ arg = rb_funcall(uri_obj, id_path, 0);
117
+ re = rb_reg_new(path_re, sizeof(path_re) - 1, 0);
118
+ match = rb_funcall(re, id_match, 1, arg);
119
+ arg = rb_reg_nth_match(2, match);
120
+ xfree(bucket->pool);
121
+ bucket->pool = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
122
+ arg = rb_reg_nth_match(4, match);
123
+ xfree(bucket->bucket);
124
+ bucket->bucket = strdup(NIL_P(arg) ? "default" : RSTRING_PTR(arg));
125
+ }
126
+ if (TYPE(opts) == T_HASH) {
127
+ arg = rb_hash_aref(opts, sym_node_list);
128
+ if (arg != Qnil) {
129
+ VALUE tt;
130
+ xfree(bucket->node_list);
131
+ Check_Type(arg, T_ARRAY);
132
+ tt = rb_ary_join(arg, STR_NEW_CSTR(";"));
133
+ bucket->node_list = strdup(StringValueCStr(tt));
134
+ }
135
+ arg = rb_hash_aref(opts, sym_hostname);
136
+ if (arg != Qnil) {
137
+ xfree(bucket->hostname);
138
+ bucket->hostname = strdup(StringValueCStr(arg));
139
+ }
140
+ arg = rb_hash_aref(opts, sym_pool);
141
+ if (arg != Qnil) {
142
+ xfree(bucket->pool);
143
+ bucket->pool = strdup(StringValueCStr(arg));
144
+ }
145
+ arg = rb_hash_aref(opts, sym_bucket);
146
+ if (arg != Qnil) {
147
+ xfree(bucket->bucket);
148
+ bucket->bucket = strdup(StringValueCStr(arg));
149
+ }
150
+ arg = rb_hash_aref(opts, sym_username);
151
+ if (arg != Qnil) {
152
+ xfree(bucket->username);
153
+ bucket->username = strdup(StringValueCStr(arg));
154
+ }
155
+ arg = rb_hash_aref(opts, sym_password);
156
+ if (arg != Qnil) {
157
+ xfree(bucket->password);
158
+ bucket->password = strdup(StringValueCStr(arg));
159
+ }
160
+ arg = rb_hash_aref(opts, sym_port);
161
+ if (arg != Qnil) {
162
+ bucket->port = (uint16_t)NUM2UINT(arg);
163
+ }
164
+ if (RTEST(rb_funcall(opts, id_has_key_p, 1, sym_quiet))) {
165
+ bucket->quiet = RTEST(rb_hash_aref(opts, sym_quiet));
166
+ }
167
+ arg = rb_hash_aref(opts, sym_timeout);
168
+ if (arg != Qnil) {
169
+ bucket->timeout = (uint32_t)NUM2ULONG(arg);
170
+ }
171
+ arg = rb_hash_aref(opts, sym_default_ttl);
172
+ if (arg != Qnil) {
173
+ bucket->default_ttl = (uint32_t)NUM2ULONG(arg);
174
+ }
175
+ arg = rb_hash_aref(opts, sym_default_observe_timeout);
176
+ if (arg != Qnil) {
177
+ bucket->default_observe_timeout = (uint32_t)NUM2ULONG(arg);
178
+ }
179
+ arg = rb_hash_aref(opts, sym_default_flags);
180
+ if (arg != Qnil) {
181
+ bucket->default_flags = (uint32_t)NUM2ULONG(arg);
182
+ }
183
+ arg = rb_hash_aref(opts, sym_default_format);
184
+ if (arg != Qnil) {
185
+ if (TYPE(arg) == T_FIXNUM) {
186
+ switch (FIX2INT(arg)) {
187
+ case FMT_DOCUMENT:
188
+ arg = sym_document;
189
+ break;
190
+ case FMT_MARSHAL:
191
+ arg = sym_marshal;
192
+ break;
193
+ case FMT_PLAIN:
194
+ arg = sym_plain;
195
+ break;
196
+ }
197
+ }
198
+ if (arg == sym_document || arg == sym_marshal || arg == sym_plain) {
199
+ bucket->default_format = arg;
200
+ bucket->default_flags = flags_set_format(bucket->default_flags, arg);
201
+ }
202
+ }
203
+ arg = rb_hash_aref(opts, sym_environment);
204
+ if (arg != Qnil) {
205
+ if (arg == sym_production || arg == sym_development) {
206
+ bucket->environment = arg;
207
+ }
208
+ }
209
+ arg = rb_hash_aref(opts, sym_key_prefix);
210
+ if (arg != Qnil) {
211
+ xfree(bucket->key_prefix);
212
+ bucket->key_prefix = strdup(StringValueCStr(arg));
213
+ bucket->key_prefix_val = STR_NEW_CSTR(bucket->key_prefix);
214
+ }
215
+ } else {
216
+ opts = Qnil;
217
+ }
218
+ }
219
+ if (bucket->password && bucket->username == NULL) {
220
+ bucket->username = strdup(bucket->bucket);
221
+ }
222
+ len = strlen(bucket->hostname) + 10;
223
+ if (bucket->default_observe_timeout < 2) {
224
+ rb_raise(rb_eArgError, "default_observe_timeout is too low");
225
+ }
226
+ xfree(bucket->authority);
227
+ bucket->authority = xcalloc(len, sizeof(char));
228
+ if (bucket->authority == NULL) {
229
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
230
+ }
231
+ snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
232
+ }
233
+
234
+ static void
235
+ do_connect(struct bucket_st *bucket)
236
+ {
237
+ lcb_error_t err;
238
+ struct lcb_create_st create_opts;
239
+
240
+ if (bucket->handle) {
241
+ lcb_destroy(bucket->handle);
242
+ bucket->handle = NULL;
243
+ bucket->io = NULL;
244
+ }
245
+ err = lcb_create_io_ops(&bucket->io, NULL);
246
+ if (err != LCB_SUCCESS) {
247
+ rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
248
+ }
249
+
250
+ memset(&create_opts, 0, sizeof(struct lcb_create_st));
251
+ create_opts.v.v0.host = bucket->node_list ? bucket-> node_list : bucket->authority;
252
+ create_opts.v.v0.user = bucket->username;
253
+ create_opts.v.v0.passwd = bucket->password;
254
+ create_opts.v.v0.bucket = bucket->bucket;
255
+ create_opts.v.v0.io = bucket->io;
256
+ err = lcb_create(&bucket->handle, &create_opts);
257
+ if (err != LCB_SUCCESS) {
258
+ rb_exc_raise(cb_check_error(err, "failed to create libcouchbase instance", Qnil));
259
+ }
260
+ lcb_set_cookie(bucket->handle, bucket);
261
+ (void)lcb_set_error_callback(bucket->handle, error_callback);
262
+ (void)lcb_set_store_callback(bucket->handle, storage_callback);
263
+ (void)lcb_set_get_callback(bucket->handle, get_callback);
264
+ (void)lcb_set_touch_callback(bucket->handle, touch_callback);
265
+ (void)lcb_set_remove_callback(bucket->handle, delete_callback);
266
+ (void)lcb_set_stat_callback(bucket->handle, stat_callback);
267
+ (void)lcb_set_arithmetic_callback(bucket->handle, arithmetic_callback);
268
+ (void)lcb_set_version_callback(bucket->handle, version_callback);
269
+ (void)lcb_set_view_complete_callback(bucket->handle, http_complete_callback);
270
+ (void)lcb_set_view_data_callback(bucket->handle, http_data_callback);
271
+ (void)lcb_set_management_complete_callback(bucket->handle, http_complete_callback);
272
+ (void)lcb_set_management_data_callback(bucket->handle, http_data_callback);
273
+ (void)lcb_set_observe_callback(bucket->handle, observe_callback);
274
+ (void)lcb_set_unlock_callback(bucket->handle, unlock_callback);
275
+
276
+ if (bucket->timeout > 0) {
277
+ lcb_set_timeout(bucket->handle, bucket->timeout);
278
+ } else {
279
+ bucket->timeout = lcb_get_timeout(bucket->handle);
280
+ }
281
+ err = lcb_connect(bucket->handle);
282
+ if (err != LCB_SUCCESS) {
283
+ lcb_destroy(bucket->handle);
284
+ bucket->handle = NULL;
285
+ bucket->io = NULL;
286
+ rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
287
+ }
288
+ bucket->exception = Qnil;
289
+ lcb_wait(bucket->handle);
290
+ if (bucket->exception != Qnil) {
291
+ lcb_destroy(bucket->handle);
292
+ bucket->handle = NULL;
293
+ bucket->io = NULL;
294
+ rb_exc_raise(bucket->exception);
295
+ }
296
+ }
297
+
298
+ VALUE
299
+ cb_bucket_alloc(VALUE klass)
300
+ {
301
+ VALUE obj;
302
+ struct bucket_st *bucket;
303
+
304
+ /* allocate new bucket struct and set it to zero */
305
+ obj = Data_Make_Struct(klass, struct bucket_st, cb_bucket_mark, cb_bucket_free,
306
+ bucket);
307
+ return obj;
308
+ }
309
+
310
+ /*
311
+ * Initialize new Bucket.
312
+ *
313
+ * @since 1.0.0
314
+ *
315
+ * @overload initialize(url, options = {})
316
+ * Initialize bucket using URI of the cluster and options. It is possible
317
+ * to override some parts of URI using the options keys (e.g. :host or
318
+ * :port)
319
+ *
320
+ * @param [String] url The full URL of management API of the cluster.
321
+ * @param [Hash] options The options for connection. See options definition
322
+ * below.
323
+ *
324
+ * @overload initialize(options = {})
325
+ * Initialize bucket using options only.
326
+ *
327
+ * @param [Hash] options The options for operation for connection
328
+ * @option options [Array] :node_list (nil) the list of nodes to connect
329
+ * to. If specified it takes precedence over +:host+ option. The list
330
+ * must be array of strings in form of host names or host names with
331
+ * ports (in first case port 8091 will be used, see examples).
332
+ * @option options [String] :host ("localhost") the hostname or IP address
333
+ * of the node
334
+ * @option options [Fixnum] :port (8091) the port of the managemenent API
335
+ * @option options [String] :pool ("default") the pool name
336
+ * @option options [String] :bucket ("default") the bucket name
337
+ * @option options [Fixnum] :default_ttl (0) the TTL used by default during
338
+ * storing key-value pairs.
339
+ * @option options [Fixnum] :default_flags (0) the default flags.
340
+ * @option options [Symbol] :default_format (:document) the format, which
341
+ * will be used for values by default. Note that changing format will
342
+ * amend flags. (see {Bucket#default_format})
343
+ * @option options [String] :username (nil) the user name to connect to the
344
+ * cluster. Used to authenticate on management API. The username could
345
+ * be skipped for protected buckets, the bucket name will be used
346
+ * instead.
347
+ * @option options [String] :password (nil) the password of the user.
348
+ * @option options [true, false] :quiet (false) the flag controlling if raising
349
+ * exception when the client executes operations on unexising keys. If it
350
+ * is +true+ it will raise {Couchbase::Error::NotFound} exceptions. The
351
+ * default behaviour is to return +nil+ value silently (might be useful in
352
+ * Rails cache).
353
+ * @option options [Symbol] :environment (:production) the mode of the
354
+ * connection. Currently it influences only on design documents set. If
355
+ * the environment is +:development+, you will able to get design
356
+ * documents with 'dev_' prefix, otherwise (in +:production+ mode) the
357
+ * library will hide them from you.
358
+ * @option options [String] :key_prefix (nil) the prefix string which will
359
+ * be prepended to each key before sending out, and sripped before
360
+ * returning back to the application.
361
+ * @option options [Fixnum] :timeout (2500000) the timeout for IO
362
+ * operations (in microseconds)
363
+ *
364
+ * @example Initialize connection using default options
365
+ * Couchbase.new
366
+ *
367
+ * @example Select custom bucket
368
+ * Couchbase.new(:bucket => 'foo')
369
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/foo')
370
+ *
371
+ * @example Connect to protected bucket
372
+ * Couchbase.new(:bucket => 'protected', :username => 'protected', :password => 'secret')
373
+ * Couchbase.new('http://localhost:8091/pools/default/buckets/protected',
374
+ * :username => 'protected', :password => 'secret')
375
+ *
376
+ * @example Use list of nodes, in case some nodes might be dead
377
+ * Couchbase.new(:node_list => ['example.com:8091', 'example.org:8091', 'example.net'])
378
+ *
379
+ * @raise [Couchbase::Error::BucketNotFound] if there no such bucket to
380
+ * connect
381
+ *
382
+ * @raise [Couchbase::Error::Connect] if the socket wasn't accessible
383
+ * (doesn't accept connections or doesn't respond in time)
384
+ *
385
+ * @return [Bucket]
386
+ */
387
+ VALUE
388
+ cb_bucket_init(int argc, VALUE *argv, VALUE self)
389
+ {
390
+ struct bucket_st *bucket = DATA_PTR(self);
391
+
392
+ bucket->self = self;
393
+ bucket->exception = Qnil;
394
+ bucket->hostname = strdup("localhost");
395
+ bucket->port = 8091;
396
+ bucket->pool = strdup("default");
397
+ bucket->bucket = strdup("default");
398
+ bucket->async = 0;
399
+ bucket->quiet = 0;
400
+ bucket->default_ttl = 0;
401
+ bucket->default_flags = 0;
402
+ bucket->default_format = sym_document;
403
+ bucket->default_observe_timeout = 2500000;
404
+ bucket->on_error_proc = Qnil;
405
+ bucket->timeout = 0;
406
+ bucket->environment = sym_production;
407
+ bucket->key_prefix = NULL;
408
+ bucket->key_prefix_val = Qnil;
409
+ bucket->node_list = NULL;
410
+ bucket->object_space = rb_hash_new();
411
+ bucket->node_list = NULL;
412
+
413
+ do_scan_connection_options(bucket, argc, argv);
414
+ do_connect(bucket);
415
+
416
+ return self;
417
+ }
418
+
419
+ /*
420
+ * Initialize copy
421
+ *
422
+ * Initializes copy of the object, used by {Couchbase::Bucket#dup}
423
+ *
424
+ * @param orig [Couchbase::Bucket] the source for copy
425
+ *
426
+ * @return [Couchbase::Bucket]
427
+ */
428
+ VALUE
429
+ cb_bucket_init_copy(VALUE copy, VALUE orig)
430
+ {
431
+ struct bucket_st *copy_b;
432
+ struct bucket_st *orig_b;
433
+
434
+ if (copy == orig)
435
+ return copy;
436
+
437
+ if (TYPE(orig) != T_DATA || TYPE(copy) != T_DATA ||
438
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)cb_bucket_free) {
439
+ rb_raise(rb_eTypeError, "wrong argument type");
440
+ }
441
+
442
+ copy_b = DATA_PTR(copy);
443
+ orig_b = DATA_PTR(orig);
444
+
445
+ copy_b->self = copy_b->self;
446
+ copy_b->port = orig_b->port;
447
+ copy_b->authority = strdup(orig_b->authority);
448
+ copy_b->hostname = strdup(orig_b->hostname);
449
+ copy_b->pool = strdup(orig_b->pool);
450
+ copy_b->bucket = strdup(orig_b->bucket);
451
+ if (orig_b->username) {
452
+ copy_b->username = strdup(orig_b->username);
453
+ }
454
+ if (orig_b->password) {
455
+ copy_b->password = strdup(orig_b->password);
456
+ }
457
+ if (orig_b->key_prefix) {
458
+ copy_b->key_prefix = strdup(orig_b->key_prefix);
459
+ }
460
+ copy_b->async = orig_b->async;
461
+ copy_b->quiet = orig_b->quiet;
462
+ copy_b->default_format = orig_b->default_format;
463
+ copy_b->default_flags = orig_b->default_flags;
464
+ copy_b->default_ttl = orig_b->default_ttl;
465
+ copy_b->environment = orig_b->environment;
466
+ copy_b->timeout = orig_b->timeout;
467
+ copy_b->exception = Qnil;
468
+ if (orig_b->on_error_proc != Qnil) {
469
+ copy_b->on_error_proc = rb_funcall(orig_b->on_error_proc, id_dup, 0);
470
+ }
471
+ if (orig_b->key_prefix_val != Qnil) {
472
+ copy_b->key_prefix_val = rb_funcall(orig_b->key_prefix_val, id_dup, 0);
473
+ }
474
+
475
+ do_connect(copy_b);
476
+
477
+ return copy;
478
+ }
479
+
480
+ /*
481
+ * Reconnect the bucket
482
+ *
483
+ * @since 1.1.0
484
+ *
485
+ * Reconnect the bucket using initial configuration with optional
486
+ * redefinition.
487
+ *
488
+ * @overload reconnect(url, options = {})
489
+ * see {Bucket#initialize Bucket#initialize(url, options)}
490
+ * @return [Couchbase::Bucket]
491
+ *
492
+ * @overload reconnect(options = {})
493
+ * see {Bucket#initialize Bucket#initialize(options)}
494
+ * @return [Couchbase::Bucket]
495
+ *
496
+ * @example reconnect with current parameters
497
+ * c.reconnect
498
+ *
499
+ * @example reconnect the instance to another bucket
500
+ * c.reconnect(:bucket => 'new')
501
+ */
502
+ VALUE
503
+ cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
504
+ {
505
+ struct bucket_st *bucket = DATA_PTR(self);
506
+
507
+ do_scan_connection_options(bucket, argc, argv);
508
+ do_connect(bucket);
509
+
510
+ return self;
511
+ }
512
+
513
+ /* Document-method: connected?
514
+ * Check whether the instance connected to the cluster.
515
+ *
516
+ * @since 1.1.0
517
+ *
518
+ * @return [true, false] +true+ if the instance connected to the cluster
519
+ */
520
+ VALUE
521
+ cb_bucket_connected_p(VALUE self)
522
+ {
523
+ struct bucket_st *bucket = DATA_PTR(self);
524
+ return bucket->handle ? Qtrue : Qfalse;
525
+ }
526
+
527
+ /* Document-method: async?
528
+ * Check whether the connection asynchronous.
529
+ *
530
+ * @since 1.0.0
531
+ *
532
+ * By default all operations are synchronous and block waiting for
533
+ * results, but you can make them asynchronous and run event loop
534
+ * explicitly. (see {Bucket#run})
535
+ *
536
+ * @example Return value of #get operation depending on async flag
537
+ * connection = Connection.new
538
+ * connection.async? #=> false
539
+ *
540
+ * connection.run do |conn|
541
+ * conn.async? #=> true
542
+ * end
543
+ *
544
+ * @return [true, false] +true+ if the connection if asynchronous
545
+ *
546
+ * @see Bucket#run
547
+ */
548
+ VALUE
549
+ cb_bucket_async_p(VALUE self)
550
+ {
551
+ struct bucket_st *bucket = DATA_PTR(self);
552
+ return bucket->async ? Qtrue : Qfalse;
553
+ }
554
+
555
+ VALUE
556
+ cb_bucket_quiet_get(VALUE self)
557
+ {
558
+ struct bucket_st *bucket = DATA_PTR(self);
559
+ return bucket->quiet ? Qtrue : Qfalse;
560
+ }
561
+
562
+ VALUE
563
+ cb_bucket_quiet_set(VALUE self, VALUE val)
564
+ {
565
+ struct bucket_st *bucket = DATA_PTR(self);
566
+ VALUE new;
567
+
568
+ bucket->quiet = RTEST(val);
569
+ new = bucket->quiet ? Qtrue : Qfalse;
570
+ return new;
571
+ }
572
+
573
+ VALUE
574
+ cb_bucket_default_flags_get(VALUE self)
575
+ {
576
+ struct bucket_st *bucket = DATA_PTR(self);
577
+ return ULONG2NUM(bucket->default_flags);
578
+ }
579
+
580
+ VALUE
581
+ cb_bucket_default_flags_set(VALUE self, VALUE val)
582
+ {
583
+ struct bucket_st *bucket = DATA_PTR(self);
584
+
585
+ bucket->default_flags = (uint32_t)NUM2ULONG(val);
586
+ bucket->default_format = flags_get_format(bucket->default_flags);
587
+ return val;
588
+ }
589
+
590
+ VALUE
591
+ cb_bucket_default_format_get(VALUE self)
592
+ {
593
+ struct bucket_st *bucket = DATA_PTR(self);
594
+ return bucket->default_format;
595
+ }
596
+
597
+ VALUE
598
+ cb_bucket_default_format_set(VALUE self, VALUE val)
599
+ {
600
+ struct bucket_st *bucket = DATA_PTR(self);
601
+
602
+ if (TYPE(val) == T_FIXNUM) {
603
+ switch (FIX2INT(val)) {
604
+ case FMT_DOCUMENT:
605
+ val = sym_document;
606
+ break;
607
+ case FMT_MARSHAL:
608
+ val = sym_marshal;
609
+ break;
610
+ case FMT_PLAIN:
611
+ val = sym_plain;
612
+ break;
613
+ }
614
+ }
615
+ if (val == sym_document || val == sym_marshal || val == sym_plain) {
616
+ bucket->default_format = val;
617
+ bucket->default_flags = flags_set_format(bucket->default_flags, val);
618
+ }
619
+
620
+ return val;
621
+ }
622
+
623
+ VALUE
624
+ cb_bucket_on_error_set(VALUE self, VALUE val)
625
+ {
626
+ struct bucket_st *bucket = DATA_PTR(self);
627
+
628
+ if (rb_respond_to(val, id_call)) {
629
+ bucket->on_error_proc = val;
630
+ } else {
631
+ bucket->on_error_proc = Qnil;
632
+ }
633
+
634
+ return bucket->on_error_proc;
635
+ }
636
+
637
+ VALUE
638
+ cb_bucket_on_error_get(VALUE self)
639
+ {
640
+ struct bucket_st *bucket = DATA_PTR(self);
641
+
642
+ if (rb_block_given_p()) {
643
+ return cb_bucket_on_error_set(self, rb_block_proc());
644
+ } else {
645
+ return bucket->on_error_proc;
646
+ }
647
+ }
648
+
649
+ VALUE
650
+ cb_bucket_timeout_get(VALUE self)
651
+ {
652
+ struct bucket_st *bucket = DATA_PTR(self);
653
+ return ULONG2NUM(bucket->timeout);
654
+ }
655
+
656
+ VALUE
657
+ cb_bucket_timeout_set(VALUE self, VALUE val)
658
+ {
659
+ struct bucket_st *bucket = DATA_PTR(self);
660
+ VALUE tmval;
661
+
662
+ bucket->timeout = (uint32_t)NUM2ULONG(val);
663
+ lcb_set_timeout(bucket->handle, bucket->timeout);
664
+ tmval = ULONG2NUM(bucket->timeout);
665
+
666
+ return tmval;
667
+ }
668
+
669
+ VALUE
670
+ cb_bucket_key_prefix_get(VALUE self)
671
+ {
672
+ struct bucket_st *bucket = DATA_PTR(self);
673
+ return bucket->key_prefix_val;
674
+ }
675
+
676
+ VALUE
677
+ cb_bucket_key_prefix_set(VALUE self, VALUE val)
678
+ {
679
+ struct bucket_st *bucket = DATA_PTR(self);
680
+
681
+ bucket->key_prefix = strdup(StringValueCStr(val));
682
+ bucket->key_prefix_val = STR_NEW_CSTR(bucket->key_prefix);
683
+
684
+ return bucket->key_prefix_val;
685
+ }
686
+
687
+ /* Document-method: hostname
688
+ *
689
+ * @since 1.0.0
690
+ *
691
+ * @return [String] the host name of the management interface (default: "localhost")
692
+ */
693
+ VALUE
694
+ cb_bucket_hostname_get(VALUE self)
695
+ {
696
+ struct bucket_st *bucket = DATA_PTR(self);
697
+ if (bucket->handle) {
698
+ xfree(bucket->hostname);
699
+ bucket->hostname = strdup(lcb_get_host(bucket->handle));
700
+ if (bucket->hostname == NULL) {
701
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
702
+ }
703
+ }
704
+ return STR_NEW_CSTR(bucket->hostname);
705
+ }
706
+
707
+ /* Document-method: port
708
+ *
709
+ * @since 1.0.0
710
+ *
711
+ * @return [Fixnum] the port number of the management interface (default: 8091)
712
+ */
713
+ VALUE
714
+ cb_bucket_port_get(VALUE self)
715
+ {
716
+ struct bucket_st *bucket = DATA_PTR(self);
717
+ if (bucket->handle) {
718
+ bucket->port = atoi(lcb_get_port(bucket->handle));
719
+ }
720
+ return UINT2NUM(bucket->port);
721
+ }
722
+
723
+ /* Document-method: authority
724
+ *
725
+ * @since 1.0.0
726
+ *
727
+ * @return [String] host with port
728
+ */
729
+ VALUE
730
+ cb_bucket_authority_get(VALUE self)
731
+ {
732
+ struct bucket_st *bucket = DATA_PTR(self);
733
+ size_t len;
734
+
735
+ (void)cb_bucket_hostname_get(self);
736
+ (void)cb_bucket_port_get(self);
737
+ len = strlen(bucket->hostname) + 10;
738
+ bucket->authority = xcalloc(len, sizeof(char));
739
+ if (bucket->authority == NULL) {
740
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for Bucket");
741
+ }
742
+ snprintf(bucket->authority, len, "%s:%u", bucket->hostname, bucket->port);
743
+ return STR_NEW_CSTR(bucket->authority);
744
+ }
745
+
746
+ /* Document-method: bucket
747
+ *
748
+ * @since 1.0.0
749
+ *
750
+ * @return [String] the bucket name
751
+ */
752
+ VALUE
753
+ cb_bucket_bucket_get(VALUE self)
754
+ {
755
+ struct bucket_st *bucket = DATA_PTR(self);
756
+ return STR_NEW_CSTR(bucket->bucket);
757
+ }
758
+
759
+ /* Document-method: pool
760
+ *
761
+ * @since 1.0.0
762
+ *
763
+ * @return [String] the pool name (usually "default")
764
+ */
765
+ VALUE
766
+ cb_bucket_pool_get(VALUE self)
767
+ {
768
+ struct bucket_st *bucket = DATA_PTR(self);
769
+ return STR_NEW_CSTR(bucket->pool);
770
+ }
771
+
772
+ /* Document-method: username
773
+ *
774
+ * @since 1.0.0
775
+ *
776
+ * @return [String] the username for protected buckets (usually matches
777
+ * the bucket name)
778
+ */
779
+ VALUE
780
+ cb_bucket_username_get(VALUE self)
781
+ {
782
+ struct bucket_st *bucket = DATA_PTR(self);
783
+ return STR_NEW_CSTR(bucket->username);
784
+ }
785
+
786
+ /* Document-method: password
787
+ *
788
+ * @since 1.0.0
789
+ *
790
+ * @return [String] the password for protected buckets
791
+ */
792
+ VALUE
793
+ cb_bucket_password_get(VALUE self)
794
+ {
795
+ struct bucket_st *bucket = DATA_PTR(self);
796
+ return STR_NEW_CSTR(bucket->password);
797
+ }
798
+
799
+ /* Document-method: environment
800
+ *
801
+ * @since 1.2.0
802
+ *
803
+ * @see Bucket#initialize
804
+ *
805
+ * @return [Symbol] the environment (+:development+ or +:production+)
806
+ */
807
+ VALUE
808
+ cb_bucket_environment_get(VALUE self)
809
+ {
810
+ struct bucket_st *bucket = DATA_PTR(self);
811
+ return bucket->environment;
812
+ }
813
+ /* Document-method: num_replicas
814
+ *
815
+ * @since 1.2.0.dp6
816
+ *
817
+ * The numbers of the replicas for each node in the cluster
818
+ *
819
+ * @return [Fixnum]
820
+ */
821
+ VALUE
822
+ cb_bucket_num_replicas_get(VALUE self)
823
+ {
824
+ struct bucket_st *bucket = DATA_PTR(self);
825
+ int32_t nr = lcb_get_num_replicas(bucket->handle);
826
+ if (nr < 0) {
827
+ return Qnil;
828
+ } else {
829
+ return INT2FIX(nr);
830
+ }
831
+ }
832
+ /* Document-method: default_observe_timeout
833
+ *
834
+ * @since 1.2.0.dp6
835
+ *
836
+ * Get default timeout value for {Bucket#observe_and_wait} operation in
837
+ * microseconds
838
+ *
839
+ * @return [Fixnum]
840
+ */
841
+ VALUE
842
+ cb_bucket_default_observe_timeout_get(VALUE self)
843
+ {
844
+ struct bucket_st *bucket = DATA_PTR(self);
845
+ return INT2FIX(bucket->default_observe_timeout);
846
+ }
847
+
848
+ /* Document-method: default_observe_timeout=
849
+ *
850
+ * @since 1.2.0.dp6
851
+ *
852
+ * Set default timeout value for {Bucket#observe_and_wait} operation in
853
+ * microseconds
854
+ *
855
+ * @return [Fixnum]
856
+ */
857
+ VALUE
858
+ cb_bucket_default_observe_timeout_set(VALUE self, VALUE val)
859
+ {
860
+ struct bucket_st *bucket = DATA_PTR(self);
861
+ bucket->default_observe_timeout = FIX2INT(val);
862
+ return val;
863
+ }
864
+ /* Document-method: url
865
+ *
866
+ * @since 1.0.0
867
+ *
868
+ * @return [String] the address of the cluster management interface
869
+ */
870
+ VALUE
871
+ cb_bucket_url_get(VALUE self)
872
+ {
873
+ struct bucket_st *bucket = DATA_PTR(self);
874
+ VALUE str;
875
+
876
+ (void)cb_bucket_authority_get(self);
877
+ str = rb_str_buf_new2("http://");
878
+ rb_str_buf_cat2(str, bucket->authority);
879
+ rb_str_buf_cat2(str, "/pools/");
880
+ rb_str_buf_cat2(str, bucket->pool);
881
+ rb_str_buf_cat2(str, "/buckets/");
882
+ rb_str_buf_cat2(str, bucket->bucket);
883
+ rb_str_buf_cat2(str, "/");
884
+ return str;
885
+ }
886
+
887
+ /*
888
+ * Returns a string containing a human-readable representation of the
889
+ * {Bucket}.
890
+ *
891
+ * @since 1.0.0
892
+ *
893
+ * @return [String]
894
+ */
895
+ VALUE
896
+ cb_bucket_inspect(VALUE self)
897
+ {
898
+ VALUE str;
899
+ struct bucket_st *bucket = DATA_PTR(self);
900
+ char buf[200];
901
+
902
+ str = rb_str_buf_new2("#<");
903
+ rb_str_buf_cat2(str, rb_obj_classname(self));
904
+ snprintf(buf, 25, ":%p \"", (void *)self);
905
+ (void)cb_bucket_authority_get(self);
906
+ rb_str_buf_cat2(str, buf);
907
+ rb_str_buf_cat2(str, "http://");
908
+ rb_str_buf_cat2(str, bucket->authority);
909
+ rb_str_buf_cat2(str, "/pools/");
910
+ rb_str_buf_cat2(str, bucket->pool);
911
+ rb_str_buf_cat2(str, "/buckets/");
912
+ rb_str_buf_cat2(str, bucket->bucket);
913
+ rb_str_buf_cat2(str, "/");
914
+ snprintf(buf, 150, "\" default_format=:%s, default_flags=0x%x, quiet=%s, connected=%s, timeout=%u",
915
+ rb_id2name(SYM2ID(bucket->default_format)),
916
+ bucket->default_flags,
917
+ bucket->quiet ? "true" : "false",
918
+ bucket->handle ? "true" : "false",
919
+ bucket->timeout);
920
+ rb_str_buf_cat2(str, buf);
921
+ if (bucket->key_prefix) {
922
+ rb_str_buf_cat2(str, ", key_prefix=");
923
+ rb_str_append(str, rb_inspect(bucket->key_prefix_val));
924
+ }
925
+ rb_str_buf_cat2(str, ">");
926
+
927
+ return str;
928
+ }
929
+
930
+ static void
931
+ do_loop(struct bucket_st *bucket)
932
+ {
933
+ lcb_wait(bucket->handle);
934
+ bucket->nbytes = 0;
935
+ }
936
+
937
+ void
938
+ maybe_do_loop(struct bucket_st *bucket)
939
+ {
940
+ if (bucket->threshold != 0 && bucket->nbytes > bucket->threshold) {
941
+ do_loop(bucket);
942
+ }
943
+ }
944
+
945
+ static VALUE
946
+ do_run(VALUE *args)
947
+ {
948
+ VALUE self = args[0], opts = args[1], proc = args[2], exc;
949
+ struct bucket_st *bucket = DATA_PTR(self);
950
+
951
+ if (bucket->handle == NULL) {
952
+ rb_raise(eConnectError, "closed connection");
953
+ }
954
+ if (bucket->async) {
955
+ rb_raise(eInvalidError, "nested #run");
956
+ }
957
+ bucket->threshold = 0;
958
+ if (opts != Qnil) {
959
+ VALUE arg;
960
+ Check_Type(opts, T_HASH);
961
+ arg = rb_hash_aref(opts, sym_send_threshold);
962
+ if (arg != Qnil) {
963
+ bucket->threshold = (uint32_t)NUM2ULONG(arg);
964
+ }
965
+ }
966
+ bucket->async = 1;
967
+ cb_proc_call(proc, 1, self);
968
+ do_loop(bucket);
969
+ if (bucket->exception != Qnil) {
970
+ exc = bucket->exception;
971
+ bucket->exception = Qnil;
972
+ rb_exc_raise(exc);
973
+ }
974
+ return Qnil;
975
+ }
976
+
977
+ static VALUE
978
+ ensure_run(VALUE *args)
979
+ {
980
+ VALUE self = args[0];
981
+ struct bucket_st *bucket = DATA_PTR(self);
982
+
983
+ bucket->async = 0;
984
+ return Qnil;
985
+ }
986
+
987
+ /*
988
+ * Run the event loop.
989
+ *
990
+ * @since 1.0.0
991
+ *
992
+ * @param [Hash] options The options for operation for connection
993
+ * @option options [Fixnum] :send_threshold (0) if the internal command
994
+ * buffer will exceeds this value, then the library will start network
995
+ * interaction and block the current thread until all scheduled commands
996
+ * will be completed.
997
+ *
998
+ * @yieldparam [Bucket] bucket the bucket instance
999
+ *
1000
+ * @example Use block to run the loop
1001
+ * c = Couchbase.new
1002
+ * c.run do
1003
+ * c.get("foo") {|ret| puts ret.value}
1004
+ * end
1005
+ *
1006
+ * @example Use lambda to run the loop
1007
+ * c = Couchbase.new
1008
+ * operations = lambda do |c|
1009
+ * c.get("foo") {|ret| puts ret.value}
1010
+ * end
1011
+ * c.run(&operations)
1012
+ *
1013
+ * @example Use threshold to send out commands automatically
1014
+ * c = Couchbase.connect
1015
+ * sent = 0
1016
+ * c.run(:send_threshold => 8192) do # 8Kb
1017
+ * c.set("foo1", "x" * 100) {|r| sent += 1}
1018
+ * # 128 bytes buffered, sent is 0 now
1019
+ * c.set("foo2", "x" * 10000) {|r| sent += 1}
1020
+ * # 10028 bytes added, sent is 2 now
1021
+ * c.set("foo3", "x" * 100) {|r| sent += 1}
1022
+ * end
1023
+ * # all commands were executed and sent is 3 now
1024
+ *
1025
+ * @return [nil]
1026
+ *
1027
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1028
+ */
1029
+ VALUE
1030
+ cb_bucket_run(int argc, VALUE *argv, VALUE self)
1031
+ {
1032
+ VALUE args[3];
1033
+
1034
+ rb_need_block();
1035
+ args[0] = self;
1036
+ rb_scan_args(argc, argv, "01&", &args[1], &args[2]);
1037
+ rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
1038
+ return Qnil;
1039
+ }
1040
+
1041
+ /*
1042
+ * Stop the event loop.
1043
+ *
1044
+ * @since 1.2.0
1045
+ *
1046
+ * @example Breakout the event loop when 5th request is completed
1047
+ * c = Couchbase.connect
1048
+ * c.run do
1049
+ * 10.times do |ii|
1050
+ * c.get("foo") do |ret|
1051
+ * puts ii
1052
+ * c.stop if ii == 5
1053
+ * end
1054
+ * end
1055
+ * end
1056
+ *
1057
+ * @return [nil]
1058
+ */
1059
+ VALUE
1060
+ cb_bucket_stop(VALUE self)
1061
+ {
1062
+ struct bucket_st *bucket = DATA_PTR(self);
1063
+ bucket->io->stop_event_loop(bucket->io);
1064
+ return Qnil;
1065
+ }
1066
+
1067
+ /*
1068
+ * Close the connection to the cluster
1069
+ *
1070
+ * @since 1.1.0
1071
+ *
1072
+ * @return [true]
1073
+ *
1074
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
1075
+ */
1076
+ VALUE
1077
+ cb_bucket_disconnect(VALUE self)
1078
+ {
1079
+ struct bucket_st *bucket = DATA_PTR(self);
1080
+
1081
+ if (bucket->handle) {
1082
+ lcb_destroy(bucket->handle);
1083
+ bucket->handle = NULL;
1084
+ bucket->io = NULL;
1085
+ return Qtrue;
1086
+ } else {
1087
+ rb_raise(eConnectError, "closed connection");
1088
+ return Qfalse;
1089
+ }
1090
+ }
1091
+
1092
+