Skip to content

Commit 7b5cdef

Browse files
committed
refactor(router): introduce Any method for multi-method route registration
- Added the `Any` method to the router interface, allowing registration of handlers for all HTTP methods at a specified path. - Enhanced the `Mount` method to support wildcard paths and ensure proper registration of handlers for both exact and wildcard routes. - Updated tests to validate the functionality of the new `Any` method, ensuring it works with various handler types and middleware. - Improved documentation for the router methods to clarify usage and behavior.
1 parent 837433b commit 7b5cdef

16 files changed

Lines changed: 859 additions & 34 deletions

File tree

.github/workflows/go.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ jobs:
6161
go test -v -short -race -timeout=10m -coverprofile=coverage.out $(go list ./... | grep -v '/bk/')
6262
continue-on-error: ${{ matrix.os == 'windows-latest' }}
6363

64+
- name: Upload coverage reports to Codecov
65+
uses: codecov/codecov-action@v5
66+
with:
67+
token: ${{ secrets.CODECOV_TOKEN }}
68+
files: coverage.out
69+
6470
- name: Upload coverage
6571
if: matrix.os == 'ubuntu-latest'
6672
uses: actions/upload-artifact@v4

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Forge™ is a backend framework, and Forge Cloud™ is its AI cloud offering, ma
77
> Build scalable, maintainable, and observable Go applications with Forge—the modern framework that brings clean architecture, dependency injection, and powerful extensions to your production services.
88
99
[![Go Version](https://img.shields.io/badge/Go-1.24+-00ADD8?style=flat&logo=go)](https://golang.org/)
10+
[![Go Report Card](https://goreportcard.com/badge/github.com/xraph/forge)](https://goreportcard.com/report/github.com/xraph/forge)
1011
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
1112
[![GitHub Stars](https://img.shields.io/github/stars/xraph/forge)](https://github.com/xraph/forge)
1213
[![CI/CD](https://github.com/xraph/forge/workflows/CI/badge.svg)](https://github.com/xraph/forge/actions)

app.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ func DefaultAppConfig() AppConfig {
121121
ConfigBaseNames: []string{"config.yaml", "config.yml"},
122122
ConfigLocalNames: []string{"config.local.yaml", "config.local.yml"},
123123
// Environment variable source defaults
124-
EnableEnvConfig: true, // Enabled by default
125-
EnvSeparator: "_", // Standard separator
126-
EnvOverridesFile: true, // Env takes precedence over files by default
124+
EnableEnvConfig: true, // Enabled by default
125+
EnvSeparator: "_", // Standard separator
126+
EnvOverridesFile: true, // Env takes precedence over files by default
127127
}
128128
}
129129

cmd/forge/plugins/database_go_migrations.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (p *DatabasePlugin) runWithGoMigrations(ctx cli.CommandContext, command str
5959
if ctx.Bool("verbose") {
6060
migrationPath, _ := p.getMigrationPath()
6161
ctx.Info(fmt.Sprintf("📁 Migration directory: %s", migrationPath))
62-
62+
6363
// Check if migrations.go exists
6464
migrationsGoPath := filepath.Join(migrationPath, "migrations.go")
6565
if _, err := os.Stat(migrationsGoPath); os.IsNotExist(err) {

examples/database-migration-docker/main.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,3 @@ func main() {
4545

4646
log.Println("✅ Application stopped gracefully")
4747
}
48-

extensions/database/migrate/migrations_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ func TestMigrationsPackageInitialization(t *testing.T) {
2222
// collection even if discovery fails.
2323
func TestGetMigrations(t *testing.T) {
2424
migrations, err := migrate.GetMigrations()
25-
25+
2626
// Even if discovery fails, we should get a migrations object
2727
// (it just won't have filesystem-discovered migrations)
2828
if migrations == nil {
2929
t.Fatal("GetMigrations should never return nil")
3030
}
31-
31+
3232
// Error is informational only - the function still returns the migrations
3333
_ = err
3434
}
@@ -38,10 +38,9 @@ func TestEnsureDiscoveredIdempotent(t *testing.T) {
3838
// Call GetMigrations multiple times
3939
_, err1 := migrate.GetMigrations()
4040
_, err2 := migrate.GetMigrations()
41-
41+
4242
// Errors should be identical (discovery only happens once)
4343
if (err1 == nil) != (err2 == nil) {
4444
t.Error("Multiple calls to GetMigrations should return the same error state")
4545
}
4646
}
47-

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module github.com/xraph/forge
22

33
go 1.24.4
44

5+
replace github.com/xraph/forgeui => ../forgeui
6+
57
require (
68
github.com/BurntSushi/toml v1.5.0
79
github.com/Flagsmith/flagsmith-go-client/v3 v3.7.0
@@ -22,6 +24,7 @@ require (
2224
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
2325
github.com/grandcat/zeroconf v1.0.0
2426
github.com/hashicorp/consul/api v1.32.1
27+
github.com/joho/godotenv v1.5.1
2528
github.com/json-iterator/go v1.1.12
2629
github.com/julienschmidt/httprouter v1.3.0
2730
github.com/launchdarkly/go-sdk-common/v3 v3.4.0
@@ -122,7 +125,6 @@ require (
122125
github.com/hashicorp/serf v0.10.1 // indirect
123126
github.com/itlightning/dateparse v0.2.0 // indirect
124127
github.com/jinzhu/inflection v1.0.0 // indirect
125-
github.com/joho/godotenv v1.5.1 // indirect
126128
github.com/josharian/intern v1.0.0 // indirect
127129
github.com/klauspost/compress v1.18.0 // indirect
128130
github.com/launchdarkly/ccache v1.1.0 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,6 @@ github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6
494494
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
495495
github.com/xraph/farp v1.0.2 h1:xu9d5JB/4+F0s5TVe3MaXrQO98a/AEOgYN0p/8moZlU=
496496
github.com/xraph/farp v1.0.2/go.mod h1:Nlli8WUsxvQL5wXiJqcAn6OsUHBzKJxrl9JLJ9J6Wqo=
497-
github.com/xraph/forgeui v0.0.1 h1:ID5q06qrPXijiWzzIjDOsuOpU54fg4dx4UXgozDRueA=
498-
github.com/xraph/forgeui v0.0.1/go.mod h1:gh16NPVUF9F/+4P/nmibhs2uTpWO2iYVADrIunQGK1U=
499497
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
500498
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
501499
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

internal/config/sources/file_env_expand_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,4 +493,3 @@ database:
493493
t.Errorf("name = %v, want testdb", db["name"])
494494
}
495495
}
496-

internal/config/sources/test_real_example.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ func TestRealConfigExample_WithDefaults(t *testing.T) {
7777
if ai["max_concurrency"] != "10" {
7878
t.Errorf("ai.max_concurrency = %v, want 10", ai["max_concurrency"])
7979
}
80-
80+
8181
if llm, ok := ai["llm"].(map[string]any); ok {
8282
if llm["default_provider"] != "lmstudio" {
8383
t.Errorf("ai.llm.default_provider = %v, want lmstudio", llm["default_provider"])
8484
}
85-
85+
8686
if providers, ok := llm["providers"].(map[string]any); ok {
8787
if lmstudio, ok := providers["lmstudio"].(map[string]any); ok {
8888
if lmstudio["base_url"] != "http://localhost:1234/v1" {
@@ -106,7 +106,7 @@ func TestRealConfigExample_WithEnvOverrides(t *testing.T) {
106106
os.Setenv("REDIS_DSN", "redis://:password@prod-redis:6379")
107107
os.Setenv("AI_LLM_DEFAULT_PROVIDER", "openai")
108108
os.Setenv("OLLAMA_BASE_URL", "http://ollama.prod:11434")
109-
109+
110110
defer func() {
111111
os.Unsetenv("DATABASE_DSN")
112112
os.Unsetenv("DATABASE_MAX_OPEN_CONNS")
@@ -166,7 +166,7 @@ func TestRealConfigExample_WithEnvOverrides(t *testing.T) {
166166
if llm["default_provider"] != "openai" {
167167
t.Errorf("ai.llm.default_provider = %v, want openai", llm["default_provider"])
168168
}
169-
169+
170170
if providers, ok := llm["providers"].(map[string]any); ok {
171171
if ollama, ok := providers["ollama"].(map[string]any); ok {
172172
if ollama["base_url"] != "http://ollama.prod:11434" {
@@ -189,7 +189,7 @@ func TestRealConfigExample_MixedDefaults(t *testing.T) {
189189
// DATABASE_MAX_OPEN_CONNS not set - should use default 25
190190
os.Setenv("AI_LLM_DEFAULT_PROVIDER", "ollama")
191191
// OLLAMA_BASE_URL not set - should use default http://localhost:11434
192-
192+
193193
defer func() {
194194
os.Unsetenv("DATABASE_DSN")
195195
os.Unsetenv("AI_LLM_DEFAULT_PROVIDER")
@@ -231,7 +231,7 @@ func TestRealConfigExample_MixedDefaults(t *testing.T) {
231231
if llm["default_provider"] != "ollama" {
232232
t.Errorf("ai.llm.default_provider = %v, want ollama", llm["default_provider"])
233233
}
234-
234+
235235
if providers, ok := llm["providers"].(map[string]any); ok {
236236
if ollama, ok := providers["ollama"].(map[string]any); ok {
237237
// URL should use default
@@ -250,26 +250,25 @@ func TestRealConfigExample_MixedDefaults(t *testing.T) {
250250

251251
func ExampleFileSource_expandEnvWithDefaults() {
252252
// This example shows how the new bash-style default syntax works
253-
253+
254254
// Without environment variables set
255255
result1 := expandEnvWithDefaults("${DATABASE_DSN:-postgres://localhost:5432/mydb}")
256256
fmt.Println("Without env var:", result1)
257-
257+
258258
// With environment variable set
259259
os.Setenv("DATABASE_DSN", "postgres://prod:5432/proddb")
260260
result2 := expandEnvWithDefaults("${DATABASE_DSN:-postgres://localhost:5432/mydb}")
261261
fmt.Println("With env var:", result2)
262262
os.Unsetenv("DATABASE_DSN")
263-
263+
264264
// Complex example with multiple variables
265265
os.Setenv("DB_HOST", "prod-server")
266266
result3 := expandEnvWithDefaults("postgres://${DB_USER:-postgres}:${DB_PASS:-postgres}@${DB_HOST:-localhost}:${DB_PORT:-5432}/${DB_NAME:-mydb}")
267267
fmt.Println("Complex example:", result3)
268268
os.Unsetenv("DB_HOST")
269-
269+
270270
// Output:
271271
// Without env var: postgres://localhost:5432/mydb
272272
// With env var: postgres://prod:5432/proddb
273273
// Complex example: postgres://postgres:postgres@prod-server:5432/mydb
274274
}
275-

0 commit comments

Comments
 (0)