jmoses-couchbase 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +15 -0
  2. data/.travis.yml +22 -0
  3. data/.yardopts +5 -0
  4. data/CONTRIBUTING.markdown +75 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +201 -0
  7. data/Makefile +3 -0
  8. data/README.markdown +665 -0
  9. data/RELEASE_NOTES.markdown +819 -0
  10. data/Rakefile +20 -0
  11. data/couchbase.gemspec +49 -0
  12. data/examples/chat-em/Gemfile +7 -0
  13. data/examples/chat-em/README.markdown +45 -0
  14. data/examples/chat-em/server.rb +82 -0
  15. data/examples/chat-goliath-grape/Gemfile +5 -0
  16. data/examples/chat-goliath-grape/README.markdown +50 -0
  17. data/examples/chat-goliath-grape/app.rb +67 -0
  18. data/examples/chat-goliath-grape/config/app.rb +20 -0
  19. data/examples/transcoders/Gemfile +3 -0
  20. data/examples/transcoders/README.markdown +59 -0
  21. data/examples/transcoders/cb-zcat +40 -0
  22. data/examples/transcoders/cb-zcp +45 -0
  23. data/examples/transcoders/gzip_transcoder.rb +49 -0
  24. data/examples/transcoders/options.rb +54 -0
  25. data/ext/couchbase_ext/.gitignore +4 -0
  26. data/ext/couchbase_ext/arguments.c +956 -0
  27. data/ext/couchbase_ext/arithmetic.c +316 -0
  28. data/ext/couchbase_ext/bucket.c +1373 -0
  29. data/ext/couchbase_ext/context.c +65 -0
  30. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  31. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  32. data/ext/couchbase_ext/delete.c +163 -0
  33. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  34. data/ext/couchbase_ext/extconf.rb +169 -0
  35. data/ext/couchbase_ext/get.c +316 -0
  36. data/ext/couchbase_ext/gethrtime.c +129 -0
  37. data/ext/couchbase_ext/http.c +432 -0
  38. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  39. data/ext/couchbase_ext/observe.c +171 -0
  40. data/ext/couchbase_ext/plugin_common.c +171 -0
  41. data/ext/couchbase_ext/result.c +129 -0
  42. data/ext/couchbase_ext/stats.c +163 -0
  43. data/ext/couchbase_ext/store.c +542 -0
  44. data/ext/couchbase_ext/timer.c +192 -0
  45. data/ext/couchbase_ext/touch.c +186 -0
  46. data/ext/couchbase_ext/unlock.c +176 -0
  47. data/ext/couchbase_ext/utils.c +551 -0
  48. data/ext/couchbase_ext/version.c +142 -0
  49. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  50. data/lib/active_support/cache/couchbase_store.rb +430 -0
  51. data/lib/couchbase.rb +155 -0
  52. data/lib/couchbase/bucket.rb +457 -0
  53. data/lib/couchbase/cluster.rb +119 -0
  54. data/lib/couchbase/connection_pool.rb +58 -0
  55. data/lib/couchbase/constants.rb +12 -0
  56. data/lib/couchbase/result.rb +26 -0
  57. data/lib/couchbase/transcoder.rb +120 -0
  58. data/lib/couchbase/utils.rb +62 -0
  59. data/lib/couchbase/version.rb +21 -0
  60. data/lib/couchbase/view.rb +506 -0
  61. data/lib/couchbase/view_row.rb +272 -0
  62. data/lib/ext/multi_json_fix.rb +56 -0
  63. data/lib/rack/session/couchbase.rb +108 -0
  64. data/tasks/benchmark.rake +6 -0
  65. data/tasks/compile.rake +160 -0
  66. data/tasks/test.rake +100 -0
  67. data/tasks/util.rake +21 -0
  68. data/test/profile/.gitignore +1 -0
  69. data/test/profile/Gemfile +6 -0
  70. data/test/profile/benchmark.rb +195 -0
  71. data/test/setup.rb +178 -0
  72. data/test/test_arithmetic.rb +185 -0
  73. data/test/test_async.rb +316 -0
  74. data/test/test_bucket.rb +276 -0
  75. data/test/test_cas.rb +235 -0
  76. data/test/test_couchbase.rb +77 -0
  77. data/test/test_couchbase_connection_pool.rb +77 -0
  78. data/test/test_couchbase_rails_cache_store.rb +361 -0
  79. data/test/test_delete.rb +120 -0
  80. data/test/test_errors.rb +82 -0
  81. data/test/test_eventmachine.rb +70 -0
  82. data/test/test_format.rb +164 -0
  83. data/test/test_get.rb +407 -0
  84. data/test/test_stats.rb +57 -0
  85. data/test/test_store.rb +216 -0
  86. data/test/test_timer.rb +42 -0
  87. data/test/test_touch.rb +97 -0
  88. data/test/test_unlock.rb +119 -0
  89. data/test/test_utils.rb +58 -0
  90. data/test/test_version.rb +52 -0
  91. metadata +353 -0
@@ -0,0 +1,551 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ cb_gc_protect_ptr(struct cb_bucket_st *bucket, void *ptr, mark_f mark_func)
22
+ {
23
+ st_insert(bucket->object_space, (st_index_t)ptr, (st_data_t)mark_func);
24
+ }
25
+
26
+ void
27
+ cb_gc_unprotect_ptr(struct cb_bucket_st *bucket, void *ptr)
28
+ {
29
+ st_delete(bucket->object_space, (st_index_t*)&ptr, NULL);
30
+ }
31
+
32
+ struct proc_params_st
33
+ {
34
+ struct cb_bucket_st *bucket;
35
+ VALUE recv;
36
+ ID mid;
37
+ int argc;
38
+ VALUE *argv;
39
+ VALUE exc;
40
+ };
41
+
42
+ static VALUE
43
+ do_async_error_notify(VALUE ptr)
44
+ {
45
+ struct proc_params_st *p = (struct proc_params_st *)ptr;
46
+ return rb_funcall(p->bucket->on_error_proc, cb_id_call, 1, p->exc);
47
+ }
48
+
49
+ void
50
+ cb_async_error_notify(struct cb_bucket_st *bucket, VALUE exc)
51
+ {
52
+ if (bucket->on_error_proc != Qnil) {
53
+ struct proc_params_st params;
54
+ int fail;
55
+ params.bucket = bucket;
56
+ params.exc = exc;
57
+ rb_protect(do_async_error_notify, (VALUE)&params, &fail);
58
+ if (fail) {
59
+ rb_warning("Couchbase::Bucket#on_error shouldn't raise exceptions");
60
+ }
61
+ } else {
62
+ if (NIL_P(bucket->exception)) {
63
+ bucket->exception = exc;
64
+ }
65
+ }
66
+ }
67
+
68
+ int
69
+ cb_bucket_connected_bang(struct cb_bucket_st *bucket, VALUE operation)
70
+ {
71
+ if (bucket->type == LCB_TYPE_BUCKET &&
72
+ (bucket->handle == NULL || !bucket->connected)) {
73
+ VALUE exc = rb_exc_new2(cb_eConnectError, "not connected to the server");
74
+ rb_ivar_set(exc, cb_id_iv_operation, operation);
75
+ rb_ivar_set(exc, cb_id_iv_value, bucket->self);
76
+ if (bucket->async) {
77
+ cb_async_error_notify(bucket, exc);
78
+ } else {
79
+ rb_exc_raise(exc);
80
+ }
81
+ return 0;
82
+ }
83
+ return 1;
84
+ }
85
+
86
+ static VALUE
87
+ func_call_failed(VALUE ptr, VALUE exc)
88
+ {
89
+ struct proc_params_st *p = (struct proc_params_st *)ptr;
90
+ cb_async_error_notify(p->bucket, exc);
91
+ return Qnil;
92
+ }
93
+
94
+ static VALUE
95
+ do_func_call(VALUE ptr)
96
+ {
97
+ struct proc_params_st *p = (struct proc_params_st *)ptr;
98
+ return rb_funcall2(p->recv, p->mid, p->argc, p->argv);
99
+ }
100
+
101
+ VALUE
102
+ cb_proc_call(struct cb_bucket_st *bucket, VALUE recv, int argc, ...)
103
+ {
104
+ VALUE *argv;
105
+ va_list ar;
106
+ int arity;
107
+ int ii;
108
+ struct proc_params_st params;
109
+
110
+ arity = FIX2INT(rb_funcall(recv, cb_id_arity, 0));
111
+ if (arity < 0) {
112
+ arity = argc;
113
+ }
114
+ if (arity > 0) {
115
+ va_init_list(ar, argc);
116
+ argv = ALLOCA_N(VALUE, argc);
117
+ for (ii = 0; ii < arity; ++ii) {
118
+ if (ii < argc) {
119
+ argv[ii] = va_arg(ar, VALUE);
120
+ } else {
121
+ argv[ii] = Qnil;
122
+ }
123
+ }
124
+ va_end(ar);
125
+ } else {
126
+ argv = NULL;
127
+ }
128
+ params.bucket = bucket;
129
+ params.recv = recv;
130
+ params.mid = cb_id_call;
131
+ params.argc = arity;
132
+ params.argv = argv;
133
+ return rb_rescue2(do_func_call, (VALUE)&params,
134
+ func_call_failed, (VALUE)&params,
135
+ rb_eException, (VALUE)0);
136
+ }
137
+
138
+ VALUE
139
+ cb_hash_delete(VALUE hash, VALUE key)
140
+ {
141
+ return rb_funcall(hash, cb_id_delete, 1, key);
142
+ }
143
+
144
+ /* Helper to convert return code from libcouchbase to meaningful exception.
145
+ * Returns nil if the code considering successful and exception object
146
+ * otherwise. Store given string to exceptions as message, and also
147
+ * initialize +error+ attribute with given return code. */
148
+ VALUE
149
+ cb_check_error_with_status(lcb_error_t rc, const char *msg, VALUE key,
150
+ lcb_http_status_t status)
151
+ {
152
+ VALUE klass, exc, str;
153
+ char buf[300];
154
+
155
+ if ((rc == LCB_SUCCESS && (status == 0 || status / 100 == 2)) ||
156
+ rc == LCB_AUTH_CONTINUE) {
157
+ return Qnil;
158
+ }
159
+ switch (rc) {
160
+ case LCB_AUTH_ERROR:
161
+ klass = cb_eAuthError;
162
+ break;
163
+ case LCB_DELTA_BADVAL:
164
+ klass = cb_eDeltaBadvalError;
165
+ break;
166
+ case LCB_E2BIG:
167
+ klass = cb_eTooBigError;
168
+ break;
169
+ case LCB_EBUSY:
170
+ klass = cb_eBusyError;
171
+ break;
172
+ case LCB_EINTERNAL:
173
+ klass = cb_eInternalError;
174
+ break;
175
+ case LCB_EINVAL:
176
+ klass = cb_eInvalidError;
177
+ break;
178
+ case LCB_ENOMEM:
179
+ klass = cb_eNoMemoryError;
180
+ break;
181
+ case LCB_ERANGE:
182
+ klass = cb_eRangeError;
183
+ break;
184
+ case LCB_ETMPFAIL:
185
+ klass = cb_eTmpFailError;
186
+ break;
187
+ case LCB_KEY_EEXISTS:
188
+ klass = cb_eKeyExistsError;
189
+ break;
190
+ case LCB_KEY_ENOENT:
191
+ klass = cb_eNotFoundError;
192
+ break;
193
+ case LCB_DLOPEN_FAILED:
194
+ klass = cb_eDlopenFailedError;
195
+ break;
196
+ case LCB_DLSYM_FAILED:
197
+ klass = cb_eDlsymFailedError;
198
+ break;
199
+ case LCB_NETWORK_ERROR:
200
+ klass = cb_eNetworkError;
201
+ break;
202
+ case LCB_NOT_MY_VBUCKET:
203
+ klass = cb_eNotMyVbucketError;
204
+ break;
205
+ case LCB_NOT_STORED:
206
+ klass = cb_eNotStoredError;
207
+ break;
208
+ case LCB_NOT_SUPPORTED:
209
+ klass = cb_eNotSupportedError;
210
+ break;
211
+ case LCB_UNKNOWN_COMMAND:
212
+ klass = cb_eUnknownCommandError;
213
+ break;
214
+ case LCB_UNKNOWN_HOST:
215
+ klass = cb_eUnknownHostError;
216
+ break;
217
+ case LCB_PROTOCOL_ERROR:
218
+ klass = cb_eProtocolError;
219
+ break;
220
+ case LCB_ETIMEDOUT:
221
+ klass = cb_eTimeoutError;
222
+ break;
223
+ case LCB_CONNECT_ERROR:
224
+ klass = cb_eConnectError;
225
+ break;
226
+ case LCB_BUCKET_ENOENT:
227
+ klass = cb_eBucketNotFoundError;
228
+ break;
229
+ case LCB_CLIENT_ENOMEM:
230
+ klass = cb_eClientNoMemoryError;
231
+ break;
232
+ case LCB_CLIENT_ETMPFAIL:
233
+ klass = cb_eClientTmpFailError;
234
+ break;
235
+ case LCB_EBADHANDLE:
236
+ klass = cb_eBadHandleError;
237
+ break;
238
+ case LCB_SERVER_BUG:
239
+ klass = cb_eServerBug;
240
+ break;
241
+ case LCB_PLUGIN_VERSION_MISMATCH:
242
+ klass = cb_ePluginVersionMismatch;
243
+ break;
244
+ case LCB_INVALID_HOST_FORMAT:
245
+ klass = cb_eInvalidHostFormat;
246
+ break;
247
+ case LCB_INVALID_CHAR:
248
+ klass = cb_eInvalidChar;
249
+ break;
250
+ case LCB_DURABILITY_ETOOMANY:
251
+ klass = cb_eDurabilityTooMany;
252
+ break;
253
+ case LCB_DUPLICATE_COMMANDS:
254
+ klass = cb_eDuplicateCommands;
255
+ break;
256
+ case LCB_NO_MATCHING_SERVER:
257
+ klass = cb_eBadEnvironment;
258
+ break;
259
+ case LCB_BAD_ENVIRONMENT:
260
+ klass = cb_eBadEnvironment;
261
+ break;
262
+ case LCB_BUSY:
263
+ klass = cb_eBusyError;
264
+ break;
265
+ case LCB_INVALID_USERNAME:
266
+ klass = cb_eInvalidUsername;
267
+ break;
268
+ case LCB_ERROR:
269
+ /* fall through */
270
+ default:
271
+ klass = cb_eLibcouchbaseError;
272
+ }
273
+
274
+ str = rb_str_buf_new2(msg ? msg : "");
275
+ rb_str_buf_cat2(str, " (");
276
+ if (key != Qnil) {
277
+ snprintf(buf, 300, "key=\"%s\", ", RSTRING_PTR(key));
278
+ rb_str_buf_cat2(str, buf);
279
+ }
280
+ if (status > 0) {
281
+ const char *reason = NULL;
282
+ klass = cb_eHTTPError;
283
+ snprintf(buf, 300, "status=\"%d\"", status);
284
+ rb_str_buf_cat2(str, buf);
285
+ switch (status) {
286
+ case LCB_HTTP_STATUS_BAD_REQUEST:
287
+ reason = " (Bad Request)";
288
+ break;
289
+ case LCB_HTTP_STATUS_UNAUTHORIZED:
290
+ reason = " (Unauthorized)";
291
+ break;
292
+ case LCB_HTTP_STATUS_PAYMENT_REQUIRED:
293
+ reason = " (Payment Required)";
294
+ break;
295
+ case LCB_HTTP_STATUS_FORBIDDEN:
296
+ reason = " (Forbidden)";
297
+ break;
298
+ case LCB_HTTP_STATUS_NOT_FOUND:
299
+ reason = " (Not Found)";
300
+ break;
301
+ case LCB_HTTP_STATUS_METHOD_NOT_ALLOWED:
302
+ reason = " (Method Not Allowed)";
303
+ break;
304
+ case LCB_HTTP_STATUS_NOT_ACCEPTABLE:
305
+ reason = " (Not Acceptable)";
306
+ break;
307
+ case LCB_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED:
308
+ reason = " (Proxy Authentication Required)";
309
+ break;
310
+ case LCB_HTTP_STATUS_REQUEST_TIMEOUT:
311
+ reason = " (Request Timeout)";
312
+ break;
313
+ case LCB_HTTP_STATUS_CONFLICT:
314
+ reason = " (Conflict)";
315
+ break;
316
+ case LCB_HTTP_STATUS_GONE:
317
+ reason = " (Gone)";
318
+ break;
319
+ case LCB_HTTP_STATUS_LENGTH_REQUIRED:
320
+ reason = " (Length Required)";
321
+ break;
322
+ case LCB_HTTP_STATUS_PRECONDITION_FAILED:
323
+ reason = " (Precondition Failed)";
324
+ break;
325
+ case LCB_HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE:
326
+ reason = " (Request Entity Too Large)";
327
+ break;
328
+ case LCB_HTTP_STATUS_REQUEST_URI_TOO_LONG:
329
+ reason = " (Request Uri Too Long)";
330
+ break;
331
+ case LCB_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE:
332
+ reason = " (Unsupported Media Type)";
333
+ break;
334
+ case LCB_HTTP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE:
335
+ reason = " (Requested Range Not Satisfiable)";
336
+ break;
337
+ case LCB_HTTP_STATUS_EXPECTATION_FAILED:
338
+ reason = " (Expectation Failed)";
339
+ break;
340
+ case LCB_HTTP_STATUS_UNPROCESSABLE_ENTITY:
341
+ reason = " (Unprocessable Entity)";
342
+ break;
343
+ case LCB_HTTP_STATUS_LOCKED:
344
+ reason = " (Locked)";
345
+ break;
346
+ case LCB_HTTP_STATUS_FAILED_DEPENDENCY:
347
+ reason = " (Failed Dependency)";
348
+ break;
349
+ case LCB_HTTP_STATUS_INTERNAL_SERVER_ERROR:
350
+ reason = " (Internal Server Error)";
351
+ break;
352
+ case LCB_HTTP_STATUS_NOT_IMPLEMENTED:
353
+ reason = " (Not Implemented)";
354
+ break;
355
+ case LCB_HTTP_STATUS_BAD_GATEWAY:
356
+ reason = " (Bad Gateway)";
357
+ break;
358
+ case LCB_HTTP_STATUS_SERVICE_UNAVAILABLE:
359
+ reason = " (Service Unavailable)";
360
+ break;
361
+ case LCB_HTTP_STATUS_GATEWAY_TIMEOUT:
362
+ reason = " (Gateway Timeout)";
363
+ break;
364
+ case LCB_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED:
365
+ reason = " (Http Version Not Supported)";
366
+ break;
367
+ case LCB_HTTP_STATUS_INSUFFICIENT_STORAGE:
368
+ reason = " (Insufficient Storage)";
369
+ break;
370
+ default:
371
+ reason = "";
372
+ }
373
+ rb_str_buf_cat2(str, reason);
374
+ rb_str_buf_cat2(str, ", ");
375
+
376
+ }
377
+ snprintf(buf, 300, "error=0x%02x)", rc);
378
+ rb_str_buf_cat2(str, buf);
379
+ exc = rb_exc_new3(klass, str);
380
+ rb_ivar_set(exc, cb_id_iv_error, INT2FIX(rc));
381
+ rb_ivar_set(exc, cb_id_iv_key, key);
382
+ rb_ivar_set(exc, cb_id_iv_cas, Qnil);
383
+ rb_ivar_set(exc, cb_id_iv_operation, Qnil);
384
+ rb_ivar_set(exc, cb_id_iv_status, status ? INT2FIX(status) : Qnil);
385
+ return exc;
386
+ }
387
+
388
+ VALUE
389
+ cb_check_error(lcb_error_t rc, const char *msg, VALUE key)
390
+ {
391
+ return cb_check_error_with_status(rc, msg, key, 0);
392
+ }
393
+
394
+ static VALUE
395
+ do_encode(VALUE *args)
396
+ {
397
+ VALUE val = args[0];
398
+ uint32_t *flags = (uint32_t *)args[1];
399
+ VALUE transcoder = args[2];
400
+ VALUE options = args[3];
401
+ VALUE ret;
402
+
403
+ ret = rb_funcall(transcoder, cb_id_dump, 3, val, ULONG2NUM(*flags), options);
404
+ Check_Type(ret, T_ARRAY);
405
+ if (RARRAY_LEN(ret) != 2) {
406
+ rb_raise(rb_eArgError, "#dump method of transcoder should return two items");
407
+ }
408
+ *flags = NUM2ULONG(rb_ary_entry(ret, 1));
409
+ return rb_ary_entry(ret, 0);
410
+ }
411
+
412
+ static VALUE
413
+ do_decode(VALUE *args)
414
+ {
415
+ VALUE blob = args[0];
416
+ VALUE transcoder = args[2];
417
+ VALUE flags = args[1];
418
+ VALUE options = args[3];
419
+
420
+ return rb_funcall(transcoder, cb_id_load, 3, blob, ULONG2NUM(flags), options);
421
+ }
422
+
423
+ static VALUE
424
+ coding_failed(VALUE unused, VALUE exc)
425
+ {
426
+ (void)unused;
427
+ return exc;
428
+ }
429
+
430
+ VALUE
431
+ cb_encode_value(VALUE transcoder, VALUE val, uint32_t *flags, VALUE options)
432
+ {
433
+ VALUE args[4];
434
+
435
+ args[0] = val;
436
+ args[1] = (VALUE)flags;
437
+ args[2] = transcoder;
438
+ args[3] = options;
439
+
440
+ /* if nil, just pass value through */
441
+ if (NIL_P(args[2])) {
442
+ return val;
443
+ }
444
+ /* bytestring or exception object */
445
+ return rb_rescue(do_encode, (VALUE)args, coding_failed, 0);
446
+ }
447
+
448
+ VALUE
449
+ cb_decode_value(VALUE transcoder, VALUE blob, uint32_t flags, VALUE options)
450
+ {
451
+ VALUE args[4];
452
+
453
+ /* first it must be bytestring */
454
+ if (TYPE(blob) != T_STRING) {
455
+ return Qundef;
456
+ }
457
+ args[0] = blob;
458
+ args[1] = (VALUE)flags;
459
+ args[2] = transcoder;
460
+ args[3] = options;
461
+
462
+ /* if nil, just pass blob through */
463
+ if (NIL_P(args[2])) {
464
+ return blob;
465
+ }
466
+ /* the value or exception object */
467
+ return rb_rescue(do_decode, (VALUE)args, coding_failed, 0);
468
+ }
469
+
470
+ void
471
+ cb_strip_key_prefix(struct cb_bucket_st *bucket, VALUE key)
472
+ {
473
+ if (RTEST(bucket->key_prefix_val)) {
474
+ rb_str_update(key, 0, RSTRING_LEN(bucket->key_prefix_val), cb_vStrEmpty);
475
+ }
476
+ }
477
+
478
+ VALUE
479
+ cb_unify_key(struct cb_bucket_st *bucket, VALUE key, int apply_prefix)
480
+ {
481
+ VALUE ret = Qnil, tmp;
482
+
483
+ if (RTEST(bucket->key_prefix_val) && apply_prefix) {
484
+ ret = rb_str_dup(bucket->key_prefix_val);
485
+ }
486
+ switch (TYPE(key)) {
487
+ case T_STRING:
488
+ return NIL_P(ret) ? key : rb_str_concat(ret, key);
489
+ case T_SYMBOL:
490
+ tmp = STR_NEW_CSTR(rb_id2name(SYM2ID(key)));
491
+ return NIL_P(ret) ? tmp : rb_str_concat(ret, tmp);
492
+ default: /* call #to_str or raise error */
493
+ tmp = StringValue(key);
494
+ return NIL_P(ret) ? tmp : rb_str_concat(ret, tmp);
495
+ }
496
+ }
497
+
498
+ void
499
+ cb_build_headers(struct cb_context_st *ctx, const char * const *headers)
500
+ {
501
+ if (!ctx->headers_built) {
502
+ VALUE key = Qnil, val;
503
+ for (size_t ii = 1; *headers != NULL; ++ii, ++headers) {
504
+ if (ii % 2 == 0) {
505
+ if (key == Qnil) {
506
+ break;
507
+ }
508
+ val = rb_hash_aref(ctx->headers_val, key);
509
+ switch (TYPE(val)) {
510
+ case T_NIL:
511
+ rb_hash_aset(ctx->headers_val, key, STR_NEW_CSTR(*headers));
512
+ break;
513
+ case T_ARRAY:
514
+ rb_ary_push(val, STR_NEW_CSTR(*headers));
515
+ break;
516
+ default:
517
+ {
518
+ VALUE ary = rb_ary_new();
519
+ rb_ary_push(ary, val);
520
+ rb_ary_push(ary, STR_NEW_CSTR(*headers));
521
+ rb_hash_aset(ctx->headers_val, key, ary);
522
+ }
523
+ }
524
+ } else {
525
+ key = STR_NEW_CSTR(*headers);
526
+ }
527
+ }
528
+ ctx->headers_built = 1;
529
+ }
530
+ }
531
+
532
+ int
533
+ cb_first_value_i(VALUE key, VALUE value, VALUE arg)
534
+ {
535
+ VALUE *val = (VALUE *)arg;
536
+
537
+ *val = value;
538
+ (void)key;
539
+ return ST_STOP;
540
+ }
541
+
542
+ #ifndef HAVE_RB_HASH_LOOKUP2
543
+ VALUE
544
+ rb_hash_lookup2(VALUE hash, VALUE key, VALUE dflt)
545
+ {
546
+ if (RTEST(rb_funcall2(hash, cb_id_has_key_p, 1, &key))) {
547
+ dflt = rb_hash_aref(hash, key);
548
+ }
549
+ return dflt;
550
+ }
551
+ #endif