funapi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.claude/25-09-01-OPENAPI_IMPLEMENTATION.md +233 -0
- data/.claude/25-09-05-RESPONSE_SCHEMA.md +383 -0
- data/.claude/25-09-10-OPENAPI_PLAN.md +219 -0
- data/.claude/25-10-26-MIDDLEWARE_IMPLEMENTATION.md +230 -0
- data/.claude/25-10-26-MIDDLEWARE_PLAN.md +353 -0
- data/.claude/25-10-27-BACKGROUND_TASKS_ANALYSIS.md +325 -0
- data/.claude/25-10-27-DEPENDENCY_IMPLEMENTATION_SUMMARY.md +325 -0
- data/.claude/25-10-27-DEPENDENCY_INJECTION_PLAN.md +753 -0
- data/.claude/25-12-24-LIFECYCLE_HOOKS_PLAN.md +421 -0
- data/.claude/25-12-24-PUBLISHING_AND_DOGFOODING_PLAN.md +327 -0
- data/.claude/25-12-24-TEMPLATE_RENDERING_PLAN.md +704 -0
- data/.claude/DECISIONS.md +397 -0
- data/.claude/PROJECT_PLAN.md +80 -0
- data/.claude/TESTING_PLAN.md +285 -0
- data/.claude/TESTING_STATUS.md +157 -0
- data/.tool-versions +1 -0
- data/AGENTS.md +416 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +660 -0
- data/Rakefile +10 -0
- data/docs +8 -0
- data/docs-site/.gitignore +3 -0
- data/docs-site/Gemfile +9 -0
- data/docs-site/app.rb +138 -0
- data/docs-site/content/essential/handler.md +156 -0
- data/docs-site/content/essential/lifecycle.md +161 -0
- data/docs-site/content/essential/middleware.md +201 -0
- data/docs-site/content/essential/openapi.md +155 -0
- data/docs-site/content/essential/routing.md +123 -0
- data/docs-site/content/essential/validation.md +166 -0
- data/docs-site/content/getting-started/at-glance.md +82 -0
- data/docs-site/content/getting-started/key-concepts.md +150 -0
- data/docs-site/content/getting-started/quick-start.md +127 -0
- data/docs-site/content/index.md +81 -0
- data/docs-site/content/patterns/async-operations.md +137 -0
- data/docs-site/content/patterns/background-tasks.md +143 -0
- data/docs-site/content/patterns/database.md +175 -0
- data/docs-site/content/patterns/dependencies.md +141 -0
- data/docs-site/content/patterns/deployment.md +212 -0
- data/docs-site/content/patterns/error-handling.md +184 -0
- data/docs-site/content/patterns/response-schema.md +159 -0
- data/docs-site/content/patterns/templates.md +193 -0
- data/docs-site/content/patterns/testing.md +218 -0
- data/docs-site/mise.toml +2 -0
- data/docs-site/public/css/style.css +234 -0
- data/docs-site/templates/layouts/docs.html.erb +28 -0
- data/docs-site/templates/page.html.erb +3 -0
- data/docs-site/templates/partials/_nav.html.erb +19 -0
- data/examples/background_tasks_demo.rb +159 -0
- data/examples/demo_middleware.rb +55 -0
- data/examples/demo_openapi.rb +63 -0
- data/examples/dependency_block_demo.rb +150 -0
- data/examples/dependency_cleanup_demo.rb +146 -0
- data/examples/dependency_injection_demo.rb +200 -0
- data/examples/lifecycle_demo.rb +57 -0
- data/examples/middleware_demo.rb +74 -0
- data/examples/templates/layouts/application.html.erb +66 -0
- data/examples/templates/todos/_todo.html.erb +15 -0
- data/examples/templates/todos/index.html.erb +12 -0
- data/examples/templates_demo.rb +87 -0
- data/lib/funapi/application.rb +521 -0
- data/lib/funapi/async.rb +57 -0
- data/lib/funapi/background_tasks.rb +52 -0
- data/lib/funapi/config.rb +23 -0
- data/lib/funapi/database/sequel/fibered_connection_pool.rb +87 -0
- data/lib/funapi/dependency_wrapper.rb +66 -0
- data/lib/funapi/depends.rb +138 -0
- data/lib/funapi/exceptions.rb +72 -0
- data/lib/funapi/middleware/base.rb +13 -0
- data/lib/funapi/middleware/cors.rb +23 -0
- data/lib/funapi/middleware/request_logger.rb +32 -0
- data/lib/funapi/middleware/trusted_host.rb +34 -0
- data/lib/funapi/middleware.rb +4 -0
- data/lib/funapi/openapi/schema_converter.rb +85 -0
- data/lib/funapi/openapi/spec_generator.rb +179 -0
- data/lib/funapi/router.rb +43 -0
- data/lib/funapi/schema.rb +65 -0
- data/lib/funapi/server/falcon.rb +38 -0
- data/lib/funapi/template_response.rb +17 -0
- data/lib/funapi/templates.rb +111 -0
- data/lib/funapi/version.rb +5 -0
- data/lib/funapi.rb +14 -0
- data/sig/fun_api.rbs +499 -0
- metadata +220 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# FunApi - Architectural Decisions
|
|
2
|
+
|
|
3
|
+
This document records key architectural and design decisions made during the development of FunApi.
|
|
4
|
+
|
|
5
|
+
## Testing Strategy (2024-10-26)
|
|
6
|
+
|
|
7
|
+
### Decision: Flat Test Structure
|
|
8
|
+
|
|
9
|
+
**Context**: Needed to organize tests for the framework. Options were nested directories (unit/, integration/) vs flat structure.
|
|
10
|
+
|
|
11
|
+
**Decision**: Use flat test structure following Sidekiq's pattern.
|
|
12
|
+
|
|
13
|
+
**Rationale**:
|
|
14
|
+
- Simplicity - easier to find tests
|
|
15
|
+
- Flexibility - tests can be unit or integration as needed
|
|
16
|
+
- No artificial boundaries - test what matters, not how
|
|
17
|
+
- Proven pattern - Sidekiq has excellent test organization
|
|
18
|
+
- Library-friendly - FunApi is a library, not an application
|
|
19
|
+
|
|
20
|
+
**Implementation**:
|
|
21
|
+
```
|
|
22
|
+
test/
|
|
23
|
+
├── test_helper.rb
|
|
24
|
+
├── test_fun_api.rb # Basic smoke tests
|
|
25
|
+
├── test_router.rb # Router functionality
|
|
26
|
+
├── test_schema.rb # Validation
|
|
27
|
+
├── test_middleware.rb # Middleware chain
|
|
28
|
+
├── test_validation.rb # Request validation
|
|
29
|
+
├── test_response_schema.rb
|
|
30
|
+
├── test_async.rb
|
|
31
|
+
└── test_exceptions.rb
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Result**: 90 tests, 217 assertions, all passing in ~220ms
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Middleware System (2024-10-26)
|
|
39
|
+
|
|
40
|
+
### Decision: Rack-Compatible Middleware with FastAPI-Style Convenience
|
|
41
|
+
|
|
42
|
+
**Context**: Needed middleware support. Options: Build custom system vs leverage Rack ecosystem.
|
|
43
|
+
|
|
44
|
+
**Decision**: Support standard Rack middleware PLUS provide FastAPI-style convenience methods.
|
|
45
|
+
|
|
46
|
+
**Rationale**:
|
|
47
|
+
- Leverage battle-tested Rack ecosystem (15+ years, 100+ middleware)
|
|
48
|
+
- No reinvention - delegate to proven libraries (rack-cors, Rack::Deflater)
|
|
49
|
+
- FastAPI-like DX - convenience methods for common use cases
|
|
50
|
+
- Zero lock-in - users can use any Rack middleware
|
|
51
|
+
- Async compatible - Rack 3.0+ supports async natively
|
|
52
|
+
|
|
53
|
+
**Implementation**:
|
|
54
|
+
```ruby
|
|
55
|
+
# Standard Rack middleware
|
|
56
|
+
app.use Rack::Attack
|
|
57
|
+
app.use Rack::Session::Cookie, secret: 'key'
|
|
58
|
+
|
|
59
|
+
# FunApi convenience methods
|
|
60
|
+
app.add_cors(allow_origins: ['*'])
|
|
61
|
+
app.add_trusted_host(allowed_hosts: ['example.com'])
|
|
62
|
+
app.add_request_logger
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Built-in Middleware**:
|
|
66
|
+
- CORS (wraps rack-cors)
|
|
67
|
+
- TrustedHost (custom implementation)
|
|
68
|
+
- RequestLogger (custom with async awareness)
|
|
69
|
+
- Gzip (delegates to Rack::Deflater)
|
|
70
|
+
|
|
71
|
+
**Result**: Full Rack compatibility + excellent developer experience
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Middleware Execution Order (2024-10-26)
|
|
76
|
+
|
|
77
|
+
### Decision: Standard Rack Ordering (LIFO wrapping, FIFO execution)
|
|
78
|
+
|
|
79
|
+
**Context**: How should middleware execute when multiple are registered?
|
|
80
|
+
|
|
81
|
+
**Decision**: First registered middleware runs first (outermost layer).
|
|
82
|
+
|
|
83
|
+
**Rationale**:
|
|
84
|
+
- Standard Ruby/Rack convention
|
|
85
|
+
- Expected by Rack developers
|
|
86
|
+
- Well-documented behavior
|
|
87
|
+
- Works with all existing Rack middleware
|
|
88
|
+
|
|
89
|
+
**Example**:
|
|
90
|
+
```ruby
|
|
91
|
+
app.use Middleware1 # Executes FIRST
|
|
92
|
+
app.use Middleware2 # Executes SECOND
|
|
93
|
+
# Router executes LAST
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Request flow: MW1 → MW2 → Router → MW2 → MW1
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## OpenAPI Implementation (2024-09)
|
|
101
|
+
|
|
102
|
+
### Decision: Automatic Schema Extraction
|
|
103
|
+
|
|
104
|
+
**Context**: How to generate OpenAPI specs from dry-schema definitions?
|
|
105
|
+
|
|
106
|
+
**Decision**: Introspect dry-schema at runtime and convert to JSON Schema.
|
|
107
|
+
|
|
108
|
+
**Rationale**:
|
|
109
|
+
- No manual duplication
|
|
110
|
+
- Single source of truth (the schema)
|
|
111
|
+
- Automatic documentation updates
|
|
112
|
+
- FastAPI-like experience
|
|
113
|
+
|
|
114
|
+
**Implementation**:
|
|
115
|
+
- SchemaConverter: dry-schema → JSON Schema
|
|
116
|
+
- SpecGenerator: routes + schemas → OpenAPI 3.0.3 spec
|
|
117
|
+
- Auto-register /openapi.json and /docs endpoints
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Response Schema Filtering (2024-09)
|
|
122
|
+
|
|
123
|
+
### Decision: Security-First Response Filtering
|
|
124
|
+
|
|
125
|
+
**Context**: How to handle sensitive data in responses (passwords, tokens)?
|
|
126
|
+
|
|
127
|
+
**Decision**: Response schemas filter output to only include specified fields.
|
|
128
|
+
|
|
129
|
+
**Rationale**:
|
|
130
|
+
- Security by default
|
|
131
|
+
- Prevent accidental data leaks
|
|
132
|
+
- FastAPI's response_model pattern
|
|
133
|
+
- Explicit is better than implicit
|
|
134
|
+
|
|
135
|
+
**Example**:
|
|
136
|
+
```ruby
|
|
137
|
+
UserOutputSchema = FunApi::Schema.define do
|
|
138
|
+
required(:id).filled(:integer)
|
|
139
|
+
required(:name).filled(:string)
|
|
140
|
+
# password NOT included
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
api.get '/user', response_schema: UserOutputSchema do
|
|
144
|
+
user = { id: 1, name: 'Alice', password: 'secret' }
|
|
145
|
+
[user, 200] # Password automatically filtered
|
|
146
|
+
end
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Async-First Design (2024-09)
|
|
152
|
+
|
|
153
|
+
### Decision: Async::Task as Third Handler Parameter
|
|
154
|
+
|
|
155
|
+
**Context**: How to expose async capabilities to route handlers?
|
|
156
|
+
|
|
157
|
+
**Decision**: Pass `Async::Task` as third parameter to all handlers.
|
|
158
|
+
|
|
159
|
+
**Rationale**:
|
|
160
|
+
- Explicit async access
|
|
161
|
+
- No magic globals
|
|
162
|
+
- True concurrent execution
|
|
163
|
+
- Ruby's Async library is mature
|
|
164
|
+
|
|
165
|
+
**Example**:
|
|
166
|
+
```ruby
|
|
167
|
+
api.get '/dashboard' do |input, req, task|
|
|
168
|
+
user_task = task.async { fetch_user }
|
|
169
|
+
posts_task = task.async { fetch_posts }
|
|
170
|
+
|
|
171
|
+
[{ user: user_task.wait, posts: posts_task.wait }, 200]
|
|
172
|
+
end
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Trade-off**: Three parameters instead of two, but explicitness wins.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Router Root Route Fix (2024-10-26)
|
|
180
|
+
|
|
181
|
+
### Decision: Special-Case Root Route `/`
|
|
182
|
+
|
|
183
|
+
**Context**: Regex generation failed for `/` route (empty regex).
|
|
184
|
+
|
|
185
|
+
**Decision**: Explicitly handle `/` as special case before regex generation.
|
|
186
|
+
|
|
187
|
+
**Rationale**:
|
|
188
|
+
- `/` is common and important
|
|
189
|
+
- Regex `/\A\z/` doesn't match `/`
|
|
190
|
+
- Simple fix with no overhead
|
|
191
|
+
- Prevents future bugs
|
|
192
|
+
|
|
193
|
+
**Implementation**:
|
|
194
|
+
```ruby
|
|
195
|
+
if path == '/'
|
|
196
|
+
regex = '/'
|
|
197
|
+
else
|
|
198
|
+
# normal regex generation
|
|
199
|
+
end
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Validation Error Format (2024-09)
|
|
205
|
+
|
|
206
|
+
### Decision: FastAPI-Compatible Error Format
|
|
207
|
+
|
|
208
|
+
**Context**: How to structure validation errors?
|
|
209
|
+
|
|
210
|
+
**Decision**: Use FastAPI's error format with `detail` array.
|
|
211
|
+
|
|
212
|
+
**Rationale**:
|
|
213
|
+
- Familiar to FastAPI users
|
|
214
|
+
- Structured and parseable
|
|
215
|
+
- Clear error location information
|
|
216
|
+
- Industry standard
|
|
217
|
+
|
|
218
|
+
**Format**:
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"detail": [
|
|
222
|
+
{
|
|
223
|
+
"loc": ["body", "email"],
|
|
224
|
+
"msg": "is missing",
|
|
225
|
+
"type": "value_error"
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Schema Validation (2024-09)
|
|
234
|
+
|
|
235
|
+
### Decision: dry-schema Over ActiveModel
|
|
236
|
+
|
|
237
|
+
**Context**: Which validation library to use?
|
|
238
|
+
|
|
239
|
+
**Decision**: Use dry-schema for validation.
|
|
240
|
+
|
|
241
|
+
**Rationale**:
|
|
242
|
+
- Lightweight (no Rails dependency)
|
|
243
|
+
- Functional approach (no mutations)
|
|
244
|
+
- Better API error messages
|
|
245
|
+
- Type coercion built-in
|
|
246
|
+
- Fast and battle-tested
|
|
247
|
+
|
|
248
|
+
**Trade-off**: Less familiar to Rails developers, but better fit for APIs.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Server Choice (2024-09)
|
|
253
|
+
|
|
254
|
+
### Decision: Falcon as Default Server
|
|
255
|
+
|
|
256
|
+
**Context**: Which Rack server to recommend?
|
|
257
|
+
|
|
258
|
+
**Decision**: Falcon for development and production.
|
|
259
|
+
|
|
260
|
+
**Rationale**:
|
|
261
|
+
- Native async support
|
|
262
|
+
- Built for Async library
|
|
263
|
+
- Better concurrency for I/O-bound work
|
|
264
|
+
- Aligns with async-first philosophy
|
|
265
|
+
|
|
266
|
+
**Note**: Any Rack 3+ server will work (Puma, Unicorn, etc.)
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Documentation Strategy (2024-10-26)
|
|
271
|
+
|
|
272
|
+
### Decision: Dual Documentation (README + AGENTS.md)
|
|
273
|
+
|
|
274
|
+
**Context**: How to document for both humans and AI agents?
|
|
275
|
+
|
|
276
|
+
**Decision**:
|
|
277
|
+
- README.md for human users (features, examples)
|
|
278
|
+
- AGENTS.md for AI coding agents (architecture, testing, conventions)
|
|
279
|
+
|
|
280
|
+
**Rationale**:
|
|
281
|
+
- Following agents.md standard
|
|
282
|
+
- Different audiences need different info
|
|
283
|
+
- Keeps README concise
|
|
284
|
+
- Provides deep context for agents
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Examples as Documentation (2024-10-26)
|
|
289
|
+
|
|
290
|
+
### Decision: Executable Examples > Test Assertions
|
|
291
|
+
|
|
292
|
+
**Context**: Should examples be automated tests?
|
|
293
|
+
|
|
294
|
+
**Decision**: Keep examples simple and executable, separate from test suite.
|
|
295
|
+
|
|
296
|
+
**Rationale**:
|
|
297
|
+
- Examples show real usage
|
|
298
|
+
- Can be run manually for smoke testing
|
|
299
|
+
- Don't clutter with assertions
|
|
300
|
+
- Living documentation
|
|
301
|
+
- Test suite covers thorough testing
|
|
302
|
+
|
|
303
|
+
**Organization**:
|
|
304
|
+
- `examples/` - Runnable demos
|
|
305
|
+
- `test/` - Automated tests
|
|
306
|
+
- `test/demo_*.rb` - Reference demos (not automated)
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Dependency Injection (2024-10-27)
|
|
311
|
+
|
|
312
|
+
### Decision: Block-Based Dependencies with Cleanup
|
|
313
|
+
|
|
314
|
+
**Context**: Need DI with automatic resource cleanup. Evaluated: dry-system, tuples, context managers, blocks.
|
|
315
|
+
|
|
316
|
+
**Decision**: Ruby blocks with `ensure` for lifecycle management.
|
|
317
|
+
|
|
318
|
+
**Rationale**:
|
|
319
|
+
- Most idiomatic Ruby (matches `File.open`, `Mutex.synchronize`)
|
|
320
|
+
- `ensure` guarantees cleanup, even on errors
|
|
321
|
+
- FastAPI parity (context managers)
|
|
322
|
+
- Lightweight (uses Fiber, no heavy deps)
|
|
323
|
+
|
|
324
|
+
**Pattern**:
|
|
325
|
+
```ruby
|
|
326
|
+
api.register(:db) do |provide|
|
|
327
|
+
conn = Database.connect
|
|
328
|
+
provide.call(conn)
|
|
329
|
+
ensure
|
|
330
|
+
conn.close
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
api.get '/users', depends: [:db] do |input, req, task, db:|
|
|
334
|
+
[db.all_users, 200]
|
|
335
|
+
end
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Features**:
|
|
339
|
+
- Request-scoped caching
|
|
340
|
+
- Nested dependencies with `FunApi::Depends()`
|
|
341
|
+
- Three patterns: simple, tuple (compat), block (preferred)
|
|
342
|
+
- Cleanup in `ensure` after response sent
|
|
343
|
+
|
|
344
|
+
**Result**: 121 tests passing, FastAPI-aligned.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Dependency Injection: Not dry-system (2024-10-27)
|
|
349
|
+
|
|
350
|
+
### Decision: Custom Lightweight DI
|
|
351
|
+
|
|
352
|
+
**Context**: dry-system exists but designed for different use case.
|
|
353
|
+
|
|
354
|
+
**Decision**: Build custom DI using only dry-container.
|
|
355
|
+
|
|
356
|
+
**Rationale**:
|
|
357
|
+
- dry-system = constructor injection, we need parameter injection
|
|
358
|
+
- FunApi is minimal, dry-system is comprehensive
|
|
359
|
+
- Custom solution maps 1:1 to FastAPI's `Depends()`
|
|
360
|
+
|
|
361
|
+
**Used**: dry-container (registry only)
|
|
362
|
+
**Skipped**: dry-system, dry-auto_inject, dry-effects
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Future Decisions Pending
|
|
367
|
+
|
|
368
|
+
1. ~~**Dependency Injection**~~ ✅ Done
|
|
369
|
+
2. **Background Tasks**: Post-response execution
|
|
370
|
+
3. **Path Parameter Types**: Type coercion/validation
|
|
371
|
+
4. **WebSocket Support**: Async integration
|
|
372
|
+
5. **Content Negotiation**: JSON + others
|
|
373
|
+
6. **Global Dependencies**: Apply to all routes
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Non-Decisions (Explicitly Rejected)
|
|
378
|
+
|
|
379
|
+
### Rails Integration
|
|
380
|
+
**Decision**: FunApi remains independent of Rails.
|
|
381
|
+
**Reason**: Keep it minimal, different use case.
|
|
382
|
+
|
|
383
|
+
### Magic DSLs
|
|
384
|
+
**Decision**: No heavy DSLs or metaprogramming.
|
|
385
|
+
**Reason**: Explicit is better than implicit.
|
|
386
|
+
|
|
387
|
+
### Database Integration
|
|
388
|
+
**Decision**: No built-in ORM or database layer.
|
|
389
|
+
**Reason**: Users choose their own (Sequel, ROM, ActiveRecord).
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Change Log
|
|
394
|
+
|
|
395
|
+
- 2024-10-27: Added dependency injection decisions
|
|
396
|
+
- 2024-10-26: Testing, middleware, documentation strategies
|
|
397
|
+
- 2024-09: Initial core framework decisions
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Project Overview
|
|
2
|
+
|
|
3
|
+
A minimal, async-first Ruby web framework inspired by FastAPI. Built on top of Falcon and dry-schema, FunApi provides a simple, performant way to build web APIs in Ruby with a focus on developer experience.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
FunApi aims to bring FastAPI's excellent developer experience to Ruby by providing:
|
|
8
|
+
|
|
9
|
+
- **Async-first**: Built on Ruby's Async library and Falcon server for high-performance concurrent operations
|
|
10
|
+
- **Simple validation**: Using dry-schema for straightforward request validation
|
|
11
|
+
- **Minimal magic**: Clear, explicit APIs without heavy DSLs
|
|
12
|
+
- **Easy to start**: Get an API up and running in minutes
|
|
13
|
+
- **Auto-documentation**: Automatic OpenAPI/Swagger documentation generation
|
|
14
|
+
|
|
15
|
+
## Current Status (Updated 2024-10-27)
|
|
16
|
+
|
|
17
|
+
### ✅ Production-Ready Features Implemented:
|
|
18
|
+
|
|
19
|
+
**Core Framework**
|
|
20
|
+
- ✅ Async-first request handling with Async::Task
|
|
21
|
+
- ✅ Route definition with path params
|
|
22
|
+
- ✅ Request validation (body/query) with array support
|
|
23
|
+
- ✅ Response schema validation and filtering
|
|
24
|
+
- ✅ FastAPI-style error responses
|
|
25
|
+
- ✅ Falcon server integration
|
|
26
|
+
|
|
27
|
+
**Dependency Injection** ⭐ NEW
|
|
28
|
+
- ✅ Block-based dependencies with `ensure` cleanup
|
|
29
|
+
- ✅ Request-scoped caching
|
|
30
|
+
- ✅ Nested dependencies with `FunApi::Depends()`
|
|
31
|
+
- ✅ Three patterns: simple, tuple, block (Ruby-idiomatic)
|
|
32
|
+
- ✅ Automatic resource lifecycle (setup → use → cleanup)
|
|
33
|
+
- ✅ FastAPI `Depends()` parity
|
|
34
|
+
|
|
35
|
+
**Documentation**
|
|
36
|
+
- ✅ OpenAPI/Swagger documentation generation
|
|
37
|
+
- ✅ Automatic /docs and /openapi.json endpoints
|
|
38
|
+
- ✅ Schema introspection and conversion
|
|
39
|
+
|
|
40
|
+
**Middleware**
|
|
41
|
+
- ✅ Rack-compatible middleware system
|
|
42
|
+
- ✅ Built-in middleware (CORS, TrustedHost, RequestLogger, Gzip)
|
|
43
|
+
- ✅ FastAPI-style convenience methods
|
|
44
|
+
- ✅ Full ecosystem support (rack-attack, rack-cache, etc.)
|
|
45
|
+
|
|
46
|
+
**Testing**
|
|
47
|
+
- ✅ Comprehensive test suite (121 tests, 281 assertions)
|
|
48
|
+
- ✅ Router, schema, middleware, async, exceptions
|
|
49
|
+
- ✅ Dependency injection (31 new tests)
|
|
50
|
+
- ✅ Fast execution (~200ms)
|
|
51
|
+
|
|
52
|
+
### 📋 What to Tackle Next
|
|
53
|
+
|
|
54
|
+
### Immediate Priority
|
|
55
|
+
|
|
56
|
+
1. **Background Tasks** - Post-response execution
|
|
57
|
+
- Fire-and-forget after response sent
|
|
58
|
+
- Email, logging, cleanup tasks
|
|
59
|
+
- Leverages async foundation
|
|
60
|
+
- Why: Common production pattern, natural async fit
|
|
61
|
+
|
|
62
|
+
2. **Lifecycle Hooks** - Startup/shutdown
|
|
63
|
+
- App initialization/cleanup
|
|
64
|
+
- Exception handlers
|
|
65
|
+
- Why: Production deployment needs
|
|
66
|
+
|
|
67
|
+
### Secondary Priority
|
|
68
|
+
|
|
69
|
+
3. **Path Parameter Types** - Type coercion/validation
|
|
70
|
+
4. **File Uploads** - Multipart handling
|
|
71
|
+
5. **WebSocket Support** - Real-time connections
|
|
72
|
+
6. **Global Dependencies** - Apply to all routes
|
|
73
|
+
7. **Dependency Overrides** - Testing utilities
|
|
74
|
+
|
|
75
|
+
### Nice to Have
|
|
76
|
+
|
|
77
|
+
- Content negotiation (JSON, XML, MessagePack)
|
|
78
|
+
- TestClient utilities
|
|
79
|
+
- Security schemes (OAuth2, JWT helpers)
|
|
80
|
+
- Response streaming
|