yerba 0.3.0 → 0.4.1
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 +4 -4
- data/README.md +93 -8
- data/ext/yerba/extconf.rb +22 -3
- data/ext/yerba/include/yerba.h +32 -9
- data/ext/yerba/yerba.c +133 -25
- data/lib/yerba/collection.rb +35 -0
- data/lib/yerba/document.rb +45 -3
- data/lib/yerba/query_result.rb +50 -0
- data/lib/yerba/sequence.rb +187 -1
- data/lib/yerba/version.rb +1 -1
- data/lib/yerba/yerbafile.rb +45 -0
- data/lib/yerba.rb +2 -0
- data/rust/Cargo.lock +1 -0
- data/rust/Cargo.toml +3 -2
- data/rust/cbindgen.toml +1 -0
- data/rust/rustfmt.toml +1 -1
- data/rust/src/commands/apply.rs +11 -2
- data/rust/src/commands/blank_lines.rs +1 -4
- data/rust/src/commands/check.rs +11 -2
- data/rust/src/commands/directives.rs +61 -0
- data/rust/src/commands/get.rs +5 -22
- data/rust/src/commands/mod.rs +16 -18
- data/rust/src/commands/quote_style.rs +12 -6
- data/rust/src/commands/selectors.rs +3 -19
- data/rust/src/commands/sort.rs +22 -157
- data/rust/src/didyoumean.rs +2 -4
- data/rust/src/document/condition.rs +139 -0
- data/rust/src/document/delete.rs +91 -0
- data/rust/src/document/get.rs +262 -0
- data/rust/src/document/insert.rs +384 -0
- data/rust/src/document/mod.rs +784 -0
- data/rust/src/document/set.rs +100 -0
- data/rust/src/document/sort.rs +639 -0
- data/rust/src/document/style.rs +473 -0
- data/rust/src/error.rs +24 -6
- data/rust/src/ffi.rs +272 -518
- data/rust/src/json.rs +1 -7
- data/rust/src/lib.rs +88 -2
- data/rust/src/main.rs +2 -0
- data/rust/src/quote_style.rs +83 -7
- data/rust/src/selector.rs +2 -7
- data/rust/src/syntax.rs +41 -21
- data/rust/src/yerbafile.rs +86 -19
- metadata +12 -2
- data/rust/Cargo.lock +0 -805
- data/rust/src/document.rs +0 -2304
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f0b8b264602f7f31483cff42e8c1c17266e6caa3f5fd8d2c737e6bec285b034
|
|
4
|
+
data.tar.gz: a0b706d50c311af27a7f0b58f64323dbb9dd38849ff31d372184b5ec4e417793
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a246cdd95c9d1b41b3e79c49501b61edef83e390d6915e43a9350d26b2012d98f319770e993f15e73e6903a7c611745f681220d9eb7569a0cb478442fd2941f4
|
|
7
|
+
data.tar.gz: 93aef7a57eae55c8b66f3271e54a7eb6021afa7cb54005ea74accab405ad584a96d998886c1eae4af7ee56442be99c0c75583715c811b5298f89f9102a83561f
|
data/README.md
CHANGED
|
@@ -47,13 +47,15 @@ Use `yerba` as a library in your Rust project:
|
|
|
47
47
|
|
|
48
48
|
```toml
|
|
49
49
|
[dependencies]
|
|
50
|
-
yerba = "0.
|
|
50
|
+
yerba = "0.4"
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
```rust
|
|
54
54
|
let mut document = yerba::parse_file("config.yml")?;
|
|
55
55
|
document.set("database.host", "0.0.0.0")?;
|
|
56
|
-
|
|
56
|
+
|
|
57
|
+
document.save()?; // saves to original path
|
|
58
|
+
document.save_to("output.yml")?; // saves to new path
|
|
57
59
|
```
|
|
58
60
|
|
|
59
61
|
### Ruby Gem
|
|
@@ -286,7 +288,7 @@ yerba sort-keys "data/**/videos.yml" "[]" "id,title,speakers"
|
|
|
286
288
|
|
|
287
289
|
### `quote-style`
|
|
288
290
|
|
|
289
|
-
Enforce a consistent quote style across keys and/or values
|
|
291
|
+
Enforce a consistent quote style across keys and/or values:
|
|
290
292
|
|
|
291
293
|
```bash
|
|
292
294
|
yerba quote-style config.yml --values double
|
|
@@ -301,6 +303,36 @@ yerba quote-style config.yml "[].speakers" --values plain
|
|
|
301
303
|
yerba quote-style "data/**/*.yml" --keys plain --values double
|
|
302
304
|
```
|
|
303
305
|
|
|
306
|
+
Use block scalar styles to enforce multiline formatting on specific fields:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
yerba quote-style videos.yml "[].description" --values literal
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Key styles** (`--keys`):
|
|
313
|
+
|
|
314
|
+
| Style | Symbol | Example |
|
|
315
|
+
|-------|--------|---------|
|
|
316
|
+
| `plain` | — | `host: value` |
|
|
317
|
+
| `single` | `'` | `'host': value` |
|
|
318
|
+
| `double` | `"` | `"host": value` |
|
|
319
|
+
|
|
320
|
+
**Value styles** (`--values`):
|
|
321
|
+
|
|
322
|
+
| Style | Symbol | Example | Behavior |
|
|
323
|
+
|-------|--------|---------|----------|
|
|
324
|
+
| `plain` | — | `host: localhost` | Unquoted |
|
|
325
|
+
| `single` | `'` | `host: 'localhost'` | Single-quoted |
|
|
326
|
+
| `double` | `"` | `host: "localhost"` | Double-quoted, supports `\n` escapes |
|
|
327
|
+
| `literal` | `\|-` | Preserves newlines | Strip trailing newline |
|
|
328
|
+
| `literal-clip` | `\|` | Preserves newlines | Keep one trailing newline |
|
|
329
|
+
| `literal-keep` | `\|+` | Preserves newlines | Keep all trailing newlines |
|
|
330
|
+
| `folded` | `>-` | Folds newlines to spaces | Strip trailing newline |
|
|
331
|
+
| `folded-clip` | `>` | Folds newlines to spaces | Keep one trailing newline |
|
|
332
|
+
| `folded-keep` | `>+` | Folds newlines to spaces | Keep all trailing newlines |
|
|
333
|
+
|
|
334
|
+
Block scalars are only converted when scoped to a specific selector. An unscoped `--values double` will not touch existing block scalars.
|
|
335
|
+
|
|
304
336
|
### `blank-lines`
|
|
305
337
|
|
|
306
338
|
Enforce a consistent number of blank lines between sequence entries:
|
|
@@ -311,6 +343,16 @@ yerba blank-lines videos.yml "[]" 1
|
|
|
311
343
|
yerba blank-lines config.yml "tags" 0
|
|
312
344
|
```
|
|
313
345
|
|
|
346
|
+
### `directives`
|
|
347
|
+
|
|
348
|
+
Add or remove the document start marker (`---`):
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
yerba directives config.yml --ensure
|
|
352
|
+
yerba directives config.yml --remove
|
|
353
|
+
yerba directives "data/**/*.yml" --ensure
|
|
354
|
+
```
|
|
355
|
+
|
|
314
356
|
### `selectors`
|
|
315
357
|
|
|
316
358
|
Show all valid selectors for a YAML file. Useful for discovering the structure of a file and knowing which selectors you can use with other commands:
|
|
@@ -371,7 +413,10 @@ Use `yerba init` to create one, then `yerba apply` to apply all rules, or `yerba
|
|
|
371
413
|
```bash
|
|
372
414
|
yerba init
|
|
373
415
|
yerba apply
|
|
416
|
+
yerba apply path/to/file.yml
|
|
417
|
+
|
|
374
418
|
yerba check
|
|
419
|
+
yerba check path/to/file.yml
|
|
375
420
|
```
|
|
376
421
|
|
|
377
422
|
Each rule specifies a file glob and a list of steps to run in order:
|
|
@@ -425,6 +470,7 @@ Available pipeline steps:
|
|
|
425
470
|
- `delete` Remove a key (supports conditions)
|
|
426
471
|
- `rename` Rename a key
|
|
427
472
|
- `remove` Remove an item from a sequence
|
|
473
|
+
- `directives` Add or remove the document start marker (`---`)
|
|
428
474
|
- `get` Read a value and store it as a variable for subsequent steps
|
|
429
475
|
|
|
430
476
|
This makes it easy to enforce project-wide YAML conventions in CI:
|
|
@@ -514,7 +560,44 @@ tags = document["tags"]
|
|
|
514
560
|
tags << "yaml"
|
|
515
561
|
tags << { name: "Rust", version: "1.80" }
|
|
516
562
|
tags.remove("obsolete")
|
|
517
|
-
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Sorting
|
|
566
|
+
|
|
567
|
+
Sort sequences in place. Works on both the document and sequence level:
|
|
568
|
+
|
|
569
|
+
```ruby
|
|
570
|
+
document.sort(by: :name)
|
|
571
|
+
document.sort(by: :name, order: :desc)
|
|
572
|
+
document.sort(by: :name, order: ["Charlie", "Bob", "Alice"])
|
|
573
|
+
document.sort("tags")
|
|
574
|
+
document.sort("tags", order: :desc)
|
|
575
|
+
document.sort("tags", order: ["rust", "ruby", "go"])
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
The `by:` option accepts symbols, strings, or dot-prefixed strings (`:name`, `"name"`, `".name"`).
|
|
579
|
+
|
|
580
|
+
### Querying
|
|
581
|
+
|
|
582
|
+
Find and filter items in sequences with `find_by`, `where`, and `pluck`:
|
|
583
|
+
|
|
584
|
+
```ruby
|
|
585
|
+
document.find_by(name: "Alice")
|
|
586
|
+
document.where(role: "admin")
|
|
587
|
+
document.pluck(:name)
|
|
588
|
+
|
|
589
|
+
document.find_by(speakers: { name: "Alice" })
|
|
590
|
+
document.where(tags: ["ruby"])
|
|
591
|
+
document.find_by("database.host": "localhost")
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
These methods work on `Document` (delegates to root), `Sequence`, and `Collection` (searches across files):
|
|
595
|
+
|
|
596
|
+
```ruby
|
|
597
|
+
collection = Yerba.files("data/**/*.yml")
|
|
598
|
+
collection.find_by(name: "Alice")
|
|
599
|
+
collection.where(kind: "talk")
|
|
600
|
+
collection.pluck(:name)
|
|
518
601
|
```
|
|
519
602
|
|
|
520
603
|
### Quote Style Control
|
|
@@ -523,7 +606,7 @@ Read and set the quote style on individual scalars:
|
|
|
523
606
|
|
|
524
607
|
```ruby
|
|
525
608
|
scalar = document["database.host"]
|
|
526
|
-
scalar.quote_style
|
|
609
|
+
scalar.quote_style # => :double
|
|
527
610
|
scalar.quote_style = :single
|
|
528
611
|
```
|
|
529
612
|
|
|
@@ -547,6 +630,10 @@ collection.each do |document|
|
|
|
547
630
|
puts document.get("[0].title")
|
|
548
631
|
end
|
|
549
632
|
|
|
633
|
+
collection.find_by(name: "Alice")
|
|
634
|
+
collection.where(kind: "talk")
|
|
635
|
+
collection.pluck(:name)
|
|
636
|
+
|
|
550
637
|
collection.apply! do |document|
|
|
551
638
|
document.set("status", "published")
|
|
552
639
|
end
|
|
@@ -572,10 +659,9 @@ After checking out the repo, run `bundle install` to install Ruby dependencies,
|
|
|
572
659
|
|
|
573
660
|
### Building from source
|
|
574
661
|
|
|
575
|
-
The Rust core is in the `rust/` directory:
|
|
662
|
+
The Rust core is in the `rust/` directory, with a workspace `Cargo.toml` at the root so all cargo commands work from the project root:
|
|
576
663
|
|
|
577
664
|
```bash
|
|
578
|
-
cd rust
|
|
579
665
|
cargo build
|
|
580
666
|
cargo test
|
|
581
667
|
```
|
|
@@ -591,7 +677,6 @@ cargo run -- get config.yml "database.host"
|
|
|
591
677
|
Or build a release binary:
|
|
592
678
|
|
|
593
679
|
```bash
|
|
594
|
-
cd rust
|
|
595
680
|
cargo build --release
|
|
596
681
|
./target/release/yerba --help
|
|
597
682
|
```
|
data/ext/yerba/extconf.rb
CHANGED
|
@@ -44,22 +44,41 @@ if cross_compiling && target_platform.nil?
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
workspace_target_dir = File.join(root_dir, "target")
|
|
48
|
+
crate_target_dir = File.join(rust_dir, "target")
|
|
49
|
+
|
|
47
50
|
if target_platform
|
|
48
51
|
puts "yerba: Cross-compiling Rust for target: #{target_platform}"
|
|
49
52
|
system("rustup target add #{target_platform}") || warn("yerba: Failed to add Rust target #{target_platform}")
|
|
50
53
|
|
|
51
54
|
cargo_args = "--release --target #{target_platform}"
|
|
52
|
-
lib_dir =
|
|
55
|
+
lib_dir = [workspace_target_dir, crate_target_dir]
|
|
56
|
+
.map { |dir| File.join(dir, target_platform, "release") }
|
|
57
|
+
.find { |dir| Dir.exist?(dir) } || File.join(crate_target_dir, target_platform, "release")
|
|
53
58
|
else
|
|
54
59
|
puts "yerba: Compiling Rust library for native platform..."
|
|
60
|
+
|
|
55
61
|
cargo_args = "--release"
|
|
56
|
-
|
|
62
|
+
|
|
63
|
+
lib_dir = [workspace_target_dir, crate_target_dir]
|
|
64
|
+
.map { |dir| File.join(dir, "release") }
|
|
65
|
+
.find { |dir| Dir.exist?(dir) } || File.join(crate_target_dir, "release")
|
|
57
66
|
end
|
|
58
67
|
|
|
59
|
-
unless system("cd #{
|
|
68
|
+
unless system("cd #{root_dir} && cargo build #{cargo_args}")
|
|
60
69
|
abort "ERROR: Failed to compile yerba from Rust source."
|
|
61
70
|
end
|
|
62
71
|
|
|
72
|
+
lib_dir = if target_platform
|
|
73
|
+
[workspace_target_dir, crate_target_dir]
|
|
74
|
+
.map { |dir| File.join(dir, target_platform, "release") }
|
|
75
|
+
.find { |dir| Dir.exist?(dir) } || lib_dir
|
|
76
|
+
else
|
|
77
|
+
[workspace_target_dir, crate_target_dir]
|
|
78
|
+
.map { |dir| File.join(dir, "release") }
|
|
79
|
+
.find { |dir| Dir.exist?(dir) } || lib_dir
|
|
80
|
+
end
|
|
81
|
+
|
|
63
82
|
if target_platform
|
|
64
83
|
platform_key = ENV.fetch("RCD_PLATFORM", "")
|
|
65
84
|
else
|
data/ext/yerba/include/yerba.h
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
#ifndef YERBA_H
|
|
8
8
|
#define YERBA_H
|
|
9
9
|
|
|
10
|
-
typedef enum
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
10
|
+
typedef enum NodeType {
|
|
11
|
+
NODE_TYPE_SCALAR = 0,
|
|
12
|
+
NODE_TYPE_MAP = 1,
|
|
13
|
+
NODE_TYPE_SEQUENCE = 2,
|
|
14
|
+
NODE_TYPE_NOT_FOUND = 3,
|
|
15
|
+
} NodeType;
|
|
16
16
|
|
|
17
17
|
typedef enum YerbaValueType {
|
|
18
18
|
YERBA_VALUE_TYPE_NULL = 0,
|
|
@@ -50,7 +50,7 @@ typedef struct YerbaLocation {
|
|
|
50
50
|
|
|
51
51
|
typedef struct YerbaGetResult {
|
|
52
52
|
bool is_list;
|
|
53
|
-
enum
|
|
53
|
+
enum NodeType node_type;
|
|
54
54
|
struct YerbaTypedValue single;
|
|
55
55
|
struct YerbaTypedList list;
|
|
56
56
|
struct YerbaLocation location;
|
|
@@ -82,11 +82,11 @@ char *yerba_document_get_value(const struct Document *document, const char *path
|
|
|
82
82
|
*/
|
|
83
83
|
char *yerba_document_get_values(const struct Document *document, const char *path);
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
char *yerba_document_get_quote_style(const struct Document *document, const char *path);
|
|
86
86
|
|
|
87
87
|
struct YerbaResult yerba_document_set_quote_style(struct Document *document,
|
|
88
88
|
const char *path,
|
|
89
|
-
|
|
89
|
+
const char *style);
|
|
90
90
|
|
|
91
91
|
bool yerba_document_evaluate_condition(const struct Document *document,
|
|
92
92
|
const char *parent_path,
|
|
@@ -121,6 +121,10 @@ struct YerbaResult yerba_document_insert_object(struct Document *document,
|
|
|
121
121
|
|
|
122
122
|
struct YerbaResult yerba_document_delete(struct Document *document, const char *path);
|
|
123
123
|
|
|
124
|
+
struct YerbaResult yerba_document_insert_objects(struct Document *document,
|
|
125
|
+
const char *path,
|
|
126
|
+
const char *json);
|
|
127
|
+
|
|
124
128
|
struct YerbaResult yerba_document_remove(struct Document *document,
|
|
125
129
|
const char *path,
|
|
126
130
|
const char *value);
|
|
@@ -129,6 +133,11 @@ struct YerbaResult yerba_document_remove_at(struct Document *document,
|
|
|
129
133
|
const char *path,
|
|
130
134
|
uintptr_t index);
|
|
131
135
|
|
|
136
|
+
struct YerbaResult yerba_document_move_item(struct Document *document,
|
|
137
|
+
const char *path,
|
|
138
|
+
uintptr_t from,
|
|
139
|
+
uintptr_t to);
|
|
140
|
+
|
|
132
141
|
struct YerbaResult yerba_document_rename(struct Document *document,
|
|
133
142
|
const char *source,
|
|
134
143
|
const char *dest);
|
|
@@ -138,6 +147,11 @@ struct YerbaResult yerba_document_sort(struct Document *document,
|
|
|
138
147
|
const char *by,
|
|
139
148
|
bool case_sensitive);
|
|
140
149
|
|
|
150
|
+
struct YerbaResult yerba_document_reorder(struct Document *document,
|
|
151
|
+
const char *path,
|
|
152
|
+
const char *by,
|
|
153
|
+
const char *order_csv);
|
|
154
|
+
|
|
141
155
|
struct YerbaResult yerba_document_sort_keys(struct Document *document,
|
|
142
156
|
const char *path,
|
|
143
157
|
const char *order);
|
|
@@ -151,6 +165,15 @@ struct YerbaResult yerba_document_blank_lines(struct Document *document,
|
|
|
151
165
|
const char *path,
|
|
152
166
|
uintptr_t count);
|
|
153
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Caller must free with yerba_string_free.
|
|
170
|
+
*/
|
|
171
|
+
char *yerba_yerbafile_find(const char *directory);
|
|
172
|
+
|
|
173
|
+
struct YerbaResult yerba_document_apply_yerbafile(struct Document *document,
|
|
174
|
+
const char *file_path,
|
|
175
|
+
const char *yerbafile_path);
|
|
176
|
+
|
|
154
177
|
char *yerba_document_to_string(const struct Document *document);
|
|
155
178
|
|
|
156
179
|
void yerba_string_free(char *s);
|
data/ext/yerba/yerba.c
CHANGED
|
@@ -230,7 +230,7 @@ static VALUE document_bracket(VALUE self, VALUE path) {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
switch (result.node_type) {
|
|
233
|
-
case
|
|
233
|
+
case NODE_TYPE_SCALAR: {
|
|
234
234
|
VALUE klass = rb_path2class("Yerba::Scalar");
|
|
235
235
|
VALUE value = typed_value_to_ruby(result.single);
|
|
236
236
|
yerba_get_result_free(result);
|
|
@@ -240,7 +240,7 @@ static VALUE document_bracket(VALUE self, VALUE path) {
|
|
|
240
240
|
return instance;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
case
|
|
243
|
+
case NODE_TYPE_MAP: {
|
|
244
244
|
yerba_get_result_free(result);
|
|
245
245
|
VALUE klass = rb_path2class("Yerba::Map");
|
|
246
246
|
|
|
@@ -249,7 +249,7 @@ static VALUE document_bracket(VALUE self, VALUE path) {
|
|
|
249
249
|
return instance;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
case
|
|
252
|
+
case NODE_TYPE_SEQUENCE: {
|
|
253
253
|
yerba_get_result_free(result);
|
|
254
254
|
VALUE klass = rb_path2class("Yerba::Sequence");
|
|
255
255
|
|
|
@@ -298,35 +298,35 @@ static VALUE document_get_values(VALUE self, VALUE path) {
|
|
|
298
298
|
return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
/* document.get_quote_style(path) → :plain, :single, :double, or nil */
|
|
301
|
+
/* document.get_quote_style(path) → :plain, :single, :double, :literal, etc. or nil */
|
|
302
302
|
static VALUE document_get_quote_style(VALUE self, VALUE path) {
|
|
303
303
|
struct Document *document = get_document(self);
|
|
304
|
-
|
|
304
|
+
char *style = yerba_document_get_quote_style(document, StringValueCStr(path));
|
|
305
305
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
306
|
+
if (style == NULL) return Qnil;
|
|
307
|
+
|
|
308
|
+
VALUE symbol = ID2SYM(rb_intern(style));
|
|
309
|
+
|
|
310
|
+
yerba_string_free(style);
|
|
311
|
+
|
|
312
|
+
return symbol;
|
|
312
313
|
}
|
|
313
314
|
|
|
314
315
|
/* document.set_quote_style(path, style) */
|
|
315
316
|
static VALUE document_set_quote_style(VALUE self, VALUE path, VALUE style) {
|
|
316
317
|
struct Document *document = get_document(self);
|
|
317
318
|
|
|
318
|
-
|
|
319
|
+
const char *style_string;
|
|
320
|
+
|
|
319
321
|
if (RB_TYPE_P(style, T_SYMBOL)) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
else if (style_id == rb_intern("double")) style_int = 2;
|
|
324
|
-
else rb_raise(rb_eError, "Invalid quote style (use :plain, :single, or :double)");
|
|
322
|
+
style_string = rb_id2name(SYM2ID(style));
|
|
323
|
+
} else if (RB_TYPE_P(style, T_STRING)) {
|
|
324
|
+
style_string = StringValueCStr(style);
|
|
325
325
|
} else {
|
|
326
|
-
|
|
326
|
+
rb_raise(rb_eError, "Invalid quote style (expected Symbol or String)");
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
YerbaResult result = yerba_document_set_quote_style(document, StringValueCStr(path),
|
|
329
|
+
YerbaResult result = yerba_document_set_quote_style(document, StringValueCStr(path), style_string);
|
|
330
330
|
|
|
331
331
|
check_result(result);
|
|
332
332
|
|
|
@@ -488,6 +488,17 @@ static VALUE document_insert_object(int argc, VALUE *argv, VALUE self) {
|
|
|
488
488
|
return self;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
/* document.insert_objects(path, array) */
|
|
492
|
+
static VALUE document_insert_objects(VALUE self, VALUE path, VALUE array) {
|
|
493
|
+
struct Document *document = get_document(self);
|
|
494
|
+
VALUE json_string = rb_funcall(rb_path2class("JSON"), rb_intern("generate"), 1, array);
|
|
495
|
+
|
|
496
|
+
YerbaResult result = yerba_document_insert_objects(document, StringValueCStr(path), StringValueCStr(json_string));
|
|
497
|
+
check_result(result);
|
|
498
|
+
|
|
499
|
+
return self;
|
|
500
|
+
}
|
|
501
|
+
|
|
491
502
|
/* document.delete(path, condition: nil) */
|
|
492
503
|
static VALUE document_delete(int argc, VALUE *argv, VALUE self) {
|
|
493
504
|
VALUE path, opts;
|
|
@@ -533,24 +544,83 @@ static VALUE document_rename(VALUE self, VALUE source, VALUE destination) {
|
|
|
533
544
|
return self;
|
|
534
545
|
}
|
|
535
546
|
|
|
536
|
-
/* document.sort(path, by: nil, case_sensitive: false) */
|
|
547
|
+
/* document.sort(path = "", by: nil, order: nil, case_sensitive: false) */
|
|
537
548
|
static VALUE document_sort(int argc, VALUE *argv, VALUE self) {
|
|
538
549
|
VALUE path, opts;
|
|
539
|
-
rb_scan_args(argc, argv, "
|
|
550
|
+
rb_scan_args(argc, argv, "01:", &path, &opts);
|
|
551
|
+
|
|
552
|
+
if (NIL_P(path)) path = rb_str_new_cstr("");
|
|
540
553
|
|
|
541
554
|
const char *by = NULL;
|
|
542
555
|
bool case_sensitive = false;
|
|
556
|
+
VALUE v_order = Qnil;
|
|
543
557
|
|
|
544
558
|
if (!NIL_P(opts)) {
|
|
545
559
|
VALUE v_by = rb_hash_aref(opts, ID2SYM(rb_intern("by")));
|
|
560
|
+
v_order = rb_hash_aref(opts, ID2SYM(rb_intern("order")));
|
|
546
561
|
VALUE v_case_sensitive = rb_hash_aref(opts, ID2SYM(rb_intern("case_sensitive")));
|
|
547
562
|
|
|
548
|
-
if (
|
|
549
|
-
|
|
563
|
+
if (SYMBOL_P(v_by)) {
|
|
564
|
+
VALUE by_string = rb_sym2str(v_by);
|
|
565
|
+
by = StringValueCStr(by_string);
|
|
566
|
+
} else if (!NIL_P(v_by)) {
|
|
567
|
+
by = StringValueCStr(v_by);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (RTEST(v_case_sensitive)) {
|
|
571
|
+
case_sensitive = true;
|
|
572
|
+
}
|
|
550
573
|
}
|
|
551
574
|
|
|
552
575
|
struct Document *document = get_document(self);
|
|
553
|
-
|
|
576
|
+
const char *path_string = StringValueCStr(path);
|
|
577
|
+
|
|
578
|
+
if (RB_TYPE_P(v_order, T_ARRAY)) {
|
|
579
|
+
VALUE order_csv = rb_ary_join(v_order, rb_str_new_cstr(","));
|
|
580
|
+
const char *order_string = StringValueCStr(order_csv);
|
|
581
|
+
const char *reorder_path = StringValueCStr(path);
|
|
582
|
+
const char *reorder_by;
|
|
583
|
+
|
|
584
|
+
if (by) {
|
|
585
|
+
VALUE reorder_by_value = rb_hash_aref(opts, ID2SYM(rb_intern("by")));
|
|
586
|
+
|
|
587
|
+
if (SYMBOL_P(reorder_by_value)) {
|
|
588
|
+
reorder_by_value = rb_sym2str(reorder_by_value);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
reorder_by = StringValueCStr(reorder_by_value);
|
|
592
|
+
} else {
|
|
593
|
+
reorder_by = ".";
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
YerbaResult result = yerba_document_reorder(document, reorder_path, reorder_by, order_string);
|
|
597
|
+
check_result(result);
|
|
598
|
+
|
|
599
|
+
return self;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const char *order = NULL;
|
|
603
|
+
|
|
604
|
+
if (SYMBOL_P(v_order)) {
|
|
605
|
+
VALUE order_string = rb_sym2str(v_order);
|
|
606
|
+
order = StringValueCStr(order_string);
|
|
607
|
+
} else if (!NIL_P(v_order)) {
|
|
608
|
+
order = StringValueCStr(v_order);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
VALUE by_with_order = Qnil;
|
|
612
|
+
|
|
613
|
+
if (order && strcmp(order, "desc") == 0) {
|
|
614
|
+
if (by) {
|
|
615
|
+
by_with_order = rb_sprintf("%s:desc", by);
|
|
616
|
+
} else {
|
|
617
|
+
by_with_order = rb_str_new_cstr(":desc");
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
by = StringValueCStr(by_with_order);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
YerbaResult result = yerba_document_sort(document, path_string, by, case_sensitive);
|
|
554
624
|
|
|
555
625
|
check_result(result);
|
|
556
626
|
|
|
@@ -612,6 +682,23 @@ static VALUE document_blank_lines(VALUE self, VALUE path, VALUE count) {
|
|
|
612
682
|
return self;
|
|
613
683
|
}
|
|
614
684
|
|
|
685
|
+
/* document.apply_yerbafile(yerbafile_path = nil) */
|
|
686
|
+
static VALUE document_apply_yerbafile(int argc, VALUE *argv, VALUE self) {
|
|
687
|
+
VALUE yerbafile_path;
|
|
688
|
+
rb_scan_args(argc, argv, "01", &yerbafile_path);
|
|
689
|
+
|
|
690
|
+
struct Document *document = get_document(self);
|
|
691
|
+
VALUE file_path = rb_iv_get(self, "@path");
|
|
692
|
+
|
|
693
|
+
const char *file_path_str = NIL_P(file_path) ? "" : StringValueCStr(file_path);
|
|
694
|
+
const char *yerbafile_path_str = NIL_P(yerbafile_path) ? NULL : StringValueCStr(yerbafile_path);
|
|
695
|
+
|
|
696
|
+
YerbaResult result = yerba_document_apply_yerbafile(document, file_path_str, yerbafile_path_str);
|
|
697
|
+
check_result(result);
|
|
698
|
+
|
|
699
|
+
return self;
|
|
700
|
+
}
|
|
701
|
+
|
|
615
702
|
/* document.to_s */
|
|
616
703
|
static VALUE document_to_s(VALUE self) {
|
|
617
704
|
struct Document *document = get_document(self);
|
|
@@ -714,6 +801,22 @@ static VALUE collection_s_find(int argc, VALUE *argv, VALUE self) {
|
|
|
714
801
|
return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
715
802
|
}
|
|
716
803
|
|
|
804
|
+
/* Yerbafile.locate(directory = nil) → path string or nil */
|
|
805
|
+
static VALUE yerbafile_s_locate(int argc, VALUE *argv, VALUE klass) {
|
|
806
|
+
VALUE directory;
|
|
807
|
+
rb_scan_args(argc, argv, "01", &directory);
|
|
808
|
+
|
|
809
|
+
const char *dir = NIL_P(directory) ? NULL : StringValueCStr(directory);
|
|
810
|
+
char *result = yerba_yerbafile_find(dir);
|
|
811
|
+
|
|
812
|
+
if (!result) return Qnil;
|
|
813
|
+
|
|
814
|
+
VALUE path = make_utf8_string(result);
|
|
815
|
+
yerba_string_free(result);
|
|
816
|
+
|
|
817
|
+
return path;
|
|
818
|
+
}
|
|
819
|
+
|
|
717
820
|
void Init_yerba(void) {
|
|
718
821
|
rb_require("json");
|
|
719
822
|
|
|
@@ -744,6 +847,7 @@ void Init_yerba(void) {
|
|
|
744
847
|
rb_define_method(rb_cDocument, "set", document_set, -1);
|
|
745
848
|
rb_define_method(rb_cDocument, "insert", document_insert, -1);
|
|
746
849
|
rb_define_method(rb_cDocument, "insert_object", document_insert_object, -1);
|
|
850
|
+
rb_define_method(rb_cDocument, "insert_objects", document_insert_objects, 2);
|
|
747
851
|
rb_define_method(rb_cDocument, "delete", document_delete, -1);
|
|
748
852
|
rb_define_method(rb_cDocument, "remove", document_remove, 2);
|
|
749
853
|
rb_define_method(rb_cDocument, "remove_at", document_remove_at, 2);
|
|
@@ -752,8 +856,12 @@ void Init_yerba(void) {
|
|
|
752
856
|
rb_define_method(rb_cDocument, "sort_keys", document_sort_keys, 2);
|
|
753
857
|
rb_define_method(rb_cDocument, "quote_style", document_quote_style, -1);
|
|
754
858
|
rb_define_method(rb_cDocument, "blank_lines", document_blank_lines, 2);
|
|
859
|
+
rb_define_method(rb_cDocument, "apply_yerbafile", document_apply_yerbafile, -1);
|
|
755
860
|
rb_define_method(rb_cDocument, "to_s", document_to_s, 0);
|
|
756
|
-
rb_define_method(rb_cDocument, "
|
|
861
|
+
rb_define_method(rb_cDocument, "write!", document_save, 0);
|
|
757
862
|
rb_define_method(rb_cDocument, "changed?", document_changed_p, 0);
|
|
758
863
|
rb_define_method(rb_cDocument, "path", document_path, 0);
|
|
864
|
+
|
|
865
|
+
VALUE rb_cYerbafile = rb_define_class_under(rb_mYerba, "Yerbafile", rb_cObject);
|
|
866
|
+
rb_define_singleton_method(rb_cYerbafile, "locate", yerbafile_s_locate, -1);
|
|
759
867
|
}
|
data/lib/yerba/collection.rb
CHANGED
|
@@ -20,6 +20,41 @@ module Yerba
|
|
|
20
20
|
self.class.find(@glob, path, condition: condition, select: select)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
def find_by(...)
|
|
24
|
+
each do |document|
|
|
25
|
+
next unless document.sequence?
|
|
26
|
+
|
|
27
|
+
result = document.root.find_by(...)
|
|
28
|
+
return result if result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def where(...)
|
|
35
|
+
results = []
|
|
36
|
+
|
|
37
|
+
each do |document|
|
|
38
|
+
next unless document.sequence?
|
|
39
|
+
|
|
40
|
+
results.concat(document.root.where(...).to_a)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
results
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def pluck(...)
|
|
47
|
+
results = []
|
|
48
|
+
|
|
49
|
+
each do |document|
|
|
50
|
+
next unless document.sequence?
|
|
51
|
+
|
|
52
|
+
results.concat(document.root.pluck(...))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
results
|
|
56
|
+
end
|
|
57
|
+
|
|
23
58
|
def apply!
|
|
24
59
|
each do |document|
|
|
25
60
|
yield document
|