Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .rubocop_gradual.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"spec/snaky_hash/bad_snake_spec.rb:3931746112": [
[3, 16, 11, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 1577626599]
],
"spec/snaky_hash/snake_spec.rb:3264128361": [
"spec/snaky_hash/snake_spec.rb:1921604918": [
[4, 3, 92, "RSpec/LeakyConstantDeclaration: Stub class constant instead of declaring explicitly.", 3047242215]
]
}
16 changes: 12 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ and this project adheres to [Semantic Versioning v2](https://semver.org/spec/v2.

## [Unreleased]
### Added
### Changed
### Fixed
### Removed

## [2.0.2] - 2025-05-21 ([tag][2.0.2t])
### Added
- Gem is signed by 20-year cert (@pboling)
- Expires 2045-04-29
- Gemspec metadata updates (@pboling)
- Documentation updates (@pboling)
- CI covering all code, and all key versions of runtime dependencies (@pboling)
- Including Hashie v0, v1, v2, v3, v4, v5, and HEAD
- [gh2](https://github.com/oauth-xx/snaky_hash/pull/2) - Serializer option (@pboling)
### Changed
### Fixed
### Removed
- [gh3](https://github.com/oauth-xx/snaky_hash/pull/3) - Serializer Extensions (@pboling)
- Documentation site at [snaky-hash.galtzo.com](https://snaky-hash.galtzo.com)

## [2.0.1] - 2022-09-23 ([tag][2.0.1t])
### Added
Expand Down Expand Up @@ -48,7 +54,9 @@ end
### Added
- Initial release

[Unreleased]: https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.1...main
[Unreleased]: https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.2...main
[2.0.21]: https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.1...v2.0.2
[2.0.2t]: https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v2.0.2
[2.0.1]: https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.0...v2.0.1
[2.0.1t]: https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v2.0.1
[2.0.0]: https://gitlab.com/oauth-xx/snaky_hash/-/compare/v1.0.1...v2.0.0
Expand Down
104 changes: 98 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
# SnakyHash
# 🐍 SnakyHash

[![Version][👽versioni]][👽version] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![Depfu][🔑depfui♻️]][🔑depfu] [![Coveralls Test Coverage][🔑coveralls-img]][🔑coveralls] [![QLTY Test Coverage][🔑qlty-covi♻️]][🔑qlty-cov] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL]

---

[![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]

This library is similar in purpose to the HashWithIndifferentAccess that is famously used in Rails.
This library is similar in purpose to the HashWithIndifferentAccess that is famously used in Rails, but does a lot more.

This gem is used by `oauth`, `oauth2`, and other, gems to normalize hash keys to `snake_case` and lookups,
This gem is used by `oauth` and `oauth2` gems to normalize hash keys to `snake_case` and lookups,
and provide a nice psuedo-object interface.

It can be thought of as a mashup, with upgrades, to the `Rash` (specifically the [`rash_alt`](https://github.com/shishi/rash_alt) flavor), which is a special `Mash`, made popular by the `hashie` gem, and the `serialized_hashie` [gem by krystal](https://github.com/krystal/serialized-hashie).
It can be thought of as a mashup of:

Classes that include `SnakyHash::Snake` should inherit from `Hashie::Mash`.
* `Rash` (specifically the [`rash_alt`](https://github.com/shishi/rash_alt) flavor), which is a special `Mash`, made popular by the `hashie` gem, and
* `serialized_hashie` [gem by krystal](https://github.com/krystal/serialized-hashie)

Classes that `include SnakyHash::Snake.new` should inherit from `Hashie::Mash`.

## New for v2.0.2: Serialization Support

Expand All @@ -29,6 +32,8 @@ class MyStringKeyedHash < Hashie::Mash
end
```

✨ Also new dump & load plugin extensions to control the way your data is dumped and loaded.

| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
|-----------------------------------------------|-------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------|
| 🧪 [oauth-xx/snaky_hash on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜wiki] | 🏀 Tiny Matrix | ➖ |
Expand Down Expand Up @@ -147,7 +152,8 @@ end
snake = MySnakedHash.new(:a => "a", "b" => "b", 2 => 2, "VeryFineHat" => "Feathers")
snake.a # => 'a'
snake.b # => 'b'
snake[2] # 2
snake[2] # => 2
snake["2"] # => nil, note that this gem only affects string / symbol keys.
snake.very_fine_hat # => 'Feathers'
snake[:very_fine_hat] # => 'Feathers'
snake["very_fine_hat"] # => 'Feathers'
Expand All @@ -158,11 +164,97 @@ The `key_type` determines how the key is actually stored, but the hash acts as "
Note also that keys which do not respond to `to_sym`, because they don't have a natural conversion to a Symbol,
are left as-is.

### Serialization

```ruby
class MySerializedSnakedHash < Hashie::Mash
include SnakyHash::Snake.new(
key_type: :symbol, # default :string
serializer: true, # default: false
)
end

snake = MySerializedSnakedHash.new(:a => "a", "b" => "b", 2 => 2, "VeryFineHat" => "Feathers") # => {a: "a", b: "b", 2 => 2, very_fine_hat: "Feathers"}
dump = MySerializedSnakedHash.dump(snake) # => "{\"a\":\"a\",\"b\":\"b\",\"2\":2,\"very_fine_hat\":\"Feathers\"}"
hydrated = MySerializedSnakedHash.load(dump) # => {a: "a", b: "b", "2": 2, very_fine_hat: "Feathers"}
hydrated.class # => MySerializedSnakedHash
hydrated.a # => 'a'
hydrated.b # => 'b'
hydrated[2] # => nil # NOTE: this is the opposite of snake[2] => 2
hydrated["2"] # => 2 # NOTE: this is the opposite of snake["2"] => nil
hydrated.very_fine_hat # => 'Feathers'
hydrated[:very_fine_hat] # => 'Feathers'
hydrated["very_fine_hat"] # => 'Feathers'
```

Note that the key `VeryFineHat` changed to `very_fine_hat`.
That is indeed the point of this library, so not a bug.

Note that the key `2` changed to `"2"` (because JSON keys are strings).
When the JSON dump was reloaded it did not know to restore it as `2` instead of `"2"`.
This is also not a bug, though if you need different behavior, there is a solution in the [next section](#extensions).

### Extensions

You can write your own arbitrary extensions:

* "Hash Load" extensions operate on the hash, and nested hashes
* use `::load_hash_extensions.add(:extension_name) { |hash| }`
* "Load" extensions operate on the values, and nested hash's values, if any
* use `::load_extensions.add(:extension_name) { |value| }`
* "Dump" extensions operate on the values, and nested hash's values, if any
* use `::dump_extensions.add(:extension_name) { |value| }`

#### Example

Let's say I want all integer-like keys, except 0, to be integer keys,
while 0 converts to, and stays, a string forever.

```ruby
class MyExtSnakedHash < Hashie::Mash
include SnakyHash::Snake.new(
key_type: :symbol, # default :string
serializer: true, # default: false
)
end

MyExtSnakedHash.load_hash_extensions.add(:non_zero_keys_to_int) do |value|
if value.is_a?(Hash)
value.transform_keys do |key|
key_int = key.to_s.to_i
if key_int > 0
key_int
else
key
end
end
else
value
end
end

snake = MyExtSnakedHash.new(1 => "a", 0 => 4, "VeryFineHat" => {3 => "v", 5 => 7, :very_fine_hat => "feathers"}) # => {1 => "a", 0 => 4, very_fine_hat: {3 => "v", 5 => 7, very_fine_hat: "feathers"}}
dump = MyExtSnakedHash.dump(snake) # => "{\"1\":\"a\",\"0\":4,\"very_fine_hat\":{\"3\":\"v\",\"5\":7,\"very_fine_hat\":\"feathers\"}}"
hydrated = MyExtSnakedHash.load(dump) # => {1 => "a", "0": 4, very_fine_hat: {3 => "v", 5 => 7, very_fine_hat: "feathers"}}
hydrated.class # => MyExtSnakedHash
hydrated["1"] # => nil
hydrated[1] # => "a"
hydrated["0"] # => 4
hydrated[0] # => nil
hydrated.very_fine_hat # => {3 => "v", 5 => 7, very_fine_hat: "feathers"}
hydrated.very_fine_hat.very_fine_hat # => "feathers"
hydrated.very_fine_hat[:very_fine_hat] # => 'feathers'
hydrated.very_fine_hat["very_fine_hat"] # => 'feathers'
```

See the specs for more examples.

### Stranger Things

I don't recommend using these features... but they exist (for now).
You can still access the original un-snaked camel keys.
And through them you can even use un-snaked camel methods.
But don't.

```ruby
snake.key?("VeryFineHat") # => true
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ <h2>Overview</h2><div class="docstring">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Error.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Extensions.html
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ <h3 class="signature " id="run-instance_method">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
4 changes: 2 additions & 2 deletions doc/SnakyHash/Serializer.html
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ <h3 class="signature " id="load-instance_method">

<span class='kw'>def</span> <span class='id identifier rubyid_load'>load</span><span class='lparen'>(</span><span class='id identifier rubyid_raw_hash'>raw_hash</span><span class='rparen'>)</span>
<span class='id identifier rubyid_hash'>hash</span> <span class='op'>=</span> <span class='const'>JSON</span><span class='period'>.</span><span class='id identifier rubyid_parse'>parse</span><span class='lparen'>(</span><span class='id identifier rubyid_presence'>presence</span><span class='lparen'>(</span><span class='id identifier rubyid_raw_hash'>raw_hash</span><span class='rparen'>)</span> <span class='op'>||</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>{}</span><span class='tstring_end'>&quot;</span></span><span class='rparen'>)</span>
<span class='id identifier rubyid_hash'>hash</span> <span class='op'>=</span> <span class='id identifier rubyid_load_hash'>load_hash</span><span class='lparen'>(</span><span class='id identifier rubyid_hash'>hash</span><span class='rparen'>)</span>
<span class='id identifier rubyid_hash'>hash</span> <span class='op'>=</span> <span class='id identifier rubyid_load_value'>load_value</span><span class='lparen'>(</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='id identifier rubyid_hash'>hash</span><span class='rparen'>)</span><span class='rparen'>)</span>
<span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='id identifier rubyid_hash'>hash</span><span class='rparen'>)</span>
<span class='kw'>end</span></pre>
</td>
Expand All @@ -316,7 +316,7 @@ <h3 class="signature " id="load-instance_method">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Serializer/BackportedInstanceMethods.html
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ <h3 class="signature first" id="transform_values-instance_method">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Serializer/Modulizer.html
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ <h3 class="signature first" id="to_extended_mod-class_method">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Snake.html
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ <h3 class="signature first" id="included-instance_method">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Snake/SnakyModulizer.html
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ <h3 class="signature first" id="to_mod-class_method">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/StringKeyed.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ <h2>Overview</h2><div class="docstring">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/SymbolKeyed.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ <h2>Overview</h2><div class="docstring">
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SnakyHash/Version.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ <h2>
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ <h2>Namespace Listing A-Z</h2>
</div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
34 changes: 22 additions & 12 deletions doc/file.CHANGELOG.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,14 @@
<p>The format (since v2) is based on <a href="https://keepachangelog.com/en/1.0.0/">Keep a Changelog v1</a>,<br>
and this project adheres to <a href="https://semver.org/spec/v2.0.0.html">Semantic Versioning v2</a>.</p>

<h2 id="unreleased"><a href="https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.1...main">Unreleased</a></h2>
<h2 id="unreleased"><a href="https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.2...main">Unreleased</a></h2>
<h3 id="added">Added</h3>
<h3 id="changed">Changed</h3>
<h3 id="fixed">Fixed</h3>
<h3 id="removed">Removed</h3>

<h2 id="202---2025-05-21-tag">[2.0.2] - 2025-05-21 (<a href="https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v2.0.2">tag</a>)</h2>
<h3 id="added-1">Added</h3>
<ul>
<li>Gem is signed by 20-year cert (@pboling)
<ul>
Expand All @@ -73,18 +79,22 @@ <h3 id="added">Added</h3>
</li>
<li>Gemspec metadata updates (@pboling)</li>
<li>Documentation updates (@pboling)</li>
<li>CI covering all code, and all key versions of runtime dependencies (@pboling)</li>
<li>
<a href="https://github.com/oauth-xx/snaky_hash/pull/2">gh2</a> - Serializer option (@pboling)
<h3 id="changed">Changed</h3>
<h3 id="fixed">Fixed</h3>
<h3 id="removed">Removed</h3>
<li>CI covering all code, and all key versions of runtime dependencies (@pboling)
<ul>
<li>Including Hashie v0, v1, v2, v3, v4, v5, and HEAD</li>
</ul>
</li>
<li>
<a href="https://github.com/oauth-xx/snaky_hash/pull/2">gh2</a> - Serializer option (@pboling)</li>
<li>
<a href="https://github.com/oauth-xx/snaky_hash/pull/3">gh3</a> - Serializer Extensions (@pboling)</li>
<li>Documentation site at <a href="https://snaky-hash.galtzo.com">snaky-hash.galtzo.com</a>
</li>
</ul>

<h2 id="201---2022-09-23-tag">
<a href="https://gitlab.com/oauth-xx/snaky_hash/-/compare/v2.0.0...v2.0.1">2.0.1</a> - 2022-09-23 (<a href="https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v2.0.1">tag</a>)</h2>
<h3 id="added-1">Added</h3>
<h3 id="added-2">Added</h3>
<ul>
<li>Certificate for signing gem releases (@pboling)</li>
<li>Gemspec metadata (@pboling)
Expand All @@ -109,7 +119,7 @@ <h3 id="changed-2">Changed</h3>
include SnakyHash::Snake.new(key_type: :string) # or :symbol
end
</code></pre>
<h3 id="added-2">Added</h3>
<h3 id="added-3">Added</h3>
</li>
<li>
<code>SnakyHash::StringKeyed</code>: a Hashie::Mash class with snake-cased String keys</li>
Expand All @@ -119,7 +129,7 @@ <h3 id="added-2">Added</h3>

<h2 id="101---2022-08-26-tag">
<a href="https://gitlab.com/oauth-xx/snaky_hash/-/compare/v1.0.0...v1.0.1">1.0.1</a> - 2022-08-26 (<a href="https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v1.0.1">tag</a>)</h2>
<h3 id="added-3">Added</h3>
<h3 id="added-4">Added</h3>
<ul>
<li>Missing LICENSE.txt file to release
<h3 id="removed-1">Removed</h3>
Expand All @@ -129,15 +139,15 @@ <h3 id="removed-1">Removed</h3>

<h2 id="100---2022-08-26-tag">
<a href="https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v1.0.0">1.0.0</a> - 2022-08-26 (<a href="https://gitlab.com/oauth-xx/snaky_hash/-/releases/tag/v1.0.0">tag</a>)</h2>
<h3 id="added-4">Added</h3>
<h3 id="added-5">Added</h3>
<ul>
<li>Initial release</li>
</ul>

</div></div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/file.CODE_OF_CONDUCT.html
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ <h2 id="attribution">Attribution</h2>
</div></div>

<div id="footer">
Generated on Thu May 22 00:16:01 2025 by
Generated on Thu May 22 03:35:07 2025 by
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
0.9.37 (ruby-3.4.3).
</div>
Expand Down
Loading