Skip to content

Tina4 Ruby ORM

🔥 Hot Tips

  • You never need the ORM — raw Database is perfect for 90% of cases
  • But when you want it… it feels like writing plain Ruby classes
  • Zero boilerplate — field types are declared with a clean DSL
  • Works with SQLite, PostgreSQL, MySQL, MSSQL, Firebird — all the same

Quick Start

ruby
require "tina4"

class User < Tina4::ORM
  integer_field :id, primary_key: true, auto_increment: true
  string_field  :name
  string_field  :email
  datetime_field :created_at
end

# That's it — use the global database from Tina4.database
user = User.create(name: "Alice", email: "alice@example.com")
puts user.id          # → 1

user.name = "Alice Wonder"
user.save             # → UPDATE

user = User.find(1)
puts user.name        # → "Alice Wonder"

Field Types

Field MethodRuby typeSQL exampleNotes
integer_fieldIntegerINTEGER
string_fieldStringVARCHAR(255)length: 500 optional
text_fieldStringTEXTunlimited
float_fieldFloatFLOAT
decimal_fieldBigDecimalNUMERIC(10,2)precision:, scale:
boolean_fieldBooleanBOOLEAN
date_fieldDateDATE
datetime_fieldDateTimeTIMESTAMP
timestamp_fieldDateTimeTIMESTAMPalias
blob_fieldStringBLOBbinary
json_fieldHash/ArrayJSON

Field Options

ruby
class Product < Tina4::ORM
  integer_field :id, primary_key: true, auto_increment: true
  string_field  :name, length: 100, nullable: false
  decimal_field :price, precision: 10, scale: 2, default: 0.0
  boolean_field :active, default: true
  datetime_field :created_at
end

Custom Table Name

ruby
class User < Tina4::ORM
  table_name "people"     # Override default "users"
  integer_field :id, primary_key: true, auto_increment: true
  string_field :name
end

Core Methods

MethodExampleWhat it doesReturns
.find(id)User.find(1)Load by primary keyInstance or nil
.where(cond, params)User.where("age > ?", [30])Query with conditionsArray of instances
.all(limit:, skip:, order_by:)User.all(limit: 10)All recordsArray of instances
.count(cond, params)User.count("active = ?", [true])Count recordsInteger
.create(attrs)User.create(name: "Bob")Insert and returnInstance
#saveuser.saveINSERT or UPDATEtrue/false
#deleteuser.deleteDelete recordtrue/false
#load(id)user.load(1)Load into instancetrue/false
#to_hashuser.to_hashConvert to hashHash
#to_jsonuser.to_jsonConvert to JSONString
#persisted?user.persisted?Is saved to DB?Boolean
#errorsuser.errorsValidation errorsArray

Full Example

ruby
require "tina4"

class Category < Tina4::ORM
  integer_field :id, primary_key: true, auto_increment: true
  string_field :name
end

class Article < Tina4::ORM
  integer_field :id, primary_key: true, auto_increment: true
  string_field :title
  text_field :content
  integer_field :category_id
  datetime_field :created_at
end

# Use it
cat = Category.create(name: "Tech")

article = Article.create(
  title: "Tina4 is awesome",
  content: "Great framework",
  category_id: cat.id
)

# Query
articles = Article.where("category_id = ?", [cat.id])
articles.each { |a| puts a.title }

Using with Routes

ruby
Tina4.get "/api/users" do |request, response|
  users = User.all(limit: 20)
  response.json(users.map(&:to_hash))
end

Tina4.get "/api/users/{id:int}" do |request, response|
  user = User.find(request.params["id"])
  user ? response.json(user.to_hash) : response.json({ error: "Not found" }, 404)
end

Tina4.post "/api/users" do |request, response|
  user = User.create(request.json_body)
  if user.persisted?
    response.json(user.to_hash, 201)
  else
    response.json({ errors: user.errors }, 422)
  end
end

Summary – The Tina4 ORM Philosophy

  • No migrations required for ORM classes
  • No query builder hell
  • No session management
  • No associations to configure

Just:

ruby
user = User.create(name: "Bob")

Tina4.post "/api/users" do |request, response|
  user = User.create(request.json_body)
  response.json(user.to_hash, 201)
end