yerba 0.8.1-aarch64-linux-gnu → 0.9.1-aarch64-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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5750e718701701f89479012747d28006c2fa5a1a7297ecd4ef8fabaa216a6623
4
- data.tar.gz: f376137f7ef241fda5a69555517da0229a0a8dbbf5ef5c076d3c9219cd30619b
3
+ metadata.gz: 511327aa49d8c9f0e9e2c739a059d5e78dcffd11acc85a1dd2b2962f6d60f4a1
4
+ data.tar.gz: 5717396d5a174922f4ce7ce2050d6bb170ced9ad9b6d22c57a80680160ca9c7a
5
5
  SHA512:
6
- metadata.gz: 235898aad18aad2d69e8557a044a14abeb21e1afc2cf8dc34e996ac83f659cbc3b6dadeb3c661551a1ab7646712fbbff952edd0bc16c236f3e0e082efed08c6f
7
- data.tar.gz: 3cf2b51f451f986f6be9705ba7cc2f2796fe2eda57ac429944ed65749f97b1644edab4e0bf0779b300b84a3028057ff6df34c9500ea3cbc84340f6d3a90880a4
6
+ metadata.gz: c7b4afdff3d69e24995407c42e0a7f7c775a9648435abb46bdb2a93f876486890b54fadfca20513fa26f8daf35de3e102c0ec75c2a26a5b711e4c92d08fe5d8f
7
+ data.tar.gz: 98308386a28b1a81680609c9b0f051090bc5f3e9eff94daafa3dad7b64aabdd8c9ebcb1873683a69945262d9d05b657a707ede983887ce4ad41f965bbc36309b
data/README.md CHANGED
@@ -47,7 +47,7 @@ Use `yerba` as a library in your Rust project:
47
47
 
48
48
  ```toml
49
49
  [dependencies]
50
- yerba = "0.8"
50
+ yerba = "0.9"
51
51
  ```
52
52
 
53
53
  ```rust
@@ -169,34 +169,62 @@ Use `--condition` to only apply the change when a sibling field matches:
169
169
  yerba set config.yml "database.host" "0.0.0.0" --condition ".port == 5432"
170
170
  ```
171
171
 
172
+ Over a wildcard selector the condition picks out *which* items to edit, the same way it filters in
173
+ `get`. Items that do not match are left alone:
174
+
175
+ ```bash
176
+ yerba set videos.yml "[].title" "New Title" --condition ".id == talk-1"
177
+ ```
178
+
179
+ If the condition matches more than one item the command stops rather than guessing, so add `--all`
180
+ when editing several is what you meant:
181
+
182
+ ```bash
183
+ yerba set videos.yml "[].published" false --plain --condition ".kind == draft" --all
184
+ ```
185
+
172
186
  Use `--all` to update all nodes matching a wildcard selector:
173
187
 
174
188
  ```bash
175
189
  yerba set videos.yml "[].description" "" --all
176
190
  ```
177
191
 
192
+ Values are written in the quote style already at the path, which keeps string edits tidy. A value is quoted anyway when writing it plain would not read back as the string you passed, so `yes` becomes `"yes"` and `12345` becomes `"12345"` — whatever the field held before. Use `--plain` to write a YAML literal instead:
193
+
194
+ ```bash
195
+ yerba set config.yml "database.replica" null --plain
196
+ yerba set config.yml "database.ssl" true --plain
197
+ ```
198
+
178
199
  ### `insert`
179
200
 
180
201
  Insert a new key into a map or a new item into a sequence. By default, new items are appended at the end.
181
202
 
182
203
  ```bash
183
- yerba insert config.yml "database.ssl" true
204
+ yerba insert config.yml "database.ssl" true --plain
184
205
  yerba insert config.yml "tags" "yaml"
185
206
  ```
186
207
 
187
208
  Control placement with `--before`, `--after`, or `--at`:
188
209
 
189
210
  ```bash
190
- yerba insert config.yml "database.ssl" true --after "host"
191
- yerba insert config.yml "database.ssl" true --before "port"
211
+ yerba insert config.yml "database.ssl" true --plain --after "host"
212
+ yerba insert config.yml "database.ssl" true --plain --before "port"
192
213
  yerba insert config.yml "tags" "yaml" --at 0
193
214
  yerba insert config.yml "tags" "yaml" --after "ruby"
194
215
  ```
195
216
 
217
+ Values are written as strings, and quoted whenever writing them plain would change what they mean. `"Conf 2018: A Talk"` stays one string instead of becoming a nested map, and `"12345"` stays a string instead of becoming a number. Use `--plain` to write a YAML literal or a map or sequence fragment instead:
218
+
219
+ ```bash
220
+ yerba insert config.yml "database.ssl" true --plain
221
+ yerba insert speakers.yml "" "name: Bob" --plain --after ".name == Alice"
222
+ ```
223
+
196
224
  For sequences of maps, use conditions to position relative to other items:
197
225
 
198
226
  ```bash
199
- yerba insert speakers.yml "" "name: Bob" --after ".name == Alice"
227
+ yerba insert speakers.yml "" "name: Bob" --plain --after ".name == Alice"
200
228
  yerba insert videos.yml "[0].speakers" "Diana" --before ".name == Charlie"
201
229
  ```
202
230
 
@@ -324,17 +352,17 @@ yerba quote-style videos.yml "[].description" --values literal
324
352
 
325
353
  **Value styles** (`--values`):
326
354
 
327
- | Style | Symbol | Example | Behavior | |
328
- |----------------|--------|--------------------------|--------------------------------------|----------------------------|
329
- | `plain` | — | `host: localhost` | Unquoted | |
330
- | `single` | `'` | `host: 'localhost'` | Single-quoted | |
331
- | `double` | `"` | `host: "localhost"` | Double-quoted, supports `\n` escapes | |
332
- | `literal` | `\ | -` | Preserves newlines | Strip trailing newline |
333
- | `literal-clip` | `\ | ` | Preserves newlines | Keep one trailing newline |
334
- | `literal-keep` | `\ | +` | Preserves newlines | Keep all trailing newlines |
335
- | `folded` | `>-` | Folds newlines to spaces | Strip trailing newline | |
336
- | `folded-clip` | `>` | Folds newlines to spaces | Keep one trailing newline | |
337
- | `folded-keep` | `>+` | Folds newlines to spaces | Keep all trailing newlines | |
355
+ | Style | Symbol | Example | Behavior |
356
+ |----------------|--------|--------------------------|--------------------------------------|
357
+ | `plain` | — | `host: localhost` | Unquoted |
358
+ | `single` | `'` | `host: 'localhost'` | Single-quoted |
359
+ | `double` | `"` | `host: "localhost"` | Double-quoted, supports `\n` escapes |
360
+ | `literal` | `\|-` | Preserves newlines | Strip trailing newline |
361
+ | `literal-clip` | `\|` | Preserves newlines | Keep one trailing newline |
362
+ | `literal-keep` | `\|+` | Preserves newlines | Keep all trailing newlines |
363
+ | `folded` | `>-` | Folds newlines to spaces | Strip trailing newline |
364
+ | `folded-clip` | `>` | Folds newlines to spaces | Keep one trailing newline |
365
+ | `folded-keep` | `>+` | Folds newlines to spaces | Keep all trailing newlines |
338
366
 
339
367
  Block scalars are only converted when scoped to a specific selector. An unscoped `--values double` will not touch existing block scalars.
340
368
 
@@ -348,6 +376,52 @@ yerba blank-lines videos.yml "[]" 1
348
376
  yerba blank-lines config.yml "tags" 0
349
377
  ```
350
378
 
379
+ By default every entry after the first is adjusted. Use `--before` to set apart only certain sections of a map and leave their neighbours alone:
380
+
381
+ ```bash
382
+ yerba blank-lines event.yml "" 1 --before "schedule,speakers"
383
+ ```
384
+
385
+ ```yaml
386
+ id: rubyconf-2024
387
+ name: RubyConf 2024
388
+ city: Chicago
389
+
390
+ schedule:
391
+ - day: 1
392
+
393
+ speakers:
394
+ - name: Alice
395
+ ```
396
+
397
+ `--after` is the mirror image, separating a key from what follows it. Listing a key in both sets it apart on both sides:
398
+
399
+ ```bash
400
+ yerba blank-lines event.yml "" 1 --before "location" --after "location"
401
+ ```
402
+
403
+ Keys that are not listed keep whatever spacing they already have, so these flags add separators without reflowing the rest of the document. Sequence entries have no key and are never matched by a filter, which means `--before` and `--after` on a sequence are no-ops.
404
+
405
+ Use `--skip-empty` to leave placeholder entries tucked against their neighbours. An entry counts as empty when its value is `null`, an empty collection, or an empty string:
406
+
407
+ ```bash
408
+ yerba blank-lines event.yml "" 1 --before "location,speakers,sponsors" --after "location" --skip-empty
409
+ ```
410
+
411
+ ```yaml
412
+ id: rubyconf-2024
413
+ name: RubyConf 2024
414
+
415
+ location:
416
+ city: Chicago
417
+ country: US
418
+
419
+ speakers: []
420
+ sponsors: []
421
+ ```
422
+
423
+ The match is ignored when the entry that *triggered* it is empty, not the one on the other side of the gap. That is why `--after "location"` still separates a populated `location:` block from the empty entries below it, while a bare `location: null` would stay packed. Without `--skip-empty`, every listed key gets its blank line whether or not it holds anything.
424
+
351
425
  ### `directives`
352
426
 
353
427
  Add or remove the document start marker (`---`):
@@ -535,15 +609,16 @@ Available pipeline steps:
535
609
  - `sequence_indent` Enforce compact or indented sequence style
536
610
  - `sort_keys` Reorder keys to match a predefined list
537
611
  - `sort` Sort sequence items by field(s)
538
- - `blank_lines` Enforce blank lines between sequence entries
539
- - `set` Set a value (supports conditions)
540
- - `insert` Insert a new key or sequence item
612
+ - `blank_lines` Enforce blank lines between sequence entries, optionally limited to named map keys with `before` and to non-empty values with `skip_empty`
613
+ - `set` Set a value (supports conditions, and `plain: true` for YAML literals)
614
+ - `insert` Insert a new key or sequence item (`plain: true` for YAML literals and fragments)
541
615
  - `delete` Remove a key (supports conditions)
542
616
  - `rename` Rename a key
543
617
  - `remove` Remove an item from a sequence
544
618
  - `directives` Add or remove the document start marker (`---`), with optional `max` validation
545
619
  - `unique` Find or remove duplicate items in a sequence
546
620
  - `schema` Validate against a JSON schema (with optional `path` for scoping)
621
+ - `escapes` Spell escaped characters literally where the quote style can carry them (with optional `path` for scoping)
547
622
  - `final_newline` Enforce exact number of trailing newlines (`count: 1` by default, strips extras)
548
623
 
549
624
  This makes it easy to enforce project-wide YAML conventions in CI:
@@ -552,6 +627,36 @@ This makes it easy to enforce project-wide YAML conventions in CI:
552
627
  yerba check
553
628
  ```
554
629
 
630
+ The `escapes` step rewrites double-quoted values in the form yerba would write them, so a character the style can carry is spelled out rather than escaped. Text that arrives from an API or a scraper often carries `\U0001F631` where an emoji belongs, or a long value wrapped across two lines. Both read back correctly either way, but neither is pleasant to review in a diff:
631
+
632
+ ```yaml
633
+ pipeline:
634
+ - escapes: {}
635
+ ```
636
+
637
+ ```diff
638
+ - title: "Deploying on a Friday \U0001F631"
639
+ + title: "Deploying on a Friday 😱"
640
+
641
+ - summary: "Why the deploy went out on a Friday, and
642
+ - what we changed afterwards"
643
+ + summary: "Why the deploy went out on a Friday, and what we changed afterwards"
644
+ ```
645
+
646
+ Escapes that have to stay do: a control character, a quote or a backslash is still written escaped, since the value would not read back the same otherwise. Plain, single-quoted and block scalars are left alone, and a `path` scopes the step to part of the document.
647
+
648
+ Every checked file is also scanned for control characters, whatever the pipeline says. Yerba's parser accepts them but libyaml does not, so without this a file could pass `check` in CI and then fail to load in the application that reads it. Offending characters are reported with their line and column:
649
+
650
+ ```console
651
+ $ yerba check
652
+ ✗ data/rubyconf-taiwan/videos.yml
653
+ found 2 control characters that YAML parsers reject:
654
+ U+0008 at line 152, column 34
655
+ U+0008 at line 154, column 12
656
+ ```
657
+
658
+ Tabs, newlines and carriage returns are fine, and so are characters written escaped as `\xNN` inside a double-quoted value — only literal ones are reported.
659
+
555
660
  ## Ruby API
556
661
 
557
662
  Yerba includes a native C extension (backed by the same Rust core) that provides a full Ruby API for YAML editing.
@@ -718,6 +823,42 @@ Insert new keys with positional control:
718
823
  document["database"].insert("ssl", true, after: "host")
719
824
  ```
720
825
 
826
+ A `String` is written as a value, not as YAML text, and quoted whenever writing it plain would change what it means. This is the same rule the CLI's `insert` and `set` follow, so `"key: value"` stays one string instead of becoming a nested map:
827
+
828
+ ```ruby
829
+ document["notes"] = "key: value" # => notes: "key: value"
830
+ document["lines"] = "line\nbreak" # => lines: "line\nbreak"
831
+ document["item"] = "- a" # => item: "- a"
832
+ document["tags"] = "[a, b]" # => tags: "[a, b]"
833
+ ```
834
+
835
+ Other types are written as the YAML literal they stand for, so the value you read back is the value you wrote:
836
+
837
+ ```ruby
838
+ document["port"] = 5432 # => port: 5432
839
+ document["ssl"] = true # => ssl: true
840
+ document["replica"] = nil # => replica: null
841
+ ```
842
+
843
+ Both `set` and `insert` take `plain: true` when the value is YAML text you built yourself, to write it verbatim instead:
844
+
845
+ ```ruby
846
+ document.set("tags", "[a, b]", plain: true) # => tags: [a, b]
847
+ ```
848
+
849
+ ```ruby
850
+ document.insert("tags", "- ruby\n- rails", plain: true)
851
+ # tags:
852
+ # - ruby
853
+ # - rails
854
+ ```
855
+
856
+ Use `style:` to pick the quote style for the inserted value. A style that cannot carry the value falls back to double quotes rather than writing something that reads back differently:
857
+
858
+ ```ruby
859
+ document.insert("city", "Berlin", style: :double) # => city: "Berlin"
860
+ ```
861
+
721
862
  Set arrays and hashes as values, they default to block style:
722
863
 
723
864
  ```ruby
@@ -823,6 +964,16 @@ scalar.quote_style # => :double
823
964
  scalar.quote_style = :single
824
965
  ```
825
966
 
967
+ `Yerba.quote_scalar` writes a value as YAML text without needing a document, using the rule the insert APIs follow. Pass a style to ask for one, and it is honoured unless the value cannot survive it:
968
+
969
+ ```ruby
970
+ Yerba.quote_scalar("plain") # => "plain"
971
+ Yerba.quote_scalar("key: value") # => "\"key: value\""
972
+ Yerba.quote_scalar("12345") # => "\"12345\""
973
+ Yerba.quote_scalar("ok", :single) # => "'ok'"
974
+ Yerba.quote_scalar("a\nb", :single) # => "\"a\\nb\""
975
+ ```
976
+
826
977
  ### Collection Style
827
978
 
828
979
  Read and change how collections are rendered, flow (inline) or block (multi-line):
Binary file
@@ -68,6 +68,8 @@ struct YerbaParseResult yerba_document_parse_file(const char *path);
68
68
 
69
69
  struct YerbaParseResult yerba_document_parse(const char *content);
70
70
 
71
+ uint64_t yerba_document_generation(const struct Document *document);
72
+
71
73
  void yerba_document_free(struct Document *document);
72
74
 
73
75
  struct YerbaGetResult yerba_document_get(const struct Document *document, const char *path);
@@ -129,13 +131,18 @@ char *yerba_document_find(const struct Document *document,
129
131
  struct YerbaResult yerba_document_set(struct Document *document,
130
132
  const char *path,
131
133
  const char *value,
134
+ uintptr_t value_length,
132
135
  enum YerbaValueType value_type,
136
+ bool plain,
133
137
  bool all);
134
138
 
135
139
  struct YerbaResult yerba_document_insert(struct Document *document,
136
140
  const char *path,
137
141
  const char *value,
142
+ uintptr_t value_length,
138
143
  enum YerbaValueType value_type,
144
+ bool plain,
145
+ const char *style,
139
146
  const char *before,
140
147
  const char *after,
141
148
  int64_t at);
@@ -211,6 +218,8 @@ struct YerbaResult yerba_document_apply_yerbafile(struct Document *document,
211
218
 
212
219
  char *yerba_document_to_string(const struct Document *document);
213
220
 
221
+ char *yerba_quote_scalar(const char *value, uintptr_t length, const char *style);
222
+
214
223
  void yerba_string_free(char *s);
215
224
 
216
225
  void yerba_result_free(struct YerbaResult result);
data/ext/yerba/yerba.c CHANGED
@@ -339,6 +339,23 @@ static VALUE document_get_value(VALUE self, VALUE path) {
339
339
  return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
340
340
  }
341
341
 
342
+ /* document.values → the whole document as Ruby objects, cached until the document changes */
343
+ static VALUE document_values(VALUE self) {
344
+ struct Document *document = get_document(self);
345
+ VALUE generation = ULL2NUM(yerba_document_generation(document));
346
+
347
+ if (rb_equal(rb_ivar_get(self, rb_intern("values_generation")), generation)) {
348
+ return rb_ivar_get(self, rb_intern("values"));
349
+ }
350
+
351
+ VALUE values = document_get_value(self, make_utf8_string(""));
352
+
353
+ rb_ivar_set(self, rb_intern("values"), values);
354
+ rb_ivar_set(self, rb_intern("values_generation"), generation);
355
+
356
+ return values;
357
+ }
358
+
342
359
  /* document.selectors → ["database", "database.host", "tags", "tags[]", ...] */
343
360
  static VALUE document_selectors(VALUE self) {
344
361
  struct Document *document = get_document(self);
@@ -414,6 +431,31 @@ static VALUE document_resolve_selectors(VALUE self, VALUE path) {
414
431
  return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
415
432
  }
416
433
 
434
+ /* Yerba.quote_scalar(value, style = nil) → YAML text for value, quoted when the style cannot carry it. */
435
+ static VALUE yerba_s_quote_scalar(int argc, VALUE *argv, VALUE self) {
436
+ VALUE value, style_value;
437
+ rb_scan_args(argc, argv, "11", &value, &style_value);
438
+
439
+ StringValue(value);
440
+
441
+ const char *style = NULL;
442
+
443
+ if (!NIL_P(style_value)) {
444
+ VALUE style_string = rb_obj_is_kind_of(style_value, rb_cString) ? style_value : rb_sym2str(style_value);
445
+ style = StringValueCStr(style_string);
446
+ }
447
+
448
+ char *quoted = yerba_quote_scalar(RSTRING_PTR(value), (size_t)RSTRING_LEN(value), style);
449
+
450
+ if (quoted == NULL) return value;
451
+
452
+ VALUE result = make_utf8_string(quoted);
453
+
454
+ yerba_string_free(quoted);
455
+
456
+ return result;
457
+ }
458
+
417
459
  /* document.get_quote_style(path) → :plain, :single, :double, :literal, etc. or nil */
418
460
  static VALUE document_get_quote_style(VALUE self, VALUE path) {
419
461
  struct Document *document = get_document(self);
@@ -574,6 +616,7 @@ static VALUE document_find(int argc, VALUE *argv, VALUE self) {
574
616
  The caller must provide a number_buffer of at least 64 bytes. */
575
617
  struct TypedValue {
576
618
  const char *text;
619
+ size_t length;
577
620
  YerbaValueType type;
578
621
  };
579
622
 
@@ -598,14 +641,21 @@ static struct TypedValue ruby_to_typed_value(VALUE value, char *number_buffer) {
598
641
  result.text = number_buffer;
599
642
  result.type = YERBA_VALUE_TYPE_FLOAT;
600
643
  } else {
601
- result.text = StringValueCStr(value);
644
+ StringValue(value);
645
+
646
+ result.text = RSTRING_PTR(value);
647
+ result.length = (size_t)RSTRING_LEN(value);
602
648
  result.type = YERBA_VALUE_TYPE_STRING;
649
+
650
+ return result;
603
651
  }
604
652
 
653
+ result.length = strlen(result.text);
654
+
605
655
  return result;
606
656
  }
607
657
 
608
- /* document.set(path, value, condition: nil, if_exists: false, if_missing: false) */
658
+ /* document.set(path, value, condition: nil, if_exists: false, if_missing: false, plain: false, all: false) */
609
659
  static VALUE document_set(int argc, VALUE *argv, VALUE self) {
610
660
  VALUE path, value, opts;
611
661
  rb_scan_args(argc, argv, "2:", &path, &value, &opts);
@@ -625,43 +675,58 @@ static VALUE document_set(int argc, VALUE *argv, VALUE self) {
625
675
  char number_buffer[64];
626
676
  struct TypedValue typed_value = ruby_to_typed_value(value, number_buffer);
627
677
  bool all = false;
678
+ bool plain = false;
628
679
 
629
680
  if (!NIL_P(opts)) {
630
681
  VALUE v_all = rb_hash_aref(opts, ID2SYM(rb_intern("all")));
682
+ VALUE v_plain = rb_hash_aref(opts, ID2SYM(rb_intern("plain")));
631
683
 
632
684
  if (RTEST(v_all)) all = true;
685
+ if (RTEST(v_plain)) plain = true;
633
686
  }
634
687
 
635
- YerbaResult result = yerba_document_set(document, StringValueCStr(path), typed_value.text, typed_value.type, all);
688
+ YerbaResult result = yerba_document_set(document, StringValueCStr(path), typed_value.text, typed_value.length, typed_value.type, plain, all);
636
689
  check_result(result);
637
690
 
638
691
  return self;
639
692
  }
640
693
 
641
- /* document.insert(path, value, before: nil, after: nil, at: nil) */
694
+ /* document.insert(path, value, before: nil, after: nil, at: nil, plain: false, style: nil) */
642
695
  static VALUE document_insert(int argc, VALUE *argv, VALUE self) {
643
696
  VALUE path, value, opts;
644
697
  rb_scan_args(argc, argv, "2:", &path, &value, &opts);
645
698
 
646
699
  const char *before = NULL;
647
700
  const char *after = NULL;
701
+ const char *style = NULL;
648
702
  long long at = -1;
703
+ bool plain = false;
649
704
 
650
705
  if (!NIL_P(opts)) {
651
706
  VALUE v_before = rb_hash_aref(opts, ID2SYM(rb_intern("before")));
652
707
  VALUE v_after = rb_hash_aref(opts, ID2SYM(rb_intern("after")));
653
708
  VALUE v_at = rb_hash_aref(opts, ID2SYM(rb_intern("at")));
709
+ VALUE v_plain = rb_hash_aref(opts, ID2SYM(rb_intern("plain")));
710
+ VALUE v_style = rb_hash_aref(opts, ID2SYM(rb_intern("style")));
654
711
 
655
712
  if (!NIL_P(v_before)) before = StringValueCStr(v_before);
656
713
  if (!NIL_P(v_after)) after = StringValueCStr(v_after);
657
714
  if (!NIL_P(v_at)) at = NUM2LL(v_at);
715
+ if (RTEST(v_plain)) plain = true;
716
+
717
+ if (!NIL_P(v_style)) {
718
+ VALUE style_string = rb_obj_is_kind_of(v_style, rb_cString) ? v_style : rb_sym2str(v_style);
719
+ style = StringValueCStr(style_string);
720
+ }
658
721
  }
659
722
 
660
723
  char number_buffer[64];
661
724
  struct TypedValue typed_value = ruby_to_typed_value(value, number_buffer);
662
725
 
663
726
  struct Document *document = get_document(self);
664
- YerbaResult result = yerba_document_insert(document, StringValueCStr(path), typed_value.text, typed_value.type, before, after, at);
727
+ YerbaResult result =
728
+ yerba_document_insert(document, StringValueCStr(path), typed_value.text, typed_value.length, typed_value.type, plain, style,
729
+ before, after, at);
665
730
  check_result(result);
666
731
 
667
732
  return self;
@@ -1122,6 +1187,7 @@ void Init_yerba(void) {
1122
1187
  rb_require("json");
1123
1188
 
1124
1189
  rb_mYerba = rb_define_module("Yerba");
1190
+ rb_define_module_function(rb_mYerba, "quote_scalar", yerba_s_quote_scalar, -1);
1125
1191
  rb_eError = rb_define_class_under(rb_mYerba, "Error", rb_eStandardError);
1126
1192
  rb_ePathNotFoundError = rb_define_class_under(rb_mYerba, "PathNotFoundError", rb_eError);
1127
1193
  rb_eParseError = rb_define_class_under(rb_mYerba, "ParseError", rb_eError);
@@ -1140,6 +1206,7 @@ void Init_yerba(void) {
1140
1206
  rb_define_method(rb_cDocument, "[]", document_bracket, 1);
1141
1207
  rb_define_method(rb_cDocument, "node_at", document_bracket, 1);
1142
1208
  rb_define_method(rb_cDocument, "value_at", document_get_value, 1);
1209
+ rb_define_method(rb_cDocument, "values", document_values, 0);
1143
1210
  rb_define_method(rb_cDocument, "get_all", document_get_all, 1);
1144
1211
  rb_define_method(rb_cDocument, "location", document_location, -1);
1145
1212
  rb_define_method(rb_cDocument, "locations", document_locations, 1);
Binary file
Binary file
Binary file
Binary file
@@ -3,16 +3,7 @@
3
3
  module Yerba
4
4
  module Formatting
5
5
  def self.quote(value, style)
6
- case style
7
- when :double
8
- escaped = value.to_s.gsub("\\", "\\\\").gsub('"', '\\"')
9
- "\"#{escaped}\""
10
- when :single
11
- escaped = value.to_s.gsub("'", "''")
12
- "'#{escaped}'"
13
- else
14
- value.to_s
15
- end
6
+ Yerba.quote_scalar(value.to_s, style)
16
7
  end
17
8
 
18
9
  def self.to_yaml_value(value)
@@ -55,9 +46,13 @@ module Yerba
55
46
  return "{}" if value.empty?
56
47
 
57
48
  value.map { |key, value|
58
- if value.is_a?(Hash) || value.is_a?(Array)
59
- inner = to_block_yaml_value(value, indent + 1)
60
- "#{prefix}#{key}:\n#{inner}"
49
+ case value
50
+ when Array, Hash
51
+ if value.empty?
52
+ "#{prefix}#{key}: #{to_yaml_value(value)}"
53
+ else
54
+ "#{prefix}#{key}:\n#{to_block_yaml_value(value, indent + 1)}"
55
+ end
61
56
  else
62
57
  "#{prefix}#{key}: #{to_scalar_value(value)}"
63
58
  end
@@ -72,6 +67,7 @@ module Yerba
72
67
  when true then "true"
73
68
  when false then "false"
74
69
  when nil then "null"
70
+ when String then Yerba.quote_scalar(value)
75
71
  else value.to_s
76
72
  end
77
73
  end
data/lib/yerba/map.rb CHANGED
@@ -32,11 +32,11 @@ module Yerba
32
32
  if connected?
33
33
  new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
34
34
  coerced = coerce_value(value, style: style)
35
- is_block_value = coerced.is_a?(String) && (coerced.include?("\n") || coerced.start_with?("- "))
35
+
36
+ is_collection = value.is_a?(Array) || value.is_a?(Hash)
37
+ is_block_value = is_collection && !coerced.start_with?("{", "[")
36
38
 
37
39
  if document.exists?(new_path) && is_block_value
38
- # Block-style collections can't replace a scalar via set().
39
- # Delete the key and re-insert at the same position.
40
40
  all_keys = keys
41
41
  key_index = all_keys.index(key.to_s)
42
42
  after_key = key_index&.positive? ? all_keys[key_index - 1] : nil
@@ -44,14 +44,14 @@ module Yerba
44
44
  document.delete(new_path)
45
45
 
46
46
  if after_key
47
- document.insert(new_path, coerced, after: after_key)
47
+ document.insert(new_path, coerced, after: after_key, plain: true)
48
48
  else
49
- document.insert(new_path, coerced)
49
+ document.insert(new_path, coerced, plain: true)
50
50
  end
51
51
  elsif document.exists?(new_path)
52
- document.set(new_path, coerced)
52
+ document.set(new_path, coerced, plain: is_collection)
53
53
  else
54
- document.insert(new_path, coerced)
54
+ document.insert(new_path, coerced, plain: is_collection)
55
55
  end
56
56
  else
57
57
  @data[key] = value
@@ -62,7 +62,10 @@ module Yerba
62
62
  if connected?
63
63
  new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
64
64
 
65
- document.insert(new_path, coerce_value(value, style: style), before: before, after: after)
65
+ coerced = coerce_value(value, style: style)
66
+ is_collection = value.is_a?(Array) || value.is_a?(Hash)
67
+
68
+ document.insert(new_path, coerced, before: before, after: after, plain: is_collection)
66
69
  else
67
70
  @data[key] = value
68
71
  end
@@ -60,10 +60,9 @@ module Yerba
60
60
  when Hash
61
61
  document.insert_object(@selector, item)
62
62
  when Scalar
63
- document.insert(@selector, item.to_yaml)
63
+ document.insert(@selector, item.to_yaml, plain: true)
64
64
  else
65
- formatted = format_for_insert(item.to_s)
66
- document.insert(@selector, formatted)
65
+ document.insert(@selector, item.to_s, style: detect_quote_style)
67
66
  end
68
67
  else
69
68
  @data << item
@@ -425,10 +424,6 @@ module Yerba
425
424
  current
426
425
  end
427
426
 
428
- def format_for_insert(value)
429
- Formatting.quote(value, detect_quote_style)
430
- end
431
-
432
427
  def detect_quote_style
433
428
  document.get_quote_style("#{@selector}[0]")
434
429
  end
data/lib/yerba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
- VERSION = "0.8.1"
4
+ VERSION = "0.9.1"
5
5
  end
data/rust/Cargo.lock CHANGED
@@ -1431,7 +1431,7 @@ dependencies = [
1431
1431
 
1432
1432
  [[package]]
1433
1433
  name = "yerba"
1434
- version = "0.8.1"
1434
+ version = "0.9.1"
1435
1435
  dependencies = [
1436
1436
  "anstyle-query",
1437
1437
  "cbindgen",
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yerba"
3
- version = "0.8.1"
3
+ version = "0.9.1"
4
4
  edition = "2021"
5
5
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
6
6
  description = "YAML Editing and Refactoring with Better Accuracy"