yaml_smith 0.2.0 → 0.3.0

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: 03e70b6c6bd6522e6a35265fc2906e5fdc05e1e5a3bb497024e7805eb89cdc2e
4
- data.tar.gz: a3b8572f3a9ead73fc96d1eb4795a995ac17a2ade613dc54334ee8c7b2e59843
3
+ metadata.gz: 1ae84128e5a9fd8e27052c2ae46fd0c824360a603a8e83bc939758c084ee9ca1
4
+ data.tar.gz: 1790ba5fa65a520ac3a55aa0ca0c718d65794c4abb007343b7e3a46cf5d740b2
5
5
  SHA512:
6
- metadata.gz: 49168721a967966498b840fc55af20beaad3be787c756cfb28aa4d08c55011f1d8cfbef4f0af5216278aceee1efce14c2f89b9cb39ff24d6bc0379e57afe09cb
7
- data.tar.gz: b7b6467e1f366f016ca038e5a7ecd2e54b55f72b9e6b071bd53ffaf2edf0726f46faa3b635d9dbb5e55d13b6e4e93ee943f52fec3bc95493c2404330b0bec03c
6
+ metadata.gz: 73c9db95bdbcbd3c7396e65387ef77d20dc64bef997db73700df90590b852db1f6c9461d1ef18f60a0c8e6d7c7d81eeeac6c0f88539556b39faccd946a62a395
7
+ data.tar.gz: 9746fcbffa82ccf28b8f8b1e21bba0286f7b5f3c5f157e46cf036368fa98b5f2de5d27098ad5064e26180de5c0fe2ff7b9881f967ee3e0ec2b16675528159212
data/AGENTS.md CHANGED
@@ -12,7 +12,12 @@
12
12
  - `exe/yaml-smith` is only the executable wrapper; CLI dispatch and error handling belong in `lib/yaml_smith/cli.rb`.
13
13
  - Each command has its own class and file: `AddKey`, `SetKey`, and `DeleteKey`.
14
14
  - Shared file loading, validation, and atomic writing live in `YamlSmith::FileCommand`.
15
- - Commands currently operate on top-level mapping keys only; do not add nested-path behavior implicitly.
15
+ - `AddKey` and `SetKey` accept a nested path in their `key` argument using dot
16
+ notation (e.g. `services.0.name`) or bracket notation (e.g. `services[0].name`).
17
+ Numeric segments index into arrays. `FileCommand#resolve_container` navigates
18
+ intermediate segments of the path, which must already exist; the final key may
19
+ be missing (for `set`, it is then added). `DeleteKey` searches matching keys
20
+ recursively and does not take a path.
16
21
 
17
22
  ## YAML Behavior
18
23
 
data/README.md CHANGED
@@ -28,12 +28,24 @@ yaml-smith add config.yml timeout 30
28
28
  The value is parsed as YAML, so values such as `true`, `null`, arrays, and
29
29
  objects retain their YAML types. Existing keys are not overwritten.
30
30
 
31
- Set or replace a top-level key:
31
+ Set or replace a key:
32
32
 
33
33
  ```bash
34
34
  yaml-smith set config.yml timeout 60
35
35
  ```
36
36
 
37
+ Add and set also accept a nested path using dot notation, with numeric
38
+ segments indexing into arrays. For example, this renames the web service in a
39
+ Render configuration:
40
+
41
+ ```bash
42
+ yaml-smith set render.yaml services.0.name rails_foo
43
+ ```
44
+
45
+ Bracket notation is accepted as well: `services[0].name`. Intermediate
46
+ segments of the path must already exist; the final key may be missing (for
47
+ `set`, it is added).
48
+
37
49
  Delete matching keys recursively:
38
50
 
39
51
  ```bash
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YamlSmith
4
- # Adds a top-level key to a YAML file.
4
+ # Adds a key to a YAML file. The key may be a top-level key or a nested
5
+ # path such as `services.0.name`.
5
6
  class AddKey < FileCommand
6
7
  def call
7
8
  document = load_file(@path)
8
9
  value = load(@value)
9
10
 
10
11
  ensure_mapping(document)
11
- raise Error, "key already exists: #{@key}" if document.key?(@key)
12
+ container, final_key = resolve_container(document)
13
+ raise Error, "key already exists: #{@key}" if container.key?(final_key)
12
14
 
13
- document[@key] = value
15
+ container[final_key] = value
14
16
  write(Psych::Pure.dump(document))
15
17
  end
16
18
  end
@@ -33,6 +33,49 @@ module YamlSmith
33
33
  raise Error, 'YAML document must contain a top-level mapping'
34
34
  end
35
35
 
36
+ def resolve_container(document)
37
+ container = document
38
+ segments[0..-2].each { |segment| container = navigate(container, segment) }
39
+ [container, segments.last]
40
+ end
41
+
42
+ def navigate(container, segment)
43
+ if integer?(segment)
44
+ raise Error, "expected an array at '#{segment}'" unless container.instance_of?(Psych::Pure::LoadedArray)
45
+
46
+ index = Integer(segment)
47
+ raise Error, "segment does not exist: #{segment}" if index >= container.length
48
+
49
+ container[index]
50
+ else
51
+ raise Error, "expected a mapping at '#{segment}'" unless container.instance_of?(Psych::Pure::LoadedHash)
52
+ raise Error, "segment does not exist: #{segment}" unless container.key?(segment)
53
+
54
+ container[segment]
55
+ end
56
+ end
57
+
58
+ def integer?(segment)
59
+ segment.match?(/\A\d+\z/)
60
+ end
61
+
62
+ def segments
63
+ @segments ||= parse_segments(@key)
64
+ end
65
+
66
+ def parse_segments(key)
67
+ return [key] unless key.include?('.') || key.include?('[')
68
+
69
+ key.scan(/[^.\s]+/).each_with_object([]) do |token, result|
70
+ segments = token.split('[')
71
+ result << segments[0] unless segments[0].empty?
72
+
73
+ segments[1..].each do |part|
74
+ result << part.delete_suffix(']')
75
+ end
76
+ end
77
+ end
78
+
36
79
  def write(contents)
37
80
  mode = File.stat(@path).mode & 0o7777
38
81
 
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YamlSmith
4
- # Sets a top-level key in a YAML file, adding or replacing it.
4
+ # Sets a key in a YAML file, adding or replacing it. The key may be a
5
+ # top-level key or a nested path such as `services.0.name`.
5
6
  class SetKey < FileCommand
6
7
  def call
7
8
  document = load_file(@path)
8
9
  value = load(@value)
9
10
 
10
11
  ensure_mapping(document)
11
- document[@key] = value
12
+ container, final_key = resolve_container(document)
13
+ container[final_key] = value
12
14
  write(Psych::Pure.dump(document))
13
15
  end
14
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YamlSmith
4
- VERSION = "0.2.0"
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yaml_smith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Waite