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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1`freeze` only affects the top object, so deep immutability requires walking every nested element yourself.
- 2A `case` over container types lets one recursive method handle hashes, arrays, ranges, and structs uniformly.
- 3Returning `obj` after freezing lets the helper wrap constant assignments inline.
Related explainers
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
ruby
class LogAggregator BUCKET_FORMAT = "%Y-%m-%dT%H:%M" def initialize(entries)
Bucketing log entries by the minute in Ruby
aggregation
hashing
enumerable
Intermediate
5 steps
ruby
class WeeklySignupsReport DEFAULT_WEEKS = 12 def initialize(weeks: DEFAULT_WEEKS, source: User.all)
Building a weekly signups report in Rails
service object
aggregation
group by
Intermediate
7 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/recursively-freezing-nested-ruby-data-explained-ruby-8946/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.