88
99This 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,
1212and provide a nice psuedo-object interface.
1313
1414It 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 ) .
147147snake = MySnakedHash .new (:a => " a" , " b" => " b" , 2 => 2 , " VeryFineHat" => " Feathers" )
148148snake.a # => 'a'
149149snake.b # => 'b'
150- snake[2 ] # 2
150+ snake[2 ] # => 2
151+ snake[" 2" ] # => nil, note that this gem only affects string / symbol keys.
151152snake.very_fine_hat # => 'Feathers'
152153snake[:very_fine_hat ] # => 'Feathers'
153154snake[" very_fine_hat" ] # => 'Feathers'
@@ -158,11 +159,97 @@ The `key_type` determines how the key is actually stored, but the hash acts as "
158159Note also that keys which do not respond to ` to_sym ` , because they don't have a natural conversion to a Symbol,
159160are 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
163249I don't recommend using these features... but they exist (for now).
164250You can still access the original un-snaked camel keys.
165251And through them you can even use un-snaked camel methods.
252+ But don't.
166253
167254``` ruby
168255snake.key?(" VeryFineHat" ) # => true
0 commit comments