rubst_api 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0421cfdff576c394312d131b716b781f0811ca24d8572386ac9a94a307759da6
4
+ data.tar.gz: 706a40d9e4be0c54e888047cbd1f46d6131cf705d1457d6b1a52ee29b4a1c85d
5
+ SHA512:
6
+ metadata.gz: 1d97840b9d9ef0bf1ef8a20abe93b3ceaeded0863d0e0f33056b373446c85d405fd3ea4a9e9f3cc76ec5277b58ce9a75f06e7767f0791e35ba1fdfcd6d368c79
7
+ data.tar.gz: e79e8e83813610bf9c1e45de62e029ebfc85072e5ae2116c3e2431be4b1116c55d8e2f6cb739c4f7ddae06cfbceebec1cc98cb644ba731c1f3b99a00fabd979f
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ All notable changes to RubstAPI will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Professional RubyGems package metadata and canonical repository links.
13
+ - FastAPI license attribution and third-party notices.
14
+
15
+ ## [0.1.0] - 2026-07-29
16
+
17
+ ### Added
18
+
19
+ - Rack-compatible API application and router.
20
+ - Typed request parameter extraction and model validation.
21
+ - Dependency injection with caching, sub-dependencies, and test overrides.
22
+ - OpenAPI 3.1, Swagger UI, and ReDoc generation.
23
+ - Basic, bearer, OAuth2 password-bearer, and API-key security helpers.
24
+ - CORS, GZip, trusted-host, and HTTPS-redirect middleware.
25
+ - JSON, HTML, text, redirect, file, streaming, and event-stream responses.
26
+ - Background tasks, static files, WebSocket wrapper, and test client.
27
+ - `rubst_api run` and `rubst_api dev` commands.
28
+
29
+ [Unreleased]: https://github.com/joryleech/RubstApi/compare/v0.1.0...HEAD
30
+ [0.1.0]: https://github.com/joryleech/RubstApi/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FastAPI Ruby contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Sebastián Ramírez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,618 @@
1
+ # RubstAPI
2
+
3
+ ### A FastAPI-inspired framework for building typed Ruby APIs
4
+
5
+ RubstAPI is a Ruby REST API framework that brings the developer experience of
6
+ [Python FastAPI](https://fastapi.tiangolo.com/) to Rack applications. Define
7
+ typed request parameters and data models once, then receive validation,
8
+ serialization, OpenAPI 3.1 documentation, Swagger UI, ReDoc, dependency
9
+ injection, and security integration automatically.
10
+
11
+ ```ruby
12
+ require "rubst_api"
13
+
14
+ class Item < RubstApi::Model
15
+ field :name, String, min_length: 2
16
+ field :price, Float, gt: 0
17
+ field :in_stock, :boolean, default: true
18
+ end
19
+
20
+ APP = RubstApi::App.new(title: "Store API", version: "1.0.0")
21
+
22
+ APP.post("/items", params: {
23
+ item: RubstApi.Body(Item)
24
+ }, response_model: Item, status_code: 201) do |item:|
25
+ item
26
+ end
27
+ ```
28
+
29
+ Start the application and visit `/docs` for an interactive Swagger UI:
30
+
31
+ ```console
32
+ rubst_api run app.rb
33
+ ```
34
+
35
+ > [!IMPORTANT]
36
+ > RubstAPI is an independent Ruby project inspired by FastAPI. It is not
37
+ > affiliated with, endorsed by, or maintained by the Python FastAPI project.
38
+ > It is an idiomatic Ruby implementation—not a Python source-compatibility
39
+ > layer.
40
+
41
+ ## Why RubstAPI?
42
+
43
+ Ruby has excellent web frameworks, but API projects often assemble request
44
+ validation, serialization, dependency injection, OpenAPI generation, security,
45
+ and documentation from separate libraries. RubstAPI provides those concerns
46
+ through one cohesive, Rack-compatible API.
47
+
48
+ - **Typed inputs:** Coerce and validate path, query, header, cookie, form, file,
49
+ and JSON body parameters.
50
+ - **Ruby data models:** Define nested request and response schemas with
51
+ `RubstApi::Model`.
52
+ - **Standards first:** Generate OpenAPI 3.1 and JSON Schema from application
53
+ declarations.
54
+ - **Documentation included:** Serve Swagger UI at `/docs`, ReDoc at `/redoc`,
55
+ and the raw schema at `/openapi.json`.
56
+ - **Dependency injection:** Compose cached dependencies and sub-dependencies,
57
+ with per-application overrides for testing.
58
+ - **Integrated security:** Describe HTTP Basic, Bearer, OAuth2 password bearer,
59
+ and API keys in headers, queries, or cookies.
60
+ - **Rack compatible:** Mount RubstAPI beside existing Ruby and Rack
61
+ applications.
62
+ - **Focused operational footprint:** Runtime dependencies are limited to Rack,
63
+ Rackup, and WEBrick for the framework's HTTP interface and bundled CLI.
64
+
65
+ ## Contents
66
+
67
+ - [Requirements](#requirements)
68
+ - [Installation](#installation)
69
+ - [Quick start](#quick-start)
70
+ - [Parameters and validation](#parameters-and-validation)
71
+ - [Models](#models)
72
+ - [Dependency injection](#dependency-injection)
73
+ - [Security](#security)
74
+ - [Routers](#routers)
75
+ - [Responses and background tasks](#responses-and-background-tasks)
76
+ - [Middleware](#middleware)
77
+ - [OpenAPI and API documentation](#openapi-and-api-documentation)
78
+ - [Testing](#testing)
79
+ - [Running with Docker](#running-with-docker)
80
+ - [FastAPI and RubstAPI](#fastapi-and-rubstapi)
81
+ - [Project status](#project-status)
82
+ - [Development](#development)
83
+ - [Contributing](#contributing)
84
+ - [Security](#security-policy)
85
+ - [License](#license)
86
+
87
+ ## Requirements
88
+
89
+ - Ruby 3.2 or newer
90
+ - A Rack-compatible server for serving HTTP
91
+
92
+ The built-in `rubst_api run` and `rubst_api dev` commands use Rackup and
93
+ WEBrick. Add these server dependencies to your application:
94
+
95
+ ```ruby
96
+ gem "rubst_api"
97
+ gem "rack", ">= 3.0"
98
+ gem "rackup", ">= 2.0"
99
+ gem "webrick", ">= 1.8"
100
+ ```
101
+
102
+ ## Installation
103
+
104
+ Add RubstAPI to your bundle:
105
+
106
+ ```console
107
+ bundle add rubst_api
108
+ ```
109
+
110
+ Or add it manually to your `Gemfile`:
111
+
112
+ ```ruby
113
+ gem "rubst_api"
114
+ ```
115
+
116
+ Then run:
117
+
118
+ ```console
119
+ bundle install
120
+ ```
121
+
122
+ For local development before a RubyGems release, reference the checkout:
123
+
124
+ ```ruby
125
+ gem "rubst_api", path: "../RubstApi"
126
+ ```
127
+
128
+ ## Quick start
129
+
130
+ Create `app.rb`:
131
+
132
+ ```ruby
133
+ require "rubst_api"
134
+
135
+ class Product < RubstApi::Model
136
+ field :name, String, min_length: 2, max_length: 100
137
+ field :price, Float, gt: 0
138
+ field :tags, { array: String }, default: []
139
+ end
140
+
141
+ APP = RubstApi::App.new(
142
+ title: "Products API",
143
+ description: "A typed REST API built with Ruby and RubstAPI.",
144
+ version: "1.0.0"
145
+ )
146
+
147
+ APP.get("/") do
148
+ { name: "Products API", docs: "/docs" }
149
+ end
150
+
151
+ APP.get("/products/{product_id}", params: {
152
+ product_id: RubstApi.Path(Integer, ge: 1),
153
+ search: RubstApi.Query(String, required: false, max_length: 50)
154
+ }) do |product_id:, search:|
155
+ { product_id:, search: }
156
+ end
157
+
158
+ APP.post("/products", params: {
159
+ product: RubstApi.Body(Product)
160
+ }, response_model: Product, status_code: 201) do |product:|
161
+ product
162
+ end
163
+ ```
164
+
165
+ Run it:
166
+
167
+ ```console
168
+ bundle exec rubst_api run app.rb --host 0.0.0.0 --port 8000
169
+ ```
170
+
171
+ Open:
172
+
173
+ | URL | Purpose |
174
+ | --- | --- |
175
+ | `http://localhost:8000/docs` | Interactive Swagger UI |
176
+ | `http://localhost:8000/redoc` | ReDoc API reference |
177
+ | `http://localhost:8000/openapi.json` | OpenAPI 3.1 document |
178
+
179
+ Test the endpoint:
180
+
181
+ ```console
182
+ curl -X POST http://localhost:8000/products \
183
+ -H "Content-Type: application/json" \
184
+ -d '{"name":"Mechanical Keyboard","price":129.0,"tags":["hardware"]}'
185
+ ```
186
+
187
+ Invalid input receives a structured `422 Unprocessable Entity` response:
188
+
189
+ ```json
190
+ {
191
+ "detail": [
192
+ {
193
+ "type": "greater_than",
194
+ "loc": ["body", "product", "price"],
195
+ "msg": "Value must be greater than 0",
196
+ "input": -1
197
+ }
198
+ ]
199
+ }
200
+ ```
201
+
202
+ ## Parameters and validation
203
+
204
+ Declare where every input comes from:
205
+
206
+ ```ruby
207
+ APP.put("/accounts/{account_id}", params: {
208
+ account_id: RubstApi.Path(Integer, ge: 1),
209
+ verbose: RubstApi.Query(:boolean, default: false),
210
+ request_id: RubstApi.Header(String, alias: "x-request-id"),
211
+ session: RubstApi.Cookie(String, required: false),
212
+ account: RubstApi.Body(Account)
213
+ }) do |account_id:, verbose:, request_id:, session:, account:|
214
+ # All values are extracted, coerced, and validated before this block runs.
215
+ end
216
+ ```
217
+
218
+ | Declaration | Request source |
219
+ | --- | --- |
220
+ | `RubstApi.Path` | URL path segment |
221
+ | `RubstApi.Query` | Query string |
222
+ | `RubstApi.Header` | HTTP header |
223
+ | `RubstApi.Cookie` | Cookie |
224
+ | `RubstApi.Body` | JSON request body |
225
+ | `RubstApi.Form` | URL-encoded form |
226
+ | `RubstApi.File` | Multipart upload |
227
+
228
+ Supported constraints include:
229
+
230
+ - `min_length` and `max_length`
231
+ - `gt`, `ge`, `lt`, and `le`
232
+ - `pattern` or `regex`
233
+ - `enum`
234
+ - `nullable`
235
+ - `required`
236
+ - `default`
237
+ - `alias`
238
+
239
+ Common Ruby types such as `String`, `Integer`, `Float`, `Numeric`, `Hash`,
240
+ `Array`, `Date`, `Time`, and boolean values are coerced automatically.
241
+
242
+ ## Models
243
+
244
+ Models validate nested input and generate JSON Schema:
245
+
246
+ ```ruby
247
+ class Address < RubstApi::Model
248
+ field :city, String, min_length: 1
249
+ field :postal_code, String, pattern: /\A[0-9A-Z -]+\z/i
250
+ end
251
+
252
+ class Customer < RubstApi::Model
253
+ field :name, String
254
+ field :address, Address
255
+ field :email, [String, NilClass], default: nil
256
+ end
257
+ ```
258
+
259
+ Type notation:
260
+
261
+ | Shape | Declaration |
262
+ | --- | --- |
263
+ | Array of strings | `{ array: String }` |
264
+ | Array of models | `{ array: Customer }` |
265
+ | Union | `[String, Integer]` |
266
+ | Nullable string | `[String, NilClass]` |
267
+
268
+ Serialize a model with `model_dump`:
269
+
270
+ ```ruby
271
+ customer.model_dump(exclude_none: true)
272
+ customer.model_dump(by_alias: true)
273
+ customer.to_json
274
+ ```
275
+
276
+ ## Dependency injection
277
+
278
+ Dependencies are ordinary Ruby callables:
279
+
280
+ ```ruby
281
+ def database
282
+ Database.connect
283
+ end
284
+
285
+ database_dependency = RubstApi.Depends(method(:database))
286
+
287
+ APP.get("/reports", params: {
288
+ db: database_dependency
289
+ }) do |db:|
290
+ db.reports
291
+ end
292
+ ```
293
+
294
+ Dependencies are cached once per request by default. They can also depend on
295
+ other dependencies:
296
+
297
+ ```ruby
298
+ database_dependency = RubstApi.Depends(method(:database))
299
+
300
+ current_user_dependency = RubstApi.Depends(
301
+ method(:current_user),
302
+ dependencies: { db: database_dependency }
303
+ )
304
+ ```
305
+
306
+ Override dependencies during tests:
307
+
308
+ ```ruby
309
+ APP.dependency_overrides[method(:database)] = -> { FakeDatabase.new }
310
+ ```
311
+
312
+ Enumerator-based dependencies can yield a value and perform cleanup after the
313
+ request, which is useful for database sessions and other scoped resources.
314
+
315
+ ## Security
316
+
317
+ Security helpers both authenticate requests and populate the OpenAPI security
318
+ schema.
319
+
320
+ ### Bearer authentication
321
+
322
+ ```ruby
323
+ bearer = RubstApi::Security::HTTPBearer.new
324
+
325
+ APP.get("/me", params: {
326
+ credentials: RubstApi.Security(bearer)
327
+ }) do |credentials:|
328
+ { token: credentials.credentials }
329
+ end
330
+ ```
331
+
332
+ ### OAuth2 password bearer
333
+
334
+ ```ruby
335
+ oauth2 = RubstApi::Security::OAuth2PasswordBearer.new(
336
+ token_url: "/token",
337
+ scopes: { "items:read" => "Read items" }
338
+ )
339
+
340
+ APP.get("/items", params: {
341
+ token: RubstApi.Security(oauth2, scopes: ["items:read"])
342
+ }) do |token:|
343
+ { token: token }
344
+ end
345
+ ```
346
+
347
+ Available helpers:
348
+
349
+ - `HTTPBasic`
350
+ - `HTTPBearer`
351
+ - `OAuth2PasswordBearer`
352
+ - `APIKeyHeader`
353
+ - `APIKeyQuery`
354
+ - `APIKeyCookie`
355
+
356
+ Authentication policy, token issuance, password hashing, and database access
357
+ remain application concerns.
358
+
359
+ ## Routers
360
+
361
+ Split larger APIs into focused routers:
362
+
363
+ ```ruby
364
+ users = RubstApi::APIRouter.new(prefix: "/users", tags: ["users"])
365
+
366
+ users.get("/{user_id}", params: {
367
+ user_id: RubstApi.Path(Integer)
368
+ }) do |user_id:|
369
+ { user_id: user_id }
370
+ end
371
+
372
+ APP.include_router(users, prefix: "/v1")
373
+ ```
374
+
375
+ RubstAPI supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `HEAD`,
376
+ and `TRACE` routes.
377
+
378
+ ## Responses and background tasks
379
+
380
+ Return ordinary Ruby objects for automatic JSON serialization, or return an
381
+ explicit response:
382
+
383
+ ```ruby
384
+ APP.get("/redirect") do
385
+ RubstApi::RedirectResponse.new("/docs")
386
+ end
387
+
388
+ APP.get("/download") do
389
+ RubstApi::FileResponse.new("report.csv", filename: "report.csv")
390
+ end
391
+ ```
392
+
393
+ Available response classes:
394
+
395
+ - `JSONResponse`
396
+ - `HTMLResponse`
397
+ - `PlainTextResponse`
398
+ - `RedirectResponse`
399
+ - `StreamingResponse`
400
+ - `FileResponse`
401
+ - `EventSourceResponse` for server-sent events
402
+
403
+ Schedule in-process work after a response:
404
+
405
+ ```ruby
406
+ APP.post("/notifications") do |background_tasks:|
407
+ background_tasks.add_task(method(:deliver_notification), "user-123")
408
+ { queued: true }
409
+ end
410
+ ```
411
+
412
+ For durable, retryable, or distributed jobs, use a dedicated Ruby job system.
413
+
414
+ ## Middleware
415
+
416
+ Add middleware with explicit options:
417
+
418
+ ```ruby
419
+ APP.add_middleware(
420
+ RubstApi::Middleware::CORSMiddleware,
421
+ allow_origins: ["https://example.com"],
422
+ allow_methods: %w[GET POST],
423
+ allow_headers: ["authorization", "content-type"]
424
+ )
425
+
426
+ APP.add_middleware(
427
+ RubstApi::Middleware::GZipMiddleware,
428
+ minimum_size: 500
429
+ )
430
+ ```
431
+
432
+ Included middleware:
433
+
434
+ - CORS
435
+ - GZip
436
+ - Trusted Host
437
+ - HTTPS Redirect
438
+
439
+ Any compatible Rack middleware can wrap a RubstAPI application.
440
+
441
+ ## OpenAPI and API documentation
442
+
443
+ RubstAPI follows the same standards-first philosophy popularized by Python
444
+ FastAPI: route declarations, request parameters, bodies, response models, and
445
+ security requirements produce an OpenAPI document automatically.
446
+
447
+ Configure documentation endpoints when creating the app:
448
+
449
+ ```ruby
450
+ APP = RubstApi::App.new(
451
+ title: "Billing API",
452
+ version: "2.1.0",
453
+ openapi_url: "/openapi.json",
454
+ docs_url: "/docs",
455
+ redoc_url: "/redoc",
456
+ openapi_tags: [
457
+ { name: "invoices", description: "Invoice operations" }
458
+ ]
459
+ )
460
+ ```
461
+
462
+ Set any documentation URL to `nil` to disable that endpoint.
463
+
464
+ ## Testing
465
+
466
+ The in-process test client does not require a running web server:
467
+
468
+ ```ruby
469
+ require "minitest/autorun"
470
+ require "rubst_api"
471
+
472
+ class ProductsApiTest < Minitest::Test
473
+ def test_get_product
474
+ client = RubstApi::TestClient.new(APP)
475
+ response = client.get("/products/42")
476
+
477
+ assert_equal 200, response.status
478
+ assert_equal 42, response.json.fetch("product_id")
479
+ end
480
+
481
+ def test_invalid_product
482
+ client = RubstApi::TestClient.new(APP)
483
+ response = client.post(
484
+ "/products",
485
+ json: { name: "x", price: -10 }
486
+ )
487
+
488
+ assert_equal 422, response.status
489
+ end
490
+ end
491
+ ```
492
+
493
+ Supported test requests include `GET`, `POST`, `PUT`, `PATCH`, `DELETE`,
494
+ `OPTIONS`, and `HEAD`, with query parameters, JSON, form data, headers, and
495
+ cookies.
496
+
497
+ ## Running with Docker
498
+
499
+ ```dockerfile
500
+ FROM ruby:4.0-slim
501
+
502
+ WORKDIR /app
503
+
504
+ COPY Gemfile Gemfile.lock ./
505
+ RUN bundle install
506
+
507
+ COPY . .
508
+
509
+ EXPOSE 8000
510
+ CMD ["bundle", "exec", "rubst_api", "run", "app.rb", "--host", "0.0.0.0", "--port", "8000"]
511
+ ```
512
+
513
+ Example Compose service:
514
+
515
+ ```yaml
516
+ services:
517
+ api:
518
+ build: .
519
+ ports:
520
+ - "8000:8000"
521
+ healthcheck:
522
+ test:
523
+ - CMD
524
+ - ruby
525
+ - -rnet/http
526
+ - -e
527
+ - 'exit(Net::HTTP.get_response(URI("http://127.0.0.1:8000/health")).is_a?(Net::HTTPSuccess) ? 0 : 1)'
528
+ interval: 10s
529
+ timeout: 3s
530
+ retries: 5
531
+ ```
532
+
533
+ ## FastAPI and RubstAPI
534
+
535
+ [FastAPI](https://fastapi.tiangolo.com/) is a high-performance Python API
536
+ framework built on Starlette and Pydantic. RubstAPI adapts several of its core
537
+ ideas to Ruby:
538
+
539
+ | Python FastAPI concept | RubstAPI equivalent |
540
+ | --- | --- |
541
+ | ASGI application | Rack-compatible callable |
542
+ | Python type annotations | Explicit `params:` declarations |
543
+ | Pydantic `BaseModel` | `RubstApi::Model` |
544
+ | `Depends()` | `RubstApi.Depends` |
545
+ | `Security()` | `RubstApi.Security` |
546
+ | `APIRouter` | `RubstApi::APIRouter` |
547
+ | Starlette responses | `RubstApi::*Response` |
548
+ | HTTPX test client | `RubstApi::TestClient` |
549
+ | Uvicorn command | `rubst_api run` |
550
+
551
+ RubstAPI does not include or emulate Python, ASGI, Starlette, Pydantic, Uvicorn,
552
+ or FastAPI Cloud. Existing FastAPI Python applications must be translated to
553
+ Ruby declarations and Rack integrations.
554
+
555
+ The FastAPI name and trademark belong to their respective owner. See the
556
+ [official FastAPI documentation](https://fastapi.tiangolo.com/) and
557
+ [FastAPI source repository](https://github.com/fastapi/fastapi) for the Python
558
+ project.
559
+
560
+ ## Project status
561
+
562
+ RubstAPI is an early-stage framework. Its public API covers the central request,
563
+ validation, routing, dependency, security, response, middleware, documentation,
564
+ and testing workflows described above. Before adopting it for critical
565
+ production systems, evaluate the current release against your performance,
566
+ security, concurrency, observability, and deployment requirements.
567
+
568
+ RubstAPI uses the `RubstApi` namespace and the `require "rubst_api"` entry
569
+ point exclusively. It does not define a `FastAPI` Ruby constant or provide a
570
+ `require "fast_api"` compatibility loader.
571
+
572
+ ## Development
573
+
574
+ Install dependencies and run the complete test suite:
575
+
576
+ ```console
577
+ bundle install
578
+ bundle exec rake test
579
+ ```
580
+
581
+ Build the gem:
582
+
583
+ ```console
584
+ gem build rubst_api.gemspec
585
+ ```
586
+
587
+ The project uses Minitest. New functionality should include success, validation,
588
+ error, and OpenAPI coverage where applicable.
589
+
590
+ ## Contributing
591
+
592
+ Bug reports, documentation improvements, tests, and focused pull requests are
593
+ welcome.
594
+
595
+ 1. Fork the repository.
596
+ 2. Create a descriptive branch.
597
+ 3. Add tests for behavioral changes.
598
+ 4. Run `bundle exec rake test`.
599
+ 5. Update user-facing documentation.
600
+ 6. Open a pull request explaining the problem and solution.
601
+
602
+ Please keep changes focused, preserve backward compatibility when practical,
603
+ and avoid claiming compatibility that is not covered by tests.
604
+
605
+ ## Security policy
606
+
607
+ Do not disclose suspected vulnerabilities in a public issue. Follow the private
608
+ reporting instructions in [SECURITY.md](SECURITY.md).
609
+
610
+ ## License
611
+
612
+ RubstAPI is available under the [MIT License](LICENSE).
613
+
614
+ RubstAPI is inspired by the Python FastAPI project. FastAPI's original MIT
615
+ license and copyright notice are preserved verbatim in
616
+ [LICENSES/FASTAPI-MIT.txt](LICENSES/FASTAPI-MIT.txt) and summarized in
617
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). These files are included in
618
+ the published gem.