diff --git a/README.md b/README.md index 2c42fa8..5726e04 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ go install github.com/mhiro2/seedling/cmd/seedling-gen@latest seedling-gen sql --explain schema.sql ``` - This generates struct types, `RegisterBlueprints()`, deterministic `Defaults` for common scalar fields, relations, and Insert stubs. Fill in the `// TODO` callbacks with your DB logic: + This generates struct types, `NewRegistry()`, `RegisterBlueprints(reg)`, deterministic `Defaults` for common scalar fields, relations, and Insert stubs. Fill in the `// TODO` callbacks with your DB logic: ```go Insert: func(ctx context.Context, db seedling.DBTX, v Company) (Company, error) { @@ -127,10 +127,9 @@ go install github.com/mhiro2/seedling/cmd/seedling-gen@latest ```go func TestUser(t *testing.T) { - seedling.ResetRegistry() - testutil.RegisterBlueprints() + reg := testutil.NewRegistry() - result := seedling.InsertOne[testutil.User](t, db) + result := seedling.NewSession[testutil.User](reg).InsertOne(t, db) user := result.Root() if user.ID == 0 { @@ -146,12 +145,11 @@ go install github.com/mhiro2/seedling/cmd/seedling-gen@latest ```go func TestNamedUser(t *testing.T) { - seedling.ResetRegistry() - testutil.RegisterBlueprints() + reg := testutil.NewRegistry() - company := seedling.InsertOne[testutil.Company](t, db).Root() + company := seedling.NewSession[testutil.Company](reg).InsertOne(t, db).Root() - result := seedling.InsertOne[testutil.User](t, db, + result := seedling.NewSession[testutil.User](reg).InsertOne(t, db, seedling.Set("Name", "alice"), seedling.Use("company", company), ) @@ -161,12 +159,11 @@ go install github.com/mhiro2/seedling/cmd/seedling-gen@latest } func TestTaskProject(t *testing.T) { - seedling.ResetRegistry() - testutil.RegisterBlueprints() + reg := testutil.NewRegistry() // Only("project") inserts task + project subtree only, // skipping the assignee relation entirely. - result := seedling.InsertOne[testutil.Task](t, db, + result := seedling.NewSession[testutil.Task](reg).InsertOne(t, db, seedling.Only("project"), ) _ = result @@ -191,7 +188,7 @@ go install github.com/mhiro2/seedling/cmd/seedling-gen@latest ## 📂 Examples - [basic](./examples/basic): register blueprints and insert rows with automatic parent creation -- [quickstart](./examples/quickstart): generated-style `RegisterBlueprints()` flow that matches the README Quick Start +- [quickstart](./examples/quickstart): generated-style `NewRegistry()` / `RegisterBlueprints(reg)` flow that matches the README Quick Start - [custom-defaults](./examples/custom-defaults): customize values with `Set`, `With`, and `Generate` - [reuse-parent](./examples/reuse-parent): reuse existing rows with `Use` - [batch-insert](./examples/batch-insert): batch inserts with shared `Ref` dependencies and per-row `SeqRef` overrides diff --git a/cmd/seedling-gen/main_test.go b/cmd/seedling-gen/main_test.go index 20ce036..b2d72f0 100644 --- a/cmd/seedling-gen/main_test.go +++ b/cmd/seedling-gen/main_test.go @@ -235,6 +235,9 @@ func TestGenerate_OutputIncludesExpectedSections(t *testing.T) { {name: "context import", substr: `"context"`, message: "output should import context"}, {name: "time import", substr: `"time"`, message: "output should import time"}, {name: "seedling import", substr: `"github.com/mhiro2/seedling"`, message: "output should import seedling"}, + {name: "new registry helper", substr: "func NewRegistry() *seedling.Registry", message: "output should contain local registry helper"}, + {name: "registry argument", substr: "func RegisterBlueprints(reg *seedling.Registry)", message: "output should accept a registry argument"}, + {name: "register to registry", substr: "seedling.MustRegisterTo(reg", message: "output should register into the provided registry"}, {name: "company struct", substr: "type Company struct", message: "output should contain Company struct"}, {name: "user struct", substr: "type User struct", message: "output should contain User struct"}, {name: "company blueprint", substr: `Name: "company"`, message: "output should register company blueprint"}, @@ -315,7 +318,10 @@ func TestGenerate_EmptyInput(t *testing.T) { if !strings.Contains(output, "package empty") { t.Fatalf("expected package declaration, got:\n%s", output) } - if !strings.Contains(output, "func RegisterBlueprints()") { + if !strings.Contains(output, "func NewRegistry() *seedling.Registry") { + t.Fatalf("expected NewRegistry function, got:\n%s", output) + } + if !strings.Contains(output, "func RegisterBlueprints(reg *seedling.Registry)") { t.Fatalf("expected RegisterBlueprints function, got:\n%s", output) } } diff --git a/cmd/seedling-gen/normalized_ir.go b/cmd/seedling-gen/normalized_ir.go index cc7bb37..b1e0e47 100644 --- a/cmd/seedling-gen/normalized_ir.go +++ b/cmd/seedling-gen/normalized_ir.go @@ -53,11 +53,17 @@ type {{.StructName}} struct { ` const normalizedBlueprintTemplate = ` -func RegisterBlueprints() { +func NewRegistry() *seedling.Registry { + reg := seedling.NewRegistry() + RegisterBlueprints(reg) + return reg +} + +func RegisterBlueprints(reg *seedling.Registry) { {{- range $i, $model := .}} {{- if $i}} {{ end }} - seedling.MustRegister(seedling.Blueprint[{{$model.TypeExpr}}]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[{{$model.TypeExpr}}]{ Name: "{{$model.BlueprintID}}", Table: "{{$model.TableName}}", {{- if isCompositePK $model}} diff --git a/doc.go b/doc.go index 3ab60af..d377f15 100644 --- a/doc.go +++ b/doc.go @@ -52,32 +52,34 @@ // // Register a [Blueprint] for each model that seedling should create: // -// seedling.MustRegister(seedling.Blueprint[Company]{ -// Name: "company", -// Table: "companies", -// PKField: "ID", -// Defaults: func() Company { -// return Company{Name: "test-company"} -// }, -// Insert: func(ctx context.Context, db seedling.DBTX, v Company) (Company, error) { -// return insertCompany(ctx, db, v) -// }, -// }) -// -// seedling.MustRegister(seedling.Blueprint[User]{ -// Name: "user", -// Table: "users", -// PKField: "ID", -// Defaults: func() User { -// return User{Name: "test-user"} -// }, -// Relations: []seedling.Relation{ -// {Name: "company", Kind: seedling.BelongsTo, LocalField: "CompanyID", RefBlueprint: "company"}, -// }, -// Insert: func(ctx context.Context, db seedling.DBTX, v User) (User, error) { -// return insertUser(ctx, db, v) -// }, -// }) +// func registerBlueprints(reg *seedling.Registry) { +// seedling.MustRegisterTo(reg, seedling.Blueprint[Company]{ +// Name: "company", +// Table: "companies", +// PKField: "ID", +// Defaults: func() Company { +// return Company{Name: "test-company"} +// }, +// Insert: func(ctx context.Context, db seedling.DBTX, v Company) (Company, error) { +// return insertCompany(ctx, db, v) +// }, +// }) +// +// seedling.MustRegisterTo(reg, seedling.Blueprint[User]{ +// Name: "user", +// Table: "users", +// PKField: "ID", +// Defaults: func() User { +// return User{Name: "test-user"} +// }, +// Relations: []seedling.Relation{ +// {Name: "company", Kind: seedling.BelongsTo, LocalField: "CompanyID", RefBlueprint: "company"}, +// }, +// Insert: func(ctx context.Context, db seedling.DBTX, v User) (User, error) { +// return insertUser(ctx, db, v) +// }, +// }) +// } // // DBTX is intentionally opaque. Your insert callback and your call sites must // agree on the concrete handle type passed as db. @@ -85,7 +87,10 @@ // Then create rows directly in your tests: // // func TestUser(t *testing.T) { -// result := seedling.InsertOne[User](t, db) +// reg := seedling.NewRegistry() +// registerBlueprints(reg) +// +// result := seedling.NewSession[User](reg).InsertOne(t, db) // user := result.Root() // // user.ID and user.CompanyID are populated. // } diff --git a/docs/guide.md b/docs/guide.md index f49319f..e8bed7d 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -258,7 +258,7 @@ Supported locales: `en` (default), `ja`, `zh`, `ko`, `de`, `fr`. ## Examples - [basic](../examples/basic) -- register blueprints and insert rows with automatic parent creation -- [quickstart](../examples/quickstart) -- generated-style `RegisterBlueprints()` flow that matches the README Quick Start +- [quickstart](../examples/quickstart) -- generated-style `NewRegistry()` / `RegisterBlueprints(reg)` flow that matches the README Quick Start - [custom-defaults](../examples/custom-defaults) -- customize values with `Set`, `With`, and `Generate` - [reuse-parent](../examples/reuse-parent) -- reuse existing rows with `Use` - [batch-insert](../examples/batch-insert) -- batch inserts with shared `Ref` dependencies and per-row `SeqRef` overrides diff --git a/examples/basic/basic_test.go b/examples/basic/basic_test.go index ec41ba9..b2ff492 100644 --- a/examples/basic/basic_test.go +++ b/examples/basic/basic_test.go @@ -7,16 +7,17 @@ import ( "github.com/mhiro2/seedling/examples/basic" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() - basic.RegisterBlueprints() + reg := seedling.NewRegistry() + basic.RegisterBlueprints(reg) + return reg } func TestInsertOne_Company(t *testing.T) { - setup(t) + reg := setup(t) - company := seedling.InsertOne[basic.Company](t, nil) + company := seedling.NewSession[basic.Company](reg).InsertOne(t, nil) if company.Root().ID == 0 { t.Fatal("expected company ID to be set") @@ -27,10 +28,10 @@ func TestInsertOne_Company(t *testing.T) { } func TestInsertOne_User(t *testing.T) { - setup(t) + reg := setup(t) // InsertOne[User] automatically creates a parent Company. - user := seedling.InsertOne[basic.User](t, nil) + user := seedling.NewSession[basic.User](reg).InsertOne(t, nil) if user.Root().ID == 0 { t.Fatal("expected user ID to be set") @@ -44,9 +45,9 @@ func TestInsertOne_User(t *testing.T) { } func TestInsertOne_UserWithSet(t *testing.T) { - setup(t) + reg := setup(t) - user := seedling.InsertOne[basic.User](t, nil, + user := seedling.NewSession[basic.User](reg).InsertOne(t, nil, seedling.Set("Name", "alice"), seedling.Set("Email", "alice@example.com"), ) diff --git a/examples/basic/blueprints.go b/examples/basic/blueprints.go index 828b495..4f88ee4 100644 --- a/examples/basic/blueprints.go +++ b/examples/basic/blueprints.go @@ -13,10 +13,9 @@ func nextID() int { return int(idSeq.Add(1)) } -// RegisterBlueprints registers the Company and User blueprints. -// Call seedling.ResetRegistry() before this in tests to start fresh. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[Company]{ +// RegisterBlueprints registers the Company and User blueprints in reg. +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[Company]{ Name: "company", Table: "companies", PKField: "ID", @@ -29,7 +28,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[User]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[User]{ Name: "user", Table: "users", PKField: "ID", diff --git a/examples/batch-insert/batch_insert_test.go b/examples/batch-insert/batch_insert_test.go index 6b15083..6148737 100644 --- a/examples/batch-insert/batch_insert_test.go +++ b/examples/batch-insert/batch_insert_test.go @@ -9,19 +9,20 @@ import ( batchinsert "github.com/mhiro2/seedling/examples/batch-insert" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() batchinsert.ResetIDs() - batchinsert.RegisterBlueprints() + reg := seedling.NewRegistry() + batchinsert.RegisterBlueprints(reg) + return reg } func TestInsertManyE_SharedProject(t *testing.T) { // Arrange - setup(t) + reg := setup(t) // Act - result, err := seedling.InsertManyE[batchinsert.Task](context.Background(), nil, 2, + result, err := seedling.NewSession[batchinsert.Task](reg).InsertManyE(context.Background(), nil, 2, seedling.Ref("project", seedling.Set("Name", "shared-project")), ) if err != nil { @@ -60,10 +61,10 @@ func TestInsertManyE_SharedProject(t *testing.T) { func TestInsertManyE_SeqRefCreatesDistinctProjects(t *testing.T) { // Arrange - setup(t) + reg := setup(t) // Act - result, err := seedling.InsertManyE[batchinsert.Task](context.Background(), nil, 2, + result, err := seedling.NewSession[batchinsert.Task](reg).InsertManyE(context.Background(), nil, 2, seedling.SeqRef("project", func(i int) []seedling.Option { return []seedling.Option{seedling.Set("Name", fmt.Sprintf("project-%d", i))} }), diff --git a/examples/batch-insert/blueprints.go b/examples/batch-insert/blueprints.go index 446908e..a173703 100644 --- a/examples/batch-insert/blueprints.go +++ b/examples/batch-insert/blueprints.go @@ -13,9 +13,9 @@ func nextID() int { return int(idSeq.Add(1)) } -// RegisterBlueprints registers Company, Project, and Task blueprints. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[Company]{ +// RegisterBlueprints registers Company, Project, and Task blueprints in reg. +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[Company]{ Name: "company", Table: "companies", PKField: "ID", @@ -28,7 +28,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[Project]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[Project]{ Name: "project", Table: "projects", PKField: "ID", @@ -44,7 +44,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[Task]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[Task]{ Name: "task", Table: "tasks", PKField: "ID", diff --git a/examples/custom-defaults/blueprints.go b/examples/custom-defaults/blueprints.go index 7952283..040de53 100644 --- a/examples/custom-defaults/blueprints.go +++ b/examples/custom-defaults/blueprints.go @@ -13,9 +13,9 @@ func nextID() int { return int(idSeq.Add(1)) } -// RegisterBlueprints registers the User blueprint with sensible defaults. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[User]{ +// RegisterBlueprints registers the User blueprint with sensible defaults in reg. +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[User]{ Name: "user", Table: "users", PKField: "ID", diff --git a/examples/custom-defaults/custom_defaults_test.go b/examples/custom-defaults/custom_defaults_test.go index 397fcba..5248e45 100644 --- a/examples/custom-defaults/custom_defaults_test.go +++ b/examples/custom-defaults/custom_defaults_test.go @@ -7,16 +7,17 @@ import ( customdefaults "github.com/mhiro2/seedling/examples/custom-defaults" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() - customdefaults.RegisterBlueprints() + reg := seedling.NewRegistry() + customdefaults.RegisterBlueprints(reg) + return reg } func TestDefaultUser(t *testing.T) { - setup(t) + reg := setup(t) - user := seedling.InsertOne[customdefaults.User](t, nil) + user := seedling.NewSession[customdefaults.User](reg).InsertOne(t, nil) if user.Root().Role != "member" { t.Fatalf("expected Role = %q, got %q", "member", user.Root().Role) @@ -27,10 +28,10 @@ func TestDefaultUser(t *testing.T) { } func TestAdminUser(t *testing.T) { - setup(t) + reg := setup(t) // Use the AdminUser() helper for type-safe default customization. - admin := seedling.InsertOne[customdefaults.User](t, nil, + admin := seedling.NewSession[customdefaults.User](reg).InsertOne(t, nil, customdefaults.AdminUser(), ) @@ -47,9 +48,9 @@ func TestAdminUser(t *testing.T) { } func TestInactiveUser(t *testing.T) { - setup(t) + reg := setup(t) - user := seedling.InsertOne[customdefaults.User](t, nil, + user := seedling.NewSession[customdefaults.User](reg).InsertOne(t, nil, customdefaults.InactiveUser(), ) @@ -63,10 +64,10 @@ func TestInactiveUser(t *testing.T) { } func TestCombineWithOptions(t *testing.T) { - setup(t) + reg := setup(t) // Multiple With options can be combined. - user := seedling.InsertOne[customdefaults.User](t, nil, + user := seedling.NewSession[customdefaults.User](reg).InsertOne(t, nil, customdefaults.AdminUser(), customdefaults.InactiveUser(), ) @@ -80,10 +81,10 @@ func TestCombineWithOptions(t *testing.T) { } func TestWithInline(t *testing.T) { - setup(t) + reg := setup(t) // With() can also be used inline for one-off customizations. - user := seedling.InsertOne[customdefaults.User](t, nil, + user := seedling.NewSession[customdefaults.User](reg).InsertOne(t, nil, seedling.With(func(u *customdefaults.User) { u.Name = "custom-name" u.Role = "viewer" diff --git a/examples/quickstart/blueprints.go b/examples/quickstart/blueprints.go index 42b2161..cb20e31 100644 --- a/examples/quickstart/blueprints.go +++ b/examples/quickstart/blueprints.go @@ -13,9 +13,16 @@ func nextID() int { return int(idSeq.Add(1)) } -// RegisterBlueprints registers the generated-style Company and User blueprints. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[Company]{ +// NewRegistry returns a local registry populated with this example's blueprints. +func NewRegistry() *seedling.Registry { + reg := seedling.NewRegistry() + RegisterBlueprints(reg) + return reg +} + +// RegisterBlueprints registers the generated-style Company and User blueprints in reg. +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[Company]{ Name: "company", Table: "companies", PKField: "ID", @@ -28,7 +35,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[User]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[User]{ Name: "user", Table: "users", PKField: "ID", diff --git a/examples/quickstart/quickstart_test.go b/examples/quickstart/quickstart_test.go index 444d258..85e4dfe 100644 --- a/examples/quickstart/quickstart_test.go +++ b/examples/quickstart/quickstart_test.go @@ -7,19 +7,18 @@ import ( "github.com/mhiro2/seedling/examples/quickstart" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() quickstart.ResetIDs() - quickstart.RegisterBlueprints() + return quickstart.NewRegistry() } func TestQuickStart_InsertOneUser(t *testing.T) { // Arrange - setup(t) + reg := setup(t) // Act - user := seedling.InsertOne[quickstart.User](t, nil).Root() + user := seedling.NewSession[quickstart.User](reg).InsertOne(t, nil).Root() // Assert if user.ID == 0 { @@ -35,13 +34,13 @@ func TestQuickStart_InsertOneUser(t *testing.T) { func TestQuickStart_ReuseCompanyWithUse(t *testing.T) { // Arrange - setup(t) - company := seedling.InsertOne[quickstart.Company](t, nil, + reg := setup(t) + company := seedling.NewSession[quickstart.Company](reg).InsertOne(t, nil, seedling.Set("Name", "shared-company"), ).Root() // Act - user := seedling.InsertOne[quickstart.User](t, nil, + user := seedling.NewSession[quickstart.User](reg).InsertOne(t, nil, seedling.Set("Name", "alice"), seedling.Use("company", company), ).Root() diff --git a/examples/reuse-parent/blueprints.go b/examples/reuse-parent/blueprints.go index 2f62350..d8529e0 100644 --- a/examples/reuse-parent/blueprints.go +++ b/examples/reuse-parent/blueprints.go @@ -13,9 +13,9 @@ func nextID() int { return int(idSeq.Add(1)) } -// RegisterBlueprints registers Company, Project, and Task blueprints. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[Company]{ +// RegisterBlueprints registers Company, Project, and Task blueprints in reg. +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[Company]{ Name: "company", Table: "companies", PKField: "ID", @@ -28,7 +28,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[Project]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[Project]{ Name: "project", Table: "projects", PKField: "ID", @@ -49,7 +49,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[Task]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[Task]{ Name: "task", Table: "tasks", PKField: "ID", diff --git a/examples/reuse-parent/reuse_parent_test.go b/examples/reuse-parent/reuse_parent_test.go index 3c4aa9a..9eaca72 100644 --- a/examples/reuse-parent/reuse_parent_test.go +++ b/examples/reuse-parent/reuse_parent_test.go @@ -7,26 +7,27 @@ import ( reuseparent "github.com/mhiro2/seedling/examples/reuse-parent" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() - reuseparent.RegisterBlueprints() + reg := seedling.NewRegistry() + reuseparent.RegisterBlueprints(reg) + return reg } func TestUse_ShareParentCompany(t *testing.T) { - setup(t) + reg := setup(t) // First, create a shared Company. - company := seedling.InsertOne[reuseparent.Company](t, nil, + company := seedling.NewSession[reuseparent.Company](reg).InsertOne(t, nil, seedling.Set("Name", "Shared Corp"), ) // Create two Projects under the same Company using Use(). - projectA := seedling.InsertOne[reuseparent.Project](t, nil, + projectA := seedling.NewSession[reuseparent.Project](reg).InsertOne(t, nil, seedling.Set("Name", "Project Alpha"), seedling.Use("company", company.Root()), ) - projectB := seedling.InsertOne[reuseparent.Project](t, nil, + projectB := seedling.NewSession[reuseparent.Project](reg).InsertOne(t, nil, seedling.Set("Name", "Project Beta"), seedling.Use("company", company.Root()), ) @@ -47,23 +48,23 @@ func TestUse_ShareParentCompany(t *testing.T) { } func TestUse_ShareParentProject(t *testing.T) { - setup(t) + reg := setup(t) // Create a shared Project (which auto-creates a Company). - project := seedling.InsertOne[reuseparent.Project](t, nil, + project := seedling.NewSession[reuseparent.Project](reg).InsertOne(t, nil, seedling.Set("Name", "Shared Project"), ) // Create multiple Tasks under the same Project using Use(). - task1 := seedling.InsertOne[reuseparent.Task](t, nil, + task1 := seedling.NewSession[reuseparent.Task](reg).InsertOne(t, nil, seedling.Set("Title", "Task 1"), seedling.Use("project", project.Root()), ) - task2 := seedling.InsertOne[reuseparent.Task](t, nil, + task2 := seedling.NewSession[reuseparent.Task](reg).InsertOne(t, nil, seedling.Set("Title", "Task 2"), seedling.Use("project", project.Root()), ) - task3 := seedling.InsertOne[reuseparent.Task](t, nil, + task3 := seedling.NewSession[reuseparent.Task](reg).InsertOne(t, nil, seedling.Set("Title", "Task 3"), seedling.Use("project", project.Root()), ) diff --git a/examples/sqlc/blueprints.go b/examples/sqlc/blueprints.go index 0483b53..e5e3d3b 100644 --- a/examples/sqlc/blueprints.go +++ b/examples/sqlc/blueprints.go @@ -14,7 +14,7 @@ func nextID() int64 { return idSeq.Add(1) } -// RegisterBlueprints registers Organization and Member blueprints. +// RegisterBlueprints registers Organization and Member blueprints in reg. // // In a real project, the Insert functions would call sqlc-generated query methods: // @@ -23,8 +23,8 @@ func nextID() int64 { // } // // Here we use mock inserts that assign incrementing IDs. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[Organization]{ +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[Organization]{ Name: "organization", Table: "organizations", PKField: "ID", @@ -38,7 +38,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[Member]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[Member]{ Name: "member", Table: "members", PKField: "ID", diff --git a/examples/sqlc/sqlc_test.go b/examples/sqlc/sqlc_test.go index 75dc230..5885c58 100644 --- a/examples/sqlc/sqlc_test.go +++ b/examples/sqlc/sqlc_test.go @@ -8,17 +8,18 @@ import ( example "github.com/mhiro2/seedling/examples/sqlc" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() - example.RegisterBlueprints() + reg := seedling.NewRegistry() + example.RegisterBlueprints(reg) + return reg } func TestInsertOne_Member(t *testing.T) { - setup(t) + reg := setup(t) // seedling auto-creates the parent Organization. - member := seedling.InsertOne[example.Member](t, nil) + member := seedling.NewSession[example.Member](reg).InsertOne(t, nil) if member.Root().ID == 0 { t.Fatal("expected member ID to be set") @@ -32,9 +33,9 @@ func TestInsertOne_Member(t *testing.T) { } func TestInsertOne_Organization(t *testing.T) { - setup(t) + reg := setup(t) - org := seedling.InsertOne[example.Organization](t, nil, + org := seedling.NewSession[example.Organization](reg).InsertOne(t, nil, seedling.Set("Name", "Acme Corp"), ) @@ -47,9 +48,9 @@ func TestInsertOne_Organization(t *testing.T) { } func TestInsertMany_Members(t *testing.T) { - setup(t) + reg := setup(t) - members := seedling.InsertMany[example.Member](t, nil, 3, + members := seedling.NewSession[example.Member](reg).InsertMany(t, nil, 3, seedling.Seq("Name", func(i int) string { return fmt.Sprintf("member-%d", i) }), diff --git a/examples/with-tx/blueprints.go b/examples/with-tx/blueprints.go index c5f6438..c73ab00 100644 --- a/examples/with-tx/blueprints.go +++ b/examples/with-tx/blueprints.go @@ -13,9 +13,9 @@ func nextID() int { return int(idSeq.Add(1)) } -// RegisterBlueprints registers the Company and User blueprints for SQL transaction examples. -func RegisterBlueprints() { - seedling.MustRegister(seedling.Blueprint[Company]{ +// RegisterBlueprints registers the Company and User blueprints for SQL transaction examples in reg. +func RegisterBlueprints(reg *seedling.Registry) { + seedling.MustRegisterTo(reg, seedling.Blueprint[Company]{ Name: "company", Table: "companies", PKField: "ID", @@ -28,7 +28,7 @@ func RegisterBlueprints() { }, }) - seedling.MustRegister(seedling.Blueprint[User]{ + seedling.MustRegisterTo(reg, seedling.Blueprint[User]{ Name: "user", Table: "users", PKField: "ID", diff --git a/examples/with-tx/with_tx_test.go b/examples/with-tx/with_tx_test.go index 358d7ad..1f855da 100644 --- a/examples/with-tx/with_tx_test.go +++ b/examples/with-tx/with_tx_test.go @@ -12,21 +12,22 @@ import ( withtx "github.com/mhiro2/seedling/examples/with-tx" ) -func setup(t *testing.T) { +func setup(t *testing.T) *seedling.Registry { t.Helper() - seedling.ResetRegistry() withtx.ResetIDs() - withtx.RegisterBlueprints() + reg := seedling.NewRegistry() + withtx.RegisterBlueprints(reg) + return reg } func TestWithTx_InsertOneUser(t *testing.T) { // Arrange - setup(t) + reg := setup(t) db := openDB(t) // Act tx := seedling.WithTx(t, db) - user := seedling.InsertOne[withtx.User](t, tx).Root() + user := seedling.NewSession[withtx.User](reg).InsertOne(t, tx).Root() // Assert if user.ID == 0 { diff --git a/skills/seedling-gen-cli/SKILL.md b/skills/seedling-gen-cli/SKILL.md index 5e555d0..66de79e 100644 --- a/skills/seedling-gen-cli/SKILL.md +++ b/skills/seedling-gen-cli/SKILL.md @@ -11,7 +11,7 @@ Use this skill when a Go project wants seedling blueprint scaffolding. - Go model structs for tables or entities - deterministic `Defaults` for common scalar fields -- `RegisterBlueprints()` scaffolding +- `NewRegistry()` / `RegisterBlueprints(reg)` scaffolding - relation definitions such as `seedling.BelongsTo` - project-specific `Insert` stubs with `// TODO: implement` markers diff --git a/skills/seedling-test-setup/SKILL.md b/skills/seedling-test-setup/SKILL.md index 6bb6bf9..3f0b6ba 100644 --- a/skills/seedling-test-setup/SKILL.md +++ b/skills/seedling-test-setup/SKILL.md @@ -198,6 +198,6 @@ result := plan.Insert(t, db) // execute the plan ## Notes -- Always register blueprints before calling `InsertOne` / `InsertMany`. If using the global registry, call `seedling.ResetRegistry()` in test setup to avoid cross-test leaks. +- Always register blueprints into a test-local registry before calling `InsertOne` / `InsertMany`; prefer `seedling.NewSession[T](reg)` over the global helpers. - `InsertOne` / `InsertMany` accept a `testing.TB` and fail the test on error. Use the `E` variants (`InsertOneE`, `InsertManyE`) in non-test contexts or when you need explicit error handling. - Prefer transaction rollback over `Cleanup` for test isolation — it is faster and guarantees no leftover data.