Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ require (
github.com/go-ruby-ostruct/ostruct v0.0.0-20260630080835-69fcd87e76bf
github.com/go-ruby-parser/parser v0.0.0-20260630055353-a831e712f1fd
github.com/go-ruby-pathname/pathname v0.0.0-20260629151955-d8d2c4e5f81b
github.com/go-ruby-pg/pg v0.0.0-20260702135906-e5650264cc5d
github.com/go-ruby-prettyprint/prettyprint v0.0.0-20260629152429-60a380e82d7d
github.com/go-ruby-prime/prime v0.0.0-20260629151744-aa26747e21a8
github.com/go-ruby-pstore/pstore v0.0.0-20260630081017-0dd55a12f94e
github.com/go-ruby-public-suffix/public-suffix v0.0.0-20260630151503-f308d4002444
github.com/go-ruby-redis/redis v0.0.0-20260701125752-5de216f6ad92
github.com/go-ruby-regexp/regexp v0.0.0-20260701043846-fa3d6604e1ee
github.com/go-ruby-resolv/resolv v0.0.0-20260629153520-df410a5796ac
github.com/go-ruby-rexml/rexml v0.0.0-20260629154021-5fb0f287ee8b
Expand All @@ -68,6 +70,7 @@ require (
github.com/go-ruby-rspec/rspec v0.0.0-20260702145830-12badaeb0d75
github.com/go-ruby-scanf/scanf v0.0.0-20260629150220-414dbb31c386
github.com/go-ruby-securerandom/securerandom v0.0.0-20260630081933-3f81ff7d7fb0
github.com/go-ruby-sequel/sequel v0.0.0-20260702151352-66413b601977
github.com/go-ruby-set/set v0.0.0-20260630204346-ab6a8a92a910
github.com/go-ruby-shellwords/shellwords v0.0.0-20260629114104-e941e4210818
github.com/go-ruby-slim/slim v0.0.0-20260701141524-ade9ddf6aec4
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ github.com/go-ruby-parser/parser v0.0.0-20260630055353-a831e712f1fd h1:4JhMDqqpX
github.com/go-ruby-parser/parser v0.0.0-20260630055353-a831e712f1fd/go.mod h1:NRbX4dYcQ6CgcaSaMmu1dh95QuKGq+RdenUoWU73214=
github.com/go-ruby-pathname/pathname v0.0.0-20260629151955-d8d2c4e5f81b h1:/ZN1eLOg0oH4wF8qmd6NOyB4ENYthx6wFwOpZiPhRIE=
github.com/go-ruby-pathname/pathname v0.0.0-20260629151955-d8d2c4e5f81b/go.mod h1:IvW5q5Kyl5l3hrYwiHylOEmk5hpGSo7XqcmUXBHF5H0=
github.com/go-ruby-pg/pg v0.0.0-20260702135906-e5650264cc5d h1:JKIZD+pJQ64/nla4DHdzMLnc4/v45MyYwnjACwS6dyM=
github.com/go-ruby-pg/pg v0.0.0-20260702135906-e5650264cc5d/go.mod h1:XIYL8wT1Thvoeh7y5v9ZrL+34ySHiieu1/GyDZauULk=
github.com/go-ruby-prettyprint/prettyprint v0.0.0-20260629152429-60a380e82d7d h1:hH6O6QdrUlbePkqb8skhFQ0BVuMe0u1c/CXFFaVUnXg=
github.com/go-ruby-prettyprint/prettyprint v0.0.0-20260629152429-60a380e82d7d/go.mod h1:SIQyfiIJRKJq0OD9gWYfmvQTdJClJf8z56BsfVVw7rY=
github.com/go-ruby-prime/prime v0.0.0-20260629151744-aa26747e21a8 h1:zghkWpebo93z7Uz/BMXnLSN30ZPeLXS35Y1HPeXkUE4=
Expand All @@ -128,6 +130,8 @@ github.com/go-ruby-pstore/pstore v0.0.0-20260630081017-0dd55a12f94e h1:JmbJL+0nq
github.com/go-ruby-pstore/pstore v0.0.0-20260630081017-0dd55a12f94e/go.mod h1:tbYmHYXjnNXJCNYABDx9nWfuGBCXeKrp134bNsQeTpM=
github.com/go-ruby-public-suffix/public-suffix v0.0.0-20260630151503-f308d4002444 h1:vkMPJDpZHmu09pVxr7CFppoh1b5Kc8LkhKpxkSBoyxA=
github.com/go-ruby-public-suffix/public-suffix v0.0.0-20260630151503-f308d4002444/go.mod h1:Wq3cgP6klsULbO4ee0DOavVIzifgmddOWnYpqljbER4=
github.com/go-ruby-redis/redis v0.0.0-20260701125752-5de216f6ad92 h1:EXqNsTpyz5KPZURl94uOjcIIyJ0mCyMA7/AFAXvFgdg=
github.com/go-ruby-redis/redis v0.0.0-20260701125752-5de216f6ad92/go.mod h1:gt67ft80L9J5DpRGe+x3qPy2hg1g75aWQ6h52inm7og=
github.com/go-ruby-regexp/regexp v0.0.0-20260701043846-fa3d6604e1ee h1:ctauEAdAq129WCrU+dndYcOjNlo1Y94vWNBtgXp1heE=
github.com/go-ruby-regexp/regexp v0.0.0-20260701043846-fa3d6604e1ee/go.mod h1:+0nK2X+aCPMO91bPMM/DT1OFUIzFQeoTPGkkOR3kd84=
github.com/go-ruby-resolv/resolv v0.0.0-20260629153520-df410a5796ac h1:Y++tdsBF6lA/cw3c9uQ/ZyQZjP2N3wkA6pLF8a3HnHc=
Expand All @@ -144,6 +148,8 @@ github.com/go-ruby-scanf/scanf v0.0.0-20260629150220-414dbb31c386 h1:mt488Km0GMk
github.com/go-ruby-scanf/scanf v0.0.0-20260629150220-414dbb31c386/go.mod h1:UpBHz/Hf4Q8h14UpfJ5Cpp+OxVEqqNuICXGnUjg2dKc=
github.com/go-ruby-securerandom/securerandom v0.0.0-20260630081933-3f81ff7d7fb0 h1:NDJ7mJ3uMoysAoBRMJJNr0RRbqLfzp+8flALEeOuahI=
github.com/go-ruby-securerandom/securerandom v0.0.0-20260630081933-3f81ff7d7fb0/go.mod h1:xjx6gnzA3xXHkJrJFQDp1pW559J3kJR2e6GPctYg4K4=
github.com/go-ruby-sequel/sequel v0.0.0-20260702151352-66413b601977 h1:2PWvE7+rEB78Ly4SlX3MvIrfpo3s6JcPVQDrR08icPI=
github.com/go-ruby-sequel/sequel v0.0.0-20260702151352-66413b601977/go.mod h1:24vGiLxZE/YuFm5qNQpiXYloUQNg3w4AGW8zTmdI2Xw=
github.com/go-ruby-set/set v0.0.0-20260630204346-ab6a8a92a910 h1:Q41u365suJ1TlrX2mlVHOGBmW904Lwh3mo+WnlVPtno=
github.com/go-ruby-set/set v0.0.0-20260630204346-ab6a8a92a910/go.mod h1:F/P0PXeE5ybEPeWYfb1JAKWny1Li3dnlm6YxG2/vffE=
github.com/go-ruby-shellwords/shellwords v0.0.0-20260629114104-e941e4210818 h1:z9b99kWJht8AgtlIbdiq7Y02j6eddxW+rr4MJpCS6kw=
Expand Down
126 changes: 126 additions & 0 deletions internal/vm/batch6_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) the go-embedded-ruby/ruby authors
//
// SPDX-License-Identifier: BSD-3-Clause

package vm

import (
"testing"

sequel "github.com/go-ruby-sequel/sequel"

"github.com/go-embedded-ruby/ruby/internal/object"
)

// TestRedisValueUnmapped covers redisValue's terminal default: a Go value of a
// type the RESP decoder never produces maps to nil. This exercises the
// defensive final return that the wire grammar cannot reach.
func TestRedisValueUnmapped(t *testing.T) {
vm := New(nil)
if got := vm.redisValue(int32(7)); got != object.NilV {
t.Errorf("redisValue(int32) = %v, want nil", got)
}
}

// TestPGValueTime covers pgValue's time.Time and array branches and the
// unexpected-type fallback, which a normal query stream reaches only for
// time/array columns.
func TestPGValueUnmapped(t *testing.T) {
vm := New(nil)
// An unexpected decoder type (never produced by the OID decoders) stringifies
// to "" via the fallback.
if got := vm.pgValue(struct{ x int }{}); got.ToS() != "" {
t.Errorf("pgValue(struct) = %q, want empty", got.ToS())
}
// A String()-carrying value renders via pgSprint.
if got := vm.pgValue(pgStringer{"hi"}); got.ToS() != "hi" {
t.Errorf("pgValue(stringer) = %q, want hi", got.ToS())
}
}

// pgStringer is a String()-carrying value for the pgSprint fallback path.
type pgStringer struct{ s string }

func (p pgStringer) String() string { return p.s }

// TestSequelRubyValueMapped covers every sequelRubyValue branch (the executor
// value model) including the terminal default for an unmapped Go type.
func TestSequelRubyValueMapped(t *testing.T) {
cases := []struct {
in any
want string
}{
{nil, "nil"},
{true, "true"},
{int64(5), "5"},
{int(6), "6"},
{3.5, "3.5"},
{"txt", `"txt"`},
{[]byte{0x41, 0x42}, `"AB"`},
{int32(7), "nil"}, // unmapped -> nil
}
for _, c := range cases {
if got := sequelRubyValue(c.in).Inspect(); got != c.want {
t.Errorf("sequelRubyValue(%v) = %q, want %q", c.in, got, c.want)
}
}
}

// TestSequelValueDefault covers sequelValue's to_s fallback for a Ruby value type
// outside the mapped set (an arbitrary object stringifies).
func TestSequelValueDefault(t *testing.T) {
// A Range is not in sequelValue's mapped set, so it falls through to ToS.
r := &object.Range{Lo: object.Integer(1), Hi: object.Integer(3)}
if got := sequelValue(r); got != r.ToS() {
t.Errorf("sequelValue(Range) = %v, want its ToS %q", got, r.ToS())
}
}

// TestSequelNameDefault covers sequelName's ToS fallback for a non-Symbol,
// non-String value.
func TestSequelNameDefault(t *testing.T) {
if got := sequelName(object.Integer(42)); got != "42" {
t.Errorf("sequelName(42) = %q, want 42", got)
}
}

// TestSequelValueArray covers sequelValue's Array branch (a top-level Array value
// -> an IN-list []sequel.Value), which the Hash-condition path does not reach.
func TestSequelValueArray(t *testing.T) {
arr := &object.Array{Elems: []object.Value{object.Integer(1), object.Integer(2)}}
v := sequelValue(arr)
vals, ok := v.([]sequel.Value)
if !ok || len(vals) != 2 {
t.Fatalf("sequelValue(Array) = %#v, want a 2-element []sequel.Value", v)
}
}

// TestSequelValueHash covers sequelValue's Hash branch (a Hash value maps to an
// ordered AND-of-equalities condition), which the where() path reaches through
// sequelCond rather than sequelValue.
func TestSequelValueHash(t *testing.T) {
h := object.NewHash()
h.Set(object.Symbol("a"), object.Integer(1))
if _, ok := sequelValue(h).(sequel.Expr); !ok {
t.Errorf("sequelValue(Hash) = %T, want a sequel.Expr", sequelValue(h))
}
}

// TestSequelIndexColsSingle covers sequelIndexCols with a single (non-Array)
// column reference.
func TestSequelIndexColsSingle(t *testing.T) {
cols := sequelIndexCols(object.Symbol("name"))
if len(cols) != 1 || cols[0] != "name" {
t.Errorf("sequelIndexCols(:name) = %v, want [name]", cols)
}
}

// TestSequelCondArity covers sequelCond's empty-args guard.
func TestSequelCondArity(t *testing.T) {
defer func() {
if recover() == nil {
t.Error("sequelCond(nil) did not raise")
}
}()
sequelCond(nil)
}
3 changes: 3 additions & 0 deletions internal/vm/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,9 @@ func (vm *VM) bootstrap() {
vm.registerJbuilder() // Jbuilder.encode / json.<name> DSL (require "jbuilder"), backed by go-ruby-jbuilder
vm.registerBuilder() // Builder::XmlMarkup (require "builder"), backed by go-ruby-builder
vm.registerSQLite3() // SQLite3::Database/Statement (require "sqlite3"), backed by go-ruby-sqlite3 (modernc); needs StandardError for SQLite3::Exception
vm.registerRedis() // Redis client (require "redis"), backed by go-ruby-redis RESP codec; socket = injected IO seam; needs StandardError for Redis::BaseError
vm.registerPG() // PG::Connection/Result (require "pg"), backed by go-ruby-pg v3 protocol; socket = injected IO seam; needs StandardError for PG::Error
vm.registerSequel() // Sequel query builder + Database (require "sequel"), backed by go-ruby-sequel; executor seam wired to SQLite3::Database (real execution)
vm.registerNokogiri() // Nokogiri::HTML/XML -> Document/Node/NodeSet (require "nokogiri"), backed by go-ruby-nokogiri; needs StandardError for Nokogiri::SyntaxError
vm.registerRSpec() // RSpec matcher + expect surface (require "rspec"), backed by go-ruby-rspec; needs Exception for ExpectationNotMetError
vm.registerRQRCode() // RQRCode::QRCode (require "rqrcode"), backed by go-ruby-rqrcode; needs StandardError for RQRCode::QRCode*Error
Expand Down
14 changes: 14 additions & 0 deletions internal/vm/object_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,20 @@ func (vm *VM) classOf(v object.Value) *RClass {
return vm.consts["SQLite3::Database"].(*RClass)
case *SQLite3Statement:
return vm.consts["SQLite3::Statement"].(*RClass)
case *RedisObj:
return x.cls
case *RedisBatch:
return x.cls
case *PGConnObj:
return x.cls
case *PGResultObj:
return x.cls
case *SequelDBObj:
return x.cls
case *SequelDatasetObj:
return x.cls
case *SequelSchemaObj:
return x.cls
case *NokogiriDocument:
return vm.consts["Nokogiri::XML::Document"].(*RClass)
case *NokogiriNode:
Expand Down
Loading
Loading