Skip to content

Bind go-ruby-grape, go-ruby-rubocop, go-ruby-xslt and go-ruby-activerecord into rbgo as native modules#102

Merged
tannevaled merged 5 commits into
mainfrom
bind/batch7
Jul 2, 2026
Merged

Bind go-ruby-grape, go-ruby-rubocop, go-ruby-xslt and go-ruby-activerecord into rbgo as native modules#102
tannevaled merged 5 commits into
mainfrom
bind/batch7

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Binds batch 7 of the go-ruby-* stdlib satellites into rbgo as native, CGO=0 modules. SQL rendering / linting / templating stay deterministic Go; the one execution seam (ActiveRecord) is wired to the already-bound go-ruby-sqlite3.

Modules

require "grape"Grape::Router (route matching + path params), Grape::Validator (presence/type/values param validation), Grape::Formatter (json/txt), and the Grape::Exceptions tree, over go-ruby-grape. Endpoint-block execution and the Rack env are documented host seams.

require "rubocop"RuboCop::Runner#inspect/#autocorrect and RuboCop::Cop::Offense, over go-ruby-rubocop (which parses with go-ruby-parser). Reports offenses and applies autocorrections.

require "nokogiri" (XSLT)Nokogiri::XSLT(stylesheet) / Nokogiri::XSLT::Stylesheet#transform + #apply_to, over go-ruby-xslt, extending the existing Nokogiri surface.

require "active_record"ActiveRecord::Base/Model/Relation/Record/Errors/Schema over go-ruby-activerecord:

  • ActiveRecord::Model.new(name, table) { column …; validates_* …; belongs_to/has_many … } builds a mapped model whose #all/#where/#select/#order/#group/#having/#joins/#limit/#offset/#distinct/#not return chainable relations; #to_sql renders ActiveRecord::Relation#to_sql byte-faithfully.
  • ActiveRecord::Base.establish_connection(database:/path) opens a real go-ruby-sqlite3 database and installs it as the process adapter (the executor seam), so a relation's #to_a/#count/#exists?/#first/#pluck and Model#create run actual SQL against sqlite3. #connection exposes the SQLite3::Database.
  • Record carries #[]/#[]=/#attributes/#valid?/#errors/#changed?; Errors is the ActiveModel::Errors shape; Schema.add_column_sql/add_index_sql are the DDL generators. The ActiveRecordError/StatementInvalid/RecordInvalid/ConnectionNotEstablished tree matches the gem.

Coverage / portability

100% coverage on all four bindings — Ruby-level tests for every binding line and error arm, white-box unit tests for the Go-only mapper arms (arValueToRuby int/bool/fallback, AdapterName, arInt). The ActiveRecord bad-connection test builds its path with filepath.ToSlash for Windows safety; no timezone- or shell-dependent assertions. gofmt/go vet clean; the exact -race -coverpkg gate reports total 100.0%.

🤖 Generated with Claude Code

tannevaled and others added 5 commits July 2, 2026 22:05
Nokogiri::XSLT(str)/.parse -> Stylesheet#transform (Nokogiri::XML::Document)
and #apply_to/#serialize (serialised String), over go-ruby-xslt's pure-Go
XSLT 1.0 engine on the go-ruby-nokogiri DOM. Stylesheet params passed as a
Hash. Rides the existing "nokogiri" require feature. 100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RuboCop::Runner.new([config])#inspect(source[, path]) -> [Cop::Offense]
(cop_name/message/severity/line/column/location/correctable?) and
#autocorrect -> corrected String, over go-ruby-rubocop's pure-Go core cop
set on the go-ruby-parser AST. RuboCop::Config.new/.parse(yml); malformed
yml raises RuboCop::Error. File-walking is the documented host seam.
100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Grape::Router.new #get/#post/#put/#patch/#delete/#head + #match -> Match
(#status/#ok?/#params/#route/#allowed); Grape::Validator.new { params DSL }
#validate coerces or raises Grape::Exceptions::ValidationErrors;
Grape::Formatter #format/#json/#txt/#xml + Grape.mime_for/.default_status,
over go-ruby-grape's deterministic router/coercion/formatting. Endpoint-block
exec + Rack env are documented host seams. 100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eries via go-ruby-sqlite3

require "active_record" installs ActiveRecord::Base/Model/Relation/Record/
Errors/Schema over the go-ruby-activerecord library:

- ActiveRecord::Model.new(name, table) { column …; validates_* …; belongs_to/
  has_many … } builds a mapped model whose #all/#where/#select/#order/#group/
  #having/#joins/#limit/#offset/#distinct/#not return chainable relations, and
  #to_sql renders ActiveRecord::Relation#to_sql byte-faithfully. #build/#new
  make validating Record instances; #insert_sql renders the INSERT.
- ActiveRecord::Base.establish_connection(database:/path) opens a real
  go-ruby-sqlite3 database and installs it as the process adapter (the executor
  seam), so a relation's #to_a/#count/#exists?/#first/#pluck and Model#create
  run actual SQL against sqlite3; #connection exposes the SQLite3::Database.
- Record carries #[]/#[]=/#attributes/#valid?/#errors/#changed?; Errors is the
  ActiveModel::Errors shape (#full_messages/#messages/#[]/#empty?/#count).
- ActiveRecord::Schema.add_column_sql/add_index_sql are the deterministic DDL
  generators. The ActiveRecordError/StatementInvalid/RecordInvalid/
  ConnectionNotEstablished error tree matches the gem.

SQL rendering + validation are deterministic Go; execution is the sqlite3
adapter. 100% coverage: Ruby-level tests for every binding line and error arm,
white-box tests for the Go-only mapper arms (arValueToRuby int/bool/fallback,
AdapterName, arInt). The bad-connection test builds its path with
filepath.ToSlash for Windows safety; no timezone-dependent assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled tannevaled merged commit 6dfc9c8 into main Jul 2, 2026
9 checks passed
@tannevaled tannevaled deleted the bind/batch7 branch July 2, 2026 21:12
tannevaled added a commit that referenced this pull request Jul 2, 2026
…require

PR #102 landed the go-ruby-activerecord binding on main, so
`require "active_record"` now succeeds — but the binding does not yet implement
`ActiveRecord::Schema.define`, so stage 4b's route hard-failed with
`NoMethodError: undefined method 'define' for Module`.

Feature-detect the specific ORM capability the route uses, mirroring the
skip-until-present pattern of the Sinatra stage: a Ruby probe runs the exact
chain (establish_connection + Schema.define + create! + where(...).order.to_a)
inside a rescue and reports "READY" only when it all runs clean, else
"GAP: <ExceptionClass>: <message>". Stage 4b skips-with-gap when the ORM path is
incomplete and flips to a hard assertion automatically once the binding gains
the methods.

Recorded gap: NoMethodError: undefined method 'define' for Module
(ActiveRecord::Schema lacks .define).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Jul 2, 2026
…#103)

* test(conformance): add reference-app RUN-conformance harness for web apps

Prove rbgo can *run* real Ruby web apps end-to-end (run, not just parse) by
driving the Rack call(env) contract directly with a synthetic Rack env — no
TCP socket, no network. scripts/conformance/webapp/ stages real .rb apps
through the public ruby.Run API and asserts the deterministic response:

  Stage 1  Pure Rack lambda                     -> GREEN
  Stage 2  Rack + ERB view                      -> GREEN
  Stage 3  Sinatra DSL (require sinatra/base)   -> GAP (no sinatra binding)
  Stage 4  sqlite3 + ERB/JSON data route        -> GREEN
  Stage 4b ActiveRecord ORM data route          -> GAP (go-ruby-activerecord, PR #102)

A data-backed route (in-memory sqlite3 seed + query + ERB/JSON render) is
servable through rbgo today via the raw sqlite3 binding (stage 4). The Sinatra
and ActiveRecord stages feature-detect the require: they assert the real
response when the binding is present and otherwise record the exact missing
feature, so the suite stays green now and lights up automatically when the
binding lands.

Test-only package: no new production code, so the 100%-coverage gate
(coverpkg=./internal/...) is unaffected. Apps embedded via go:embed so the
suite is CWD-independent under the coverage job, on Windows, and under qemu on
every 64-bit target. Deterministic and TZ-independent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(conformance): probe AR ORM capability in stage 4b, not just the require

PR #102 landed the go-ruby-activerecord binding on main, so
`require "active_record"` now succeeds — but the binding does not yet implement
`ActiveRecord::Schema.define`, so stage 4b's route hard-failed with
`NoMethodError: undefined method 'define' for Module`.

Feature-detect the specific ORM capability the route uses, mirroring the
skip-until-present pattern of the Sinatra stage: a Ruby probe runs the exact
chain (establish_connection + Schema.define + create! + where(...).order.to_a)
inside a rescue and reports "READY" only when it all runs clean, else
"GAP: <ExceptionClass>: <message>". Stage 4b skips-with-gap when the ORM path is
incomplete and flips to a hard assertion automatically once the binding gains
the methods.

Recorded gap: NoMethodError: undefined method 'define' for Module
(ActiveRecord::Schema lacks .define).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: clarify stage 4b probes the AR ORM chain, not just the require

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: align stage 4b fixture header with the AR ORM capability probe

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant