rapitapir 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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +57 -0
  4. data/CHANGELOG.md +94 -0
  5. data/CLEANUP_SUMMARY.md +155 -0
  6. data/CONTRIBUTING.md +280 -0
  7. data/LICENSE +21 -0
  8. data/README.md +485 -0
  9. data/debug_hash.rb +20 -0
  10. data/docs/EXTENSION_COMPARISON.md +388 -0
  11. data/docs/SINATRA_EXTENSION.md +467 -0
  12. data/docs/archive/PHASE_1_2_COMPLETE.md +77 -0
  13. data/docs/archive/PHASE_1_3_COMPLETE.md +152 -0
  14. data/docs/archive/PHASE_2_1_OBSERVABILITY_COMPLETED.md +203 -0
  15. data/docs/archive/PHASE_2_SUMMARY.md +209 -0
  16. data/docs/archive/REFACTORING_SUMMARY.md +184 -0
  17. data/docs/archive/phase_1_3_plan.md +136 -0
  18. data/docs/archive/sinatra_extension_summary.md +188 -0
  19. data/docs/archive/sinatra_working_solution.md +113 -0
  20. data/docs/archive/typescript-client-generator-summary.md +259 -0
  21. data/docs/auto-derivation.md +146 -0
  22. data/docs/blueprint.md +1091 -0
  23. data/docs/endpoint-definition.md +211 -0
  24. data/docs/github_pages_fix.md +52 -0
  25. data/docs/github_pages_setup.md +49 -0
  26. data/docs/implementation-status.md +357 -0
  27. data/docs/observability.md +647 -0
  28. data/docs/phase3-plan.md +108 -0
  29. data/docs/sinatra_rapitapir.md +87 -0
  30. data/docs/type_shortcuts.md +146 -0
  31. data/examples/README_ENTERPRISE.md +202 -0
  32. data/examples/authentication_example.rb +192 -0
  33. data/examples/auto_derivation_ruby_friendly.rb +163 -0
  34. data/examples/cli/user_api_endpoints.rb +56 -0
  35. data/examples/client/typescript_client_example.rb +102 -0
  36. data/examples/client/user-api-client.ts +193 -0
  37. data/examples/demo_api.rb +41 -0
  38. data/examples/docs/documentation_example.rb +112 -0
  39. data/examples/docs/user-api-docs.html +789 -0
  40. data/examples/docs/user-api-docs.md +403 -0
  41. data/examples/enhanced_auto_derivation_test.rb +83 -0
  42. data/examples/enterprise_extension_demo.rb +417 -0
  43. data/examples/enterprise_rapitapir_api.rb +662 -0
  44. data/examples/getting_started_extension.rb +218 -0
  45. data/examples/hello_world.rb +74 -0
  46. data/examples/oauth2/.env.example +19 -0
  47. data/examples/oauth2/README.md +205 -0
  48. data/examples/oauth2/generic_oauth2_api.rb +226 -0
  49. data/examples/oauth2/get_token.rb +72 -0
  50. data/examples/oauth2/songs_api_with_auth0.rb +320 -0
  51. data/examples/oauth2/test_api.sh +16 -0
  52. data/examples/oauth2/test_songs_api.sh +110 -0
  53. data/examples/observability/.env.example +35 -0
  54. data/examples/observability/README.md +230 -0
  55. data/examples/observability/README_HONEYCOMB.md +332 -0
  56. data/examples/observability/advanced_setup.rb +384 -0
  57. data/examples/observability/basic_setup.rb +192 -0
  58. data/examples/observability/complete_test.rb +121 -0
  59. data/examples/observability/honeycomb_example.rb +523 -0
  60. data/examples/observability/honeycomb_rapitapir_clean.rb +488 -0
  61. data/examples/observability/honeycomb_rapitapir_example.rb +523 -0
  62. data/examples/observability/honeycomb_working_example.rb +489 -0
  63. data/examples/observability/quick_test.rb +78 -0
  64. data/examples/observability/simple_test.rb +14 -0
  65. data/examples/observability/test_honeycomb_demo.rb +354 -0
  66. data/examples/observability/test_live_honeycomb.rb +111 -0
  67. data/examples/observability/test_validation.rb +78 -0
  68. data/examples/observability/test_working_validation.rb +66 -0
  69. data/examples/openapi/user_api_schema.rb +132 -0
  70. data/examples/production_ready_example.rb +105 -0
  71. data/examples/rails/users_controller.rb +146 -0
  72. data/examples/readme/basic_sinatra_example.rb +128 -0
  73. data/examples/server/user_api.rb +179 -0
  74. data/examples/simple_auto_derivation_demo.rb +44 -0
  75. data/examples/simple_demo_api.rb +18 -0
  76. data/examples/sinatra/user_app.rb +127 -0
  77. data/examples/t_shortcut_demo.rb +59 -0
  78. data/examples/user_api.rb +190 -0
  79. data/examples/working_getting_started.rb +184 -0
  80. data/examples/working_simple_example.rb +195 -0
  81. data/lib/rapitapir/auth/configuration.rb +129 -0
  82. data/lib/rapitapir/auth/context.rb +122 -0
  83. data/lib/rapitapir/auth/errors.rb +104 -0
  84. data/lib/rapitapir/auth/middleware.rb +324 -0
  85. data/lib/rapitapir/auth/oauth2.rb +350 -0
  86. data/lib/rapitapir/auth/schemes.rb +420 -0
  87. data/lib/rapitapir/auth.rb +113 -0
  88. data/lib/rapitapir/cli/command.rb +535 -0
  89. data/lib/rapitapir/cli/server.rb +243 -0
  90. data/lib/rapitapir/cli/validator.rb +373 -0
  91. data/lib/rapitapir/client/generator_base.rb +272 -0
  92. data/lib/rapitapir/client/typescript_generator.rb +350 -0
  93. data/lib/rapitapir/core/endpoint.rb +158 -0
  94. data/lib/rapitapir/core/enhanced_endpoint.rb +235 -0
  95. data/lib/rapitapir/core/input.rb +182 -0
  96. data/lib/rapitapir/core/output.rb +164 -0
  97. data/lib/rapitapir/core/request.rb +19 -0
  98. data/lib/rapitapir/core/response.rb +17 -0
  99. data/lib/rapitapir/docs/html_generator.rb +780 -0
  100. data/lib/rapitapir/docs/markdown_generator.rb +464 -0
  101. data/lib/rapitapir/dsl/endpoint_dsl.rb +116 -0
  102. data/lib/rapitapir/dsl/enhanced_endpoint_dsl.rb +62 -0
  103. data/lib/rapitapir/dsl/enhanced_input.rb +73 -0
  104. data/lib/rapitapir/dsl/enhanced_output.rb +63 -0
  105. data/lib/rapitapir/dsl/enhanced_structures.rb +393 -0
  106. data/lib/rapitapir/dsl/fluent_dsl.rb +72 -0
  107. data/lib/rapitapir/dsl/fluent_endpoint_builder.rb +316 -0
  108. data/lib/rapitapir/dsl/http_verbs.rb +77 -0
  109. data/lib/rapitapir/dsl/input_methods.rb +47 -0
  110. data/lib/rapitapir/dsl/observability_methods.rb +81 -0
  111. data/lib/rapitapir/dsl/output_methods.rb +43 -0
  112. data/lib/rapitapir/dsl/type_resolution.rb +43 -0
  113. data/lib/rapitapir/observability/configuration.rb +108 -0
  114. data/lib/rapitapir/observability/health_check.rb +236 -0
  115. data/lib/rapitapir/observability/logging.rb +270 -0
  116. data/lib/rapitapir/observability/metrics.rb +203 -0
  117. data/lib/rapitapir/observability/middleware.rb +243 -0
  118. data/lib/rapitapir/observability/tracing.rb +143 -0
  119. data/lib/rapitapir/observability.rb +28 -0
  120. data/lib/rapitapir/openapi/schema_generator.rb +403 -0
  121. data/lib/rapitapir/schema.rb +136 -0
  122. data/lib/rapitapir/server/enhanced_rack_adapter.rb +379 -0
  123. data/lib/rapitapir/server/middleware.rb +120 -0
  124. data/lib/rapitapir/server/path_matcher.rb +45 -0
  125. data/lib/rapitapir/server/rack_adapter.rb +215 -0
  126. data/lib/rapitapir/server/rails_adapter.rb +17 -0
  127. data/lib/rapitapir/server/rails_adapter_class.rb +53 -0
  128. data/lib/rapitapir/server/rails_controller.rb +72 -0
  129. data/lib/rapitapir/server/rails_input_processor.rb +73 -0
  130. data/lib/rapitapir/server/rails_response_handler.rb +29 -0
  131. data/lib/rapitapir/server/sinatra_adapter.rb +200 -0
  132. data/lib/rapitapir/server/sinatra_integration.rb +93 -0
  133. data/lib/rapitapir/sinatra/configuration.rb +91 -0
  134. data/lib/rapitapir/sinatra/extension.rb +214 -0
  135. data/lib/rapitapir/sinatra/oauth2_helpers.rb +236 -0
  136. data/lib/rapitapir/sinatra/resource_builder.rb +152 -0
  137. data/lib/rapitapir/sinatra/swagger_ui_generator.rb +166 -0
  138. data/lib/rapitapir/sinatra_rapitapir.rb +40 -0
  139. data/lib/rapitapir/types/array.rb +163 -0
  140. data/lib/rapitapir/types/auto_derivation.rb +265 -0
  141. data/lib/rapitapir/types/base.rb +146 -0
  142. data/lib/rapitapir/types/boolean.rb +46 -0
  143. data/lib/rapitapir/types/date.rb +92 -0
  144. data/lib/rapitapir/types/datetime.rb +98 -0
  145. data/lib/rapitapir/types/email.rb +32 -0
  146. data/lib/rapitapir/types/float.rb +134 -0
  147. data/lib/rapitapir/types/hash.rb +161 -0
  148. data/lib/rapitapir/types/integer.rb +143 -0
  149. data/lib/rapitapir/types/object.rb +156 -0
  150. data/lib/rapitapir/types/optional.rb +65 -0
  151. data/lib/rapitapir/types/string.rb +185 -0
  152. data/lib/rapitapir/types/uuid.rb +32 -0
  153. data/lib/rapitapir/types.rb +155 -0
  154. data/lib/rapitapir/version.rb +5 -0
  155. data/lib/rapitapir.rb +173 -0
  156. data/rapitapir.gemspec +66 -0
  157. metadata +387 -0
data/README.md ADDED
@@ -0,0 +1,485 @@
1
+ # RapiTapir## ๐Ÿ†• What's New
2
+
3
+ - **โœจ - **๐Ÿ“ Type Shortcuts**: Global `T.string`, `T.integer`, etc. (automatically available!)
4
+ - **๐Ÿ”„ GitHub Pages**: Modern documentation deployment with GitHub Actionsean Base Class**: `class MyAPI < SinatraRapiTapir` - the simplest way to create APIs
5
+ - **๐ŸŽฏ Enhanced HTTP DSL**: Built-in GET, POST, PUT, DELETE methods with fluent chaining
6
+ - **๐Ÿ”ง Zero Boilerplate**: Automatic extension registration and feature setup
7
+ - ๐Ÿ“ **Type Shortcuts**: Clean syntax with global `T` constant (automatic - no setup needed!)
8
+ - **๐Ÿ“š GitHub Pages Ready**: Modern documentation deployment with GitHub Actions
9
+ - **๐Ÿงช Comprehensive Tests**: 470 tests passing with 70% coverage modern Ruby library for building type-safe HTTP APIs with automatic OpenAPI documentation**
10
+
11
+ [![Tests](https://img.shields.io/badge/tests-470%20passing-brightgreen)](spec/)
12
+ [![Coverage](https://img.shields.io/badge/coverage-70.13%25-green)](coverage/)
13
+ [![Ruby](https://img.shields.io/badge/ruby-3.1%2B-red)](Gemfile)
14
+ [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
15
+
16
+ **RapiTapir ๐Ÿฆ™** combines the expressiveness of Ruby with the safety of strong typing to create APIs that are both powerful and reliable. Define your endpoints once with our fluent DSL, and get automatic validation, documentation, and client generation.
17
+
18
+ ## ๐Ÿ†• What's New
19
+
20
+ - **โœจ Clean Base Class**: `class MyAPI < SinatraRapiTapir` - the simplest way to create APIs
21
+ - **๐ŸŽฏ Enhanced HTTP DSL**: Built-in GET, POST, PUT, DELETE methods with fluent chaining
22
+ - **๐Ÿ”ง Zero Boilerplate**: Automatic extension registration and feature setup
23
+ - **๏ฟฝ Type Shortcuts**: Use `T.string` instead of `RapiTapir::Types.string` for cleaner code
24
+ - **๏ฟฝ๐Ÿ“š GitHub Pages Ready**: Modern documentation deployment with GitHub Actions
25
+ - **๐Ÿงช Comprehensive Tests**: 470 tests passing with 70% coverage
26
+
27
+ ## โœจ Why RapiTapir?
28
+
29
+ - **๐Ÿ”’ Type Safety**: Strong typing for inputs and outputs with runtime validation
30
+ - **๐Ÿ“– Auto Documentation**: OpenAPI 3.0 specs generated automatically from your code
31
+ - **๐Ÿš€ Framework Agnostic**: Works with Sinatra, Rails, and any Rack-based framework
32
+ - **๐Ÿ›ก๏ธ Production Ready**: Built-in security, observability, and authentication features
33
+ - **๐Ÿ’Ž Ruby Native**: Designed specifically for Ruby developers who love clean, readable code
34
+ - **๐Ÿ”ง Zero Config**: Get started in minutes with sensible defaults
35
+ - **โœจ Clean Syntax**: Elegant base class: `class MyAPI < SinatraRapiTapir`
36
+ - **๐ŸŽฏ Enhanced DSL**: Built-in HTTP verb methods (GET, POST, PUT, etc.)
37
+ - **๏ฟฝ Type Shortcuts**: Clean type syntax with `T.string`, `T.integer`, etc.
38
+ - **๏ฟฝ๐Ÿ”„ GitHub Pages**: Modern documentation deployment with GitHub Actions
39
+
40
+ ## ๐Ÿš€ Quick Start
41
+
42
+ ### Installation
43
+
44
+ Add to your Gemfile:
45
+
46
+ ```ruby
47
+ gem 'rapitapir'
48
+ ```
49
+
50
+ ### Basic Sinatra Example
51
+
52
+ ```ruby
53
+ require 'rapitapir' # Only one require needed!
54
+
55
+ class BookAPI < SinatraRapiTapir
56
+ # Configure API information
57
+ rapitapir do
58
+ info(
59
+ title: 'Book API',
60
+ description: 'A simple book management API',
61
+ version: '1.0.0'
62
+ )
63
+ development_defaults! # Auto CORS, docs, health checks
64
+ end
65
+
66
+ # Define your data schema with T shortcut (globally available!)
67
+ BOOK_SCHEMA = T.hash({
68
+ "id" => T.integer,
69
+ "title" => T.string(min_length: 1, max_length: 255),
70
+ "author" => T.string(min_length: 1),
71
+ "published" => T.boolean,
72
+ "isbn" => T.optional(T.string),
73
+ "pages" => T.optional(T.integer(minimum: 1))
74
+ })
75
+
76
+ # Define endpoints with the elegant resource DSL and enhanced HTTP verbs
77
+ api_resource '/books', schema: BOOK_SCHEMA do
78
+ crud do
79
+ index { Book.all }
80
+
81
+ show do |inputs|
82
+ Book.find(inputs[:id]) || halt(404, { error: 'Book not found' }.to_json)
83
+ end
84
+
85
+ create do |inputs|
86
+ Book.create(inputs[:body])
87
+ end
88
+ end
89
+
90
+ # Custom endpoint using enhanced DSL
91
+ custom :get, 'featured' do
92
+ Book.where(featured: true)
93
+ end
94
+ end
95
+
96
+ # Alternative endpoint definition using enhanced HTTP verb DSL
97
+ endpoint(
98
+ GET('/books/search')
99
+ .query(:q, T.string(min_length: 1), description: 'Search query')
100
+ .query(:limit, T.optional(T.integer(minimum: 1, maximum: 100)), description: 'Results limit')
101
+ .summary('Search books')
102
+ .description('Search books by title or author')
103
+ .tags('Search')
104
+ .ok(T.array(BOOK_SCHEMA))
105
+ .bad_request(T.hash({ "error" => T.string }), description: 'Invalid search parameters')
106
+ .build
107
+ ) do |inputs|
108
+ query = inputs[:q]
109
+ limit = inputs[:limit] || 20
110
+
111
+ books = Book.search(query).limit(limit)
112
+ books.map(&:to_h)
113
+ end
114
+
115
+ run! if __FILE__ == $0
116
+ end
117
+ ```
118
+
119
+ Start your server and visit:
120
+ - **๐Ÿ“– Interactive Documentation**: `http://localhost:4567/docs`
121
+ - **๐Ÿ“‹ OpenAPI Specification**: `http://localhost:4567/openapi.json`
122
+
123
+ That's it! You now have a fully documented, type-safe API with interactive documentation.
124
+
125
+ ## ๐Ÿ—๏ธ Core Features
126
+
127
+ ### Clean Base Class Syntax
128
+
129
+ Create APIs with the cleanest possible syntax:
130
+
131
+ ```ruby
132
+ require 'rapitapir'
133
+
134
+ class MyAPI < SinatraRapiTapir
135
+ rapitapir do
136
+ info(title: 'My API', version: '1.0.0')
137
+ development_defaults! # Auto CORS, docs, health checks
138
+ end
139
+
140
+ # Enhanced HTTP verb DSL automatically available + T shortcut for types
141
+ endpoint(
142
+ GET('/books')
143
+ .summary('List all books')
144
+ .ok(T.array(BOOK_SCHEMA))
145
+ .error_response(500, T.hash({ "error" => T.string }))
146
+ .build
147
+ ) { Book.all }
148
+ end
149
+ ```
150
+
151
+ ### Type-Safe API Design
152
+
153
+ Define your data schemas once and use them everywhere:
154
+
155
+ ```ruby
156
+ # T shortcut is automatically available - no setup needed!
157
+ USER_SCHEMA = T.hash({
158
+ "id" => T.integer,
159
+ "name" => T.string(min_length: 1, max_length: 100),
160
+ "email" => T.email,
161
+ "age" => T.optional(T.integer(min: 0, max: 150)),
162
+ "profile" => T.optional(T.hash({
163
+ "bio" => T.string(max_length: 500),
164
+ "avatar_url" => T.string(format: :url)
165
+ }))
166
+ })
167
+ ```
168
+
169
+ ### Fluent Endpoint Definition
170
+
171
+ Create endpoints with a clean, readable DSL:
172
+
173
+ ```ruby
174
+ # Using the enhanced HTTP verb DSL with T shortcut
175
+ endpoint(
176
+ GET('/users/:id')
177
+ .summary('Get user by ID')
178
+ .path_param(:id, T.integer(minimum: 1))
179
+ .query(:include, T.optional(T.array(T.string)), description: 'Related data to include')
180
+ .ok(USER_SCHEMA)
181
+ .error_response(404, T.hash({ "error" => T.string }), description: 'User not found')
182
+ .error_response(422, T.hash({
183
+ "error" => T.string,
184
+ "details" => T.array(T.hash({
185
+ "field" => T.string,
186
+ "message" => T.string
187
+ }))
188
+ }))
189
+ .build
190
+ ) do |inputs|
191
+ user = User.find(inputs[:id])
192
+ halt 404, { error: 'User not found' }.to_json unless user
193
+
194
+ # Handle optional includes
195
+ if inputs[:include]&.include?('profile')
196
+ user = user.with_profile
197
+ end
198
+
199
+ user.to_h
200
+ end
201
+ ```
202
+
203
+ ### RESTful Resource Builder
204
+
205
+ Build complete CRUD APIs with minimal code:
206
+
207
+ ```ruby
208
+ # Enhanced resource builder with custom validations and relationships
209
+ api_resource '/users', schema: USER_SCHEMA do
210
+ crud do
211
+ index do
212
+ # Automatic pagination and filtering
213
+ users = User.all
214
+ users = users.where(active: true) if params[:active] == 'true'
215
+ users.limit(params[:limit] || 50)
216
+ end
217
+
218
+ show { |inputs| User.find(inputs[:id]) }
219
+
220
+ create do |inputs|
221
+ user = User.create(inputs[:body])
222
+ status 201
223
+ user.to_h
224
+ end
225
+
226
+ update { |inputs| User.update(inputs[:id], inputs[:body]) }
227
+ destroy { |inputs| User.delete(inputs[:id]); status 204 }
228
+ end
229
+
230
+ # Add custom endpoints with full type safety
231
+ custom :get, 'active' do
232
+ User.where(active: true).map(&:to_h)
233
+ end
234
+
235
+ custom :post, ':id/avatar' do |inputs|
236
+ user = User.find(inputs[:id])
237
+ user.update_avatar(inputs[:body][:avatar_data])
238
+ { success: true }
239
+ end
240
+ end
241
+ ```
242
+
243
+ ### Automatic OpenAPI Documentation
244
+
245
+ Your API documentation is always up-to-date because it's generated from your actual code:
246
+
247
+ - **Interactive Swagger UI** with try-it-out functionality
248
+ - **Complete OpenAPI 3.0 specification** with schemas, examples, and security
249
+ - **TypeScript client generation** for frontend teams
250
+ - **Markdown documentation** for wikis and READMEs
251
+
252
+ ## ๐Ÿ”ง Framework Integration
253
+
254
+ ### Sinatra (Recommended)
255
+
256
+ **Option 1: Clean Base Class (Recommended)**
257
+ ```ruby
258
+ require 'rapitapir'
259
+
260
+ class MyAPI < SinatraRapiTapir
261
+ rapitapir do
262
+ info(title: 'My API', version: '1.0.0')
263
+ development_defaults!
264
+ end
265
+ # Enhanced HTTP verb DSL automatically available
266
+ end
267
+ ```
268
+
269
+ **Option 2: Manual Extension Registration**
270
+ ```ruby
271
+ require 'rapitapir/sinatra/extension'
272
+
273
+ class MyAPI < Sinatra::Base
274
+ register RapiTapir::Sinatra::Extension
275
+ # Use the full DSL...
276
+ end
277
+ ```
278
+
279
+ ### Rack Applications
280
+
281
+ ```ruby
282
+ require 'rapitapir/server/rack_adapter'
283
+
284
+ class MyRackApp
285
+ def call(env)
286
+ # Manual integration with Rack
287
+ end
288
+ end
289
+ ```
290
+
291
+ ### Rails Support
292
+
293
+ ```ruby
294
+ # In your Rails controller
295
+ include RapiTapir::Rails::Controller
296
+ ```
297
+
298
+ ## ๐Ÿ›ก๏ธ Production Features
299
+
300
+ ### Authentication & Authorization
301
+
302
+ ```ruby
303
+ # Bearer token authentication with enhanced syntax
304
+ class SecureAPI < SinatraRapiTapir
305
+ rapitapir do
306
+ info(title: 'Secure API', version: '1.0.0')
307
+ bearer_auth :api_key, realm: 'API'
308
+ production_defaults!
309
+ end
310
+
311
+ # Protected endpoint with scope-based authorization
312
+ endpoint(
313
+ GET('/admin/users')
314
+ .summary('List all users (admin only)')
315
+ .bearer_auth(scopes: ['admin'])
316
+ .query(:page, T.optional(T.integer(minimum: 1)), description: 'Page number')
317
+ .query(:per_page, T.optional(T.integer(minimum: 1, maximum: 100)), description: 'Items per page')
318
+ .ok(T.hash({
319
+ "users" => T.array(USER_SCHEMA),
320
+ "pagination" => T.hash({
321
+ "page" => T.integer,
322
+ "per_page" => T.integer,
323
+ "total" => T.integer,
324
+ "pages" => T.integer
325
+ })
326
+ }))
327
+ .error_response(401, T.hash({ "error" => T.string }), description: 'Unauthorized')
328
+ .error_response(403, T.hash({ "error" => T.string }), description: 'Insufficient permissions')
329
+ .build
330
+ ) do |inputs|
331
+ require_scope!('admin')
332
+
333
+ page = inputs[:page] || 1
334
+ per_page = inputs[:per_page] || 20
335
+
336
+ users = User.paginate(page: page, per_page: per_page)
337
+
338
+ {
339
+ users: users.map(&:to_h),
340
+ pagination: {
341
+ page: page,
342
+ per_page: per_page,
343
+ total: users.total_count,
344
+ pages: users.total_pages
345
+ }
346
+ }
347
+ end
348
+ end
349
+ ```
350
+
351
+ ### Observability
352
+
353
+ ```ruby
354
+ class MonitoredAPI < SinatraRapiTapir
355
+ rapitapir do
356
+ info(title: 'Monitored API', version: '1.0.0')
357
+ enable_health_checks path: '/health'
358
+ enable_metrics
359
+ production_defaults!
360
+ end
361
+
362
+ # Endpoint with metrics and tracing
363
+ endpoint(
364
+ GET('/api/data')
365
+ .summary('Get data with monitoring')
366
+ .with_metrics('api_data_requests')
367
+ .with_tracing('fetch_api_data')
368
+ .query(:filter, T.optional(T.string), description: 'Data filter')
369
+ .ok(T.hash({
370
+ "data" => T.array(T.hash({
371
+ "id" => T.integer,
372
+ "value" => T.string,
373
+ "timestamp" => T.datetime
374
+ })),
375
+ "metadata" => T.hash({
376
+ "total" => T.integer,
377
+ "filtered" => T.boolean
378
+ })
379
+ }))
380
+ .build
381
+ ) do |inputs|
382
+ # Your endpoint code with automatic metrics collection
383
+ data = DataService.fetch(filter: inputs[:filter])
384
+
385
+ {
386
+ data: data.map(&:to_h),
387
+ metadata: {
388
+ total: data.count,
389
+ filtered: inputs[:filter].present?
390
+ }
391
+ }
392
+ end
393
+ end
394
+ ```
395
+
396
+ ### Security Middleware
397
+
398
+ ```ruby
399
+ # Built-in security features
400
+ use RapiTapir::Server::Middleware::CORS
401
+ use RapiTapir::Server::Middleware::RateLimit, requests_per_minute: 100
402
+ use RapiTapir::Server::Middleware::SecurityHeaders
403
+ ```
404
+
405
+ ## ๐ŸŽจ Examples
406
+
407
+ Explore our comprehensive examples:
408
+
409
+ - **[Hello World](examples/hello_world.rb)** - Minimal API with SinatraRapiTapir base class
410
+ - **[Getting Started](examples/getting_started_extension.rb)** - Complete bookstore API with CRUD operations
411
+ - **[Enterprise API](examples/enterprise_rapitapir_api.rb)** - Production-ready example with auth
412
+ - **[Authentication](examples/authentication_example.rb)** - Bearer token and scope-based auth
413
+ - **[Observability](examples/observability/)** - Health checks, metrics, and tracing
414
+
415
+ ## ๐Ÿ“š Documentation
416
+
417
+ - **[API Reference](docs/endpoint-definition.md)** - Complete endpoint definition guide
418
+ - **[SinatraRapiTapir Base Class](docs/sinatra_rapitapir.md)** - Clean inheritance syntax guide
419
+ - **[Sinatra Extension](docs/SINATRA_EXTENSION.md)** - Detailed Sinatra integration
420
+ - **[Type System](docs/types.md)** - All available types and validations (use `T.` shortcut!)
421
+ - **[Authentication](docs/authentication.md)** - Security and auth patterns
422
+ - **[Observability](docs/observability.md)** - Monitoring and health checks
423
+ - **[GitHub Pages Setup](docs/github_pages_setup.md)** - Documentation deployment guide
424
+
425
+ ## ๐Ÿงช Testing
426
+
427
+ RapiTapir includes comprehensive testing utilities:
428
+
429
+ ```ruby
430
+ # Validate your endpoint definitions
431
+ RapiTapir::CLI::Validator.new(endpoints).validate
432
+
433
+ # Generate test fixtures
434
+ RapiTapir::Testing.generate_fixtures(USER_SCHEMA)
435
+ ```
436
+
437
+ Run the test suite:
438
+
439
+ ```bash
440
+ bundle exec rspec
441
+ ```
442
+
443
+ ## ๐Ÿค Contributing
444
+
445
+ We love contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
446
+
447
+ ### Development Setup
448
+
449
+ ```bash
450
+ git clone https://github.com/riccardomerolla/rapitapir.git
451
+ cd ruby-tapir
452
+ bundle install
453
+ bundle exec rspec
454
+ ```
455
+
456
+ ### Roadmap
457
+
458
+ - **Phase 4**: Advanced client generation (Python, Go, etc.)
459
+ - **Phase 5**: GraphQL integration
460
+ - **Phase 6**: gRPC support
461
+ - **Community**: Plugin ecosystem
462
+
463
+ ## ๏ฟฝ License
464
+
465
+ RapiTapir is released under the [MIT License](LICENSE).
466
+
467
+ ## ๐Ÿ™‹โ€โ™‚๏ธ Support
468
+
469
+ - **๐Ÿ› Bug Reports**: [GitHub Issues](https://github.com/riccardomerolla/rapitapir/issues)
470
+ - **๐Ÿ’ก Feature Requests**: [GitHub Discussions](https://github.com/riccardomerolla/rapitapir/discussions)
471
+ - **๐Ÿ“ง Email**: riccardo.merolla@gmail.com
472
+
473
+ ---
474
+
475
+ **Built with โค๏ธ for the Ruby and Sinatra community**
476
+
477
+
478
+ ## ๐Ÿ™ Acknowledgments
479
+
480
+ Inspired by:
481
+ - [Scala Tapir](https://github.com/softwaremill/tapir) - Type-safe endpoints
482
+
483
+ ---
484
+
485
+ **RapiTapir** - APIs so fast and clean, they practically run wild! ๐Ÿฆ™โšก
data/debug_hash.rb ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test === operator behavior
5
+ puts '=== Testing === operator ==='
6
+ puts "Hash === Hash: #{Hash.is_a?(Hash)}"
7
+ puts "Hash === {}: #{{}.is_a?(Hash)}"
8
+ puts "Array === Array: #{Array.is_a?(Array)}"
9
+ puts "Array === []: #{[].is_a?(Array)}"
10
+
11
+ # The issue is that case uses ===, and Class === Class is false
12
+ # but Class === instance is true
13
+
14
+ puts "\n=== Testing workaround ==="
15
+ case Hash
16
+ when ->(t) { t == Hash }
17
+ puts 'Lambda workaround works for Hash'
18
+ else
19
+ puts 'Lambda workaround failed for Hash'
20
+ end