LiveCodes supports Ruby execution in the browser using Opal (Ruby-to-JavaScript compiler) and ruby.wasm (CRuby via WebAssembly).
Configuration
Language Name: ruby (Opal) or ruby-wasm (CRuby)
File Extensions: .rb
Editor: Script editor
Compiler: Opal compiler or ruby.wasm
Runtime: Opal stdlib with auto-loading
Ruby with Opal
Opal compiles Ruby to JavaScript, allowing Ruby code to run in the browser:
# Basic Ruby syntax
name = "World"
puts "Hello, #{ name } !"
# Variables and types
age = 30
pi = 3.14
is_active = true
# String interpolation
puts "Age: #{ age } , Pi: #{ pi } "
# Arrays
numbers = [ 1 , 2 , 3 , 4 , 5 ]
puts numbers. inspect
# Hashes
user = {
name: "John Doe" ,
age: 30 ,
email: "[email protected] "
}
puts user[ :name ]
Methods
# Method definition
def greet ( name , greeting = "Hello" )
" #{ greeting } , #{ name } !"
end
puts greet ( "Alice" )
puts greet ( "Bob" , "Hi" )
# Method with block
def repeat ( n )
n. times { | i | yield i }
end
repeat ( 3 ) { | i | puts "Iteration #{ i } " }
# Multiple return values
def divide ( a , b )
return nil , "Division by zero" if b == 0
return a / b, nil
end
result, error = divide ( 10 , 2 )
if error
puts "Error: #{ error } "
else
puts "Result: #{ result } "
end
Classes and Objects
class Person
attr_accessor :name , :age
attr_reader :email
def initialize ( name , age , email )
@name = name
@age = age
@email = email
end
def greet
"Hello, I'm #{ @name } "
end
def have_birthday
@age += 1
end
def to_s
" #{ @name } ( #{ @age } )"
end
end
person = Person . new ( "Alice" , 25 , "[email protected] " )
puts person. greet
person. have_birthday
puts "New age: #{ person. age } "
puts person. to_s
Blocks, Procs, and Lambdas
# Blocks
[ 1 , 2 , 3 , 4 , 5 ]. each { | n | puts n * 2 }
# Multi-line blocks
[ 1 , 2 , 3 , 4 , 5 ]. each do | n |
squared = n ** 2
puts " #{ n } squared is #{ squared } "
end
# Proc
multiplier = Proc . new { | x , y | x * y }
puts multiplier. call ( 3 , 4 )
# Lambda
adder = -> ( x , y ) { x + y }
puts adder. call ( 5 , 3 )
# Block as parameter
def process_numbers ( numbers , & block )
numbers. map ( & block)
end
result = process_numbers ([ 1 , 2 , 3 , 4 , 5 ]) { | n | n * 2 }
puts result. inspect
Enumerables
numbers = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]
# Map
squared = numbers. map { | n | n ** 2 }
puts "Squared: #{ squared. inspect } "
# Select (filter)
even = numbers. select { | n | n. even? }
puts "Even: #{ even. inspect } "
# Reject
odd = numbers. reject { | n | n. even? }
puts "Odd: #{ odd. inspect } "
# Reduce
sum = numbers. reduce ( 0 ) { | acc , n | acc + n }
puts "Sum: #{ sum } "
# Any? and All?
has_even = numbers. any? ( & :even? )
all_positive = numbers. all? { | n | n > 0 }
puts "Has even: #{ has_even } , All positive: #{ all_positive } "
# Find
first_even = numbers. find ( & :even? )
puts "First even: #{ first_even } "
String Methods
text = "Hello, World!"
# Case manipulation
puts text. upcase
puts text. downcase
puts text. capitalize
puts text. swapcase
# Substring
puts text[ 0 .. 4 ] # "Hello"
puts text[ - 6 .. - 1 ] # "World!"
# Split and join
words = text. split ( ", " )
puts words. inspect
puts words. join ( " - " )
# Include?
puts text. include? ( "World" ) # true
# Replace
new_text = text. gsub ( "World" , "Ruby" )
puts new_text
# Strip
padded = " hello "
puts padded. strip
DOM Manipulation
Access browser DOM through Opal:
require 'native'
# Access document
document = Native ( `document` )
# Create element
div = document. createElement ( 'div' )
div. textContent = 'Hello from Ruby!'
div. classList . add ( 'container' )
# Append to body
body = document. querySelector ( 'body' )
body. appendChild (div)
# Create button
button = document. createElement ( 'button' )
button. textContent = 'Click me'
# Add event listener
button. addEventListener ( 'click' ) do | event |
`alert('Button clicked!')`
event. target . textContent = 'Clicked!'
end
body. appendChild (button)
Counter Example
require 'native'
count = 0
document = Native ( `document` )
# Create display
display = document. createElement ( 'div' )
display. id = 'display'
display. textContent = "Count: #{ count } "
document. body . appendChild (display)
# Increment button
inc_button = document. createElement ( 'button' )
inc_button. textContent = 'Increment'
inc_button. addEventListener ( 'click' ) do
count += 1
document. getElementById ( 'display' ). textContent = "Count: #{ count } "
end
document. body . appendChild (inc_button)
# Decrement button
dec_button = document. createElement ( 'button' )
dec_button. textContent = 'Decrement'
dec_button. addEventListener ( 'click' ) do
count -= 1
document. getElementById ( 'display' ). textContent = "Count: #{ count } "
end
document. body . appendChild (dec_button)
Modules and Mixins
module Greetable
def greet
"Hello, #{ name } !"
end
end
module Timestamps
def created_at
@created_at ||= Time . now
end
end
class User
include Greetable
include Timestamps
attr_accessor :name
def initialize ( name )
@name = name
end
end
user = User . new ( "Alice" )
puts user. greet
puts user. created_at
Exception Handling
def divide ( a , b )
raise "Division by zero" if b == 0
a / b
end
begin
result = divide ( 10 , 2 )
puts "Result: #{ result } "
result = divide ( 10 , 0 )
rescue => e
puts "Error: #{ e. message } "
ensure
puts "Always executed"
end
# Custom exceptions
class ValidationError < StandardError
end
def validate_age ( age )
raise ValidationError , "Age must be positive" if age < 0
raise ValidationError , "Age too high" if age > 150
true
end
begin
validate_age ( - 5 )
rescue ValidationError => e
puts "Validation failed: #{ e. message } "
end
Configuration Options
Configure Opal compiler:
{
"customSettings" : {
"ruby" : {
"autoloadStdlib" : true ,
"requireMap" : {
"custom_gem" : "https://cdn.example.com/gem.js"
},
"arity_check" : false ,
"freezing" : true
}
}
}
Opal Compiler Options
arity_check: Enable/disable method arity checking
freezing: Enable/disable object freezing
autoloadStdlib: Auto-load Opal standard library
requireMap: Map require paths to URLs
Standard Library
Opal includes many Ruby standard library features:
require 'date'
require 'json'
require 'set'
# Date and Time
today = Date . today
puts today. to_s
# JSON
data = { name: "Alice" , age: 25 }
json_str = JSON . generate (data)
puts json_str
parsed = JSON . parse (json_str)
puts parsed[ 'name' ]
# Set
set = Set . new ([ 1 , 2 , 3 , 2 , 1 ])
puts set. inspect # #<Set: {1, 2, 3}>
set. add ( 4 )
puts set. include? ( 3 ) # true
Example Projects
Todo List
require 'native'
class TodoApp
def initialize
@todos = []
@document = Native ( `document` )
setup_ui
end
def setup_ui
container = @document . createElement ( 'div' )
container. className = 'todo-app'
# Input
@input = @document . createElement ( 'input' )
@input . placeholder = 'Add a todo...'
@input . addEventListener ( 'keypress' ) do | e |
add_todo if e. key == 'Enter'
end
# Add button
add_btn = @document . createElement ( 'button' )
add_btn. textContent = 'Add'
add_btn. addEventListener ( 'click' ) { add_todo }
# List
@list = @document . createElement ( 'ul' )
container. appendChild ( @input )
container. appendChild (add_btn)
container. appendChild ( @list )
@document . body . appendChild (container)
end
def add_todo
text = @input . value . strip
return if text. empty?
@todos << { id: Time . now . to_i , text: text, done: false }
@input . value = ''
render
end
def toggle_todo ( id )
todo = @todos . find { | t | t[ :id ] == id }
todo[ :done ] = ! todo[ :done ] if todo
render
end
def delete_todo ( id )
@todos . reject! { | t | t[ :id ] == id }
render
end
def render
@list . innerHTML = ''
@todos . each do | todo |
item = @document . createElement ( 'li' )
item. style . textDecoration = 'line-through' if todo[ :done ]
checkbox = @document . createElement ( 'input' )
checkbox. type = 'checkbox'
checkbox. checked = todo[ :done ]
checkbox. addEventListener ( 'change' ) { toggle_todo (todo[ :id ]) }
text = @document . createTextNode (todo[ :text ])
delete_btn = @document . createElement ( 'button' )
delete_btn. textContent = 'Delete'
delete_btn. addEventListener ( 'click' ) { delete_todo (todo[ :id ]) }
item. appendChild (checkbox)
item. appendChild (text)
item. appendChild (delete_btn)
@list . appendChild (item)
end
end
end
TodoApp . new
Array Operations
# Functional programming style
numbers = ( 1 .. 10 ). to_a
# Chaining operations
result = numbers
. select ( & :even? )
. map { | n | n ** 2 }
. reduce ( :+ )
puts "Sum of squared evens: #{ result } "
# Partition
even, odd = numbers. partition ( & :even? )
puts "Even: #{ even. inspect } "
puts "Odd: #{ odd. inspect } "
# Group by
grouped = numbers. group_by { | n | n % 3 }
puts grouped. inspect
# Zip
letters = [ 'a' , 'b' , 'c' ]
zipped = numbers. first ( 3 ). zip (letters)
puts zipped. inspect # [[1, "a"], [2, "b"], [3, "c"]]
Ruby.wasm (WebAssembly)
For full CRuby support, use ruby-wasm:
# Full CRuby 3.x support
require 'json'
data = {
users: [
{ name: 'Alice' , age: 25 },
{ name: 'Bob' , age: 30 }
]
}
puts JSON . pretty_generate (data)
ruby.wasm provides full CRuby support but has a larger download size (~15MB) compared to Opal.
Best Practices
Use Symbols for Hash Keys
Prefer symbols for hash keys: # Good
user = { name: 'Alice' , age: 25 }
# Less efficient
user = { 'name' => 'Alice' , 'age' => 25 }
Leverage Ruby’s block syntax: # Good
numbers. select ( & :even? )
# Verbose
numbers. select { | n | n. even? }
Use safe navigation operator: # Good
user &. profile &. name
# Verbose
user && user. profile && user. profile . name
Limitations
Opal Differences : Opal is not 100% compatible with CRuby. Some features may work differently or be unavailable.
No Native Extensions : C extensions and native gems are not supported in browser environments.
For full CRuby compatibility, use ruby-wasm, though it has a larger initial load time.
Python Similar dynamic language
JavaScript Browser native language
CoffeeScript Ruby-inspired JavaScript