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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/25-09-01-OPENAPI_IMPLEMENTATION.md +233 -0
  3. data/.claude/25-09-05-RESPONSE_SCHEMA.md +383 -0
  4. data/.claude/25-09-10-OPENAPI_PLAN.md +219 -0
  5. data/.claude/25-10-26-MIDDLEWARE_IMPLEMENTATION.md +230 -0
  6. data/.claude/25-10-26-MIDDLEWARE_PLAN.md +353 -0
  7. data/.claude/25-10-27-BACKGROUND_TASKS_ANALYSIS.md +325 -0
  8. data/.claude/25-10-27-DEPENDENCY_IMPLEMENTATION_SUMMARY.md +325 -0
  9. data/.claude/25-10-27-DEPENDENCY_INJECTION_PLAN.md +753 -0
  10. data/.claude/25-12-24-LIFECYCLE_HOOKS_PLAN.md +421 -0
  11. data/.claude/25-12-24-PUBLISHING_AND_DOGFOODING_PLAN.md +327 -0
  12. data/.claude/25-12-24-TEMPLATE_RENDERING_PLAN.md +704 -0
  13. data/.claude/DECISIONS.md +397 -0
  14. data/.claude/PROJECT_PLAN.md +80 -0
  15. data/.claude/TESTING_PLAN.md +285 -0
  16. data/.claude/TESTING_STATUS.md +157 -0
  17. data/.tool-versions +1 -0
  18. data/AGENTS.md +416 -0
  19. data/CHANGELOG.md +5 -0
  20. data/CODE_OF_CONDUCT.md +132 -0
  21. data/LICENSE.txt +21 -0
  22. data/README.md +660 -0
  23. data/Rakefile +10 -0
  24. data/docs +8 -0
  25. data/docs-site/.gitignore +3 -0
  26. data/docs-site/Gemfile +9 -0
  27. data/docs-site/app.rb +138 -0
  28. data/docs-site/content/essential/handler.md +156 -0
  29. data/docs-site/content/essential/lifecycle.md +161 -0
  30. data/docs-site/content/essential/middleware.md +201 -0
  31. data/docs-site/content/essential/openapi.md +155 -0
  32. data/docs-site/content/essential/routing.md +123 -0
  33. data/docs-site/content/essential/validation.md +166 -0
  34. data/docs-site/content/getting-started/at-glance.md +82 -0
  35. data/docs-site/content/getting-started/key-concepts.md +150 -0
  36. data/docs-site/content/getting-started/quick-start.md +127 -0
  37. data/docs-site/content/index.md +81 -0
  38. data/docs-site/content/patterns/async-operations.md +137 -0
  39. data/docs-site/content/patterns/background-tasks.md +143 -0
  40. data/docs-site/content/patterns/database.md +175 -0
  41. data/docs-site/content/patterns/dependencies.md +141 -0
  42. data/docs-site/content/patterns/deployment.md +212 -0
  43. data/docs-site/content/patterns/error-handling.md +184 -0
  44. data/docs-site/content/patterns/response-schema.md +159 -0
  45. data/docs-site/content/patterns/templates.md +193 -0
  46. data/docs-site/content/patterns/testing.md +218 -0
  47. data/docs-site/mise.toml +2 -0
  48. data/docs-site/public/css/style.css +234 -0
  49. data/docs-site/templates/layouts/docs.html.erb +28 -0
  50. data/docs-site/templates/page.html.erb +3 -0
  51. data/docs-site/templates/partials/_nav.html.erb +19 -0
  52. data/examples/background_tasks_demo.rb +159 -0
  53. data/examples/demo_middleware.rb +55 -0
  54. data/examples/demo_openapi.rb +63 -0
  55. data/examples/dependency_block_demo.rb +150 -0
  56. data/examples/dependency_cleanup_demo.rb +146 -0
  57. data/examples/dependency_injection_demo.rb +200 -0
  58. data/examples/lifecycle_demo.rb +57 -0
  59. data/examples/middleware_demo.rb +74 -0
  60. data/examples/templates/layouts/application.html.erb +66 -0
  61. data/examples/templates/todos/_todo.html.erb +15 -0
  62. data/examples/templates/todos/index.html.erb +12 -0
  63. data/examples/templates_demo.rb +87 -0
  64. data/lib/funapi/application.rb +521 -0
  65. data/lib/funapi/async.rb +57 -0
  66. data/lib/funapi/background_tasks.rb +52 -0
  67. data/lib/funapi/config.rb +23 -0
  68. data/lib/funapi/database/sequel/fibered_connection_pool.rb +87 -0
  69. data/lib/funapi/dependency_wrapper.rb +66 -0
  70. data/lib/funapi/depends.rb +138 -0
  71. data/lib/funapi/exceptions.rb +72 -0
  72. data/lib/funapi/middleware/base.rb +13 -0
  73. data/lib/funapi/middleware/cors.rb +23 -0
  74. data/lib/funapi/middleware/request_logger.rb +32 -0
  75. data/lib/funapi/middleware/trusted_host.rb +34 -0
  76. data/lib/funapi/middleware.rb +4 -0
  77. data/lib/funapi/openapi/schema_converter.rb +85 -0
  78. data/lib/funapi/openapi/spec_generator.rb +179 -0
  79. data/lib/funapi/router.rb +43 -0
  80. data/lib/funapi/schema.rb +65 -0
  81. data/lib/funapi/server/falcon.rb +38 -0
  82. data/lib/funapi/template_response.rb +17 -0
  83. data/lib/funapi/templates.rb +111 -0
  84. data/lib/funapi/version.rb +5 -0
  85. data/lib/funapi.rb +14 -0
  86. data/sig/fun_api.rbs +499 -0
  87. metadata +220 -0
data/sig/fun_api.rbs ADDED
@@ -0,0 +1,499 @@
1
+ # Type definitions for FunApi
2
+ # A minimal, async-first Ruby web framework inspired by FastAPI
3
+
4
+ module FunApi
5
+ VERSION: String
6
+
7
+ # Base error class for FunApi
8
+ class Error < StandardError
9
+ end
10
+
11
+ # Type aliases for common patterns
12
+ # Using permissive types for third-party integrations
13
+
14
+ # Rack environment hash
15
+ type rack_env = Hash[String, untyped]
16
+
17
+ # Standard Rack response: [status, headers, body]
18
+ type rack_response = [Integer, Hash[String, String], Array[String]]
19
+
20
+ # Route handler input hash with path params, query params, and body
21
+ type route_input = {
22
+ path: Hash[String, String],
23
+ query: Hash[Symbol, untyped],
24
+ body: untyped
25
+ }
26
+
27
+ # Route handler return value: [data, status_code]
28
+ type handler_return = [untyped, Integer]
29
+
30
+ # OpenAPI configuration
31
+ type openapi_config = {
32
+ title: String,
33
+ version: String,
34
+ description: String
35
+ }
36
+
37
+ # Dependency specification: array of symbols or hash mapping names to dependencies
38
+ type depends_spec = Array[Symbol] | Hash[Symbol, untyped] | nil
39
+
40
+ # The main application class - entry point for FunApi
41
+ #
42
+ # Example:
43
+ # app = FunApi::App.new(title: "My API") do |api|
44
+ # api.get '/hello' do |input, req, task|
45
+ # [{ message: 'Hello!' }, 200]
46
+ # end
47
+ # end
48
+ #
49
+ class App
50
+ attr_reader openapi_config: openapi_config
51
+ attr_reader container: untyped
52
+ attr_reader startup_hooks: Array[^() -> void]
53
+ attr_reader shutdown_hooks: Array[^() -> void]
54
+
55
+ # Create a new FunApi application
56
+ #
57
+ # @param title OpenAPI documentation title
58
+ # @param version API version string
59
+ # @param description API description for OpenAPI docs
60
+ # @yield [api] Configuration block receiving the app instance
61
+ def initialize: (?title: String, ?version: String, ?description: String) ?{ (App) -> void } -> void
62
+
63
+ # Register a startup hook to run before the server accepts requests
64
+ #
65
+ # Example:
66
+ # api.on_startup { DB.connect }
67
+ def on_startup: () { () -> void } -> self
68
+
69
+ # Register a shutdown hook to run when the server stops
70
+ #
71
+ # Example:
72
+ # api.on_shutdown { DB.disconnect }
73
+ def on_shutdown: () { () -> void } -> self
74
+
75
+ # Run all registered startup hooks
76
+ def run_startup_hooks: () -> void
77
+
78
+ # Run all registered shutdown hooks (errors are logged but don't stop other hooks)
79
+ def run_shutdown_hooks: () -> void
80
+
81
+ # Register a dependency in the container
82
+ #
83
+ # Example:
84
+ # api.register(:db) { Database.new }
85
+ # api.register(:logger) { Logger.new(STDOUT) }
86
+ def register: (Symbol key) { () -> untyped } -> void
87
+
88
+ # Resolve a dependency from the container
89
+ def resolve: (Symbol key) -> untyped
90
+
91
+ # Define a GET route
92
+ #
93
+ # @param path URL path pattern (e.g., '/users/:id')
94
+ # @param query Optional query parameter schema
95
+ # @param response_schema Optional response validation schema
96
+ # @param depends Dependencies to inject into the handler
97
+ # @yield [input, req, task] Route handler block
98
+ def get: (
99
+ String path,
100
+ ?query: untyped,
101
+ ?response_schema: untyped,
102
+ ?depends: depends_spec
103
+ ) { (route_input, untyped, untyped, **untyped) -> (handler_return | TemplateResponse) } -> void
104
+
105
+ # Define a POST route
106
+ #
107
+ # @param path URL path pattern
108
+ # @param body Optional request body schema
109
+ # @param query Optional query parameter schema
110
+ # @param response_schema Optional response validation schema
111
+ # @param depends Dependencies to inject into the handler
112
+ # @yield [input, req, task] Route handler block
113
+ def post: (
114
+ String path,
115
+ ?body: untyped,
116
+ ?query: untyped,
117
+ ?response_schema: untyped,
118
+ ?depends: depends_spec
119
+ ) { (route_input, untyped, untyped, **untyped) -> (handler_return | TemplateResponse) } -> void
120
+
121
+ # Define a PUT route
122
+ def put: (
123
+ String path,
124
+ ?body: untyped,
125
+ ?query: untyped,
126
+ ?response_schema: untyped,
127
+ ?depends: depends_spec
128
+ ) { (route_input, untyped, untyped, **untyped) -> (handler_return | TemplateResponse) } -> void
129
+
130
+ # Define a PATCH route
131
+ def patch: (
132
+ String path,
133
+ ?body: untyped,
134
+ ?query: untyped,
135
+ ?response_schema: untyped,
136
+ ?depends: depends_spec
137
+ ) { (route_input, untyped, untyped, **untyped) -> (handler_return | TemplateResponse) } -> void
138
+
139
+ # Define a DELETE route
140
+ def delete: (
141
+ String path,
142
+ ?query: untyped,
143
+ ?response_schema: untyped,
144
+ ?depends: depends_spec
145
+ ) { (route_input, untyped, untyped, **untyped) -> (handler_return | TemplateResponse) } -> void
146
+
147
+ # Add Rack middleware to the stack
148
+ #
149
+ # Example:
150
+ # api.use Rack::Session::Cookie, secret: 'key'
151
+ def use: (Class middleware, *untyped args) ?{ () -> void } -> self
152
+
153
+ # Add CORS middleware with sensible defaults
154
+ #
155
+ # Example:
156
+ # api.add_cors(allow_origins: ['http://localhost:3000'])
157
+ def add_cors: (
158
+ ?allow_origins: Array[String],
159
+ ?allow_methods: Array[String],
160
+ ?allow_headers: Array[String],
161
+ ?expose_headers: Array[String],
162
+ ?max_age: Integer,
163
+ ?allow_credentials: bool
164
+ ) -> void
165
+
166
+ # Add trusted host validation middleware
167
+ #
168
+ # Example:
169
+ # api.add_trusted_host(allowed_hosts: ['example.com', /\.example\.com$/])
170
+ def add_trusted_host: (allowed_hosts: Array[String | Regexp]) -> void
171
+
172
+ # Add request logging middleware
173
+ def add_request_logger: (?logger: untyped, ?level: Symbol) -> void
174
+
175
+ # Add gzip compression for JSON responses
176
+ def add_gzip: () -> void
177
+
178
+ # Rack interface - called by the web server
179
+ def call: (rack_env env) -> rack_response
180
+ end
181
+
182
+ # Schema validation using dry-schema
183
+ #
184
+ # Example:
185
+ # UserSchema = FunApi::Schema.define do
186
+ # required(:name).filled(:string)
187
+ # required(:email).filled(:string)
188
+ # end
189
+ #
190
+ class Schema
191
+ # Define a new validation schema using dry-schema DSL
192
+ #
193
+ # Returns a Dry::Schema::Params object
194
+ def self.define: () { () -> void } -> untyped
195
+
196
+ # Validate data against a schema, raising ValidationError on failure
197
+ #
198
+ # @param schema The schema to validate against (or array with single schema for arrays)
199
+ # @param data The data to validate
200
+ # @param location Error location prefix ('body', 'query')
201
+ # @return Validated and coerced data
202
+ def self.validate: (untyped schema, untyped data, ?location: String) -> untyped
203
+
204
+ # Validate response data, raising HTTPException on failure
205
+ def self.validate_response: (untyped schema, untyped data) -> untyped
206
+ end
207
+
208
+ # HTTP exception for returning error responses
209
+ #
210
+ # Example:
211
+ # raise FunApi::HTTPException.new(status_code: 404, detail: "User not found")
212
+ #
213
+ class HTTPException < StandardError
214
+ attr_reader status_code: Integer
215
+ attr_reader detail: String | Array[Hash[Symbol, untyped]]
216
+ attr_reader headers: Hash[String, String]
217
+
218
+ # Create an HTTP exception
219
+ #
220
+ # @param status_code HTTP status code (e.g., 404, 500)
221
+ # @param detail Error message or structured error details
222
+ # @param headers Additional response headers
223
+ def initialize: (status_code: Integer, ?detail: String?, ?headers: Hash[String, String]?) -> void
224
+
225
+ # Convert to Rack response format
226
+ def to_response: () -> rack_response
227
+ end
228
+
229
+ # Validation error with detailed field-level errors
230
+ #
231
+ # Automatically raised by Schema.validate on validation failure
232
+ #
233
+ class ValidationError < HTTPException
234
+ attr_reader errors: untyped
235
+
236
+ # Create a validation error from schema errors
237
+ def initialize: (errors: untyped, ?headers: Hash[String, String]?) -> void
238
+
239
+ def to_response: () -> rack_response
240
+ end
241
+
242
+ # Error raised when a template file is not found
243
+ class TemplateNotFoundError < StandardError
244
+ attr_reader template_name: String
245
+
246
+ def initialize: (String template_name) -> void
247
+ end
248
+
249
+ # ERB template renderer with layout and partial support
250
+ #
251
+ # Example:
252
+ # templates = FunApi::Templates.new(
253
+ # directory: 'templates',
254
+ # layout: 'layouts/application.html.erb'
255
+ # )
256
+ #
257
+ # api.get '/' do |input, req, task|
258
+ # templates.response('home.html.erb', title: 'Home')
259
+ # end
260
+ #
261
+ class Templates
262
+ # Create a new template renderer
263
+ #
264
+ # @param directory Path to templates directory
265
+ # @param layout Optional default layout template path
266
+ def initialize: (directory: String, ?layout: String?) -> void
267
+
268
+ # Create a scoped renderer with a different default layout
269
+ #
270
+ # Example:
271
+ # admin = templates.with_layout('layouts/admin.html.erb')
272
+ def with_layout: (String layout) -> ScopedTemplates
273
+
274
+ # Render a template to a string
275
+ #
276
+ # @param name Template file path relative to directory
277
+ # @param layout Override layout (false to disable, string to use different layout)
278
+ # @param context Template variables as keyword arguments
279
+ def render: (String name, ?layout: String | false | nil, **untyped context) -> String
280
+
281
+ # Render a template and wrap in TemplateResponse
282
+ #
283
+ # @param name Template file path
284
+ # @param status HTTP status code
285
+ # @param headers Additional response headers
286
+ # @param layout Override layout
287
+ # @param context Template variables
288
+ def response: (
289
+ String name,
290
+ ?status: Integer,
291
+ ?headers: Hash[String, String],
292
+ ?layout: String | false | nil,
293
+ **untyped context
294
+ ) -> TemplateResponse
295
+
296
+ # Render a partial template (no layout applied)
297
+ def render_partial: (String name, **untyped context) -> String
298
+ end
299
+
300
+ # Scoped template renderer with a fixed layout
301
+ class ScopedTemplates
302
+ def initialize: (Templates templates, String layout) -> void
303
+
304
+ def render: (String name, ?layout: String | false | nil, **untyped context) -> String
305
+
306
+ def response: (
307
+ String name,
308
+ ?status: Integer,
309
+ ?headers: Hash[String, String],
310
+ ?layout: String | false | nil,
311
+ **untyped context
312
+ ) -> TemplateResponse
313
+
314
+ def render_partial: (String name, **untyped context) -> String
315
+ end
316
+
317
+ # HTML response wrapper for template rendering
318
+ #
319
+ # Returned from Templates#response, signals the framework to return HTML
320
+ # instead of JSON
321
+ #
322
+ class TemplateResponse
323
+ attr_reader body: String
324
+ attr_reader status: Integer
325
+ attr_reader headers: Hash[String, String]
326
+
327
+ def initialize: (String body, ?status: Integer, ?headers: Hash[String, String]) -> void
328
+
329
+ # Convert to Rack response format
330
+ def to_response: () -> rack_response
331
+ end
332
+
333
+ # Background task manager for post-response execution
334
+ #
335
+ # Tasks run after the response is sent but before dependencies are cleaned up.
336
+ # Perfect for emails, logging, webhooks.
337
+ #
338
+ # Example:
339
+ # api.post '/signup' do |input, req, task, background:|
340
+ # user = create_user(input[:body])
341
+ # background.add_task(method(:send_welcome_email), user[:email])
342
+ # [{ user: user }, 201]
343
+ # end
344
+ #
345
+ class BackgroundTasks
346
+ def initialize: (untyped task) -> void
347
+
348
+ # Add a task to execute after response is sent
349
+ #
350
+ # @param callable A callable object (lambda, proc, method)
351
+ # @param args Positional arguments to pass to callable
352
+ # @param kwargs Keyword arguments to pass to callable
353
+ def add_task: (
354
+ ^(*untyped, **untyped) -> void callable,
355
+ *untyped args,
356
+ **untyped kwargs
357
+ ) -> nil
358
+
359
+ # Execute all queued tasks (called automatically by framework)
360
+ def execute: () -> void
361
+
362
+ # Check if there are any queued tasks
363
+ def empty?: () -> bool
364
+
365
+ # Number of queued tasks
366
+ def size: () -> Integer
367
+ end
368
+
369
+ # Dependency injection wrapper with sub-dependency support
370
+ #
371
+ # Example:
372
+ # db_dep = FunApi::Depends.new(->(req:) { DB.connect(req.env['tenant']) })
373
+ #
374
+ class Depends
375
+ attr_reader callable: ^(**untyped) -> untyped
376
+ attr_reader sub_dependencies: Hash[Symbol, untyped]
377
+
378
+ # Create a new dependency
379
+ #
380
+ # @param callable A callable that produces the dependency value
381
+ # @param sub_dependencies Named dependencies this depends on
382
+ def initialize: (^(**untyped) -> untyped callable, **untyped sub_dependencies) -> void
383
+
384
+ # Resolve this dependency and its sub-dependencies
385
+ #
386
+ # @param context Resolution context with :input, :req, :task, :container
387
+ # @param cache Dependency cache for deduplication
388
+ # @return [value, cleanup_proc] tuple
389
+ def call: (Hash[Symbol, untyped] context, ?Hash[untyped, untyped] cache) -> [untyped, (^() -> void)?]
390
+ end
391
+
392
+ # Factory method for creating Depends instances
393
+ #
394
+ # Example:
395
+ # FunApi.Depends(->(req:) { extract_user(req) })
396
+ #
397
+ def self.Depends: (^(**untyped) -> untyped callable, **untyped sub_dependencies) -> Depends
398
+
399
+ # Simple dependency wrapper (no cleanup needed)
400
+ class SimpleDependency
401
+ attr_reader resource: untyped
402
+
403
+ def initialize: (untyped resource) -> void
404
+ def call: () -> untyped
405
+ def cleanup: () -> void
406
+ end
407
+
408
+ # Managed dependency with explicit cleanup
409
+ class ManagedDependency
410
+ attr_reader resource: untyped
411
+ attr_reader cleanup_proc: ^() -> void
412
+
413
+ def initialize: (untyped resource, ^() -> void cleanup_proc) -> void
414
+ def call: () -> untyped
415
+ def cleanup: () -> void
416
+ end
417
+
418
+ # Block-based dependency with yield-style cleanup (like Python context managers)
419
+ class BlockDependency
420
+ def initialize: (^(^(untyped) -> untyped) -> untyped block) -> void
421
+ def call: () -> untyped
422
+ def cleanup: () -> void
423
+ end
424
+
425
+ # Built-in middleware
426
+ module Middleware
427
+ # Base class for middleware (optional to inherit from)
428
+ class Base
429
+ def initialize: (untyped app) -> void
430
+ def call: (rack_env env) -> rack_response
431
+ end
432
+
433
+ # CORS middleware
434
+ class Cors
435
+ def initialize: (
436
+ untyped app,
437
+ ?allow_origins: Array[String],
438
+ ?allow_methods: Array[String],
439
+ ?allow_headers: Array[String],
440
+ ?expose_headers: Array[String],
441
+ ?max_age: Integer,
442
+ ?allow_credentials: bool
443
+ ) -> void
444
+
445
+ def call: (rack_env env) -> rack_response
446
+ end
447
+
448
+ # Trusted host validation middleware
449
+ class TrustedHost
450
+ def initialize: (untyped app, allowed_hosts: Array[String | Regexp]) -> void
451
+ def call: (rack_env env) -> rack_response
452
+ end
453
+
454
+ # Request logging middleware
455
+ class RequestLogger
456
+ def initialize: (untyped app, ?logger: untyped, ?level: Symbol) -> void
457
+ def call: (rack_env env) -> rack_response
458
+ end
459
+ end
460
+
461
+ # Server adapters
462
+ module Server
463
+ # Falcon server adapter
464
+ #
465
+ # Example:
466
+ # FunApi::Server::Falcon.start(app, port: 3000)
467
+ #
468
+ class Falcon
469
+ # Start the Falcon server with the given app
470
+ #
471
+ # @param app FunApi::App instance
472
+ # @param host Bind address
473
+ # @param port Port number
474
+ def self.start: (App app, ?host: String, ?port: Integer) -> void
475
+ end
476
+ end
477
+
478
+ # Router for path matching (internal, but exposed for advanced use)
479
+ class Router
480
+ # Route struct holding route metadata
481
+ class Route
482
+ attr_accessor verb: String
483
+ attr_accessor pattern: Regexp
484
+ attr_accessor keys: Array[String]
485
+ attr_accessor handler: ^(untyped, Hash[String, String]) -> rack_response
486
+ attr_accessor metadata: Hash[Symbol, untyped]
487
+ end
488
+
489
+ attr_reader routes: Array[Route]
490
+
491
+ def initialize: () -> void
492
+
493
+ # Add a route
494
+ def add: (String verb, String path, ?metadata: Hash[Symbol, untyped]) { (untyped, Hash[String, String]) -> rack_response } -> void
495
+
496
+ # Rack interface
497
+ def call: (rack_env env) -> rack_response
498
+ end
499
+ end
metadata ADDED
@@ -0,0 +1,220 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: funapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Felipe Moyano
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: async
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.8'
26
+ - !ruby/object:Gem::Dependency
27
+ name: dry-container
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.11'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.11'
40
+ - !ruby/object:Gem::Dependency
41
+ name: dry-schema
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.13'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '1.13'
54
+ - !ruby/object:Gem::Dependency
55
+ name: falcon
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0.44'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0.44'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rack
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.0.0
75
+ - - "<"
76
+ - !ruby/object:Gem::Version
77
+ version: '4'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 3.0.0
85
+ - - "<"
86
+ - !ruby/object:Gem::Version
87
+ version: '4'
88
+ - !ruby/object:Gem::Dependency
89
+ name: rack-cors
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '2.0'
95
+ type: :runtime
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '2.0'
102
+ description: Get an API started quickly, with performance in mind and, of course,
103
+ have some fun with Ruby.
104
+ email:
105
+ - gafemoyano@gmail.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - ".claude/25-09-01-OPENAPI_IMPLEMENTATION.md"
111
+ - ".claude/25-09-05-RESPONSE_SCHEMA.md"
112
+ - ".claude/25-09-10-OPENAPI_PLAN.md"
113
+ - ".claude/25-10-26-MIDDLEWARE_IMPLEMENTATION.md"
114
+ - ".claude/25-10-26-MIDDLEWARE_PLAN.md"
115
+ - ".claude/25-10-27-BACKGROUND_TASKS_ANALYSIS.md"
116
+ - ".claude/25-10-27-DEPENDENCY_IMPLEMENTATION_SUMMARY.md"
117
+ - ".claude/25-10-27-DEPENDENCY_INJECTION_PLAN.md"
118
+ - ".claude/25-12-24-LIFECYCLE_HOOKS_PLAN.md"
119
+ - ".claude/25-12-24-PUBLISHING_AND_DOGFOODING_PLAN.md"
120
+ - ".claude/25-12-24-TEMPLATE_RENDERING_PLAN.md"
121
+ - ".claude/DECISIONS.md"
122
+ - ".claude/PROJECT_PLAN.md"
123
+ - ".claude/TESTING_PLAN.md"
124
+ - ".claude/TESTING_STATUS.md"
125
+ - ".tool-versions"
126
+ - AGENTS.md
127
+ - CHANGELOG.md
128
+ - CODE_OF_CONDUCT.md
129
+ - LICENSE.txt
130
+ - README.md
131
+ - Rakefile
132
+ - docs
133
+ - docs-site/.gitignore
134
+ - docs-site/Gemfile
135
+ - docs-site/app.rb
136
+ - docs-site/content/essential/handler.md
137
+ - docs-site/content/essential/lifecycle.md
138
+ - docs-site/content/essential/middleware.md
139
+ - docs-site/content/essential/openapi.md
140
+ - docs-site/content/essential/routing.md
141
+ - docs-site/content/essential/validation.md
142
+ - docs-site/content/getting-started/at-glance.md
143
+ - docs-site/content/getting-started/key-concepts.md
144
+ - docs-site/content/getting-started/quick-start.md
145
+ - docs-site/content/index.md
146
+ - docs-site/content/patterns/async-operations.md
147
+ - docs-site/content/patterns/background-tasks.md
148
+ - docs-site/content/patterns/database.md
149
+ - docs-site/content/patterns/dependencies.md
150
+ - docs-site/content/patterns/deployment.md
151
+ - docs-site/content/patterns/error-handling.md
152
+ - docs-site/content/patterns/response-schema.md
153
+ - docs-site/content/patterns/templates.md
154
+ - docs-site/content/patterns/testing.md
155
+ - docs-site/mise.toml
156
+ - docs-site/public/css/style.css
157
+ - docs-site/templates/layouts/docs.html.erb
158
+ - docs-site/templates/page.html.erb
159
+ - docs-site/templates/partials/_nav.html.erb
160
+ - examples/background_tasks_demo.rb
161
+ - examples/demo_middleware.rb
162
+ - examples/demo_openapi.rb
163
+ - examples/dependency_block_demo.rb
164
+ - examples/dependency_cleanup_demo.rb
165
+ - examples/dependency_injection_demo.rb
166
+ - examples/lifecycle_demo.rb
167
+ - examples/middleware_demo.rb
168
+ - examples/templates/layouts/application.html.erb
169
+ - examples/templates/todos/_todo.html.erb
170
+ - examples/templates/todos/index.html.erb
171
+ - examples/templates_demo.rb
172
+ - lib/funapi.rb
173
+ - lib/funapi/application.rb
174
+ - lib/funapi/async.rb
175
+ - lib/funapi/background_tasks.rb
176
+ - lib/funapi/config.rb
177
+ - lib/funapi/database/sequel/fibered_connection_pool.rb
178
+ - lib/funapi/dependency_wrapper.rb
179
+ - lib/funapi/depends.rb
180
+ - lib/funapi/exceptions.rb
181
+ - lib/funapi/middleware.rb
182
+ - lib/funapi/middleware/base.rb
183
+ - lib/funapi/middleware/cors.rb
184
+ - lib/funapi/middleware/request_logger.rb
185
+ - lib/funapi/middleware/trusted_host.rb
186
+ - lib/funapi/openapi/schema_converter.rb
187
+ - lib/funapi/openapi/spec_generator.rb
188
+ - lib/funapi/router.rb
189
+ - lib/funapi/schema.rb
190
+ - lib/funapi/server/falcon.rb
191
+ - lib/funapi/template_response.rb
192
+ - lib/funapi/templates.rb
193
+ - lib/funapi/version.rb
194
+ - sig/fun_api.rbs
195
+ homepage: https://github.com/gafemoyano/funapi
196
+ licenses:
197
+ - MIT
198
+ metadata:
199
+ homepage_uri: https://github.com/gafemoyano/funapi
200
+ source_code_uri: https://github.com/gafemoyano/funapi
201
+ changelog_uri: https://github.com/gafemoyano/funapi/blob/main/CHANGELOG.md
202
+ rubygems_mfa_required: 'true'
203
+ rdoc_options: []
204
+ require_paths:
205
+ - lib
206
+ required_ruby_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: 3.2.0
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ requirements: []
217
+ rubygems_version: 3.7.1
218
+ specification_version: 4
219
+ summary: Minimal async web framework for ruby inspired by FastAPI
220
+ test_files: []