couchbase 1.1.5-x86-mingw32 → 1.2.0.beta-x86-mingw32

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 (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 +102 -12
@@ -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
+