Every cell starting with = is evaluated as a Ruby expression.
Plain text and numbers are stored as-is.
Arithmetic
Standard Ruby operators work in formulas:
| Operator |
Example |
Result |
+ - * / |
=2 + 3 * 4 |
14 |
** |
=(10 - 2) ** 3 |
512 |
% |
=100 % 7 |
2 |
Cell References
Reference cells by column letter + row number:
=A1 * 4
=A1 + B1
References cascade: if B1 depends on A1, changing A1 updates B1 automatically.
Cell Ranges
Use Ruby’s .. range syntax to work with multiple cells.
The result is a full Enumerable:
=(A1..A10).sum(&:to_f)
=(A1..A10).count { |v| v > 50 }
=(A1..A10).max_by(&:to_f)
=(A1..A10).min_by(&:to_f)
=(A1..A10).sort_by(&:to_f).first(3)
=(A1..A10).select { |v| v > 0 }
=(A1..A10).map { |v| v * 2 }
Ranges can run horizontally across a row or cover a rectangle, not just
down a column:
=(B2..K2).count { |v| v == "o" } # one row, many columns
=(A1..C3).sum(&:to_f) # a 3×3 block
Column Ranges
Use A:A syntax to reference an entire column (all rows):
=(A:A).sum(&:to_f)
=(B:B).count { |v| v > 0 }
=(A:A).max_by(&:to_f)
Multi-column ranges work too: A:C covers all cells in columns A, B, and C.
String Methods
Call any Ruby string method on cell values:
| Method |
Example |
Description |
.upcase |
=A1.upcase |
Uppercase |
.downcase |
=A1.downcase |
Lowercase |
.capitalize |
=A1.capitalize |
Capitalize first letter |
.reverse |
=A1.reverse |
Reverse string |
.length |
=A1.length |
Character count |
.gsub(a, b) |
=A1.gsub("a", "@") |
Replace characters |
.split(sep) |
=A1.split(",") |
Split into array |
.strip |
=A1.strip |
Remove whitespace |
.include?(s) |
=A1.include?("text") |
Check if contains |
.start_with?(s) |
=A1.start_with?("http") |
Check prefix |
Helper Functions
Math
| Function |
Description |
Example |
sum(...) |
Sum of values |
=sum(1, 2, 3) |
avg(...) |
Average |
=avg(10, 20, 30) |
min(...) |
Minimum |
=min(5, 2, 8) |
max(...) |
Maximum |
=max(5, 2, 8) |
round(n, digits) |
Round to digits |
=round(3.14159, 2) |
sqrt(n) |
Square root |
=sqrt(144) |
abs(n) |
Absolute value |
=abs(-42) |
ceil(n) |
Round up |
=ceil(3.2) |
floor(n) |
Round down |
=floor(3.8) |
pow(base, exp) |
Exponentiation |
=pow(2, 10) |
pi |
Pi constant |
=round(pi, 4) |
All math helpers accept cell references: =sum(A1, A2, A3).
Text
| Function |
Description |
Example |
concat(...) |
Join values as text |
=concat(A1, " ", B1) |
md5(text) |
MD5 hex digest |
=md5("hello") |
Objects
Some formulas return a rich object that renders itself in the cell instead of
plain text.
Image — display an image from an HTTP(S) URL:
=Image.new("https://example.com/photo.jpg")
Combine with md5 for gravatar avatars:
=Image.new("https://s.gravatar.com/avatar/" + md5(A1) + "?s=80")
Only HTTP and HTTPS URLs are accepted.
Link — render a clickable hyperlink that opens in a new tab. Label first,
URL second (same order as Rails’ link_to):
=Link.new("Example.com", "https://example.com")
=Link.new("Ruby on Rails", "https://rubyonrails.org")
=Link.new("Search #{A1}", "https://www.google.com/search?q=#{A1}") # interpolation
Only HTTP and HTTPS URLs are accepted.
Button — render a clickable button that triggers a custom action method
(defined in the Functions panel):
=Button.new("Roll over", :rollover)
Unlike a formula, an action may write to the grid. Inside an action method
you can call:
| Helper |
Description |
set(ref, value) |
Write a value into a cell |
clear(ref) |
Clear a cell |
sleep(seconds) |
Pause the action (max 10s) |
An action runs as plain sequential Ruby. Each set/clear shows up live in the
grid as it happens, so you can sleep between steps to animate changes:
# In the Functions panel:
def greet
set("C41", "Hello from RubyGrid! 👋")
sleep(2)
clear("C41")
end
Action writes are recorded in history, so they can be undone like any edit.
HTTP
| Function |
Description |
Example |
fetch_json(url) |
Fetch and parse JSON |
=fetch_json("https://api.example.com/data")["key"] |
fetch_text(url) |
Fetch raw text |
=fetch_text("https://example.com/file.txt") |
JSON responses support [], dig, keys, values, and size.
Requests are cached for 5 minutes. Private networks and localhost are blocked.
Date & Time
Date and Time are available in formulas via safe wrappers.
Shorthand helpers today and now are also available.
=today.to_s # "2025-06-15"
=now.strftime("%H:%M") # "14:30"
=today.strftime("%A") # "Sunday"
=Date.parse("2025-12-31").to_s # "2025-12-31"
=Date.civil(2025, 6, 15).to_s # "2025-06-15"
=(Date.parse("2025-12-31") - today).to_i # days until
=today.year # 2025
=today.month # 6
=today.monday? # true/false
=(Date.parse(A1) - Date.parse(B1)).to_i # days between two cells
Date arithmetic works naturally: Date.today + 30 returns a date 30 days
from now. Date ranges are iterable: (date1..date2).count.
Volatile values (today, now, rand)
Most formulas only recompute when a cell they reference changes. Formulas that
read the wall clock or randomness — today, now (including Date.today /
Time.now), and rand — are volatile: their value can change on its own.
=now.strftime("%H:%M:%S") # current time
=rand(100) # 0–99, re-rolled each time
Volatile cells (and anything that depends on them) are recomputed automatically:
- on every page load, so reopening a sheet never shows a stale clock, and
- whenever any cell in the sheet changes, even an unrelated one.
rand mirrors Ruby’s Kernel#rand: no argument gives a float in [0, 1), an
integer n gives 0…n-1, and a range like rand(1..6) picks within it.
Date Cell Arithmetic
A cell that holds an ISO date (YYYY-MM-DD) supports date arithmetic directly,
so you can build a date header from a single anchor date:
=B1 + 1 # the day after the date in B1
=F1 + 3 # skip a weekend (Fri + 3 = Mon)
=A1 - B1 # whole days between two date cells
Adding or subtracting an integer returns a date; subtracting one date cell from
another returns the number of days between them.
Custom Functions
Define reusable Ruby methods in the Custom Functions panel (f(x) button).
Functions are available in any cell formula:
# In the panel:
def tax(amount)
amount * 0.077
end
def grade(score)
case score
when 90.. then "A"
when 80..89 then "B"
when 70..79 then "C"
else "F"
end
end
# In any cell:
=tax(A1)
=grade(D5)
=tax(discount(B2, 15))
Functions can call other custom functions, use cell references, and access
all built-in helpers. Press Cmd+S in the editor to save.
Custom Classes
The Functions panel is plain Ruby — define classes and modules alongside defs,
and use them in any formula:
# In the panel:
class Money
def initialize(amount, currency)
@amount = amount.to_f
@currency = currency.to_s
end
attr_reader :amount, :currency
def +(other)
Money.new(@amount + other.amount, @currency)
end
def to_s
format("%.2f %s", @amount, @currency)
end
end
# In cells:
=Money.new(100, "CHF") # A1 displays "100.00 CHF"
=Money.new(50, "CHF") # B1 displays "50.00 CHF"
=A1 + B1 # C1 displays "150.00 CHF" — Money#+ is called
Cells store the to_s of whatever the formula returned. When a later formula
references the cell, RubyGrid re-evaluates the source so the live object — not
just its display string — flows through. That’s what lets =A1 + B1 actually
call Money#+ instead of doing numeric coercion on the display string.
Classes you define are visible across all sheets in the same deployment.
Pick distinct names if two sheets need different versions of the same concept.
Conditionals
Use Ruby’s ternary operator or case/when:
=A1 > 100 ? "high" : "low"
=A1.to_i.even? ? "even" : "odd"
Rules are managed in the Style & Format panel. Each rule targets a cell or range
and can define a style expression, a format expression, or both.
Style Expressions
Return a CSS class name to style the cell:
value > 100 ? "danger" : value > 50 ? "warning" : "success"
Available styles: danger, warning, success, info, highlight, muted, bold, italic.
Border styles come on two independent axes that you combine in one expression:
- What to draw — a full box:
border (thin), border-thick, border-dashed;
or a single thick edge between sections: divider-left, divider-right,
divider-top, divider-bottom.
- What colour —
border-success, border-danger, border-warning,
border-info, border-muted set the colour of whichever border is drawn
(the default is the theme’s primary colour).
A style expression may return several space-separated classes to combine them —
a background colour, a border, and a border colour all at once:
"border-thick border-success" # thick green box
(value.to_s.downcase == "o" ? "success" : "danger") + " divider-right border-info"
You don’t have to pack everything into one expression, though: overlapping
rules stack. If one rule paints the background of B2:K6 and a second rule
adds divider-right to F2:F6, the cells in column F get both. Classes are
gathered from every matching rule (in position order) and de-duplicated. A
per-cell style expression (set on the cell itself) takes priority and replaces
rule-based styling for that cell.
Transform how the cell value is displayed without changing the underlying data:
"#{value} CHF" # append currency
value.to_i > 0 ? "ON" : "OFF" # boolean display
"#{value}%" # percentage
value > 1000 ? "#{value / 1000.0}k" : value.to_s # compact numbers
The expression receives value (the cell’s computed value) and can reference
other cells and custom functions. The original value is preserved — only the
display changes.
Keyboard Shortcuts
| Key |
Action |
| Arrow keys |
Navigate cells |
| Shift + Arrow |
Extend selection |
| Enter |
Edit cell / Confirm |
| Escape |
Cancel / Clear selection |
| Tab / Shift+Tab |
Move right / left |
| Delete |
Clear cell |
| Ctrl+C |
Copy |
| Ctrl+X |
Cut |
| Ctrl+V |
Paste |
| F1 |
Show this help |