Chapter 35: Release Notes
v3.11.13 (2026-04-16)
Issue-driven release. Everything reported in the open tina4-book issues either was fixed in this version or is already fixed in 3.11.12; this release consolidates the remaining bits and corrects documentation drift.
- feat (router / all 4): Explicit typed-parameter system shared across Python, PHP, Ruby, Node. Adds
alpha,alnum,slug,uuid, and explicitstringtypes in addition to the existingint/integer,float/number,path/.*. Unknown type names now throw at registration —{name:str},{id:inetger}, etc. raise with a clear message listing the valid types instead of silently falling through to the default matcher. Fixes tina4-book#125. +45 new tests across the four suites. - fix (gallery / python+php+ruby): Gallery Try-It / View buttons now open the deployed example in a new tab (
window.open(url, '_blank')) instead of navigating away from the gallery home. Fixes tina4-book#115. - fix (ruby gemspec):
sqlite3promoted fromadd_development_dependencytoadd_dependency. Matches the "zero-config SQLite on first run" promise. Fixes tina4-book#100. - docs (tina4-book): PHP Chapter 2 updated — correct port (7145),
->noAuth()on write-method examples, and an explicit callout explaining the secure-by-default policy for POST/PUT/PATCH/DELETE. Addresses tina4-book#87, #94, #123. - docs (tina4-book): Python
@templatedecorator ordering corrected (must sit BELOW the route decorator) in book chapters 04 and 10; Pythonrequest->queryvsrequest->paramsdistinction in PHP chapter 1. - tests (python): Session-handler tests updated to reflect the real default TTL of 3600s (were stale at 1800s).
- verified already fixed in earlier 3.11.x releases — closed comments posted on all of these:
- #79 dotted numeric index (
{{ items.0.name }}) - #80
truncatefilter - #82
{{ parent() }}/{{ super() }}across all 4 frameworks - #83 Ruby dashboard — WEBrick is runtime dep
- #89
load_dotenvrename,DatabaseResultmethods, SQLite WAL locking - #91 Ruby
request.paramssymbol + string keys viaIndifferentHash - #93 Ruby
/docs/*and bare/*wildcard routes - #97 Frond ternary operator
- parity: All 4 frameworks bumped to 3.11.13.
v3.11.12 (2026-04-16)
Breaking: sqlite:///X URLs are now relative to the project root (cwd), matching the documented convention. For absolute paths use four slashes (sqlite:////abs/path.db) or a Windows drive letter (sqlite:///C:/Users/app.db).
Before this release, DATABASE_URL=sqlite:///data/app.db was interpreted differently by every framework. Python/Node/Ruby tried to open /data/app.db (absolute) which crashed on macOS with OSError: [Errno 30] Read-only file system: '/data'. PHP did the same under the hood. All four frameworks now agree: three slashes = relative, four slashes = absolute.
- fix (all 4):
sqlite:///Xresolves under cwd; parent directory auto-created only when inside cwd. Absolute paths are trusted and never mkdir'd at root. - fix (python):
_ensure_foldersno longer creates a bogussrc/migrations/directory. The migration runner always looks atmigrations/at the project root — there is only one correct location. - parity (php, ruby, node): Same
sqlite:///Xparsing as Python. Dedicatedresolve_path/resolveSqlitePathhelpers in each framework so adapters consistently handle:memory:,./forms, Windows drive letters. - tests: 9 new Python tests in
TestSQLiteConnectionPath+TestProjectFolders. 4 new PHP tests inDatabaseUrlTestcovering relative/absolute/Windows/bruce-regression. 6 new Ruby specs indatabase_drivers_spec.rb :: SqliteDriver.resolve_path. Node URL tests expanded indatabase.test.tswith the full relative/absolute/Windows/:memory: matrix. - parity: All 4 frameworks bumped to 3.11.12.
Migration note: If your .env has DATABASE_URL=sqlite:///data/app.db, it will now create ./data/app.db in the project root (which is what most users actually want). If you genuinely want an absolute path, change to sqlite:////data/app.db (four slashes).
v3.11.11 (2026-04-16)
- fix (python ORM):
Field.validateno longer re-coerces values that are already the correct type. Previously, any PostgreSQL/MSSQL read of a row containing aDateTimeFieldcrashed becausedatetime(datetime_instance)raisesTypeError. The fix accepts native driver types (datetime,bytes,int,bool,float,str) without re-wrapping, and parses ISO-8601 strings intodatetimefor SQLite. Seetina4-python/plan/orm-field-validate-native-types.md. - fix (python ORM):
BooleanFieldvsIntegerFieldordering handled explicitly.BooleanField(1)still coerces toTrue,IntegerField(True)still coerces to1; no regression for either direction (bool is a subclass of int in Python). - tests (python): 10 new
TestFieldsNativeTypescases covering datetime/int/bool/float/bytes/string/ForeignKey round-trips. - tests (parity): Regression-guard "datetime round-trip on read path" tests added to PHP (
ORMV3Test), Ruby (orm_spec) and Node.js (orm.test.ts) so an equivalent bug can't creep in there later. - parity: All 4 frameworks bumped to 3.11.11.
v3.11.10 (2026-04-15)
- fix (php): Hot-reload loop — DevAdmin's polling fallback used
mt=0as the baseline, so the first poll after every page load triggeredlocation.reload(), which resetmt=0again. Loop now initialises the baseline on the first poll. - fix (php): Reload sentinel removed — PHP was the only framework recursively walking
src/and touchingsrc/.reload_sentinelon every reload POST. The sentinel lived inside the Rust CLI's watched tree and fed back into the watcher, triggering a second loop. Replaced with the same in-memory counter used by Python/Ruby/Node. - fix (php): Polling no longer starts more than once when the WebSocket reconnect retry budget is exhausted (added a
pollStartedguard). - feat (parity):
GET /__dev/api/queue/topicsandGET /__dev/api/queue/dead-lettersadded to PHP, Ruby and Node (previously only in Python). PHP queue endpoints now read from the realTina4\Queuebackend instead of returning stubs. - feat (devadmin): Refreshed
tina4-dev-admin.jsbundle (87.8 KB) across all 4 frameworks — adds the topic selector dropdown, inline payload expand/copy, and corrected version display. - tests: 4-way parity tests for hot-reload: mtime starts at 0, POST /__dev/api/reload bumps the counter, no sentinel file is written to disk, mtime is monotonic across successive reloads. Mirrored in
tina4-php/tests/DevAdminTest.php,tina4-python/tests/test_dev_admin.py,tina4-ruby/spec/dev_admin_spec.rb,tina4-nodejs/test/devAdmin.test.ts. - parity: All 4 frameworks bumped to 3.11.10.
v3.11.9 (2026-04-15)
Catch-up release covering v3.11.0 → v3.11.9 across all 4 frameworks.
- feat (websocket): Full WebSocket parity across Python/PHP/Node/Ruby —
get_client_rooms()/getClientRooms(),route()usable as decorator or direct handler registration, matching room/broadcast semantics, plus new parity tests on all 4. - feat (graphql): Input validation and field-level
@authdirectives with context threading. - feat (graphql): Auto-discovery of schemas; removed legacy DevAdmin HTML/JS in favour of the new UI.
- feat (devadmin — Python): Queue tab with topic selector, dead-letter listing and replay endpoints, inline payload expand/copy, version display.
- feat (cli): Rust CLI now owns file watching — frameworks receive
POST /__dev/api/reloadand internal watchers are disabled when launched by the Rust CLI (--managed). - fix (cli):
parseFlags/parse_flags/parseCliArgsno longer swallowhost:portor positional args after boolean flags. - fix (scss): SCSS recompilation loop fixed; output path corrected to
src/public/css/to match CLI and static serving. - fix (frond — Python): Numeric dotted index for lists (
items.0.name) now resolves correctly. - fix (router — Ruby): Bare
/*wildcard capture exposed under"*"key for parity. - fix (orm — PHP): Three data-sync bugs fixed:
load()double-fill,getPrimaryKeyValue,save()ID sync. - fix (graphql):
from_orm/fromOrmlist resolver usedselect(skip=)instead ofall(offset=). - fix (metrics): Windows backslash paths normalised to forward slashes.
- fix (app — PHP): No longer crashes on notices/deprecations in loaded files;
run()now prints the banner when starting the server directly. - chore: Example demo store ships with the repo; Windows-friendly setup;
.env.exampleand setup scripts added. - parity: All 4 frameworks bumped to 3.11.9. PHP aligned to the 3.x tag scheme on
v3.
v3.10.99 (2026-04-12)
- breaking:
auto_mapnow defaults totrue— ORM models automatically map between camelCase properties and snake_case DB columns. Setself.auto_map = falseon your model class to restore the old behaviour. - feat:
to_h(case:)parameter — passcase: 'camel'to get camelCase keys (for JSON APIs) orcase: 'snake'(default) for snake_case keys matching DB columns. All aliases (to_dict,to_hash,to_assoc,to_object) support the parameter.
- feat: Frond
replacefilter now accepts Hash args —{{ v|replace({"T": " ", "-": "/"}) }}for multiple substitutions in one call. - tests: 6 new parity tests covering
to_h(case:),auto_mapdefault,replacefilter (Hash + positional), andServiceRunnerregistration. 2,519 tests passing. - parity: All features shipped identically across Python, PHP, Ruby, Node.js.
v3.10.97 (2026-04-11)
- fix: frond.form.submit redirect handling — XHR follows 3xx redirects transparently; fixed by detecting
xhr.responseURLmismatch and navigating instead. - dep: Updated frond.min.js to v2.1.2.
- parity: All 4 frameworks bumped to 3.10.97.
v3.10.93 (2026-04-11)
- fix: Frond bracket depth tracking in
find_outside_quotes— expressions likearr[i % 2]no longer treated as top-level arithmetic. - fix: Frond subscript expression evaluation — bracket content uses
eval_expr()instead of simple variable lookup, enablingarr[loop.index0 % 2]. - fix: Frond slice with variable bounds —
items[start:end]evaluates bounds througheval_expr(). - docs: Developer skills updated — Metrics Dashboard guidance, Frond Template Parity rules,
@noauthsecurity warnings. - parity: All Frond fixes applied identically across Python, PHP, Ruby, Node.js. 2,513 tests passing.
v3.10.92 (2026-04-10)
- breaking: Rename
ErrorOverlaymethods —render→render_error_overlay,render_production→render_production_error,debug_mode?→is_debug_mode. - feat: Add
Server.handle(env)for cross-framework parity. - breaking: Rename
WebSocketBackplane.create→WebSocketBackplane.create_backplane. - feat: Add
ScssCompiler.compile,add_import_path,set_variablemethods. - feat: Add
DevAdmin.registermethod. - parity: 44/44 cross-framework features green. 2,487 tests passing.
v3.10.91 (2026-04-10)
- feat: Add parity methods —
Response.sendparams,Middleware.check/is_preflight, AI/Log aliases, MCP optional router. - breaking: Rename
from()→from_table(),error_envelope→error_response, remove aliases.
v3.10.90 (2026-04-09)
- docs: Chapter 4 (Templates) — new "Dumping Values for Debugging" section covering both
{{ x|dump }}and{{ dump(x) }}forms, their shared<pre>value.inspect</pre>output, and theTINA4_DEBUG=trueproduction gate. Filter table entry updated to reference the new section. - docs:
plan/parity/parity-template.mdupdated with a cross-framework dump helper comparison table and marks dump parity as confirmed across all 4 frameworks at v3.10.89. - chore: Version sync release — brings all 4 frameworks to the same patch version (3.10.90) so downstream users can upgrade PHP/Python/Ruby/Node.js in lockstep without hunting version mismatches.
v3.10.89 (2026-04-09)
- feat:
{{ dump(value) }}global function form added to Frond alongside the existing{{ value|dump }}filter. Both call a singleTina4::Frond.render_dumphelper and produce identical output (<pre>value.inspect</pre>HTML-escaped). - security: Dump is now gated on
TINA4_DEBUG=true. In production (env var unset orfalse) both the filter and function silently return an emptySafeString. This prevents accidental leaks of internal state, object shapes, and sensitive values into rendered HTML when a developer leaves a{{ dump(x) }}call in a template. - test: 3 new
spec/frond_spec.rbexamples covering debug-mode output, production silencing, function/filter parity, and function-form production silencing.
v3.10.86 (2026-04-09)
- feat:
foreign_key_fieldDSL auto-wires both sides of a foreign key relationship. Declaringforeign_key_field :user_id, references: Userregisters the integer column, callsbelongs_to :useron the declaring class, and callshas_many :postson the referenced class. Supportsrelated_name:for custom has-many names and deferred wiring via a module-level registry so the referenced class can be defined either before or after the declaring one. - feat: Cross-framework parity — same FK auto-wiring semantics now available in Python (
ForeignKeyField), PHP ($foreignKeys), and Node.js (type: "foreignKey") - docs: Chapter 6 (ORM) updated with a new "foreign_key_field — Auto-Wired Relationships" section
v3.10.85 (2026-04-09)
- Version bump for parity with Python and PHP releases
v3.10.84 (2026-04-09)
- fix: Router/middleware was setting
request.user/request.auth/ auth payload totrue(boolean) instead of the actual JWT payload aftervalid_token?was changed to return bool — any code readingrequest.user["sub"]etc. would have failed silently or crashed - fix: CSRF middleware was not correctly rejecting invalid tokens (nil check on bool result always passed)
- add: Headless routing auth payload integration tests to prevent regression
v3.10.83 (2026-04-08)
- feat: WebSocket rooms —
join_room,leave_room,broadcast_to_room,room_count,get_room_connections - feat: Queue signature parity — instance-scoped
push/pop/retry, no topic params on public methods - feat: Auth cleanup — canonical
getToken/validTokenmethods - Full parity across Python, PHP, Ruby, Node.js
v3.10.70 (2026-04-06)
- New: SSE (Server-Sent Events) support via
response.stream()— pass a generator, framework handles chunked transfer encoding, keep-alive, andtext/event-streamcontent type - New: Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4 Ruby follows semantic versioning. The major version (3) marks the ground-up rewrite from v2. Minor versions (3.1, 3.2, etc.) introduce features and non-breaking API additions. Patch versions carry bug fixes and small improvements.
This chapter covers every v3 release from the initial launch through the current stable line. Each section groups releases by minor version, highlights the changes that affect your code, and shows migration steps for anything that breaks.
v3.10.68 (2026-04-03) — Full Parity Release
- 100% API parity across Python, PHP, Ruby, Node.js — 30+ issues fixed
- ORM: save() returns self/false, arrays not tuples, toDict/toAssoc, scope registers method, where()/all() on Node, count() on PHP
- Auth: expires_in minutes, PBKDF2 260k, env SECRET fallback, API key fallback
- Session: dual-mode flash(), get_flash, cookieHeader, getSessionId
- Database: execute() bool/DatabaseResult, get_last_id/get_error, getColumns, cacheStats
- Request/Response: files dict, query, cookies, contentType, xml(), callable
- Queue: consume() poll_interval
- WebSocket: event naming, connection properties
- GraphQL: schema_sdl() + introspect() on all 4
- Events: emitAsync() on all 4
- i18n: zero-dep YAML support
v3.10.67 (2026-04-03)
- load() is now an instance method —
model.load(sql, params)calls select_one internally, populates the instance, returnstrue/false. Usefind(id)for PK lookups - api.upload() added to tina4-js — sends FormData with Bearer token auth for multipart file uploads
- ORM CLAUDE.md rewrite — all method stubs now match actual API signatures
- File upload docs —
request.filesformat documented in CLAUDE.md
v3.10.66 (2026-04-03)
- Metrics file detail fix — clicking bubbles in framework scanning mode now resolves paths correctly via scan root tracking
v3.10.65 (2026-04-03)
- Metrics 3-stage test detection — filename, path, and content matching
- Metrics framework mode — scans framework source with correct relative paths
- tina4 console — interactive REPL with framework loaded
- tina4 env — interactive environment configuration
- Brand — "TINA4 — The Intelligent Native Application 4ramework"
- Quick references — 36 sections, DotEnv API documented
- 37 chapters — 7 new (Events, Localization, Logging, API Client, WSDL/SOAP, DI Container, Service Runner)
- MongoDB + ODBC adapters across all 4 frameworks
- Pagination standardized — limit/offset primary, merged dual-key response
- Port kill-and-take-over on startup
v3.10.60 (2026-04-03)
- tina4 console — already existed, now matches Python/PHP/Node API
- tina4 env — interactive environment configuration
- Brand update — "TINA4 — The Intelligent Native Application 4ramework"
- Imperative relationships — query_has_one/many/belongs_to for ad-hoc queries
- Port kill-and-take-over — default port always reclaimed
- MongoDB adapter (mongo gem), ODBC adapter (ruby-odbc gem)
- Pagination standardized — limit/offset primary, merged dual-key response
- CORS fix — returns empty string when origin not allowed
v3.10.57 (2026-04-02)
- MongoDB adapter —
Database.new("mongodb://host:port/db"), requiresgem install mongo - ODBC adapter —
Database.new("odbc:///DSN=MyDSN"), requiresgem install ruby-odbc - Imperative relationships —
query_has_one/query_has_many/query_belongs_to - Pagination standardized — limit/offset primary, merged dual-key to_paginate response
- Test port at +1000 — user testing port (e.g. 8147) stable, no hot-reload
- CORS fix — returns empty string when origin not allowed
- ORM DATABASE_URL discovery — auto-connect from env
- 108 features at 100% parity, 2,333 tests
v3.10.54 (2026-04-02)
- Auto AI dev port — second WEBrick on port+1 with no-reload when TINA4_DEBUG=true
- TINA4_NO_RELOAD env var + --no-reload CLI flag
- CORS fix — returns empty string when origin not allowed (not *)
- ORM DATABASE_URL discovery — auto-connect from env
- QueryBuilder docs — added to ORM chapter
v3.10.48 — April 2, 2026
Bug Fixes
Puma requires --production flag — Puma no longer auto-selected when TINA4_DEBUG=false. Use tina4ruby serve --production to enable Puma. Added FakeData (46), Gallery (16), and DevReload (37) tests.
v3.10.46 — April 1, 2026
Test Coverage
344 new tests added across cache (56), ORM (19), Frond (28), database drivers (85), auth (21), SCSS (10), dotenv (30), queue backends (10), migration (10), session handlers (11), router (14), log (13), CSRF middleware (17). Fixed session handler DB key bug (symbol vs string). Ruby now at 2,274 tests with full parity across all 49 core areas.
v3.10.45 — April 1, 2026
Notes
Version bump for parity with PHP CLI serve fix. No Ruby-specific changes.
v3.10.44 — April 1, 2026
New Features
Database tab redesign — Split-screen layout with tables navigation on the left and query editor + results on the right. Click-to-select table highlighting.
Copy CSV / Copy JSON — Copy query results to clipboard in CSV or JSON format.
Paste data — Modal for pasting JSON arrays or CSV/tab-separated data. Auto-generates INSERT statements targeting the selected table, or prompts for a new table name with CREATE TABLE generation. SQL input passes through unchanged.
Multi-statement execution — Query runner handles batched SQL statements in a transaction.
Database badge on load — Table count shows immediately without clicking the Database tab.
Star wiggle animation — Empty star (☆) on the landing page with delayed wiggle animation at random intervals.
Bug Fixes
Default port — Ruby default port set to 7147 (PHP=7145, Python=7146, Ruby=7147, Node=7148).
SQLite LIMIT fix — Prevents double-LIMIT errors in the database browser.
browseTable quote escaping — Fixed table name click handlers.
ORM table name pluralization — Fixed default table name resolution. Table names are now pluralized by default (adding "s" suffix), only skipping when ORM_PLURAL_TABLE_NAMES is explicitly set to false.
QueryBuilder closed-connection detection — ensure_db! now checks if the resolved database connection is still open, raising a proper error instead of crashing with ArgumentError: prepare called on a closed database.
Metrics directory validation — quick_metrics and full_analysis now check directory existence before _resolve_root fallback, so missing-directory errors are raised correctly.
Test Coverage
88 new tests added (DevMailbox 40, Static files 18, CLI scaffolding 30), plus 13 v3.10.44 feature specs and 60 pre-existing ORM/metrics bug fixes. 1,913 tests passing, 0 failures.
v3.10.40 — April 1, 2026
Bug Fixes
Dev overlay version check — Fixed misleading "You are up to date" message when running a version ahead of what's published on RubyGems. The overlay now shows a purple "ahead of RubyGems" message. Also added a breaking changes warning (red banner with changelog link) when a major or minor version update is available.
v3.10.39 — April 1, 2026
New Features
Container.singleton(name, &block) — Register a memoized factory. The block is called once on first resolve() and the same instance is returned on all subsequent calls. register() with a block is now always transient (new instance per call), matching Python's behavior.
Tina4::Container.singleton(:db) { Tina4::Database.new(ENV["DATABASE_URL"]) }
db1 = Tina4::Container.resolve(:db) # creates instance
db2 = Tina4::Container.resolve(:db) # same instanceRouter.match(method, path) — primary route lookup (replaces find_route; consistent with Python, PHP, Node.js). Router.add(method, path, handler) — primary imperative registration (replaces add_route; all convenience methods delegate to this).
Router.get_routes and Router.list_routes — explicit listing methods (remove ambiguous routes alias).
AI installer — ai_spec.rb and smoke tests updated to reflect the menu-based API (installed?, install_selected, install_all, generate_context).
v3.10.38 -- April 1, 2026
Code Metrics & Bubble Chart
The dev dashboard (/__dev) now includes a Code Metrics tab with a PHPMetrics-style bubble chart visualization. Files appear as animated bubbles sized by LOC and colored by maintainability index. Click any bubble to drill down into per-function cyclomatic complexity.
The metrics engine uses Ripper (Ruby stdlib) for zero-dependency static analysis covering cyclomatic complexity, Halstead volume, maintainability index, coupling, and violation detection. File analysis is sorted worst-first. Results are cached for 60 seconds.
AI Context Installer
tina4ruby ai now presents a simple numbered menu instead of auto-detection. Select tools by number, comma-separated or all. Already-installed tools show green. Generated context includes the full skills table.
Dashboard Improvements
Full-width layout, sticky header/tabs, full-screen overlay.
Cleanup
Removed demo/ directory. Removed old plan/ spec documents, replaced with PARITY.md and TESTS.md. Central parity matrix added to tina4-book.
v3.10.x -- Previous Releases
Released: March 28 -- 30, 2026
The v3.10 line is the most active release series. It delivered Auto-CRUD, ORM transaction safety, Frond template engine hardening, and full cross-language parity with the Python, PHP, and Node.js implementations.
v3.10.29 -- Version Parity (March 30)
Version parity release. All four Tina4 frameworks now share the same version number and feature set.
v3.10.27 -- Frond Macro HTML Escaping Fix (March 30)
Bug fix: Macro output was HTML-escaped when used inside {{ }} expressions. Characters like <, >, and " rendered as <, >, &quot; instead of raw HTML. Nested macro calls double-escaped.
# BEFORE (broken): macro output escaped
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# Template: {{ my_macro() }}
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# Rendered: <div class="card">...</div>
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# AFTER (fixed): macro output treated as safe HTML
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# Template: {{ my_macro() }}
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# Rendered: <div class="card">...</div>
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.jsv3.10.25 -- ORM Transaction Fix (March 30)
Bug fix: ORM save and delete called commit without an active transaction on SQLite. This raised cannot commit -- no transaction is active errors.
# BEFORE (broken):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
user = User.new(name: "Alice")
user.save # => RuntimeError: cannot commit
# AFTER (fixed): save/delete wrap operations in a transaction block
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
user = User.new(name: "Alice")
user.save # => works on all database enginesv3.10.22 -- Unique Form Tokens (March 30)
Form tokens now include a nonce in the JWT payload. Each token is unique per form render, which prevents replay attacks.
# In your Frond template:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
<input type="hidden" name="formToken" value="{{ formTokenValue() }}">v3.10.18 -- Frond Ternary Parser Fix (March 29)
Bug fix: The Frond template ternary/inline-if parser failed on quoted strings containing special characters.
# BEFORE (broken):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# {{ status == "active" ? "Yes" : "No" }} => parse error
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# AFTER (fixed):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# {{ status == "active" ? "Yes" : "No" }} => "Yes"
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.jsv3.10.16 -- Template Filters: to_json, js_escape (March 28)
Three new Frond template filters for working with data in JavaScript contexts.
# Convert a Ruby hash to JSON inside a template:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
<script>
const data = {{ user|to_json }};
const name = "{{ user.name|js_escape }}";
</script>v3.10.15 -- Replace Filter Backslash Fix (March 28)
Bug fix: The |replace filter mishandled backslash characters in replacement strings.
{# Before (broken) — backslash produced corrupted output #}
{{ "hello\\world"|replace("\\\\", "/") }}
{# rendered: helo/world (ate a character) #}
{# After (fixed) — backslash escaping works correctly #}
{{ "hello\\world"|replace("\\\\", "/") }}
{# renders: hello/world #}v3.10.14 -- get_next_id() (March 28)
Pre-generate the next primary key before inserting a record. The method detects your database engine and uses the correct sequence or auto-increment mechanism.
next_id = User.get_next_id
user = User.new(id: next_id, name: "Alice")
user.savev3.10.13 -- ORM Auto-Commit on Write (March 28)
Write operations (save, delete) now auto-commit by default. No more forgotten commit calls leaving data uncommitted.
v3.10.12 -- Session GC and NATS Backplane (March 28)
- Session garbage collection runs on a configurable interval
- NATS added as a WebSocket backplane option alongside Redis
v3.10.11 -- Frond Variable Key Access (March 28)
Bug fix: Accessing a hash value with a variable key (dict[variable_key]) failed in Frond templates.
# BEFORE (broken):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# {% set key = "name" %}
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# {{ user[key] }} => empty
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# AFTER (fixed):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# {% set key = "name" %}
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# {{ user[key] }} => "Alice"
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.jsv3.10.10 -- Firebird Migration Runner Fixes (March 28)
Firebird migrations now use generators and VARCHAR instead of AUTOINCREMENT and TEXT. The migration tracking table uses a proper Firebird sequence (GEN_TINA4_MIGRATION_ID).
v3.10.6 -- WSDL/SOAP Rewrite (March 28)
Complete rewrite of the WSDL/SOAP module. Frond templates now support dotted function names in expressions.
v3.10.5 -- Frond Quote-Aware Operator Matching (March 28)
Bug fix: Operators inside quoted strings were incorrectly parsed as expression operators. The Frond engine now respects quote boundaries.
v3.10.4 -- Auto-CRUD REST Endpoint Generator (March 28)
Generate a complete CRUD interface from a single method call. The generator creates searchable, sortable, paginated HTML tables with create/edit/delete modals, plus REST API routes for POST, PUT, and DELETE.
Tina4::Router.get("/admin/users") do |request, response|
Tina4::CRUD.to_crud(request, model: User, fields: [:name, :email, :role])
endv3.10.2 -- Frond Hash Method Calls (March 28)
Frond templates can now call methods on Hash and object values inside expressions.
v3.10.1 -- autoMap and Case Conversion (March 28)
auto_mapclass attribute added to ORM for cross-language API parity (no-op in Ruby sincesnake_caseis native)Tina4.snake_to_camel("my_field")returns"myField"Tina4.camel_to_snake("myField")returns"my_field"
v3.10.0 -- Optimized For-Loops (March 28)
The Frond template engine rewrote its for-loop renderer. Templates with large iteration counts render faster.
v3.9.x
Released: March 26 -- 27, 2026
v3.9.0 -- QueryBuilder, Sessions, Path Injection (March 26)
Three features arrived together.
QueryBuilder. A fluent SQL builder that integrates with the ORM.
# Through the ORM:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
admins = User.query
.where("role = ?", ["admin"])
.order_by("name")
.limit(10)
.get
# Standalone:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
rows = Tina4::QueryBuilder.from("users")
.where("active = ?", [true])
.select("name", "email")
.getThe builder supports where, or_where, join, left_join, group_by, having, order_by, limit, first, count, exists, and to_sql.
Path parameter injection. Route handlers receive path parameters as named arguments.
Tina4::Router.get("/users/{id:int}") do |request, response, id|
user = User.find(id)
response.json(user.to_hash)
endAuto-start sessions. Every route handler has access to request.session with zero configuration. The session API includes get, set, delete, has, clear, destroy, save, regenerate, flash, get_flash, and all.
v3.9.1 -- Security Defaults (March 27)
Breaking change: POST, PUT, PATCH, and DELETE routes now require authentication by default.
# BEFORE (v3.8.x): all routes open
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Router.post("/api/users") do |request, response|
# anyone could call this
end
# AFTER (v3.9.1): unauthenticated requests get 401
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
# To allow public access, add .public:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Router.post("/api/users").public do |request, response|
# open to all
endThis release also added:
- CSRF middleware with session-bound form tokens
- Standardized environment variables for CORS headers, session TTL, token limits
- Queue parity:
pushwith priority/delay,size(status),message.retry
v3.9.2 -- NoSQL QueryBuilder, WebSocket Backplane (March 27)
- QueryBuilder works with MongoDB
- WebSocket backplane support for multi-process deployments
SameSite=Laxset as the default cookie policy
v3.8.x
Released: March 25 -- 26, 2026
v3.8.0 -- Base64 Filters, Template Cache (March 25)
base64encodeandbase64decodefilters in Frond templates- Production template cache: single filesystem scan at startup, O(1) lookups after
v3.8.1 -- Security Headers Middleware (March 25)
A built-in middleware that sets X-Frame-Options, Strict-Transport-Security, Content-Security-Policy, and X-Content-Type-Options on every response.
# In your .env:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
TINA4_MAX_UPLOAD_SIZE=10485760 # 10 MB (default)Upload size limits and input validation also landed in this release.
v3.8.2 -- Connection Pooling (March 26)
Database connections now pool. Pass pool: N to the constructor for round-robin, mutex-protected pooling.
db = Tina4::Database.new("sqlite://data.db", pool: 5)v3.8.3 -- Claude Code Commands (March 26)
Seventeen .claude/commands/ slash commands shipped for AI-assisted development.
v3.8.7 -- Benchmark and Stability (March 26)
- Keyword argument fix for
run!():port:,host:, anddebug:no longer crash the environment loader - Updated benchmarks against Roda, Sinatra, and Rails
v3.7.x
Released: March 25, 2026
v3.7.0 -- Template Auto-Serve, Firebird Migrations (March 25)
The framework serves index.html or index.twig from src/templates/ at / without a route definition. User-registered GET / routes take priority.
Firebird migrations now check RDB$RELATION_FIELDS before executing ALTER TABLE ADD. Columns that exist are skipped.
v3.7.1 -- Full Template Auto-Serve (March 25)
Any .twig or .html file in src/templates/ is now browsable by URL path. /hello serves src/templates/hello.twig. Production mode caches the lookup table at startup.
v3.6.x
Released: March 25, 2026
v3.6.0 -- Architectural Parity (March 25)
Breaking change: fetch(skip:) is replaced by fetch(offset:). No alias.
# BEFORE (v3.5.x):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
users = User.fetch(limit: 10, skip: 20)
# AFTER (v3.6.0):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
users = User.fetch(limit: 10, offset: 20)Other changes:
- Source directories follow the
src/prefix convention across all languages TINA4_LOCALEis the only supported locale environment variable (other names removed)- Migration file paths standardized to
src/migrations/
v3.5.x
Released: March 24, 2026
v3.5.0 -- Bundled Frontend, Swagger CRUD, Middleware (March 24)
tina4js.min.js(13.6 KB) ships inside the gem. The reactive frontend library loads without a CDN or npm install- Auto-CRUD routes now include Swagger metadata
- Middleware standardized to
before_*andafter_*naming with three built-in middlewares
v3.4.x
Released: March 24, 2026
v3.4.0 -- Auth, WebSocket, DatabaseResult (March 24)
Breaking change: Auth method names changed. The old names remain as aliases.
# BEFORE (v3.3.x):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
token = auth.create_token(payload)
valid = auth.validate_token(token)
# AFTER (v3.4.0 -- preferred):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
token = auth.get_token(payload)
valid = auth.valid_token(token)
# Old names still work but are deprecated.
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.jsHS256 authentication. Set TINA4_AUTH_SECRET in your .env and auth uses HS256. Provide RSA key files and it uses RS256. The framework picks the right algorithm.
# .env for HS256:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
TINA4_AUTH_SECRET=my-secret-key
# .env for RS256:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
TINA4_AUTH_PRIVATE_KEY=keys/private.pem
TINA4_AUTH_PUBLIC_KEY=keys/public.pemBug fix: Base64url padding in HS256 tokens caused validation failures. Fixed.
WebSocket improvements:
Router.websocket("/ws/chat")for route-based WebSocket handlers- Path-scoped broadcast: messages sent to
/ws/chatreach only clients connected to that path send_textrenamed tosendonWebSocketConnection(send_textkept as alias)
DatabaseResult enhancements:
columnsreturns column namescolumn_infoprovides schema metadata (type, nullable, default) on demandto_paginateformats results for paginated responses
Frond template additions:
- Ternary-with-filter:
{{ value ? value|upper : "default" }} data_urifilter for inline file display in templates
v3.3.x
Released: March 24, 2026
v3.3.0 -- Queue API, Route Chaining (March 24)
Breaking change: Producer and Consumer classes removed. Use queue.produce() and queue.consume() directly.
# BEFORE (v3.2.x):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
producer = Tina4::Producer.new(queue)
producer.send(message)
consumer = Tina4::Consumer.new(queue)
consumer.listen { |msg| handle(msg) }
# AFTER (v3.3.0):
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
queue.produce("channel", { data: "payload" })
queue.consume("channel") do |job|
handle(job)
job.complete
endRoute chaining. Mark routes as authenticated or cached with chainable modifiers.
Tina4::Router.get("/dashboard").secure do |request, response|
response.html("<h1>Dashboard</h1>")
end
Tina4::Router.get("/static-page").cache(ttl: 3600) do |request, response|
response.html("<h1>Cached for one hour</h1>")
endOther additions:
- MongoDB queue backend
- Database session handler for full backend parity
- Valkey added to session handler options
- Migration parity: advanced SQL splitting, status tracking, rollback via CLI
- Auto-increment port if the default is in use; browser opens on startup
v3.2.x
Released: March 23, 2026
v3.2.0 -- Flexible Route Handlers (March 23)
Route handlers now accept zero, one, or two parameters. The framework detects what your block expects and provides the right objects.
# Zero params -- just return a response:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Router.get("/health") { "OK" }
# One param -- response only:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Router.get("/hello") { |response| response.html("Hello") }
# Two params -- request and response:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Router.get("/echo") do |request, response|
response.json({ body: request.body })
end
# Named :request or :req -- single param receives the request:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Router.post("/submit") { |request| process(request.body) }Bug fix: The 500 error overlay crashed because it did not receive the Rack environment. Fixed.
v3.1.x
Released: March 21 -- 22, 2026
v3.1.0 -- ORM Relationships, Caching, Queues (March 22)
The largest feature release after the initial launch. Fourteen capabilities landed in one version.
ORM relationships. Define has_many, has_one, and belongs_to with eager loading.
class User < Tina4::ORM
has_many :posts
has_one :profile
end
class Post < Tina4::ORM
belongs_to :user
end
user = User.find(1)
user.posts # => eager-loaded array of Post objectsCaching. Switch between memory, Redis, and file cache by setting one environment variable.
# .env:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
TINA4_CACHE=redis
TINA4_CACHE_REDIS_URL=redis://localhost:6379
# Code stays the same:
## v3.10.70 (2026-04-06)
- **New:** SSE (Server-Sent Events) support via `response.stream()` — pass a generator, framework handles chunked transfer encoding, keep-alive, and `text/event-stream` content type
- **New:** Chapter 24 added to documentation: Server-Sent Events
- Feature count: 45 (was 44)
- Full parity across Python, PHP, Ruby, Node.js
Tina4::Cache.set("key", "value", ttl: 300)
Tina4::Cache.get("key")Database query caching. Set TINA4_DB_CACHE=true for transparent query result caching.
Queue system. Switch between SQLite, RabbitMQ, and Kafka via .env without changing code.
Messenger. Unified messaging driven by environment configuration.
Scaffolding. tina4 generate model User, tina4 generate route api/users, tina4 generate migration create_users, tina4 generate middleware auth.
Frond template engine. raw/endraw blocks and from imports.
Performance. Frond pre-compilation caches parsed tokens. File rendering runs faster.
Other additions:
- Production server auto-detection (Puma, cluster mode)
- GitHub Actions CI/CD
- Error pages: clean 404/500/403 without branding
numeric_fieldtype in ORMtruthy?()helper method- Log rotation
v3.1.1 -- DevMailbox Fix (March 22)
Bug fix: DevMailbox timestamp precision was insufficient for reliable sort ordering.
v3.1.2 -- Documentation Fixes (March 22)
README code examples updated to match the actual v3 API. Quick start guide added.
v3.0.x
Released: March 21, 2026
v3.0.0 -- Initial Release (March 21)
The ground-up rewrite. Zero gem dependencies. Everything the framework needs -- HTTP server, template engine, ORM, migrations, auth, queue, GraphQL, WebSocket, WSDL -- ships inside a single gem.
Core features:
- Rack-based HTTP server (compatible with Puma, Thin, WEBrick)
- Frond template engine (Twig-compatible syntax)
- ORM with support for SQLite, PostgreSQL, MySQL, MSSQL, and Firebird
- JWT authentication (RS256)
- Queue system
- GraphQL endpoint
- WebSocket server
- WSDL/SOAP service generation
- DevAdmin dashboard with developer tooling
- AI coding tool integration (auto-detect and install context for seven tools)
- Full test suite passing
Quick start:
require "tina4"
Tina4::Router.get("/") { |request, response| response.html("<h1>Hello Tina4!</h1>") }
Tina4::App.new.rungem install tina4-rubyThe server starts on port 7147 by default. Set host: "0.0.0.0" for Docker deployments.
Pre-Release (v0.x)
Released: March 18, 2026
Versions v0.4.0 through v0.5.2 were development previews. They established the gem structure and basic routing but lacked the ORM, template engine, and queue system. If you used a v0.x release, upgrade directly to v3.0.0 -- there is no migration path from v0.x.