Skip to content

Commit 0bf5536

Browse files
committed
✨ Serialization Extensions
1 parent 70e4c19 commit 0bf5536

23 files changed

+317
-32
lines changed

README.md

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
This library is similar in purpose to the HashWithIndifferentAccess that is famously used in Rails.
1010

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

1414
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).
@@ -147,7 +147,8 @@ end
147147
snake = MySnakedHash.new(:a => "a", "b" => "b", 2 => 2, "VeryFineHat" => "Feathers")
148148
snake.a # => 'a'
149149
snake.b # => 'b'
150-
snake[2] # 2
150+
snake[2] # => 2
151+
snake["2"] # => nil, note that this gem only affects string / symbol keys.
151152
snake.very_fine_hat # => 'Feathers'
152153
snake[:very_fine_hat] # => 'Feathers'
153154
snake["very_fine_hat"] # => 'Feathers'
@@ -158,11 +159,97 @@ The `key_type` determines how the key is actually stored, but the hash acts as "
158159
Note also that keys which do not respond to `to_sym`, because they don't have a natural conversion to a Symbol,
159160
are left as-is.
160161

162+
### Serialization
163+
164+
```ruby
165+
class MySerializedSnakedHash < Hashie::Mash
166+
include SnakyHash::Snake.new(
167+
key_type: :symbol, # default :string
168+
serializer: true, # default: false
169+
)
170+
end
171+
172+
snake = MySerializedSnakedHash.new(:a => "a", "b" => "b", 2 => 2, "VeryFineHat" => "Feathers") # => {a: "a", b: "b", 2 => 2, very_fine_hat: "Feathers"}
173+
dump = MySerializedSnakedHash.dump(snake) # => "{\"a\":\"a\",\"b\":\"b\",\"2\":2,\"very_fine_hat\":\"Feathers\"}"
174+
hydrated = MySerializedSnakedHash.load(dump) # => {a: "a", b: "b", "2": 2, very_fine_hat: "Feathers"}
175+
hydrated.class # => MySerializedSnakedHash
176+
hydrated.a # => 'a'
177+
hydrated.b # => 'b'
178+
hydrated[2] # => nil # NOTE: this is the opposite of snake[2] => 2
179+
hydrated["2"] # => 2 # NOTE: this is the opposite of snake["2"] => nil
180+
hydrated.very_fine_hat # => 'Feathers'
181+
hydrated[:very_fine_hat] # => 'Feathers'
182+
hydrated["very_fine_hat"] # => 'Feathers'
183+
```
184+
185+
Note that the key `VeryFineHat` changed to `very_fine_hat`.
186+
That is indeed the point of this library, so not a bug.
187+
188+
Note that the key `2` changed to `"2"` (because JSON keys are strings).
189+
When the JSON dump was reloaded it did not know to restore it as `2` instead of `"2"`.
190+
This is also not a bug, though if you need different behavior, there is a solution in the next section.
191+
192+
### Extensions
193+
194+
You can write your own arbitrary extensions:
195+
196+
* hash load extensions operate on the hash, and nested hashes
197+
* use `::load_hash_extensions.add(:extension_name) {}`
198+
* load extensions operate on the values, and nested hash values, if any
199+
* use `::load_extensions.add(:extension_name) {}`
200+
* dump extensions operate on the values, and nested hash values, if any
201+
* use `::dump_extensions.add(:extension_name) {}`
202+
203+
#### Example
204+
205+
Let's say I want all integer-like keys, except 0, to be integer keys,
206+
while 0 converts to, and stays, a string forever.
207+
208+
```ruby
209+
class MyExtSnakedHash < Hashie::Mash
210+
include SnakyHash::Snake.new(
211+
key_type: :symbol, # default :string
212+
serializer: true, # default: false
213+
)
214+
end
215+
216+
MyExtSnakedHash.load_hash_extensions.add(:non_zero_keys_to_int) do |value|
217+
if value.is_a?(Hash)
218+
value.transform_keys do |key|
219+
key_int = key.to_s.to_i
220+
if key_int > 0
221+
key_int
222+
else
223+
key
224+
end
225+
end
226+
else
227+
value
228+
end
229+
end
230+
231+
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"}}
232+
dump = MyExtSnakedHash.dump(snake) # => "{\"1\":\"a\",\"0\":4,\"very_fine_hat\":{\"3\":\"v\",\"5\":7,\"very_fine_hat\":\"feathers\"}}"
233+
hydrated = MyExtSnakedHash.load(dump) # => {1 => "a", "0": 4, very_fine_hat: {3 => "v", 5 => 7, very_fine_hat: "feathers"}}
234+
hydrated.class # => MyExtSnakedHash
235+
hydrated["1"] # => nil
236+
hydrated[1] # => "a"
237+
hydrated["2"] # => nil
238+
hydrated[2] # => 4
239+
hydrated["0"] # => 4
240+
hydrated[0] # => nil
241+
hydrated.very_fine_hat # => {3 => "v", 5 => 7, very_fine_hat: "feathers"}
242+
hydrated.very_fine_hat.very_fine_hat # => "feathers"
243+
hydrated.very_fine_hat[:very_fine_hat] # => 'Feathers'
244+
hydrated.very_fine_hat["very_fine_hat"] # => 'Feathers'
245+
```
246+
161247
### Stranger Things
162248

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

167254
```ruby
168255
snake.key?("VeryFineHat") # => true

doc/SnakyHash.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ <h2>Overview</h2><div class="docstring">
123123
</div>
124124

125125
<div id="footer">
126-
Generated on Thu May 22 00:16:01 2025 by
126+
Generated on Thu May 22 01:26:13 2025 by
127127
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
128128
0.9.37 (ruby-3.4.3).
129129
</div>

doc/SnakyHash/Error.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@
114114
</div>
115115

116116
<div id="footer">
117-
Generated on Thu May 22 00:16:01 2025 by
117+
Generated on Thu May 22 01:26:13 2025 by
118118
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
119119
0.9.37 (ruby-3.4.3).
120120
</div>

doc/SnakyHash/Extensions.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ <h3 class="signature " id="run-instance_method">
446446
</div>
447447

448448
<div id="footer">
449-
Generated on Thu May 22 00:16:01 2025 by
449+
Generated on Thu May 22 01:26:13 2025 by
450450
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
451451
0.9.37 (ruby-3.4.3).
452452
</div>

doc/SnakyHash/Serializer.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ <h3 class="signature " id="load-instance_method">
303303

304304
<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>
305305
<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>
306-
<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>
306+
<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='kw'>self</span><span class='lbracket'>[</span><span class='id identifier rubyid_hash'>hash</span><span class='rbracket'>]</span><span class='rparen'>)</span>
307307
<span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='id identifier rubyid_hash'>hash</span><span class='rparen'>)</span>
308308
<span class='kw'>end</span></pre>
309309
</td>
@@ -316,7 +316,7 @@ <h3 class="signature " id="load-instance_method">
316316
</div>
317317

318318
<div id="footer">
319-
Generated on Thu May 22 00:16:01 2025 by
319+
Generated on Thu May 22 01:26:13 2025 by
320320
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
321321
0.9.37 (ruby-3.4.3).
322322
</div>

doc/SnakyHash/Serializer/BackportedInstanceMethods.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ <h3 class="signature first" id="transform_values-instance_method">
190190
</div>
191191

192192
<div id="footer">
193-
Generated on Thu May 22 00:16:01 2025 by
193+
Generated on Thu May 22 01:26:13 2025 by
194194
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
195195
0.9.37 (ruby-3.4.3).
196196
</div>

doc/SnakyHash/Serializer/Modulizer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ <h3 class="signature first" id="to_extended_mod-class_method">
189189
</div>
190190

191191
<div id="footer">
192-
Generated on Thu May 22 00:16:01 2025 by
192+
Generated on Thu May 22 01:26:13 2025 by
193193
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
194194
0.9.37 (ruby-3.4.3).
195195
</div>

doc/SnakyHash/Snake.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ <h3 class="signature first" id="included-instance_method">
275275
</div>
276276

277277
<div id="footer">
278-
Generated on Thu May 22 00:16:01 2025 by
278+
Generated on Thu May 22 01:26:13 2025 by
279279
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
280280
0.9.37 (ruby-3.4.3).
281281
</div>

doc/SnakyHash/Snake/SnakyModulizer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ <h3 class="signature first" id="to_mod-class_method">
261261
</div>
262262

263263
<div id="footer">
264-
Generated on Thu May 22 00:16:01 2025 by
264+
Generated on Thu May 22 01:26:13 2025 by
265265
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
266266
0.9.37 (ruby-3.4.3).
267267
</div>

doc/SnakyHash/StringKeyed.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ <h2>Overview</h2><div class="docstring">
130130
</div>
131131

132132
<div id="footer">
133-
Generated on Thu May 22 00:16:01 2025 by
133+
Generated on Thu May 22 01:26:13 2025 by
134134
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
135135
0.9.37 (ruby-3.4.3).
136136
</div>

0 commit comments

Comments
 (0)