ruby
49 lines · 9 steps
How undo/redo history works in Ruby
An editor's undo stack is just an array of snapshots and a cursor that slides back and forth over them.
Explained by
highlit
1class EditorHistory
2 MAX_DEPTH = 100
3
4 def initialize(initial = "")
5 @snapshots = [initial.freeze]
6 @cursor = 0
7 end
8
9 def current
10 @snapshots[@cursor]
11 end
12
13 def commit(text)
14 text = text.dup.freeze
15 return current if text == current
16
17 @snapshots = @snapshots[0..@cursor]
18 @snapshots << text
19
20 if @snapshots.size > MAX_DEPTH
21 @snapshots.shift(@snapshots.size - MAX_DEPTH)
22 end
23
24 @cursor = @snapshots.size - 1
25 current
26 end
27
28 def undo
29 return current if @cursor.zero?
30
31 @cursor -= 1
32 current
33 end
34
35 def redo
36 return current if @cursor >= @snapshots.size - 1
37
38 @cursor += 1
39 current
40 end
41
42 def can_undo?
43 @cursor.positive?
44 end
45
46 def can_redo?
47 @cursor < @snapshots.size - 1
48 end
49end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A cursor index over an append-only snapshot array turns undo/redo into simple pointer arithmetic.
- 2Committing after an undo must truncate the forward history, because that redo branch is now invalid.
- 3Freezing stored snapshots and capping the array bounds memory and prevents mutation from corrupting past states.
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
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
typescript
import { Injectable, effect, signal, computed } from '@angular/core'; interface Preferences { theme: 'light' | 'dark';
A signal-based preferences store in Angular
signals
state-management
persistence
Intermediate
7 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
typescript
import { useEffect, useState } from "react"; interface Section { id: string;
Building a scroll-spy hook in React
custom-hooks
intersectionobserver
dom-observation
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/how-undo-redo-history-works-in-ruby-explained-ruby-c84e/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.