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,124 @@
1
+ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2010, 2011 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
+ #include <stdlib.h>
20
+ #include <time.h>
21
+ #include <assert.h>
22
+
23
+ #ifdef HAVE_MACH_MACH_TIME_H
24
+ #include <mach/mach_time.h>
25
+ #endif
26
+
27
+ #ifdef HAVE_SYS_TIME_H
28
+ #include <sys/time.h>
29
+ #endif
30
+
31
+ /*
32
+ * OS X doesn't have clock_gettime, but for monotonic, we can build
33
+ * one with mach_absolute_time as shown below.
34
+ *
35
+ * Most of the idea came from
36
+ * http://www.wand.net.nz/~smr26/wordpress/2009/01/19/monotonic-time-in-mac-os-x/
37
+ */
38
+
39
+ #if defined(HAVE_MACH_ABSOLUTE_TIME) && !defined(HAVE_CLOCK_GETTIME)
40
+
41
+ #define CLOCK_MONOTONIC 192996728
42
+
43
+ static void mach_absolute_difference(uint64_t start, uint64_t end,
44
+ struct timespec *tp)
45
+ {
46
+ uint64_t difference = end - start;
47
+ static mach_timebase_info_data_t info = {0, 0};
48
+
49
+ if (info.denom == 0) {
50
+ mach_timebase_info(&info);
51
+ }
52
+
53
+ uint64_t elapsednano = difference * (info.numer / info.denom);
54
+
55
+ tp->tv_sec = elapsednano * 1e-9;
56
+ tp->tv_nsec = elapsednano - (tp->tv_sec * 1e9);
57
+ }
58
+
59
+ static int clock_gettime(int which, struct timespec *tp)
60
+ {
61
+ assert(which == CLOCK_MONOTONIC);
62
+
63
+ static uint64_t epoch = 0;
64
+
65
+ if (epoch == 0) {
66
+ epoch = mach_absolute_time();
67
+ }
68
+
69
+ uint64_t now = mach_absolute_time();
70
+
71
+ mach_absolute_difference(epoch, now, tp);
72
+
73
+ return 0;
74
+ }
75
+
76
+ #define HAVE_CLOCK_GETTIME 1
77
+ #endif
78
+
79
+ hrtime_t gethrtime(void)
80
+ {
81
+ #ifdef HAVE_CLOCK_GETTIME
82
+ struct timespec tm;
83
+ if (clock_gettime(CLOCK_MONOTONIC, &tm) == -1) {
84
+ abort();
85
+ }
86
+ return (((hrtime_t)tm.tv_sec) * 1000000000) + (hrtime_t)tm.tv_nsec;
87
+ #elif HAVE_GETTIMEOFDAY
88
+
89
+ hrtime_t ret;
90
+ struct timeval tv;
91
+ if (gettimeofday(&tv, NULL) == -1) {
92
+ return (-1ULL);
93
+ }
94
+
95
+ ret = (hrtime_t)tv.tv_sec * 1000000000;
96
+ ret += tv.tv_usec * 1000;
97
+ return ret;
98
+ #elif defined(HAVE_QUERYPERFORMANCECOUNTER)
99
+ double ret;
100
+ // To fix the potential race condition for the local static variable,
101
+ // gethrtime should be called in a global static variable first.
102
+ // It will guarantee the local static variable will be initialized
103
+ // before any thread calls the function.
104
+ static LARGE_INTEGER pf = { 0 };
105
+ static double freq;
106
+ LARGE_INTEGER currtime;
107
+
108
+ if (pf.QuadPart == 0) {
109
+ if (QueryPerformanceFrequency(&pf)) {
110
+ assert(pf.QuadPart != 0);
111
+ freq = 1.0e9 / (double)pf.QuadPart;
112
+ } else {
113
+ abort();
114
+ }
115
+ }
116
+
117
+ QueryPerformanceCounter(&currtime);
118
+
119
+ ret = (double)currtime.QuadPart * freq ;
120
+ return (hrtime_t)ret;
121
+ #else
122
+ #error "I don't know how to build a highres clock..."
123
+ #endif
124
+ }
@@ -0,0 +1,402 @@
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
+ http_complete_callback(lcb_http_request_t request, lcb_t handle, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ struct bucket_st *bucket = ctx->bucket;
25
+ VALUE *rv = ctx->rv, key, val, res;
26
+
27
+ ctx->request->completed = 1;
28
+ key = STR_NEW((const char*)resp->v.v0.path, resp->v.v0.npath);
29
+ ctx->exception = cb_check_error_with_status(error,
30
+ "failed to execute HTTP request", key, resp->v.v0.status);
31
+ if (ctx->exception != Qnil) {
32
+ cb_gc_protect(bucket, ctx->exception);
33
+ }
34
+ val = resp->v.v0.nbytes ? STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes) : Qnil;
35
+ if (resp->v.v0.headers) {
36
+ cb_build_headers(ctx, resp->v.v0.headers);
37
+ cb_gc_unprotect(bucket, ctx->headers_val);
38
+ }
39
+ if (ctx->extended) {
40
+ res = rb_class_new_instance(0, NULL, cResult);
41
+ rb_ivar_set(res, id_iv_error, ctx->exception);
42
+ rb_ivar_set(res, id_iv_operation, sym_http_request);
43
+ rb_ivar_set(res, id_iv_key, key);
44
+ rb_ivar_set(res, id_iv_value, val);
45
+ rb_ivar_set(res, id_iv_completed, Qtrue);
46
+ rb_ivar_set(res, id_iv_headers, ctx->headers_val);
47
+ } else {
48
+ res = val;
49
+ }
50
+ if (ctx->proc != Qnil) {
51
+ cb_proc_call(ctx->proc, 1, res);
52
+ }
53
+ if (!bucket->async && ctx->exception == Qnil) {
54
+ *rv = res;
55
+ }
56
+ (void)handle;
57
+ (void)request;
58
+ }
59
+
60
+ void
61
+ http_data_callback(lcb_http_request_t request, lcb_t handle, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp)
62
+ {
63
+ struct context_st *ctx = (struct context_st *)cookie;
64
+ struct bucket_st *bucket = ctx->bucket;
65
+ VALUE key, val, res;
66
+
67
+ key = STR_NEW((const char*)resp->v.v0.path, resp->v.v0.npath);
68
+ ctx->exception = cb_check_error_with_status(error,
69
+ "failed to execute HTTP request", key, resp->v.v0.status);
70
+ val = resp->v.v0.nbytes ? STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes) : Qnil;
71
+ if (ctx->exception != Qnil) {
72
+ cb_gc_protect(bucket, ctx->exception);
73
+ lcb_cancel_http_request(bucket->handle, request);
74
+ }
75
+ if (resp->v.v0.headers) {
76
+ cb_build_headers(ctx, resp->v.v0.headers);
77
+ }
78
+ if (ctx->proc != Qnil) {
79
+ if (ctx->extended) {
80
+ res = rb_class_new_instance(0, NULL, cResult);
81
+ rb_ivar_set(res, id_iv_error, ctx->exception);
82
+ rb_ivar_set(res, id_iv_operation, sym_http_request);
83
+ rb_ivar_set(res, id_iv_key, key);
84
+ rb_ivar_set(res, id_iv_value, val);
85
+ rb_ivar_set(res, id_iv_completed, Qfalse);
86
+ rb_ivar_set(res, id_iv_headers, ctx->headers_val);
87
+ } else {
88
+ res = val;
89
+ }
90
+ cb_proc_call(ctx->proc, 1, res);
91
+ }
92
+ (void)handle;
93
+ }
94
+
95
+ void
96
+ cb_http_request_free(void *ptr)
97
+ {
98
+ struct http_request_st *request = ptr;
99
+ if (request) {
100
+ request->running = 0;
101
+ if (TYPE(request->bucket_obj) == T_DATA
102
+ && RDATA(request->bucket_obj)->dfree == (RUBY_DATA_FUNC)cb_bucket_free
103
+ && !request->completed) {
104
+ lcb_cancel_http_request(request->bucket->handle, request->request);
105
+ }
106
+ xfree((char *)request->cmd.v.v0.content_type);
107
+ xfree((char *)request->cmd.v.v0.path);
108
+ xfree((char *)request->cmd.v.v0.body);
109
+ xfree(request);
110
+ }
111
+ }
112
+
113
+ void
114
+ cb_http_request_mark(void *ptr)
115
+ {
116
+ struct http_request_st *request = ptr;
117
+ if (request) {
118
+ rb_gc_mark(request->on_body_callback);
119
+ }
120
+ }
121
+
122
+ VALUE
123
+ cb_http_request_alloc(VALUE klass)
124
+ {
125
+ VALUE obj;
126
+ struct http_request_st *request;
127
+
128
+ /* allocate new bucket struct and set it to zero */
129
+ obj = Data_Make_Struct(klass, struct http_request_st, cb_http_request_mark,
130
+ cb_http_request_free, request);
131
+ return obj;
132
+ }
133
+
134
+ /*
135
+ * Returns a string containing a human-readable representation of the
136
+ * CouchRequest.
137
+ *
138
+ * @since 1.2.0
139
+ *
140
+ * @return [String]
141
+ */
142
+ VALUE
143
+ cb_http_request_inspect(VALUE self)
144
+ {
145
+ VALUE str;
146
+ struct http_request_st *req = DATA_PTR(self);
147
+ char buf[200];
148
+
149
+ str = rb_str_buf_new2("#<");
150
+ rb_str_buf_cat2(str, rb_obj_classname(self));
151
+ snprintf(buf, 20, ":%p \"", (void *)self);
152
+ rb_str_buf_cat2(str, buf);
153
+ rb_str_buf_cat2(str, req->cmd.v.v0.path);
154
+ snprintf(buf, 100, "\" chunked:%s>", req->cmd.v.v0.chunked ? "true" : "false");
155
+ rb_str_buf_cat2(str, buf);
156
+
157
+ return str;
158
+ }
159
+
160
+ /*
161
+ * Initialize new CouchRequest
162
+ *
163
+ * @since 1.2.0
164
+ *
165
+ * @return [Bucket::CouchRequest]
166
+ */
167
+ VALUE
168
+ cb_http_request_init(int argc, VALUE *argv, VALUE self)
169
+ {
170
+ struct http_request_st *request = DATA_PTR(self);
171
+ VALUE bucket, path, opts, on_body, pp, arg;
172
+ rb_scan_args(argc, argv, "22", &bucket, &pp, &opts, &on_body);
173
+
174
+ if (NIL_P(on_body) && rb_block_given_p()) {
175
+ on_body = rb_block_proc();
176
+ }
177
+ if (CLASS_OF(bucket) != cBucket) {
178
+ rb_raise(rb_eTypeError, "wrong argument type (expected Couchbase::Bucket)");
179
+ }
180
+ memset(&request->cmd, 0, sizeof(lcb_http_cmd_t));
181
+ request->type = LCB_HTTP_TYPE_VIEW;
182
+ request->on_body_callback = on_body;
183
+ request->bucket = DATA_PTR(bucket);
184
+ request->bucket_obj = bucket;
185
+ request->extended = Qfalse;
186
+ path = StringValue(pp); /* convert path to string */
187
+ request->cmd.v.v0.path = strdup(RSTRING_PTR(path));
188
+ request->cmd.v.v0.npath = RSTRING_LEN(path);
189
+ request->cmd.v.v0.method = LCB_HTTP_METHOD_GET;
190
+ request->cmd.v.v0.content_type = strdup("application/json");
191
+
192
+ if (opts != Qnil) {
193
+ Check_Type(opts, T_HASH);
194
+ request->extended = RTEST(rb_hash_aref(opts, sym_extended));
195
+ request->cmd.v.v0.chunked = RTEST(rb_hash_aref(opts, sym_chunked));
196
+ if ((arg = rb_hash_aref(opts, sym_type)) != Qnil) {
197
+ if (arg == sym_view) {
198
+ request->type = LCB_HTTP_TYPE_VIEW;
199
+ } else if (arg == sym_management) {
200
+ request->type = LCB_HTTP_TYPE_MANAGEMENT;
201
+ } else {
202
+ rb_raise(rb_eArgError, "unsupported request type");
203
+ }
204
+ }
205
+ if ((arg = rb_hash_aref(opts, sym_method)) != Qnil) {
206
+ if (arg == sym_get) {
207
+ request->cmd.v.v0.method = LCB_HTTP_METHOD_GET;
208
+ } else if (arg == sym_post) {
209
+ request->cmd.v.v0.method = LCB_HTTP_METHOD_POST;
210
+ } else if (arg == sym_put) {
211
+ request->cmd.v.v0.method = LCB_HTTP_METHOD_PUT;
212
+ } else if (arg == sym_delete) {
213
+ request->cmd.v.v0.method = LCB_HTTP_METHOD_DELETE;
214
+ } else {
215
+ rb_raise(rb_eArgError, "unsupported HTTP method");
216
+ }
217
+ }
218
+ if ((arg = rb_hash_aref(opts, sym_body)) != Qnil) {
219
+ Check_Type(arg, T_STRING);
220
+ request->cmd.v.v0.body = strdup(RSTRING_PTR(arg));
221
+ request->cmd.v.v0.nbody = RSTRING_LEN(arg);
222
+ }
223
+ if ((arg = rb_hash_aref(opts, sym_content_type)) != Qnil) {
224
+ Check_Type(arg, T_STRING);
225
+ xfree((char *)request->cmd.v.v0.content_type);
226
+ request->cmd.v.v0.content_type = strdup(RSTRING_PTR(arg));
227
+ }
228
+ }
229
+
230
+ return self;
231
+ }
232
+
233
+ /*
234
+ * Set +on_body+ callback
235
+ *
236
+ * @since 1.2.0
237
+ */
238
+ VALUE
239
+ cb_http_request_on_body(VALUE self)
240
+ {
241
+ struct http_request_st *request = DATA_PTR(self);
242
+ VALUE old = request->on_body_callback;
243
+ if (rb_block_given_p()) {
244
+ request->on_body_callback = rb_block_proc();
245
+ }
246
+ return old;
247
+ }
248
+
249
+ /*
250
+ * Execute {Bucket::CouchRequest}
251
+ *
252
+ * @since 1.2.0
253
+ */
254
+ VALUE
255
+ cb_http_request_perform(VALUE self)
256
+ {
257
+ struct http_request_st *req = DATA_PTR(self);
258
+ struct context_st *ctx;
259
+ VALUE rv, exc;
260
+ lcb_error_t err;
261
+ struct bucket_st *bucket;
262
+
263
+ ctx = xcalloc(1, sizeof(struct context_st));
264
+ if (ctx == NULL) {
265
+ rb_raise(eClientNoMemoryError, "failed to allocate memory");
266
+ }
267
+ rv = Qnil;
268
+ ctx->rv = &rv;
269
+ ctx->bucket = bucket = req->bucket;
270
+ ctx->proc = rb_block_given_p() ? rb_block_proc() : req->on_body_callback;
271
+ ctx->extended = req->extended;
272
+ ctx->request = req;
273
+ ctx->headers_val = cb_gc_protect(bucket, rb_hash_new());
274
+
275
+ err = lcb_make_http_request(bucket->handle, (const void *)ctx,
276
+ req->type, &req->cmd, &req->request);
277
+ exc = cb_check_error(err, "failed to schedule document request",
278
+ STR_NEW(req->cmd.v.v0.path, req->cmd.v.v0.npath));
279
+ if (exc != Qnil) {
280
+ xfree(ctx);
281
+ rb_exc_raise(exc);
282
+ }
283
+ req->running = 1;
284
+ req->ctx = ctx;
285
+ if (bucket->async) {
286
+ return Qnil;
287
+ } else {
288
+ lcb_wait(bucket->handle);
289
+ if (req->completed) {
290
+ exc = ctx->exception;
291
+ xfree(ctx);
292
+ if (exc != Qnil) {
293
+ cb_gc_unprotect(bucket, exc);
294
+ rb_exc_raise(exc);
295
+ }
296
+ return rv;
297
+ } else {
298
+ return Qnil;
299
+ }
300
+ }
301
+ return Qnil;
302
+ }
303
+
304
+ VALUE
305
+ cb_http_request_pause(VALUE self)
306
+ {
307
+ struct http_request_st *req = DATA_PTR(self);
308
+ req->bucket->io->stop_event_loop(req->bucket->io);
309
+ return Qnil;
310
+ }
311
+
312
+ VALUE
313
+ cb_http_request_continue(VALUE self)
314
+ {
315
+ VALUE exc, *rv;
316
+ struct http_request_st *req = DATA_PTR(self);
317
+
318
+ if (req->running) {
319
+ lcb_wait(req->bucket->handle);
320
+ if (req->completed) {
321
+ exc = req->ctx->exception;
322
+ rv = req->ctx->rv;
323
+ xfree(req->ctx);
324
+ if (exc != Qnil) {
325
+ cb_gc_unprotect(req->bucket, exc);
326
+ rb_exc_raise(exc);
327
+ }
328
+ return *rv;
329
+ }
330
+ } else {
331
+ cb_http_request_perform(self);
332
+ }
333
+ return Qnil;
334
+ }
335
+
336
+ /* Document-method: path
337
+ *
338
+ * @since 1.2.0
339
+ *
340
+ * @return [String] the requested path
341
+ */
342
+ VALUE
343
+ cb_http_request_path_get(VALUE self)
344
+ {
345
+ struct http_request_st *req = DATA_PTR(self);
346
+ return STR_NEW_CSTR(req->cmd.v.v0.path);
347
+ }
348
+
349
+ /* Document-method: chunked
350
+ *
351
+ * @since 1.2.0
352
+ *
353
+ * @return [Boolean] +false+ if library should collect whole response before
354
+ * yielding, +true+ if the client is ready to handle response in chunks.
355
+ */
356
+ VALUE
357
+ cb_http_request_chunked_get(VALUE self)
358
+ {
359
+ struct http_request_st *req = DATA_PTR(self);
360
+ return req->cmd.v.v0.chunked ? Qtrue : Qfalse;
361
+ }
362
+
363
+ /* Document-method: extended
364
+ *
365
+ * @since 1.2.0
366
+ *
367
+ * @return [Boolean] if +false+ the callbacks should receive just the data,
368
+ * and {Couchbase::Result} instance otherwise.
369
+ */
370
+ VALUE
371
+ cb_http_request_extended_get(VALUE self)
372
+ {
373
+ struct http_request_st *req = DATA_PTR(self);
374
+ return req->extended ? Qtrue : Qfalse;
375
+ }
376
+
377
+ /* Document-method: make_http_request(path, options = {})
378
+ *
379
+ * @since 1.2.0
380
+ *
381
+ * @param path [String]
382
+ * @param options [Hash]
383
+ * @option options [Boolean] :extended (false) set it to +true+ if the
384
+ * {Couchbase::Result} object needed. The response chunk will be
385
+ * accessible through +#value+ attribute.
386
+ * @yieldparam [String,Couchbase::Result] res the response chunk if the
387
+ * :extended option is +false+ and result object otherwise
388
+ *
389
+ * @return [Couchbase::Bucket::CouchRequest]
390
+ */
391
+ VALUE
392
+ cb_bucket_make_http_request(int argc, VALUE *argv, VALUE self)
393
+ {
394
+ VALUE args[4]; /* bucket, path, options, block */
395
+
396
+ args[0] = self;
397
+ rb_scan_args(argc, argv, "11&", &args[1], &args[2], &args[3]);
398
+
399
+ return rb_class_new_instance(4, args, cCouchRequest);
400
+ }
401
+
402
+