yerba 0.9.1-arm-linux-gnu → 0.10.0-arm-linux-gnu

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -16
  3. data/exe/arm-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +67 -32
  5. data/ext/yerba/yerba.c +296 -186
  6. data/lib/yerba/3.2/yerba.so +0 -0
  7. data/lib/yerba/3.3/yerba.so +0 -0
  8. data/lib/yerba/3.4/yerba.so +0 -0
  9. data/lib/yerba/4.0/yerba.so +0 -0
  10. data/lib/yerba/collection.rb +4 -4
  11. data/lib/yerba/document.rb +134 -0
  12. data/lib/yerba/formatting.rb +1 -0
  13. data/lib/yerba/location.rb +8 -1
  14. data/lib/yerba/map.rb +16 -6
  15. data/lib/yerba/node.rb +41 -0
  16. data/lib/yerba/query_result.rb +33 -10
  17. data/lib/yerba/scalar.rb +29 -0
  18. data/lib/yerba/selector.rb +142 -0
  19. data/lib/yerba/sequence.rb +38 -33
  20. data/lib/yerba/version.rb +1 -1
  21. data/lib/yerba.rb +1 -0
  22. data/rust/Cargo.lock +1 -1
  23. data/rust/Cargo.toml +1 -1
  24. data/rust/src/commands/get.rs +12 -12
  25. data/rust/src/commands/init.rs +8 -8
  26. data/rust/src/commands/insert.rs +4 -18
  27. data/rust/src/commands/location.rs +2 -0
  28. data/rust/src/commands/mod.rs +34 -6
  29. data/rust/src/commands/move_item.rs +1 -1
  30. data/rust/src/commands/move_key.rs +8 -8
  31. data/rust/src/commands/rename.rs +1 -1
  32. data/rust/src/commands/selectors.rs +1 -1
  33. data/rust/src/commands/set.rs +14 -6
  34. data/rust/src/document/condition.rs +25 -25
  35. data/rust/src/document/delete.rs +57 -43
  36. data/rust/src/document/get.rs +91 -82
  37. data/rust/src/document/insert.rs +77 -77
  38. data/rust/src/document/mod.rs +138 -64
  39. data/rust/src/document/nodes.rs +162 -0
  40. data/rust/src/document/schema.rs +8 -8
  41. data/rust/src/document/set.rs +49 -26
  42. data/rust/src/document/sort.rs +54 -54
  43. data/rust/src/document/style.rs +50 -49
  44. data/rust/src/document/unique.rs +5 -5
  45. data/rust/src/error.rs +8 -4
  46. data/rust/src/ffi.rs +272 -102
  47. data/rust/src/json.rs +62 -2
  48. data/rust/src/lib.rs +2 -1
  49. data/rust/src/main.rs +2 -1
  50. data/rust/src/selector.rs +48 -7
  51. data/rust/src/syntax.rs +449 -56
  52. data/rust/src/validation.rs +5 -3
  53. data/rust/src/yerbafile.rs +124 -76
  54. metadata +4 -2
data/ext/yerba/yerba.c CHANGED
@@ -1,5 +1,6 @@
1
1
  #include <ruby.h>
2
2
  #include <ruby/encoding.h>
3
+ #include <math.h>
3
4
  #include <string.h>
4
5
  #include "include/yerba.h"
5
6
 
@@ -48,9 +49,9 @@ static void check_result(YerbaResult result) {
48
49
  VALUE message = make_utf8_string(result.error);
49
50
  VALUE error_class = rb_eError;
50
51
 
51
- if (strstr(result.error, "invalid path")) {
52
+ if (strstr(result.error, "invalid selector")) {
52
53
  error_class = rb_ePathValidationError;
53
- } else if (strstr(result.error, "path not found") || strstr(result.error, "not a sequence")) {
54
+ } else if (strstr(result.error, "selector not found") || strstr(result.error, "not a sequence")) {
54
55
  error_class = rb_ePathNotFoundError;
55
56
  } else if (strstr(result.error, "parse error")) {
56
57
  error_class = rb_eParseError;
@@ -72,21 +73,23 @@ static VALUE typed_value_to_ruby(YerbaTypedValue typed_value) {
72
73
  break;
73
74
 
74
75
  case YERBA_VALUE_TYPE_BOOLEAN:
75
- result = (strcmp(typed_value.text, "true") == 0 || strcmp(typed_value.text, "True") == 0 ||
76
- strcmp(typed_value.text, "TRUE") == 0 || strcmp(typed_value.text, "yes") == 0 ||
77
- strcmp(typed_value.text, "Yes") == 0 || strcmp(typed_value.text, "YES") == 0 ||
78
- strcmp(typed_value.text, "on") == 0 || strcmp(typed_value.text, "On") == 0 ||
79
- strcmp(typed_value.text, "ON") == 0 || strcmp(typed_value.text, "y") == 0 ||
80
- strcmp(typed_value.text, "Y") == 0)
81
- ? Qtrue : Qfalse;
76
+ result = strcmp(typed_value.text, "true") == 0 ? Qtrue : Qfalse;
82
77
  break;
83
78
 
84
79
  case YERBA_VALUE_TYPE_INTEGER:
85
- result = rb_cstr_to_inum(typed_value.text, 0, 0);
80
+ result = rb_cstr_to_inum(typed_value.text, 10, 0);
86
81
  break;
87
82
 
88
83
  case YERBA_VALUE_TYPE_FLOAT:
89
- result = rb_float_new(rb_cstr_to_dbl(typed_value.text, 0));
84
+ if (strcmp(typed_value.text, ".inf") == 0) {
85
+ result = rb_float_new(HUGE_VAL);
86
+ } else if (strcmp(typed_value.text, "-.inf") == 0) {
87
+ result = rb_float_new(-HUGE_VAL);
88
+ } else if (strcmp(typed_value.text, ".nan") == 0) {
89
+ result = rb_float_new(nan(""));
90
+ } else {
91
+ result = rb_float_new(rb_cstr_to_dbl(typed_value.text, 0));
92
+ }
90
93
  break;
91
94
 
92
95
  case YERBA_VALUE_TYPE_STRING:
@@ -116,12 +119,12 @@ static int should_proceed(struct Document *document, VALUE opts) {
116
119
  VALUE v_condition = rb_hash_aref(opts, ID2SYM(rb_intern("condition")));
117
120
  if (NIL_P(v_condition)) return 1;
118
121
 
119
- VALUE v_path = rb_hash_aref(opts, ID2SYM(rb_intern("condition_path")));
120
- const char *parent_path = NIL_P(v_path) ? "" : StringValueCStr(v_path);
122
+ VALUE v_selector = rb_hash_aref(opts, ID2SYM(rb_intern("condition_path")));
123
+ const char *parent_selector = NIL_P(v_selector) ? "" : StringValueCStr(v_selector);
121
124
 
122
- check_condition(StringValueCStr(v_condition), parent_path[0] != '\0');
125
+ check_condition(StringValueCStr(v_condition), parent_selector[0] != '\0');
123
126
 
124
- return yerba_document_evaluate_condition(document, parent_path, StringValueCStr(v_condition));
127
+ return yerba_document_evaluate_condition(document, parent_selector, StringValueCStr(v_condition));
125
128
  }
126
129
 
127
130
  /* Document.new(path) */
@@ -199,19 +202,21 @@ static VALUE document_s_parse(VALUE klass, VALUE content) {
199
202
  static VALUE location_to_ruby(YerbaLocation location) {
200
203
  VALUE klass = rb_path2class("Yerba::Location");
201
204
 
202
- return rb_funcall(klass, rb_intern("new"), 6,
205
+ return rb_funcall(klass, rb_intern("new"), 8,
203
206
  SIZET2NUM(location.start_line),
204
207
  SIZET2NUM(location.start_column),
205
208
  SIZET2NUM(location.end_line),
206
209
  SIZET2NUM(location.end_column),
207
210
  SIZET2NUM(location.start_offset),
208
- SIZET2NUM(location.end_offset)
211
+ SIZET2NUM(location.end_offset),
212
+ SIZET2NUM(location.start_character_column),
213
+ SIZET2NUM(location.end_character_column)
209
214
  );
210
215
  }
211
216
 
212
217
  /* Build a Yerba::Scalar, Map, Sequence, or nil from a YerbaGetResult.
213
218
  Frees the result internally. */
214
- static VALUE node_from_get_result(YerbaGetResult result, VALUE self, VALUE path) {
219
+ static VALUE node_from_get_result(YerbaGetResult result, VALUE self, VALUE selector) {
215
220
  if (result.error) {
216
221
  VALUE message = make_utf8_string(result.error);
217
222
  yerba_get_result_free(result);
@@ -239,18 +244,18 @@ static VALUE node_from_get_result(YerbaGetResult result, VALUE self, VALUE path)
239
244
  VALUE kwargs = rb_hash_new();
240
245
  rb_hash_aset(kwargs, ID2SYM(rb_intern("value")), value);
241
246
 
242
- return rb_funcallv_kw(rb_path2class("Yerba::Scalar"), rb_intern("from_document"), 5, (VALUE[]){ self, path, location, key, kwargs }, RB_PASS_KEYWORDS);
247
+ return rb_funcallv_kw(rb_path2class("Yerba::Scalar"), rb_intern("from_document"), 5, (VALUE[]){ self, selector, location, key, kwargs }, RB_PASS_KEYWORDS);
243
248
  }
244
249
 
245
250
  case NODE_TYPE_MAP:
246
251
  yerba_get_result_free(result);
247
252
 
248
- return rb_funcall(rb_path2class("Yerba::Map"), rb_intern("from_document"), 4, self, path, location, key);
253
+ return rb_funcall(rb_path2class("Yerba::Map"), rb_intern("from_document"), 4, self, selector, location, key);
249
254
 
250
255
  case NODE_TYPE_SEQUENCE:
251
256
  yerba_get_result_free(result);
252
257
 
253
- return rb_funcall(rb_path2class("Yerba::Sequence"), rb_intern("from_document"), 4, self, path, location, key);
258
+ return rb_funcall(rb_path2class("Yerba::Sequence"), rb_intern("from_document"), 4, self, selector, location, key);
254
259
 
255
260
  default:
256
261
  yerba_get_result_free(result);
@@ -259,20 +264,20 @@ static VALUE node_from_get_result(YerbaGetResult result, VALUE self, VALUE path)
259
264
  }
260
265
  }
261
266
 
262
- /* document[](path) → Yerba::Scalar, Yerba::Map, Yerba::Sequence, Array, or nil */
263
- static VALUE document_bracket(VALUE self, VALUE path) {
267
+ /* document[](selector) → Yerba::Scalar, Yerba::Map, Yerba::Sequence, Array, or nil */
268
+ static VALUE document_bracket(VALUE self, VALUE selector) {
264
269
  struct Document *document = get_document(self);
265
270
  char index_buffer[32];
266
271
 
267
- if (RB_TYPE_P(path, T_FIXNUM)) {
268
- snprintf(index_buffer, sizeof(index_buffer), "[%ld]", FIX2LONG(path));
269
- path = rb_str_new_cstr(index_buffer);
272
+ if (RB_TYPE_P(selector, T_FIXNUM)) {
273
+ snprintf(index_buffer, sizeof(index_buffer), "[%ld]", FIX2LONG(selector));
274
+ selector = rb_str_new_cstr(index_buffer);
270
275
  }
271
276
 
272
- const char *selector = StringValueCStr(path);
277
+ const char *selector_string = StringValueCStr(selector);
273
278
 
274
- if (yerba_selector_has_wildcard(selector)) {
275
- char *json = yerba_document_resolve_selectors(document, selector);
279
+ if (yerba_selector_has_wildcard(selector_string)) {
280
+ char *json = yerba_document_resolve_selectors(document, selector_string);
276
281
 
277
282
  if (!json) return rb_ary_new();
278
283
 
@@ -284,9 +289,9 @@ static VALUE document_bracket(VALUE self, VALUE path) {
284
289
  VALUE array = rb_ary_new_capa(length);
285
290
 
286
291
  for (long i = 0; i < length; i++) {
287
- VALUE concrete_path = rb_ary_entry(resolved, i);
288
- YerbaGetResult result = yerba_document_get(document, StringValueCStr(concrete_path));
289
- VALUE node = node_from_get_result(result, self, concrete_path);
292
+ VALUE concrete_selector = rb_ary_entry(resolved, i);
293
+ YerbaGetResult result = yerba_document_get(document, StringValueCStr(concrete_selector));
294
+ VALUE node = node_from_get_result(result, self, concrete_selector);
290
295
 
291
296
  if (!NIL_P(node)) rb_ary_push(array, node);
292
297
  }
@@ -294,29 +299,44 @@ static VALUE document_bracket(VALUE self, VALUE path) {
294
299
  return array;
295
300
  }
296
301
 
297
- YerbaGetResult result = yerba_document_get(document, selector);
302
+ VALUE concrete_selector = selector;
303
+
304
+ if (strstr(selector_string, "[-") != NULL) {
305
+ char *json = yerba_document_resolve_selectors(document, selector_string);
306
+
307
+ if (json) {
308
+ VALUE json_string = make_utf8_string(json);
309
+ yerba_string_free(json);
298
310
 
299
- return node_from_get_result(result, self, path);
311
+ VALUE resolved = rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
312
+
313
+ if (RARRAY_LEN(resolved) > 0) concrete_selector = rb_ary_entry(resolved, 0);
314
+ }
315
+ }
316
+
317
+ YerbaGetResult result = yerba_document_get(document, StringValueCStr(selector));
318
+
319
+ return node_from_get_result(result, self, concrete_selector);
300
320
  }
301
321
 
302
- /* document.exists?(path) */
303
- static VALUE document_exists_p(VALUE self, VALUE path) {
322
+ /* document.exists?(selector) */
323
+ static VALUE document_exists_p(VALUE self, VALUE selector) {
304
324
  struct Document *document = get_document(self);
305
325
 
306
- return yerba_document_exists(document, StringValueCStr(path)) ? Qtrue : Qfalse;
326
+ return yerba_document_exists(document, StringValueCStr(selector)) ? Qtrue : Qfalse;
307
327
  }
308
328
 
309
329
  /* document.valid_selector?(selector) → true/false */
310
- static VALUE document_valid_selector_p(VALUE self, VALUE path) {
330
+ static VALUE document_valid_selector_p(VALUE self, VALUE selector) {
311
331
  struct Document *document = get_document(self);
312
332
 
313
- return yerba_document_valid_selector(document, StringValueCStr(path)) ? Qtrue : Qfalse;
333
+ return yerba_document_valid_selector(document, StringValueCStr(selector)) ? Qtrue : Qfalse;
314
334
  }
315
335
 
316
- /* document.source(path) → raw YAML text of the node at path */
317
- static VALUE document_source(VALUE self, VALUE path) {
336
+ /* document.source(selector) → raw YAML text of the node at selector */
337
+ static VALUE document_source(VALUE self, VALUE selector) {
318
338
  struct Document *document = get_document(self);
319
- char *text = yerba_document_source(document, StringValueCStr(path));
339
+ char *text = yerba_document_source(document, StringValueCStr(selector));
320
340
 
321
341
  if (!text) return Qnil;
322
342
 
@@ -326,17 +346,22 @@ static VALUE document_source(VALUE self, VALUE path) {
326
346
  return result;
327
347
  }
328
348
 
329
- /* document.get_value(path) → parsed Ruby object (Hash/Array/String/Integer/etc) */
330
- static VALUE document_get_value(VALUE self, VALUE path) {
349
+ /* document.get_value(selector) → parsed Ruby object (Hash/Array/String/Integer/etc) */
350
+ static VALUE document_get_value(VALUE self, VALUE selector) {
331
351
  struct Document *document = get_document(self);
332
- char *json = yerba_document_get_value(document, StringValueCStr(path));
352
+ char *json = yerba_document_get_value(document, StringValueCStr(selector));
333
353
 
334
354
  if (!json) return Qnil;
335
355
 
336
356
  VALUE json_string = make_utf8_string(json);
337
357
  yerba_string_free(json);
338
358
 
339
- return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
359
+ VALUE options = rb_hash_new();
360
+ rb_hash_aset(options, ID2SYM(rb_intern("allow_nan")), Qtrue);
361
+
362
+ VALUE arguments[] = {json_string, options};
363
+
364
+ return rb_funcallv_kw(rb_path2class("JSON"), rb_intern("parse"), 2, arguments, RB_PASS_KEYWORDS);
340
365
  }
341
366
 
342
367
  /* document.values → the whole document as Ruby objects, cached until the document changes */
@@ -356,6 +381,44 @@ static VALUE document_values(VALUE self) {
356
381
  return values;
357
382
  }
358
383
 
384
+ /* document.native_node_records → [[parent, type, selector, key, value, has_value, location, key_location], ...] */
385
+ static VALUE document_node_records(VALUE self) {
386
+ YerbaNodeList list = yerba_document_nodes(get_document(self));
387
+ VALUE records = rb_ary_new_capa((long)list.length);
388
+
389
+ for (size_t i = 0; i < list.length; i++) {
390
+ YerbaNodeRecord record = list.records[i];
391
+ VALUE type;
392
+
393
+ switch (record.node_type) {
394
+ case NODE_TYPE_SCALAR: type = ID2SYM(rb_intern("scalar")); break;
395
+ case NODE_TYPE_MAP: type = ID2SYM(rb_intern("map")); break;
396
+ case NODE_TYPE_SEQUENCE: type = ID2SYM(rb_intern("sequence")); break;
397
+ default: type = Qnil; break;
398
+ }
399
+
400
+ rb_ary_push(records, rb_ary_new_from_args(8,
401
+ LONG2NUM(record.parent),
402
+ type,
403
+ make_utf8_string(record.selector),
404
+ record.key_name ? make_utf8_string(record.key_name) : Qnil,
405
+ typed_value_to_ruby(record.value),
406
+ record.has_value ? Qtrue : Qfalse,
407
+ location_to_ruby(record.location),
408
+ record.key_name ? location_to_ruby(record.key_location) : Qnil
409
+ ));
410
+ }
411
+
412
+ yerba_node_list_free(list);
413
+
414
+ return records;
415
+ }
416
+
417
+ /* document.native_generation → changes whenever the document is edited */
418
+ static VALUE document_generation(VALUE self) {
419
+ return ULL2NUM(yerba_document_generation(get_document(self)));
420
+ }
421
+
359
422
  /* document.selectors → ["database", "database.host", "tags", "tags[]", ...] */
360
423
  static VALUE document_selectors(VALUE self) {
361
424
  struct Document *document = get_document(self);
@@ -370,9 +433,9 @@ static VALUE document_selectors(VALUE self) {
370
433
  }
371
434
 
372
435
  /* document.keys_at(selector) → ["host", "port", ...] */
373
- static VALUE document_keys_at(VALUE self, VALUE path) {
436
+ static VALUE document_keys_at(VALUE self, VALUE selector) {
374
437
  struct Document *document = get_document(self);
375
- char *json = yerba_document_keys(document, StringValueCStr(path));
438
+ char *json = yerba_document_keys(document, StringValueCStr(selector));
376
439
 
377
440
  if (!json) return rb_ary_new();
378
441
 
@@ -383,9 +446,9 @@ static VALUE document_keys_at(VALUE self, VALUE path) {
383
446
  }
384
447
 
385
448
  /* document.locations(selector) → [Yerba::Location, ...] */
386
- static VALUE document_locations(VALUE self, VALUE path) {
449
+ static VALUE document_locations(VALUE self, VALUE selector) {
387
450
  struct Document *document = get_document(self);
388
- char *json = yerba_document_resolve_selectors(document, StringValueCStr(path));
451
+ char *json = yerba_document_resolve_selectors(document, StringValueCStr(selector));
389
452
 
390
453
  if (!json) return rb_ary_new();
391
454
 
@@ -397,8 +460,8 @@ static VALUE document_locations(VALUE self, VALUE path) {
397
460
  VALUE array = rb_ary_new_capa(length);
398
461
 
399
462
  for (long i = 0; i < length; i++) {
400
- VALUE concrete_path = rb_ary_entry(resolved, i);
401
- YerbaGetResult result = yerba_document_get(document, StringValueCStr(concrete_path));
463
+ VALUE concrete_selector = rb_ary_entry(resolved, i);
464
+ YerbaGetResult result = yerba_document_get(document, StringValueCStr(concrete_selector));
402
465
 
403
466
  if (result.error) {
404
467
  yerba_get_result_free(result);
@@ -418,10 +481,10 @@ static VALUE document_locations(VALUE self, VALUE path) {
418
481
  return array;
419
482
  }
420
483
 
421
- /* document.resolve_selectors(path) → ["[0].speakers[0]", "[0].speakers[1]", ...] */
422
- static VALUE document_resolve_selectors(VALUE self, VALUE path) {
484
+ /* document.resolve_selectors(selector) → ["[0].speakers[0]", "[0].speakers[1]", ...] */
485
+ static VALUE document_resolve_selectors(VALUE self, VALUE selector) {
423
486
  struct Document *document = get_document(self);
424
- char *json = yerba_document_resolve_selectors(document, StringValueCStr(path));
487
+ char *json = yerba_document_resolve_selectors(document, StringValueCStr(selector));
425
488
 
426
489
  if (!json) return rb_ary_new();
427
490
 
@@ -456,10 +519,10 @@ static VALUE yerba_s_quote_scalar(int argc, VALUE *argv, VALUE self) {
456
519
  return result;
457
520
  }
458
521
 
459
- /* document.get_quote_style(path) → :plain, :single, :double, :literal, etc. or nil */
460
- static VALUE document_get_quote_style(VALUE self, VALUE path) {
522
+ /* document.get_quote_style(selector) → :plain, :single, :double, :literal, etc. or nil */
523
+ static VALUE document_get_quote_style(VALUE self, VALUE selector) {
461
524
  struct Document *document = get_document(self);
462
- char *style = yerba_document_get_quote_style(document, StringValueCStr(path));
525
+ char *style = yerba_document_get_quote_style(document, StringValueCStr(selector));
463
526
 
464
527
  if (style == NULL) return Qnil;
465
528
 
@@ -470,8 +533,8 @@ static VALUE document_get_quote_style(VALUE self, VALUE path) {
470
533
  return symbol;
471
534
  }
472
535
 
473
- /* document.set_quote_style(path, style) */
474
- static VALUE document_set_quote_style(VALUE self, VALUE path, VALUE style) {
536
+ /* document.set_quote_style(selector, style) */
537
+ static VALUE document_set_quote_style(VALUE self, VALUE selector, VALUE style) {
475
538
  struct Document *document = get_document(self);
476
539
 
477
540
  const char *style_string;
@@ -484,17 +547,17 @@ static VALUE document_set_quote_style(VALUE self, VALUE path, VALUE style) {
484
547
  rb_raise(rb_eError, "Invalid quote style (expected Symbol or String)");
485
548
  }
486
549
 
487
- YerbaResult result = yerba_document_set_quote_style(document, StringValueCStr(path), style_string);
550
+ YerbaResult result = yerba_document_set_quote_style(document, StringValueCStr(selector), style_string);
488
551
 
489
552
  check_result(result);
490
553
 
491
554
  return self;
492
555
  }
493
556
 
494
- /* document.get_collection_style(path) → :flow, :block, or nil */
495
- static VALUE document_get_collection_style(VALUE self, VALUE path) {
557
+ /* document.get_collection_style(selector) → :flow, :block, or nil */
558
+ static VALUE document_get_collection_style(VALUE self, VALUE selector) {
496
559
  struct Document *document = get_document(self);
497
- char *style = yerba_document_get_collection_style(document, StringValueCStr(path));
560
+ char *style = yerba_document_get_collection_style(document, StringValueCStr(selector));
498
561
 
499
562
  if (style == NULL) return Qnil;
500
563
 
@@ -505,8 +568,8 @@ static VALUE document_get_collection_style(VALUE self, VALUE path) {
505
568
  return symbol;
506
569
  }
507
570
 
508
- /* document.set_collection_style(path, style) */
509
- static VALUE document_set_collection_style(VALUE self, VALUE path, VALUE style) {
571
+ /* document.set_collection_style(selector, style) */
572
+ static VALUE document_set_collection_style(VALUE self, VALUE selector, VALUE style) {
510
573
  struct Document *document = get_document(self);
511
574
 
512
575
  const char *style_string;
@@ -519,17 +582,17 @@ static VALUE document_set_collection_style(VALUE self, VALUE path, VALUE style)
519
582
  rb_raise(rb_eError, "Invalid collection style (expected Symbol or String)");
520
583
  }
521
584
 
522
- YerbaResult result = yerba_document_set_collection_style(document, StringValueCStr(path), style_string);
585
+ YerbaResult result = yerba_document_set_collection_style(document, StringValueCStr(selector), style_string);
523
586
 
524
587
  check_result(result);
525
588
 
526
589
  return self;
527
590
  }
528
591
 
529
- /* document.get_sequence_indent(path) → :compact, :indented, or nil */
530
- static VALUE document_get_sequence_indent(VALUE self, VALUE path) {
592
+ /* document.get_sequence_indent(selector) → :compact, :indented, or nil */
593
+ static VALUE document_get_sequence_indent(VALUE self, VALUE selector) {
531
594
  struct Document *document = get_document(self);
532
- char *style = yerba_document_get_sequence_indent(document, StringValueCStr(path));
595
+ char *style = yerba_document_get_sequence_indent(document, StringValueCStr(selector));
533
596
 
534
597
  if (style == NULL) return Qnil;
535
598
 
@@ -540,8 +603,8 @@ static VALUE document_get_sequence_indent(VALUE self, VALUE path) {
540
603
  return symbol;
541
604
  }
542
605
 
543
- /* document.set_sequence_indent(path, style) */
544
- static VALUE document_set_sequence_indent(VALUE self, VALUE path, VALUE style) {
606
+ /* document.set_sequence_indent(selector, style) */
607
+ static VALUE document_set_sequence_indent(VALUE self, VALUE selector, VALUE style) {
545
608
  struct Document *document = get_document(self);
546
609
 
547
610
  const char *style_string;
@@ -554,38 +617,51 @@ static VALUE document_set_sequence_indent(VALUE self, VALUE path, VALUE style) {
554
617
  rb_raise(rb_eError, "Invalid sequence indent style (expected Symbol or String)");
555
618
  }
556
619
 
557
- YerbaResult result = yerba_document_set_sequence_indent(document, StringValueCStr(path), style_string);
620
+ YerbaResult result = yerba_document_set_sequence_indent(document, StringValueCStr(selector), style_string);
558
621
 
559
622
  check_result(result);
560
623
 
561
624
  return self;
562
625
  }
563
626
 
564
- /* document.condition?(condition, path: "") */
627
+ /* TODO(v1.0): stop reading the deprecated path: keyword. */
628
+ static VALUE selector_option(VALUE opts) {
629
+ VALUE selector = rb_hash_aref(opts, ID2SYM(rb_intern("selector")));
630
+
631
+ if (!NIL_P(selector)) return selector;
632
+
633
+ VALUE path = rb_hash_aref(opts, ID2SYM(rb_intern("path")));
634
+
635
+ if (!NIL_P(path)) rb_warn("Yerba: the path: keyword is deprecated, use selector: instead");
636
+
637
+ return path;
638
+ }
639
+
640
+ /* document.condition?(condition, selector: "") */
565
641
  static VALUE document_condition_p(int argc, VALUE *argv, VALUE self) {
566
642
  VALUE condition, opts;
567
643
  rb_scan_args(argc, argv, "1:", &condition, &opts);
568
644
 
569
- const char *parent_path = "";
645
+ const char *parent_selector = "";
570
646
  if (!NIL_P(opts)) {
571
- VALUE v_path = rb_hash_aref(opts, ID2SYM(rb_intern("path")));
647
+ VALUE v_selector = selector_option(opts);
572
648
 
573
- if (!NIL_P(v_path)) {
574
- parent_path = StringValueCStr(v_path);
649
+ if (!NIL_P(v_selector)) {
650
+ parent_selector = StringValueCStr(v_selector);
575
651
  }
576
652
  }
577
653
 
578
654
  struct Document *document = get_document(self);
579
655
 
580
- check_condition(StringValueCStr(condition), parent_path[0] != '\0');
656
+ check_condition(StringValueCStr(condition), parent_selector[0] != '\0');
581
657
 
582
- return yerba_document_evaluate_condition(document, parent_path, StringValueCStr(condition)) ? Qtrue : Qfalse;
658
+ return yerba_document_evaluate_condition(document, parent_selector, StringValueCStr(condition)) ? Qtrue : Qfalse;
583
659
  }
584
660
 
585
- /* document.find(path, condition: nil, select: nil) */
661
+ /* document.find(selector, condition: nil, select: nil) */
586
662
  static VALUE document_find(int argc, VALUE *argv, VALUE self) {
587
- VALUE path, opts;
588
- rb_scan_args(argc, argv, "1:", &path, &opts);
663
+ VALUE selector, opts;
664
+ rb_scan_args(argc, argv, "1:", &selector, &opts);
589
665
 
590
666
  const char *condition = NULL;
591
667
  const char *select = NULL;
@@ -602,7 +678,7 @@ static VALUE document_find(int argc, VALUE *argv, VALUE self) {
602
678
 
603
679
  check_condition(condition, true);
604
680
 
605
- char *json = yerba_document_find(document, StringValueCStr(path), condition, select);
681
+ char *json = yerba_document_find(document, StringValueCStr(selector), condition, select);
606
682
 
607
683
  if (!json) return rb_ary_new();
608
684
 
@@ -655,10 +731,10 @@ static struct TypedValue ruby_to_typed_value(VALUE value, char *number_buffer) {
655
731
  return result;
656
732
  }
657
733
 
658
- /* document.set(path, value, condition: nil, if_exists: false, if_missing: false, plain: false, all: false) */
734
+ /* document.set(selector, value, condition: nil, if_exists: false, if_missing: false, plain: false, all: false) */
659
735
  static VALUE document_set(int argc, VALUE *argv, VALUE self) {
660
- VALUE path, value, opts;
661
- rb_scan_args(argc, argv, "2:", &path, &value, &opts);
736
+ VALUE selector, value, opts;
737
+ rb_scan_args(argc, argv, "2:", &selector, &value, &opts);
662
738
 
663
739
  struct Document *document = get_document(self);
664
740
 
@@ -668,8 +744,19 @@ static VALUE document_set(int argc, VALUE *argv, VALUE self) {
668
744
  VALUE v_if_exists = rb_hash_aref(opts, ID2SYM(rb_intern("if_exists")));
669
745
  VALUE v_if_missing = rb_hash_aref(opts, ID2SYM(rb_intern("if_missing")));
670
746
 
671
- if (RTEST(v_if_exists) && !yerba_document_exists(document, StringValueCStr(path))) return self;
672
- if (RTEST(v_if_missing) && yerba_document_exists(document, StringValueCStr(path))) return self;
747
+ if (RTEST(v_if_exists) && !yerba_document_exists(document, StringValueCStr(selector))) return self;
748
+
749
+ if (RTEST(v_if_missing)) {
750
+ if (yerba_document_exists(document, StringValueCStr(selector))) return self;
751
+
752
+ VALUE insert_opts = rb_hash_new();
753
+ rb_hash_aset(insert_opts, ID2SYM(rb_intern("plain")), rb_hash_aref(opts, ID2SYM(rb_intern("plain"))));
754
+
755
+ VALUE insert_argv[3] = { selector, value, insert_opts };
756
+ rb_funcallv_kw(self, rb_intern("insert"), 3, insert_argv, RB_PASS_KEYWORDS);
757
+
758
+ return self;
759
+ }
673
760
  }
674
761
 
675
762
  char number_buffer[64];
@@ -685,16 +772,16 @@ static VALUE document_set(int argc, VALUE *argv, VALUE self) {
685
772
  if (RTEST(v_plain)) plain = true;
686
773
  }
687
774
 
688
- YerbaResult result = yerba_document_set(document, StringValueCStr(path), typed_value.text, typed_value.length, typed_value.type, plain, all);
775
+ YerbaResult result = yerba_document_set(document, StringValueCStr(selector), typed_value.text, typed_value.length, typed_value.type, plain, all);
689
776
  check_result(result);
690
777
 
691
778
  return self;
692
779
  }
693
780
 
694
- /* document.insert(path, value, before: nil, after: nil, at: nil, plain: false, style: nil) */
781
+ /* document.insert(selector, value, before: nil, after: nil, at: nil, plain: false, style: nil) */
695
782
  static VALUE document_insert(int argc, VALUE *argv, VALUE self) {
696
- VALUE path, value, opts;
697
- rb_scan_args(argc, argv, "2:", &path, &value, &opts);
783
+ VALUE selector, value, opts;
784
+ rb_scan_args(argc, argv, "2:", &selector, &value, &opts);
698
785
 
699
786
  const char *before = NULL;
700
787
  const char *after = NULL;
@@ -725,17 +812,17 @@ static VALUE document_insert(int argc, VALUE *argv, VALUE self) {
725
812
 
726
813
  struct Document *document = get_document(self);
727
814
  YerbaResult result =
728
- yerba_document_insert(document, StringValueCStr(path), typed_value.text, typed_value.length, typed_value.type, plain, style,
815
+ yerba_document_insert(document, StringValueCStr(selector), typed_value.text, typed_value.length, typed_value.type, plain, style,
729
816
  before, after, at);
730
817
  check_result(result);
731
818
 
732
819
  return self;
733
820
  }
734
821
 
735
- /* document.insert_object(path, object, before: nil, after: nil, at: nil) */
822
+ /* document.insert_object(selector, object, before: nil, after: nil, at: nil) */
736
823
  static VALUE document_insert_object(int argc, VALUE *argv, VALUE self) {
737
- VALUE path, object, opts;
738
- rb_scan_args(argc, argv, "2:", &path, &object, &opts);
824
+ VALUE selector, object, opts;
825
+ rb_scan_args(argc, argv, "2:", &selector, &object, &opts);
739
826
 
740
827
  const char *before = NULL;
741
828
  const char *after = NULL;
@@ -754,51 +841,51 @@ static VALUE document_insert_object(int argc, VALUE *argv, VALUE self) {
754
841
  struct Document *document = get_document(self);
755
842
  VALUE json_string = rb_funcall(rb_path2class("JSON"), rb_intern("generate"), 1, object);
756
843
 
757
- YerbaResult result = yerba_document_insert_object(document, StringValueCStr(path), StringValueCStr(json_string), before, after, at);
844
+ YerbaResult result = yerba_document_insert_object(document, StringValueCStr(selector), StringValueCStr(json_string), before, after, at);
758
845
  check_result(result);
759
846
  return self;
760
847
  }
761
848
 
762
- /* document.insert_objects(path, array) */
763
- static VALUE document_insert_objects(VALUE self, VALUE path, VALUE array) {
849
+ /* document.insert_objects(selector, array) */
850
+ static VALUE document_insert_objects(VALUE self, VALUE selector, VALUE array) {
764
851
  struct Document *document = get_document(self);
765
852
  VALUE json_string = rb_funcall(rb_path2class("JSON"), rb_intern("generate"), 1, array);
766
853
 
767
- YerbaResult result = yerba_document_insert_objects(document, StringValueCStr(path), StringValueCStr(json_string));
854
+ YerbaResult result = yerba_document_insert_objects(document, StringValueCStr(selector), StringValueCStr(json_string));
768
855
  check_result(result);
769
856
 
770
857
  return self;
771
858
  }
772
859
 
773
- /* document.delete(path, condition: nil) */
860
+ /* document.delete(selector, condition: nil) */
774
861
  static VALUE document_delete(int argc, VALUE *argv, VALUE self) {
775
- VALUE path, opts;
776
- rb_scan_args(argc, argv, "1:", &path, &opts);
862
+ VALUE selector, opts;
863
+ rb_scan_args(argc, argv, "1:", &selector, &opts);
777
864
 
778
865
  struct Document *document = get_document(self);
779
866
  if (!should_proceed(document, opts)) return self;
780
867
 
781
- YerbaResult result = yerba_document_delete(document, StringValueCStr(path));
868
+ YerbaResult result = yerba_document_delete(document, StringValueCStr(selector));
782
869
 
783
870
  check_result(result);
784
871
 
785
872
  return self;
786
873
  }
787
874
 
788
- /* document.remove(path, value) */
789
- static VALUE document_remove(VALUE self, VALUE path, VALUE value) {
875
+ /* document.remove(selector, value) */
876
+ static VALUE document_remove(VALUE self, VALUE selector, VALUE value) {
790
877
  struct Document *document = get_document(self);
791
- YerbaResult result = yerba_document_remove(document, StringValueCStr(path), StringValueCStr(value));
878
+ YerbaResult result = yerba_document_remove(document, StringValueCStr(selector), StringValueCStr(value));
792
879
 
793
880
  check_result(result);
794
881
 
795
882
  return self;
796
883
  }
797
884
 
798
- /* document.remove_at(path, index) */
799
- static VALUE document_remove_at(VALUE self, VALUE path, VALUE index) {
885
+ /* document.remove_at(selector, index) */
886
+ static VALUE document_remove_at(VALUE self, VALUE selector, VALUE index) {
800
887
  struct Document *document = get_document(self);
801
- YerbaResult result = yerba_document_remove_at(document, StringValueCStr(path), NUM2SIZET(index));
888
+ YerbaResult result = yerba_document_remove_at(document, StringValueCStr(selector), NUM2SIZET(index));
802
889
 
803
890
  check_result(result);
804
891
 
@@ -815,27 +902,46 @@ static VALUE document_rename(VALUE self, VALUE source, VALUE destination) {
815
902
  return self;
816
903
  }
817
904
 
818
- /* document.sort(path = "", by: nil, order: nil, case_sensitive: false) */
905
+ /* document.sort(selector = "", by: nil, order: nil, case_sensitive: false)
906
+ *
907
+ * by: a field, several fields separated by commas or spaces, or an Array of fields
908
+ * order: :asc, :desc, or an explicit order of values (Array or comma-separated String)
909
+ */
819
910
  static VALUE document_sort(int argc, VALUE *argv, VALUE self) {
820
- VALUE path, opts;
821
- rb_scan_args(argc, argv, "01:", &path, &opts);
911
+ VALUE selector, opts;
912
+ rb_scan_args(argc, argv, "01:", &selector, &opts);
822
913
 
823
- if (NIL_P(path)) path = rb_str_new_cstr("");
914
+ if (NIL_P(selector)) selector = rb_str_new_cstr("");
824
915
 
825
- const char *by = NULL;
916
+ VALUE by_value = Qnil;
917
+ VALUE order_csv = Qnil;
918
+ bool descending = false;
826
919
  bool case_sensitive = false;
827
- VALUE v_order = Qnil;
828
920
 
829
921
  if (!NIL_P(opts)) {
830
922
  VALUE v_by = rb_hash_aref(opts, ID2SYM(rb_intern("by")));
831
- v_order = rb_hash_aref(opts, ID2SYM(rb_intern("order")));
923
+ VALUE v_order = rb_hash_aref(opts, ID2SYM(rb_intern("order")));
832
924
  VALUE v_case_sensitive = rb_hash_aref(opts, ID2SYM(rb_intern("case_sensitive")));
833
925
 
834
- if (SYMBOL_P(v_by)) {
835
- VALUE by_string = rb_sym2str(v_by);
836
- by = StringValueCStr(by_string);
926
+ if (RB_TYPE_P(v_by, T_ARRAY)) {
927
+ by_value = rb_ary_join(v_by, rb_str_new_cstr(","));
928
+ } else if (SYMBOL_P(v_by)) {
929
+ by_value = rb_sym2str(v_by);
837
930
  } else if (!NIL_P(v_by)) {
838
- by = StringValueCStr(v_by);
931
+ by_value = rb_obj_as_string(v_by);
932
+ }
933
+
934
+ if (RB_TYPE_P(v_order, T_ARRAY)) {
935
+ order_csv = rb_ary_join(v_order, rb_str_new_cstr(","));
936
+ } else if (!NIL_P(v_order)) {
937
+ VALUE order_string = SYMBOL_P(v_order) ? rb_sym2str(v_order) : rb_obj_as_string(v_order);
938
+ const char *order = StringValueCStr(order_string);
939
+
940
+ if (strcmp(order, "desc") == 0 || strcmp(order, "descending") == 0) {
941
+ descending = true;
942
+ } else if (strcmp(order, "asc") != 0 && strcmp(order, "ascending") != 0) {
943
+ order_csv = order_string;
944
+ }
839
945
  }
840
946
 
841
947
  if (RTEST(v_case_sensitive)) {
@@ -844,62 +950,37 @@ static VALUE document_sort(int argc, VALUE *argv, VALUE self) {
844
950
  }
845
951
 
846
952
  struct Document *document = get_document(self);
847
- const char *path_string = StringValueCStr(path);
848
-
849
- if (RB_TYPE_P(v_order, T_ARRAY)) {
850
- VALUE order_csv = rb_ary_join(v_order, rb_str_new_cstr(","));
851
- const char *order_string = StringValueCStr(order_csv);
852
- const char *reorder_path = StringValueCStr(path);
853
- const char *reorder_by;
854
-
855
- if (by) {
856
- VALUE reorder_by_value = rb_hash_aref(opts, ID2SYM(rb_intern("by")));
857
-
858
- if (SYMBOL_P(reorder_by_value)) {
859
- reorder_by_value = rb_sym2str(reorder_by_value);
860
- }
861
-
862
- reorder_by = StringValueCStr(reorder_by_value);
863
- } else {
864
- reorder_by = ".";
865
- }
953
+ const char *path_string = StringValueCStr(selector);
954
+ const char *by = NIL_P(by_value) ? NULL : StringValueCStr(by_value);
866
955
 
867
- YerbaResult result = yerba_document_reorder(document, reorder_path, reorder_by, order_string);
956
+ if (!NIL_P(order_csv)) {
957
+ YerbaResult result = yerba_document_reorder(document, path_string, by ? by : ".", StringValueCStr(order_csv));
868
958
  check_result(result);
869
959
 
870
- return self;
871
- }
872
-
873
- const char *order = NULL;
960
+ RB_GC_GUARD(by_value);
961
+ RB_GC_GUARD(order_csv);
874
962
 
875
- if (SYMBOL_P(v_order)) {
876
- VALUE order_string = rb_sym2str(v_order);
877
- order = StringValueCStr(order_string);
878
- } else if (!NIL_P(v_order)) {
879
- order = StringValueCStr(v_order);
963
+ return self;
880
964
  }
881
965
 
882
966
  VALUE by_with_order = Qnil;
883
967
 
884
- if (order && strcmp(order, "desc") == 0) {
885
- if (by) {
886
- by_with_order = rb_sprintf("%s:desc", by);
887
- } else {
888
- by_with_order = rb_str_new_cstr(":desc");
889
- }
890
-
968
+ if (descending) {
969
+ by_with_order = by ? rb_sprintf("%s:desc", by) : rb_str_new_cstr(":desc");
891
970
  by = StringValueCStr(by_with_order);
892
971
  }
893
972
 
894
973
  YerbaResult result = yerba_document_sort(document, path_string, by, case_sensitive);
895
-
896
974
  check_result(result);
897
975
 
976
+ RB_GC_GUARD(by_value);
977
+ RB_GC_GUARD(by_with_order);
978
+
898
979
  return self;
899
980
  }
900
981
 
901
- /* document.sort_keys(path, order) */
902
- static VALUE document_sort_keys(VALUE self, VALUE path, VALUE order) {
982
+ /* document.sort_keys(selector, order) */
983
+ static VALUE document_sort_keys(VALUE self, VALUE selector, VALUE order) {
903
984
  struct Document *document = get_document(self);
904
985
 
905
986
  VALUE order_string;
@@ -909,44 +990,44 @@ static VALUE document_sort_keys(VALUE self, VALUE path, VALUE order) {
909
990
  order_string = order;
910
991
  }
911
992
 
912
- YerbaResult result = yerba_document_sort_keys(document, StringValueCStr(path), StringValueCStr(order_string));
993
+ YerbaResult result = yerba_document_sort_keys(document, StringValueCStr(selector), StringValueCStr(order_string));
913
994
 
914
995
  check_result(result);
915
996
 
916
997
  return self;
917
998
  }
918
999
 
919
- /* document.quote_style(path: nil, key_style: nil, value_style: nil) */
1000
+ /* document.quote_style(selector: nil, key_style: nil, value_style: nil) */
920
1001
  static VALUE document_quote_style(int argc, VALUE *argv, VALUE self) {
921
1002
  VALUE opts;
922
1003
  rb_scan_args(argc, argv, ":", &opts);
923
1004
 
924
- const char *path = NULL;
1005
+ const char *selector = NULL;
925
1006
  const char *key_style = NULL;
926
1007
  const char *value_style = NULL;
927
1008
 
928
1009
  if (!NIL_P(opts)) {
929
- VALUE v_path = rb_hash_aref(opts, ID2SYM(rb_intern("path")));
1010
+ VALUE v_selector = selector_option(opts);
930
1011
  VALUE v_key_style = rb_hash_aref(opts, ID2SYM(rb_intern("key_style")));
931
1012
  VALUE v_value_style = rb_hash_aref(opts, ID2SYM(rb_intern("value_style")));
932
1013
 
933
- if (!NIL_P(v_path)) path = StringValueCStr(v_path);
1014
+ if (!NIL_P(v_selector)) selector = StringValueCStr(v_selector);
934
1015
  if (!NIL_P(v_key_style)) key_style = StringValueCStr(v_key_style);
935
1016
  if (!NIL_P(v_value_style)) value_style = StringValueCStr(v_value_style);
936
1017
  }
937
1018
 
938
1019
  struct Document *document = get_document(self);
939
- YerbaResult result = yerba_document_quote_style(document, path, key_style, value_style);
1020
+ YerbaResult result = yerba_document_quote_style(document, selector, key_style, value_style);
940
1021
 
941
1022
  check_result(result);
942
1023
 
943
1024
  return self;
944
1025
  }
945
1026
 
946
- /* document.blank_lines(path, count) */
947
- static VALUE document_blank_lines(VALUE self, VALUE path, VALUE count) {
1027
+ /* document.blank_lines(selector, count) */
1028
+ static VALUE document_blank_lines(VALUE self, VALUE selector, VALUE count) {
948
1029
  struct Document *document = get_document(self);
949
- YerbaResult result = yerba_document_blank_lines(document, StringValueCStr(path), NUM2SIZET(count));
1030
+ YerbaResult result = yerba_document_blank_lines(document, StringValueCStr(selector), NUM2SIZET(count));
950
1031
 
951
1032
  check_result(result);
952
1033
 
@@ -1027,20 +1108,20 @@ static VALUE document_changed_p(VALUE self) {
1027
1108
 
1028
1109
  /* document.location(selector = nil) → Yerba::Location */
1029
1110
  static VALUE document_location(int argc, VALUE *argv, VALUE self) {
1030
- VALUE path;
1031
- rb_scan_args(argc, argv, "01", &path);
1111
+ VALUE selector;
1112
+ rb_scan_args(argc, argv, "01", &selector);
1032
1113
 
1033
1114
  struct Document *document = get_document(self);
1034
- const char *selector = NIL_P(path) ? "" : StringValueCStr(path);
1115
+ const char *selector_string = NIL_P(selector) ? "" : StringValueCStr(selector);
1035
1116
 
1036
- YerbaGetResult result = yerba_document_get(document, selector);
1117
+ YerbaGetResult result = yerba_document_get(document, selector_string);
1037
1118
 
1038
1119
  if (result.error) {
1039
1120
  yerba_get_result_free(result);
1040
1121
  return Qnil;
1041
1122
  }
1042
1123
 
1043
- if (result.location.start_line == 0 && result.location.end_line == 0 && strlen(selector) > 0) {
1124
+ if (result.location.start_line == 0 && result.location.end_line == 0 && strlen(selector_string) > 0) {
1044
1125
  yerba_get_result_free(result);
1045
1126
  return Qnil;
1046
1127
  }
@@ -1125,24 +1206,24 @@ static VALUE located_list_to_ruby(YerbaTypedList result, VALUE document) {
1125
1206
  }
1126
1207
 
1127
1208
  /* Collection.get(glob, selector) → [Yerba::Scalar|Map|Sequence, ...] */
1128
- static VALUE collection_s_get(VALUE self, VALUE pattern, VALUE path) {
1209
+ static VALUE collection_s_get(VALUE self, VALUE pattern, VALUE selector) {
1129
1210
  (void) self;
1130
1211
 
1131
- return located_list_to_ruby(yerba_glob_get(StringValueCStr(pattern), StringValueCStr(path)), Qnil);
1212
+ return located_list_to_ruby(yerba_glob_get(StringValueCStr(pattern), StringValueCStr(selector)), Qnil);
1132
1213
  }
1133
1214
 
1134
1215
  /* document.get_all(selector) → [Yerba::Scalar|Map|Sequence, ...] */
1135
- static VALUE document_get_all(VALUE self, VALUE path) {
1216
+ static VALUE document_get_all(VALUE self, VALUE selector) {
1136
1217
  struct Document *document = get_document(self);
1137
1218
 
1138
- return located_list_to_ruby(yerba_document_get_all(document, StringValueCStr(path)), self);
1219
+ return located_list_to_ruby(yerba_document_get_all(document, StringValueCStr(selector)), self);
1139
1220
  }
1140
1221
 
1141
- /* Collection.find(glob, path, condition: nil, select: nil) */
1222
+ /* Collection.find(glob, selector, condition: nil, select: nil) */
1142
1223
  static VALUE collection_s_find(int argc, VALUE *argv, VALUE self) {
1143
1224
  (void)self;
1144
- VALUE pattern, path, opts;
1145
- rb_scan_args(argc, argv, "2:", &pattern, &path, &opts);
1225
+ VALUE pattern, selector, opts;
1226
+ rb_scan_args(argc, argv, "2:", &pattern, &selector, &opts);
1146
1227
 
1147
1228
  const char *condition = NULL;
1148
1229
  const char *select = NULL;
@@ -1157,7 +1238,7 @@ static VALUE collection_s_find(int argc, VALUE *argv, VALUE self) {
1157
1238
 
1158
1239
  check_condition(condition, true);
1159
1240
 
1160
- YerbaTypedList result = yerba_glob_find(StringValueCStr(pattern), StringValueCStr(path), condition, select);
1241
+ YerbaTypedList result = yerba_glob_find(StringValueCStr(pattern), StringValueCStr(selector), condition, select);
1161
1242
 
1162
1243
  if (!result.json) return rb_ary_new();
1163
1244
 
@@ -1167,6 +1248,30 @@ static VALUE collection_s_find(int argc, VALUE *argv, VALUE self) {
1167
1248
  return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
1168
1249
  }
1169
1250
 
1251
+ /* Selector.native_parse(selector) → {"relative" => bool, "segments" => [...]} */
1252
+ static VALUE selector_s_native_parse(VALUE klass, VALUE selector) {
1253
+ char *json = yerba_selector_parse(StringValueCStr(selector));
1254
+
1255
+ if (!json) return Qnil;
1256
+
1257
+ VALUE json_string = make_utf8_string(json);
1258
+ yerba_string_free(json);
1259
+
1260
+ return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
1261
+ }
1262
+
1263
+ /* Selector.native_build(segments_json, relative) → selector string */
1264
+ static VALUE selector_s_native_build(VALUE klass, VALUE segments_json, VALUE relative) {
1265
+ char *result = yerba_selector_build(StringValueCStr(segments_json), RTEST(relative));
1266
+
1267
+ if (!result) return rb_str_new_cstr("");
1268
+
1269
+ VALUE selector = make_utf8_string(result);
1270
+ yerba_string_free(result);
1271
+
1272
+ return selector;
1273
+ }
1274
+
1170
1275
  /* Yerbafile.locate(directory = nil) → path string or nil */
1171
1276
  static VALUE yerbafile_s_locate(int argc, VALUE *argv, VALUE klass) {
1172
1277
  VALUE directory;
@@ -1193,6 +1298,10 @@ void Init_yerba(void) {
1193
1298
  rb_eParseError = rb_define_class_under(rb_mYerba, "ParseError", rb_eError);
1194
1299
  rb_ePathValidationError = rb_define_class_under(rb_mYerba, "PathValidationError", rb_eError);
1195
1300
 
1301
+ VALUE rb_cSelector = rb_define_class_under(rb_mYerba, "Selector", rb_cObject);
1302
+ rb_define_private_method(rb_singleton_class(rb_cSelector), "native_parse", selector_s_native_parse, 1);
1303
+ rb_define_private_method(rb_singleton_class(rb_cSelector), "native_build", selector_s_native_build, 2);
1304
+
1196
1305
  VALUE rb_cCollection = rb_define_class_under(rb_mYerba, "Collection", rb_cObject);
1197
1306
  rb_define_singleton_method(rb_cCollection, "get", collection_s_get, 2);
1198
1307
  rb_define_singleton_method(rb_cCollection, "find", collection_s_find, -1);
@@ -1203,10 +1312,11 @@ void Init_yerba(void) {
1203
1312
  rb_define_method(rb_cDocument, "initialize", document_initialize, -1);
1204
1313
  rb_define_method(rb_cDocument, "replace_content!", document_replace_content, 1);
1205
1314
  rb_define_singleton_method(rb_cDocument, "parse", document_s_parse, 1);
1206
- rb_define_method(rb_cDocument, "[]", document_bracket, 1);
1207
- rb_define_method(rb_cDocument, "node_at", document_bracket, 1);
1208
- rb_define_method(rb_cDocument, "value_at", document_get_value, 1);
1315
+ rb_define_private_method(rb_cDocument, "native_node_at", document_bracket, 1);
1316
+ rb_define_private_method(rb_cDocument, "native_value_at", document_get_value, 1);
1209
1317
  rb_define_method(rb_cDocument, "values", document_values, 0);
1318
+ rb_define_private_method(rb_cDocument, "native_node_records", document_node_records, 0);
1319
+ rb_define_private_method(rb_cDocument, "native_generation", document_generation, 0);
1210
1320
  rb_define_method(rb_cDocument, "get_all", document_get_all, 1);
1211
1321
  rb_define_method(rb_cDocument, "location", document_location, -1);
1212
1322
  rb_define_method(rb_cDocument, "locations", document_locations, 1);