philiprehberger-dot_access 0.7.0 → 0.8.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +28 -0
- data/lib/philiprehberger/dot_access/version.rb +1 -1
- data/lib/philiprehberger/dot_access.rb +94 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c067bfe9e48161257e318e8504f10a2604d9899e4340dadbfc42a76f8df23466
|
|
4
|
+
data.tar.gz: ccf282fefd6897e5ae90bf664fffb8efda891d67a357bf226a2fdd1a3af79cce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0f6ce17f68d1685a8742327c215a5af5a14879cf4a9560eccd20bf127d06a04fd99a81acfc0e15ae895836c3a87c8c5034e615336f255a8313ee09977deff4d
|
|
7
|
+
data.tar.gz: 31f94f7fa8ee543f7ee9304bd3814ef24cc30b7199108821ec61c4da4b6e54c9983c7d8fb0024dae94aace73c06017275019e9febcf7e88cdd7c2c5f77bfbb52
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-05-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- JSON Pointer (RFC 6901) support: `Wrapper#get_pointer`, `#set_pointer`, `#delete_pointer`, and `#has_pointer?` plus the helper `Wrapper.parse_pointer`, for interop with JSON Schema and JSON Patch tooling
|
|
14
|
+
- Card image reference in the README for registry-side rendering
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Raised the `Metrics/ClassLength` RuboCop ceiling on `Wrapper` to fit the new pointer methods
|
|
18
|
+
|
|
10
19
|
## [0.7.0] - 2026-04-30
|
|
11
20
|
|
|
12
21
|
### Added
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
[](https://rubygems.org/gems/philiprehberger-dot_access)
|
|
5
5
|
[](https://github.com/philiprehberger/rb-dot-access/commits/main)
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
Dot-notation accessor for nested hashes with nil-safe traversal
|
|
8
10
|
|
|
9
11
|
## Requirements
|
|
@@ -230,6 +232,28 @@ config = Philiprehberger::DotAccess.wrap({ a: { b: 1 } })
|
|
|
230
232
|
config.to_h # => { a: { b: 1 } }
|
|
231
233
|
```
|
|
232
234
|
|
|
235
|
+
### JSON Pointer (RFC 6901) Access
|
|
236
|
+
|
|
237
|
+
Useful when interoperating with JSON Schema, JSON Patch, or any tooling that already speaks JSON Pointer paths.
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
data = Philiprehberger::DotAccess.wrap(
|
|
241
|
+
users: [{ name: "alice" }, { name: "bob" }],
|
|
242
|
+
"a/b": { "c~d": 1 }
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
data.get_pointer("/users/0/name") # => "alice"
|
|
246
|
+
data.get_pointer("") # => the wrapper itself (root)
|
|
247
|
+
data.get_pointer("/missing", default: :nf) # => :nf
|
|
248
|
+
|
|
249
|
+
# ~1 escapes /, ~0 escapes ~ — so the key "a/b" → "a~1b"
|
|
250
|
+
data.get_pointer("/a~1b/c~0d") # => 1
|
|
251
|
+
|
|
252
|
+
data.has_pointer?("/users/1/name") # => true
|
|
253
|
+
updated = data.set_pointer("/users/0/name", "alex")
|
|
254
|
+
trimmed = data.delete_pointer("/users/0/name")
|
|
255
|
+
```
|
|
256
|
+
|
|
233
257
|
## API
|
|
234
258
|
|
|
235
259
|
### `Philiprehberger::DotAccess`
|
|
@@ -263,6 +287,10 @@ config.to_h # => { a: { b: 1 } }
|
|
|
263
287
|
| `#to_json` | Serialize back to a JSON string |
|
|
264
288
|
| `#to_yaml` | Serialize back to a YAML string |
|
|
265
289
|
| `#to_h` | Convert back to a plain hash |
|
|
290
|
+
| `#get_pointer(pointer, default: nil)` | Retrieve a value by a JSON Pointer (RFC 6901) path |
|
|
291
|
+
| `#has_pointer?(pointer)` | Check if a JSON Pointer path exists |
|
|
292
|
+
| `#set_pointer(pointer, value)` | Return a new wrapper with the value set at a JSON Pointer path |
|
|
293
|
+
| `#delete_pointer(pointer)` | Return a new wrapper without the value at a JSON Pointer path |
|
|
266
294
|
|
|
267
295
|
### `Philiprehberger::DotAccess::NullAccess`
|
|
268
296
|
|
|
@@ -257,6 +257,100 @@ module Philiprehberger
|
|
|
257
257
|
Wrapper.new(new_data)
|
|
258
258
|
end
|
|
259
259
|
|
|
260
|
+
# Access a value by a JSON Pointer (RFC 6901) path.
|
|
261
|
+
#
|
|
262
|
+
# JSON Pointer paths start with `/`, separate segments with `/`, and escape
|
|
263
|
+
# `~` as `~0` and `/` as `~1`. An empty pointer (`""`) refers to the whole
|
|
264
|
+
# wrapped document.
|
|
265
|
+
#
|
|
266
|
+
# @param pointer [String] the JSON Pointer (e.g. `"/users/0/name"`)
|
|
267
|
+
# @param default [Object] value returned if the pointer is not found
|
|
268
|
+
# @return [Object] the value at the pointer, or ``default``
|
|
269
|
+
def get_pointer(pointer, default: nil)
|
|
270
|
+
segments = Wrapper.parse_pointer(pointer)
|
|
271
|
+
return self if segments.empty?
|
|
272
|
+
|
|
273
|
+
result = segments.reduce(@data) do |current, seg|
|
|
274
|
+
case current
|
|
275
|
+
when Hash then current[seg.to_sym]
|
|
276
|
+
when Array
|
|
277
|
+
index = array_index(seg, current)
|
|
278
|
+
return default if index.nil?
|
|
279
|
+
|
|
280
|
+
current[index]
|
|
281
|
+
else return default
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
result.nil? ? default : result
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Whether a JSON Pointer (RFC 6901) path exists in the wrapped structure.
|
|
289
|
+
#
|
|
290
|
+
# @param pointer [String] the JSON Pointer
|
|
291
|
+
# @return [Boolean]
|
|
292
|
+
def has_pointer?(pointer)
|
|
293
|
+
segments = Wrapper.parse_pointer(pointer)
|
|
294
|
+
return true if segments.empty?
|
|
295
|
+
|
|
296
|
+
current = @data
|
|
297
|
+
segments.each do |seg|
|
|
298
|
+
case current
|
|
299
|
+
when Hash
|
|
300
|
+
return false unless current.key?(seg.to_sym)
|
|
301
|
+
|
|
302
|
+
current = current[seg.to_sym]
|
|
303
|
+
when Array
|
|
304
|
+
index = array_index(seg, current)
|
|
305
|
+
return false if index.nil?
|
|
306
|
+
|
|
307
|
+
current = current[index]
|
|
308
|
+
else
|
|
309
|
+
return false
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
true
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Set a value at a JSON Pointer path, returning a new Wrapper.
|
|
316
|
+
#
|
|
317
|
+
# @param pointer [String] the JSON Pointer
|
|
318
|
+
# @param value [Object] the value to set
|
|
319
|
+
# @return [Wrapper] a new wrapper with the updated value
|
|
320
|
+
# @raise [Error] if the pointer is empty (would replace the entire document)
|
|
321
|
+
def set_pointer(pointer, value)
|
|
322
|
+
segments = Wrapper.parse_pointer(pointer)
|
|
323
|
+
raise Error, 'cannot set the root pointer' if segments.empty?
|
|
324
|
+
|
|
325
|
+
Wrapper.new(deep_set(to_h, segments, value))
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Delete the value at a JSON Pointer path, returning a new Wrapper.
|
|
329
|
+
#
|
|
330
|
+
# @param pointer [String] the JSON Pointer
|
|
331
|
+
# @return [Wrapper] a new wrapper without the specified pointer
|
|
332
|
+
# @raise [Error] if the pointer is empty
|
|
333
|
+
def delete_pointer(pointer)
|
|
334
|
+
segments = Wrapper.parse_pointer(pointer)
|
|
335
|
+
raise Error, 'cannot delete the root pointer' if segments.empty?
|
|
336
|
+
|
|
337
|
+
Wrapper.new(deep_delete(to_h, segments))
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Parse a JSON Pointer string into an array of unescaped segments.
|
|
341
|
+
# Public so callers and {#get_pointer}/{#set_pointer}/etc. share one parser.
|
|
342
|
+
#
|
|
343
|
+
# @param pointer [String]
|
|
344
|
+
# @return [Array<String>] the unescaped segments
|
|
345
|
+
# @raise [Error] if the pointer is not a String or does not start with `/`
|
|
346
|
+
def self.parse_pointer(pointer)
|
|
347
|
+
raise Error, 'pointer must be a String' unless pointer.is_a?(String)
|
|
348
|
+
return [] if pointer.empty?
|
|
349
|
+
raise Error, "JSON Pointer must start with '/' (got #{pointer.inspect})" unless pointer.start_with?('/')
|
|
350
|
+
|
|
351
|
+
pointer[1..].split('/', -1).map { |seg| seg.gsub('~1', '/').gsub('~0', '~') }
|
|
352
|
+
end
|
|
353
|
+
|
|
260
354
|
# Flatten the nested structure into a hash whose keys are dot-paths.
|
|
261
355
|
#
|
|
262
356
|
# @return [Hash] flat hash where keys are dot-path strings
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-dot_access
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Access deeply nested hash values using dot notation (config.database.host)
|
|
14
14
|
with nil-safe traversal that never raises on missing keys. Supports path-based get/set,
|