ruby 41 lines · 9 steps

Recursively freezing nested Ruby data

A recursive helper walks any nested structure and freezes every object inside, giving you deeply immutable constants.

Explained by highlit
1module DeepFreeze
2 module_function
3 
4 def call(obj)
5 case obj
6 when Hash
7 obj.each { |key, value| call(key); call(value) }
8 obj.freeze
9 when Array, Set
10 obj.each { |element| call(element) }
11 obj.freeze
12 when Range
13 call(obj.begin)
14 call(obj.end)
15 obj.freeze
16 when Struct
17 obj.each_pair { |_, value| call(value) }
18 obj.freeze
19 else
20 obj.freeze
21 end
22 
23 obj
24 end
25end
26 
27class Configuration
28 SUPPORTED_CURRENCIES = DeepFreeze.call(%w[USD EUR GBP JPY])
29 
30 RATE_LIMITS = DeepFreeze.call(
31 free: { requests_per_minute: 60, burst: 10 },
32 pro: { requests_per_minute: 600, burst: 100 },
33 enterprise: { requests_per_minute: 6_000, burst: 1_000 }
34 )
35 
36 FEATURE_FLAGS = DeepFreeze.call(
37 beta_search: false,
38 dark_mode: true,
39 allowed_hosts: %w[app.example.com api.example.com]
40 )
41end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1`freeze` only affects the top object, so deep immutability requires walking every nested element yourself.
  2. 2A `case` over container types lets one recursive method handle hashes, arrays, ranges, and structs uniformly.
  3. 3Returning `obj` after freezing lets the helper wrap constant assignments inline.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Recursively freezing nested Ruby data — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code