Ruby
Primate runs Ruby with WebAssembly compilation, strongly-typed validation, sessions, and server-side routing.
Setup
Install Ruby
First, install Ruby 3.0 or later from the official website.
Install Module
npm install @primate/ruby
Configure
import config from "primate/config";
import ruby from "@primate/ruby";
export default config({
modules: [ruby()],
});
Initialize Ruby
Create a Gemfile
in your project root:
bundle init
Add the Primate Ruby dependency:
# Gemfile
source "https://rubygems.org"
gem "primate-run", "~> 0.1.0"
Install dependencies to vendor/bundle (required for WASM):
bundle config set --local path 'vendor/bundle'
bundle install
This creates Gemfile
and Gemfile.lock
files that manage your Ruby
dependencies.
Routes
Create Ruby route handlers in routes
using .rb
files. Routes are
compiled to WebAssembly and run in the JavaScript runtime.
# routes/hello.rb
require 'primate/route'
Route.get do |request|
'Hello, world!'
end
HTTP Methods
All standard HTTP methods are supported:
require 'primate/route'
Route.get do |request|
'GET request'
end
Route.post do |request|
'POST request'
end
Route.put do |request|
'PUT request'
end
Route.delete do |request|
'DELETE request'
end
Request Handling
Query Parameters
Access query parameters through the query
request bag:
# routes/query.rb
require 'primate/route'
Route.get do |request|
if request.query.has?('foo')
request.query.get('foo')
else
'foo missing'
end
end
Request Body
Handle different body types based on content:
JSON Body
# routes/json.rb
require 'primate/route'
Route.post do |request|
begin
request.body.json
rescue => e
{ error: e.message }
end
end
Form Fields
# routes/form.rb
require 'primate/route'
Route.post do |request|
begin
request.body.fields
rescue => e
{ error: e.message }
end
end
Text Body
# routes/text.rb
require 'primate/route'
Route.post do |request|
request.body.text
end
Binary Data
# routes/binary.rb
require 'primate/route'
Route.post do |request|
readable = request.body.binary
# Get first 4 bytes for file type detection
head = readable.head(4)
{
type: readable.content_type,
size: readable.size,
head: head
}
end
File Uploads
Handle multipart file uploads:
# routes/upload.rb
require 'primate/route'
Route.post do |request|
begin
# Get form fields
fields = request.body.fields
# Get uploaded files
files = request.body.files
# Process files
file_info = files.map do |file|
{
field: file.field, # form field name
name: file.filename, # original filename
type: file.content_type, # MIME type
size: file.size # file size in bytes
}
end
{
fields: fields,
files: file_info
}
rescue => e
{ error: e.message }
end
end
Validation
Use Primate's strongly-typed validation system with the pema
module:
# routes/validate.rb
require 'primate/route'
require 'primate/pema'
schema = Pema.schema({
'baz' => Pema.int,
'foo' => Pema.string
})
Route.get do |request|
begin
request.query.parse(schema, true)
rescue Pema::ValidationError => e
e.message
end
end
Field Types
The validation system supports multiple strongly-typed field types:
Pema.string
- validates string valuesPema.int
- validates integer valuesPema.float
- validates float valuesPema.boolean
- validates boolean values
Coercion
Enable automatic type coercion by passing true
as the second parameter:
request.query.parse(schema, true) # enables coercion
With coercion enabled:
- Strings are converted to numbers when possible
- Empty strings become
false
for booleans,0
for numbers - Numbers are converted between types as needed
Responses
Plain Data
Return any Ruby object that can be JSON serialized:
Route.get do |request|
{ name: 'Donald' }
end
Route.get do |request|
'Hello, world!'
end
Route.get do |request|
[
{ name: 'Donald' },
{ name: 'Ryan' }
]
end
Views
Render components with props:
# routes/view.rb
require 'primate/route'
require 'primate/response'
Route.get do |request|
Response.view('index.html', hello: 'world')
end
With options:
require 'primate/route'
require 'primate/response'
Route.get do |request|
Response.view('index.html',
{ hello: 'world' },
{ partial: true }
)
end
Redirects
Redirect to another route:
# routes/redirect.rb
require 'primate/route'
require 'primate/response'
Route.get do |request|
Response.redirect('/redirected')
end
With custom status code:
Route.get do |request|
Response.redirect('/redirected', status: 301) # moved permanently
end
Error Responses
Return error responses:
# routes/error.rb
require 'primate/route'
require 'primate/response'
Route.get do |request|
Response.error
end
With custom error options:
Route.get do |request|
Response.error(body: 'Custom error message')
end
Sessions
Manage user sessions with the session module:
# routes/session.rb
require 'primate/route'
require 'primate/session'
Route.get do |request|
# Create a session
Session.create(foo: 'bar')
# Get session data
Session.get
end
Session Methods
Session.create(data)
- creates a new session with dataSession.get
- gets session data (raises if no session)Session.try
- gets session data (returns nil if no session)Session.set(data)
- updates session dataSession.destroy
- destroys the sessionSession.exists?
- checks if session existsSession.id
- gets the session ID
Ruby Conventions
Primate Ruby follows standard Ruby conventions:
Method Names
- Use
snake_case
for method and variable names - Use
?
suffix for boolean methods:has?
,exists?
- Use
!
suffix for destructive methods (when applicable)
Hash Syntax
Use modern Ruby hash syntax with symbols:
# Preferred
{ name: 'John', age: 30 }
# Also valid
{ :name => 'John', :age => 30 }
String Literals
Use single quotes for plain strings, double quotes for interpolation:
# Plain strings
message = 'Hello, world!'
# String interpolation
greeting = "Hello, #{name}!"
Configuration
Option | Type | Default | Description |
---|---|---|---|
fileExtension | string |
".rb" |
Associated file extension |
Example
import ruby from "@primate/ruby";
import config from "primate/config";
export default config({
modules: [
ruby({
// use `.ruby` as associated file extension
fileExtension: ".ruby",
}),
],
});