spikard 0.12.0 → 0.15.2

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/Steepfile +6 -0
  3. data/ext/spikard_rb/extconf.rb +1 -2
  4. data/ext/spikard_rb/{Cargo.lock → native/Cargo.lock} +897 -451
  5. data/ext/spikard_rb/native/Cargo.toml +24 -0
  6. data/ext/spikard_rb/src/lib.rs +5366 -3
  7. data/lib/spikard/native.rb +86 -0
  8. data/lib/spikard/version.rb +6 -1
  9. data/lib/spikard.rb +8 -45
  10. data/lib/spikard_rb.so +0 -0
  11. data/sig/types.rbs +427 -0
  12. metadata +14 -242
  13. data/LICENSE +0 -1
  14. data/README.md +0 -267
  15. data/ext/spikard_rb/Cargo.toml +0 -17
  16. data/lib/spikard/app.rb +0 -428
  17. data/lib/spikard/background.rb +0 -58
  18. data/lib/spikard/config.rb +0 -506
  19. data/lib/spikard/converters.rb +0 -13
  20. data/lib/spikard/grpc.rb +0 -182
  21. data/lib/spikard/handler_wrapper.rb +0 -113
  22. data/lib/spikard/provide.rb +0 -214
  23. data/lib/spikard/response.rb +0 -173
  24. data/lib/spikard/schema.rb +0 -243
  25. data/lib/spikard/sse.rb +0 -111
  26. data/lib/spikard/streaming_response.rb +0 -44
  27. data/lib/spikard/testing.rb +0 -432
  28. data/lib/spikard/upload_file.rb +0 -131
  29. data/lib/spikard/websocket.rb +0 -59
  30. data/sig/spikard.rbs +0 -719
  31. data/vendor/crates/spikard-bindings-shared/Cargo.toml +0 -80
  32. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +0 -132
  33. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +0 -905
  34. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +0 -210
  35. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +0 -252
  36. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +0 -404
  37. data/vendor/crates/spikard-bindings-shared/src/grpc_metadata.rs +0 -199
  38. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +0 -252
  39. data/vendor/crates/spikard-bindings-shared/src/json_conversion.rs +0 -829
  40. data/vendor/crates/spikard-bindings-shared/src/lazy_cache.rs +0 -587
  41. data/vendor/crates/spikard-bindings-shared/src/lib.rs +0 -33
  42. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +0 -298
  43. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +0 -594
  44. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +0 -743
  45. data/vendor/crates/spikard-bindings-shared/src/response_interpreter.rs +0 -944
  46. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +0 -260
  47. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +0 -369
  48. data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +0 -192
  49. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +0 -383
  50. data/vendor/crates/spikard-bindings-shared/tests/full_coverage.rs +0 -459
  51. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +0 -280
  52. data/vendor/crates/spikard-bindings-shared/tests/integration_tests.rs +0 -669
  53. data/vendor/crates/spikard-core/Cargo.toml +0 -60
  54. data/vendor/crates/spikard-core/src/bindings/mod.rs +0 -3
  55. data/vendor/crates/spikard-core/src/bindings/response.rs +0 -130
  56. data/vendor/crates/spikard-core/src/debug.rs +0 -127
  57. data/vendor/crates/spikard-core/src/di/container.rs +0 -702
  58. data/vendor/crates/spikard-core/src/di/dependency.rs +0 -273
  59. data/vendor/crates/spikard-core/src/di/error.rs +0 -118
  60. data/vendor/crates/spikard-core/src/di/factory.rs +0 -538
  61. data/vendor/crates/spikard-core/src/di/graph.rs +0 -507
  62. data/vendor/crates/spikard-core/src/di/mod.rs +0 -192
  63. data/vendor/crates/spikard-core/src/di/resolved.rs +0 -428
  64. data/vendor/crates/spikard-core/src/di/value.rs +0 -282
  65. data/vendor/crates/spikard-core/src/errors.rs +0 -72
  66. data/vendor/crates/spikard-core/src/http.rs +0 -492
  67. data/vendor/crates/spikard-core/src/lib.rs +0 -29
  68. data/vendor/crates/spikard-core/src/lifecycle.rs +0 -1273
  69. data/vendor/crates/spikard-core/src/metadata.rs +0 -378
  70. data/vendor/crates/spikard-core/src/parameters.rs +0 -2546
  71. data/vendor/crates/spikard-core/src/problem.rs +0 -358
  72. data/vendor/crates/spikard-core/src/request_data.rs +0 -1146
  73. data/vendor/crates/spikard-core/src/router.rs +0 -530
  74. data/vendor/crates/spikard-core/src/schema_registry.rs +0 -197
  75. data/vendor/crates/spikard-core/src/type_hints.rs +0 -311
  76. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +0 -710
  77. data/vendor/crates/spikard-core/src/validation/mod.rs +0 -470
  78. data/vendor/crates/spikard-core/tests/bindings_response_tests.rs +0 -136
  79. data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +0 -37
  80. data/vendor/crates/spikard-core/tests/error_mapper.rs +0 -761
  81. data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +0 -106
  82. data/vendor/crates/spikard-core/tests/parameters_full.rs +0 -701
  83. data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +0 -301
  84. data/vendor/crates/spikard-core/tests/request_data_roundtrip.rs +0 -67
  85. data/vendor/crates/spikard-core/tests/validation_coverage.rs +0 -250
  86. data/vendor/crates/spikard-core/tests/validation_error_paths.rs +0 -45
  87. data/vendor/crates/spikard-http/Cargo.toml +0 -87
  88. data/vendor/crates/spikard-http/examples/sse-notifications.rs +0 -148
  89. data/vendor/crates/spikard-http/examples/websocket-chat.rs +0 -92
  90. data/vendor/crates/spikard-http/src/auth.rs +0 -301
  91. data/vendor/crates/spikard-http/src/background.rs +0 -1860
  92. data/vendor/crates/spikard-http/src/bindings/mod.rs +0 -3
  93. data/vendor/crates/spikard-http/src/bindings/response.rs +0 -1
  94. data/vendor/crates/spikard-http/src/body_metadata.rs +0 -8
  95. data/vendor/crates/spikard-http/src/cors.rs +0 -1026
  96. data/vendor/crates/spikard-http/src/debug.rs +0 -128
  97. data/vendor/crates/spikard-http/src/di_handler.rs +0 -1672
  98. data/vendor/crates/spikard-http/src/grpc/framing.rs +0 -469
  99. data/vendor/crates/spikard-http/src/grpc/handler.rs +0 -1122
  100. data/vendor/crates/spikard-http/src/grpc/mod.rs +0 -434
  101. data/vendor/crates/spikard-http/src/grpc/service.rs +0 -622
  102. data/vendor/crates/spikard-http/src/grpc/streaming.rs +0 -319
  103. data/vendor/crates/spikard-http/src/handler_response.rs +0 -901
  104. data/vendor/crates/spikard-http/src/handler_trait.rs +0 -1015
  105. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +0 -290
  106. data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +0 -502
  107. data/vendor/crates/spikard-http/src/jsonrpc/method_registry.rs +0 -648
  108. data/vendor/crates/spikard-http/src/jsonrpc/mod.rs +0 -58
  109. data/vendor/crates/spikard-http/src/jsonrpc/protocol.rs +0 -1207
  110. data/vendor/crates/spikard-http/src/jsonrpc/router.rs +0 -2262
  111. data/vendor/crates/spikard-http/src/lib.rs +0 -548
  112. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +0 -230
  113. data/vendor/crates/spikard-http/src/lifecycle.rs +0 -1193
  114. data/vendor/crates/spikard-http/src/middleware/mod.rs +0 -560
  115. data/vendor/crates/spikard-http/src/middleware/multipart.rs +0 -912
  116. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +0 -513
  117. data/vendor/crates/spikard-http/src/middleware/validation.rs +0 -768
  118. data/vendor/crates/spikard-http/src/openapi/mod.rs +0 -309
  119. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +0 -535
  120. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +0 -1363
  121. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +0 -667
  122. data/vendor/crates/spikard-http/src/query_parser.rs +0 -793
  123. data/vendor/crates/spikard-http/src/response.rs +0 -720
  124. data/vendor/crates/spikard-http/src/server/fast_router.rs +0 -186
  125. data/vendor/crates/spikard-http/src/server/grpc_routing.rs +0 -858
  126. data/vendor/crates/spikard-http/src/server/handler.rs +0 -1661
  127. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +0 -253
  128. data/vendor/crates/spikard-http/src/server/mod.rs +0 -1649
  129. data/vendor/crates/spikard-http/src/server/request_extraction.rs +0 -871
  130. data/vendor/crates/spikard-http/src/server/routing_factory.rs +0 -618
  131. data/vendor/crates/spikard-http/src/sse.rs +0 -1409
  132. data/vendor/crates/spikard-http/src/testing/form.rs +0 -52
  133. data/vendor/crates/spikard-http/src/testing/multipart.rs +0 -64
  134. data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -787
  135. data/vendor/crates/spikard-http/src/testing.rs +0 -617
  136. data/vendor/crates/spikard-http/src/websocket.rs +0 -1477
  137. data/vendor/crates/spikard-http/tests/auth_integration.rs +0 -645
  138. data/vendor/crates/spikard-http/tests/background_behavior.rs +0 -832
  139. data/vendor/crates/spikard-http/tests/common/grpc_helpers.rs +0 -1012
  140. data/vendor/crates/spikard-http/tests/common/handlers.rs +0 -309
  141. data/vendor/crates/spikard-http/tests/common/mod.rs +0 -33
  142. data/vendor/crates/spikard-http/tests/common/test_builders.rs +0 -628
  143. data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +0 -162
  144. data/vendor/crates/spikard-http/tests/di_integration.rs +0 -192
  145. data/vendor/crates/spikard-http/tests/doc_snippets.rs +0 -5
  146. data/vendor/crates/spikard-http/tests/grpc_bidirectional_streaming.rs +0 -430
  147. data/vendor/crates/spikard-http/tests/grpc_client_streaming.rs +0 -738
  148. data/vendor/crates/spikard-http/tests/grpc_error_handling_test.rs +0 -652
  149. data/vendor/crates/spikard-http/tests/grpc_integration_test.rs +0 -334
  150. data/vendor/crates/spikard-http/tests/grpc_metadata_test.rs +0 -532
  151. data/vendor/crates/spikard-http/tests/grpc_server_integration.rs +0 -495
  152. data/vendor/crates/spikard-http/tests/grpc_server_streaming.rs +0 -974
  153. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +0 -1093
  154. data/vendor/crates/spikard-http/tests/middleware_stack_integration.rs +0 -389
  155. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +0 -656
  156. data/vendor/crates/spikard-http/tests/request_extraction_full.rs +0 -513
  157. data/vendor/crates/spikard-http/tests/server_auth_middleware_behavior.rs +0 -328
  158. data/vendor/crates/spikard-http/tests/server_config_builder.rs +0 -314
  159. data/vendor/crates/spikard-http/tests/server_configured_router_behavior.rs +0 -200
  160. data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +0 -83
  161. data/vendor/crates/spikard-http/tests/server_handler_wrappers.rs +0 -464
  162. data/vendor/crates/spikard-http/tests/server_method_router_additional_behavior.rs +0 -286
  163. data/vendor/crates/spikard-http/tests/server_method_router_coverage.rs +0 -118
  164. data/vendor/crates/spikard-http/tests/server_middleware_behavior.rs +0 -99
  165. data/vendor/crates/spikard-http/tests/server_middleware_branches.rs +0 -204
  166. data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +0 -421
  167. data/vendor/crates/spikard-http/tests/server_router_behavior.rs +0 -121
  168. data/vendor/crates/spikard-http/tests/sse_behavior.rs +0 -620
  169. data/vendor/crates/spikard-http/tests/sse_full_behavior.rs +0 -584
  170. data/vendor/crates/spikard-http/tests/sse_handler_behavior.rs +0 -130
  171. data/vendor/crates/spikard-http/tests/test_client_requests.rs +0 -167
  172. data/vendor/crates/spikard-http/tests/testing_helpers.rs +0 -87
  173. data/vendor/crates/spikard-http/tests/testing_module_coverage.rs +0 -155
  174. data/vendor/crates/spikard-http/tests/urlencoded_content_type.rs +0 -82
  175. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +0 -663
  176. data/vendor/crates/spikard-http/tests/websocket_full_behavior.rs +0 -440
  177. data/vendor/crates/spikard-http/tests/websocket_integration.rs +0 -150
  178. data/vendor/crates/spikard-rb/Cargo.toml +0 -68
  179. data/vendor/crates/spikard-rb/build.rs +0 -200
  180. data/vendor/crates/spikard-rb/src/background.rs +0 -63
  181. data/vendor/crates/spikard-rb/src/config/mod.rs +0 -5
  182. data/vendor/crates/spikard-rb/src/config/server_config.rs +0 -401
  183. data/vendor/crates/spikard-rb/src/conversion.rs +0 -688
  184. data/vendor/crates/spikard-rb/src/di/builder.rs +0 -100
  185. data/vendor/crates/spikard-rb/src/di/mod.rs +0 -375
  186. data/vendor/crates/spikard-rb/src/grpc/handler.rs +0 -834
  187. data/vendor/crates/spikard-rb/src/grpc/mod.rs +0 -13
  188. data/vendor/crates/spikard-rb/src/gvl.rs +0 -80
  189. data/vendor/crates/spikard-rb/src/handler.rs +0 -699
  190. data/vendor/crates/spikard-rb/src/integration/mod.rs +0 -3
  191. data/vendor/crates/spikard-rb/src/lib.rs +0 -2264
  192. data/vendor/crates/spikard-rb/src/lifecycle.rs +0 -303
  193. data/vendor/crates/spikard-rb/src/metadata/mod.rs +0 -5
  194. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +0 -507
  195. data/vendor/crates/spikard-rb/src/request.rs +0 -439
  196. data/vendor/crates/spikard-rb/src/runtime/mod.rs +0 -5
  197. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +0 -344
  198. data/vendor/crates/spikard-rb/src/server.rs +0 -307
  199. data/vendor/crates/spikard-rb/src/sse.rs +0 -231
  200. data/vendor/crates/spikard-rb/src/testing/client.rs +0 -698
  201. data/vendor/crates/spikard-rb/src/testing/mod.rs +0 -7
  202. data/vendor/crates/spikard-rb/src/testing/sse.rs +0 -108
  203. data/vendor/crates/spikard-rb/src/testing/websocket.rs +0 -573
  204. data/vendor/crates/spikard-rb/src/websocket.rs +0 -475
  205. data/vendor/crates/spikard-rb-macros/Cargo.toml +0 -25
  206. data/vendor/crates/spikard-rb-macros/src/lib.rs +0 -51
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'converters'
4
-
5
- module Spikard
6
- # Handler wrapper utilities.
7
- #
8
- # UploadFile conversion now happens in the Rust binding, so these wrappers
9
- # simply forward the already-converted body/params.
10
- #
11
- # @example Basic usage with body only
12
- # app.post('/upload', &wrap_body_handler do |body|
13
- # {
14
- # filename: body[:file].filename,
15
- # content: body[:file].read
16
- # }
17
- # end)
18
- #
19
- # @example With all parameters
20
- # app.post('/upload', &wrap_handler do |params, query, body|
21
- # {
22
- # id: params[:id],
23
- # search: query[:q],
24
- # file: body[:file].filename
25
- # }
26
- # end)
27
- module HandlerWrapper
28
- module_function
29
-
30
- # Wrap a handler that receives only the request body
31
- #
32
- # Automatically converts file metadata in the body to UploadFile instances.
33
- #
34
- # @yield [body] Handler block that receives converted body
35
- # @yieldparam body [Hash] Request body with file metadata converted to UploadFile
36
- # @yieldreturn [Hash, Spikard::Response] Response data or Response object
37
- # @return [Proc] Wrapped handler proc
38
- #
39
- # @example
40
- # app.post('/upload', &wrap_body_handler do |body|
41
- # { filename: body[:file].filename }
42
- # end)
43
- def wrap_body_handler(&handler)
44
- raise ArgumentError, 'block required for wrap_body_handler' unless handler
45
-
46
- # Return a proc that matches the signature expected by Spikard::App
47
- # The actual handler receives path params, query params, and body from Rust
48
- lambda do |_params, _query, body|
49
- handler.call(body)
50
- end
51
- end
52
-
53
- # Wrap a handler that receives path params, query params, and body
54
- #
55
- # Automatically converts file metadata in the body to UploadFile instances.
56
- #
57
- # @yield [params, query, body] Handler block that receives all request data
58
- # @yieldparam params [Hash] Path parameters
59
- # @yieldparam query [Hash] Query parameters
60
- # @yieldparam body [Hash] Request body with file metadata converted to UploadFile
61
- # @yieldreturn [Hash, Spikard::Response] Response data or Response object
62
- # @return [Proc] Wrapped handler proc
63
- #
64
- # @example
65
- # app.post('/users/{id}/upload', &wrap_handler do |params, query, body|
66
- # {
67
- # user_id: params[:id],
68
- # description: query[:desc],
69
- # file: body[:file].filename
70
- # }
71
- # end)
72
- def wrap_handler(&handler)
73
- raise ArgumentError, 'block required for wrap_handler' unless handler
74
-
75
- lambda do |params, query, body|
76
- handler.call(params, query, body)
77
- end
78
- end
79
-
80
- # Wrap a handler that receives a context hash with all request data
81
- #
82
- # Automatically converts file metadata in the body to UploadFile instances.
83
- # Useful when you want all request data in a single hash.
84
- #
85
- # @yield [context] Handler block that receives context hash
86
- # @yieldparam context [Hash] Request context with:
87
- # - :params [Hash] Path parameters
88
- # - :query [Hash] Query parameters
89
- # - :body [Hash] Request body with file metadata converted to UploadFile
90
- # @yieldreturn [Hash, Spikard::Response] Response data or Response object
91
- # @return [Proc] Wrapped handler proc
92
- #
93
- # @example
94
- # app.post('/upload', &wrap_handler_with_context do |ctx|
95
- # {
96
- # file: ctx[:body][:file].filename,
97
- # query_params: ctx[:query]
98
- # }
99
- # end)
100
- def wrap_handler_with_context(&handler)
101
- raise ArgumentError, 'block required for wrap_handler_with_context' unless handler
102
-
103
- lambda do |params, query, body|
104
- context = {
105
- params: params,
106
- query: query,
107
- body: body
108
- }
109
- handler.call(context)
110
- end
111
- end
112
- end
113
- end
@@ -1,214 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spikard
4
- # Wrapper class for dependency providers
5
- #
6
- # This class wraps factory functions and configuration for dependency injection.
7
- # It provides a consistent API across Python, Node.js, and Ruby bindings.
8
- #
9
- # @example Factory with caching
10
- # app.provide("db", Spikard::Provide.new(method("create_db"), cacheable: true))
11
- #
12
- # @example Factory with dependencies
13
- # app.provide("auth", Spikard::Provide.new(
14
- # method("create_auth_service"),
15
- # depends_on: ["db", "cache"],
16
- # singleton: true
17
- # ))
18
- class Provide
19
- attr_reader :factory, :depends_on, :singleton, :cacheable
20
-
21
- # Create a new dependency provider
22
- #
23
- # @param factory [Proc, Method] The factory function that creates the dependency value
24
- # @param depends_on [Array<String, Symbol>] List of dependency keys this factory depends on
25
- # @param singleton [Boolean] Whether to cache the value globally (default: false)
26
- # @param cacheable [Boolean] Whether to cache the value per-request (default: true)
27
- def initialize(factory, depends_on: [], singleton: false, cacheable: true)
28
- @factory = factory
29
- @depends_on = Array(depends_on).map(&:to_s)
30
- @singleton = singleton
31
- @cacheable = cacheable
32
- end
33
-
34
- # Check if the factory is async (based on method arity or other heuristics)
35
- #
36
- # @return [Boolean] True if the factory appears to be async
37
- def async?
38
- # Ruby doesn't have explicit async/await like Python/JS
39
- # We could check if it returns a Thread or uses Fiber
40
- false
41
- end
42
-
43
- # Check if the factory is an async generator
44
- #
45
- # @return [Boolean] True if the factory is an async generator
46
- def async_generator?
47
- false
48
- end
49
- end
50
-
51
- # Dependency Injection support for Spikard applications
52
- #
53
- # Provides methods for registering and managing dependencies that can be
54
- # automatically injected into route handlers.
55
- #
56
- # @example Registering a value dependency
57
- # app.provide("database_url", "postgresql://localhost/mydb")
58
- #
59
- # @example Registering a factory dependency
60
- # app.provide("db_pool", depends_on: ["database_url"]) do |database_url:|
61
- # ConnectionPool.new(database_url)
62
- # end
63
- #
64
- # @example Singleton dependency (shared across all requests)
65
- # app.provide("config", singleton: true) do
66
- # Config.load_from_file("config.yml")
67
- # end
68
- #
69
- # @example Using Provide wrapper
70
- # app.provide("db", Spikard::Provide.new(method("create_db"), cacheable: true))
71
- module ProvideSupport
72
- # Register a dependency in the DI container
73
- #
74
- # This method supports three patterns:
75
- # 1. **Value dependency**: Pass a value directly (e.g., string, number, object)
76
- # 2. **Factory dependency**: Pass a block that computes the value
77
- # 3. **Provide wrapper**: Pass a Spikard::Provide instance
78
- #
79
- # @param key [String, Symbol] Unique identifier for the dependency
80
- # @param value [Object, Provide, nil] Static value, Provide instance, or nil
81
- # @param depends_on [Array<String, Symbol>] List of dependency keys this factory depends on
82
- # @param singleton [Boolean] Whether to cache the value globally (default: false)
83
- # @param cacheable [Boolean] Whether to cache the value per-request (default: true)
84
- # @yield Optional factory block that receives dependencies as keyword arguments
85
- # @yieldparam **deps [Hash] Resolved dependencies as keyword arguments
86
- # @yieldreturn [Object] The computed dependency value
87
- # @return [self] Returns self for method chaining
88
- #
89
- # @example Value dependency
90
- # app.provide("app_name", "MyApp")
91
- # app.provide("port", 8080)
92
- #
93
- # @example Factory with dependencies
94
- # app.provide("database", depends_on: ["config"]) do |config:|
95
- # Database.connect(config["db_url"])
96
- # end
97
- #
98
- # @example Singleton factory
99
- # app.provide("thread_pool", singleton: true) do
100
- # ThreadPool.new(size: 10)
101
- # end
102
- #
103
- # @example Non-cacheable factory (resolves every time)
104
- # app.provide("request_id", cacheable: false) do
105
- # SecureRandom.uuid
106
- # end
107
- #
108
- # @example Using Provide wrapper
109
- # app.provide("db", Spikard::Provide.new(method("create_db"), cacheable: true))
110
- def provide(key, value = nil, depends_on: [], singleton: false, cacheable: true, &block)
111
- key_str = key.to_s
112
- registry = ensure_native_dependencies!
113
-
114
- # Handle Provide wrapper instances
115
- if value.is_a?(Provide)
116
- registry.register_factory(key_str, value.factory, value.depends_on, value.singleton, value.cacheable)
117
- elsif block
118
- registry.register_factory(key_str, block, Array(depends_on).map(&:to_s), singleton, cacheable)
119
- else
120
- raise ArgumentError, 'Either provide a value or a block, not both' if value.nil?
121
-
122
- registry.register_value(key_str, value)
123
- end
124
-
125
- self
126
- end
127
-
128
- # Get all registered dependencies
129
- #
130
- # @return [Hash] Dictionary mapping dependency keys to their definitions
131
- # @api private
132
- def dependencies
133
- ensure_native_dependencies!
134
- end
135
-
136
- private
137
-
138
- def ensure_native_dependencies!
139
- registry = (@native_dependencies if instance_variable_defined?(:@native_dependencies) && @native_dependencies)
140
- raise 'Spikard native dependency registry unavailable' unless registry
141
-
142
- registry
143
- end
144
- end
145
-
146
- # Dependency injection handler wrapper
147
- #
148
- # Wraps a route handler to inject dependencies based on parameter names.
149
- # Dependencies are resolved from the DI container and passed as keyword arguments.
150
- #
151
- # @api private
152
- module DIHandlerWrapper
153
- # Wrap a handler to inject dependencies
154
- #
155
- # @param handler [Proc] The original route handler
156
- # @param dependencies [Hash] Available dependencies from the app
157
- # @return [Proc] Wrapped handler with DI support
158
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
159
- def self.wrap_handler(handler, dependencies)
160
- # Extract parameter names from the handler
161
- params = handler.parameters.map { |_type, name| name.to_s }
162
-
163
- # Find which parameters match registered dependencies
164
- injectable_params = params & dependencies.keys
165
-
166
- if injectable_params.empty?
167
- # No DI needed, return original handler
168
- return handler
169
- end
170
-
171
- # Create wrapped handler that injects dependencies
172
- lambda do |request|
173
- # Build kwargs with injected dependencies
174
- kwargs = {}
175
-
176
- injectable_params.each do |param_name|
177
- dep_def = dependencies[param_name]
178
- kwargs[param_name.to_sym] = resolve_dependency(dep_def, request)
179
- end
180
-
181
- # Call original handler with injected dependencies
182
- if handler.arity.zero?
183
- # Handler takes no arguments (dependencies injected via closure or instance vars)
184
- handler.call
185
- elsif injectable_params.length == params.length
186
- # All parameters are dependencies
187
- handler.call(**kwargs)
188
- else
189
- # Mix of request data and dependencies
190
- handler.call(request, **kwargs)
191
- end
192
- end
193
- end
194
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
195
-
196
- # Resolve a dependency definition
197
- #
198
- # @param dep_def [Hash] Dependency definition
199
- # @param request [Hash] Request context (unused for now, future: per-request deps)
200
- # @return [Object] Resolved dependency value
201
- # @api private
202
- def self.resolve_dependency(dep_def, _request)
203
- case dep_def[:type]
204
- when :value
205
- dep_def[:value]
206
- when :factory
207
- factory = dep_def[:factory]
208
- dep_def[:depends_on]
209
- # TODO: Implement nested dependency resolution when dependencies are provided
210
- factory.call
211
- end
212
- end
213
- end
214
- end
@@ -1,173 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # ⚠️ GENERATED BY crates/spikard-rb/build.rs — DO NOT EDIT BY HAND
4
- module Spikard
5
- class Response # :nodoc: Native-backed HTTP response facade generated from Rust metadata.
6
- attr_reader :content, :status_code, :headers, :native_response
7
-
8
- def initialize(content: nil, body: nil, status_code: 200, headers: nil, content_type: nil)
9
- @content = content.nil? ? body : content
10
- @status_code = Integer(status_code || 200)
11
- @headers = normalize_headers(headers)
12
- set_header('content-type', content_type) if content_type
13
- rebuild_native!
14
- end
15
-
16
- def status
17
- @status_code
18
- end
19
-
20
- def status_code=(value)
21
- @status_code = Integer(value)
22
- rebuild_native!
23
- rescue ArgumentError, TypeError
24
- raise ArgumentError, 'status_code must be an integer'
25
- end
26
-
27
- def headers=(value)
28
- @headers = normalize_headers(value)
29
- rebuild_native!
30
- end
31
-
32
- def content=(value)
33
- @content = value
34
- rebuild_native!
35
- end
36
-
37
- def set_header(name, value)
38
- @headers[name.to_s] = value.to_s
39
- rebuild_native!
40
- end
41
-
42
- def set_cookie(name, value, **options)
43
- raise ArgumentError, 'cookie name required' if name.nil? || name.empty?
44
-
45
- header_value = ["#{name}=#{value}", *cookie_parts(options)].join('; ')
46
- set_header('set-cookie', header_value)
47
- end
48
-
49
- def to_native_response
50
- @native_response
51
- end
52
-
53
- private
54
-
55
- def rebuild_native!
56
- ensure_native!
57
- @native_response = Spikard::Native.build_response(@content, @status_code, @headers)
58
- return unless @native_response
59
-
60
- @status_code = @native_response.status_code
61
- @headers = @native_response.headers
62
- end
63
-
64
- def ensure_native!
65
- return if defined?(Spikard::Native) && Spikard::Native.respond_to?(:build_response)
66
-
67
- raise 'Spikard native extension is not loaded'
68
- end
69
-
70
- def cookie_parts(options)
71
- [
72
- options[:max_age] && "Max-Age=#{Integer(options[:max_age])}",
73
- options[:domain] && "Domain=#{options[:domain]}",
74
- "Path=#{options.fetch(:path, '/') || '/'}",
75
- options[:secure] ? 'Secure' : nil,
76
- options[:httponly] ? 'HttpOnly' : nil,
77
- options[:samesite] && "SameSite=#{options[:samesite]}"
78
- ].compact
79
- end
80
-
81
- def normalize_headers(value)
82
- case value
83
- when nil
84
- {}
85
- when Hash
86
- value.each_with_object({}) do |(key, val), acc|
87
- acc[key.to_s.downcase] = val.to_s
88
- end
89
- else
90
- raise ArgumentError, 'headers must be a Hash'
91
- end
92
- end
93
- end
94
-
95
- class StreamingResponse # :nodoc: Streaming response wrapper backed by the native Rust builder.
96
- attr_reader :stream, :status_code, :headers, :native_response
97
-
98
- def initialize(stream, status_code: 200, headers: nil)
99
- unless stream.respond_to?(:next) || stream.respond_to?(:each)
100
- raise ArgumentError, 'StreamingResponse requires an object responding to #next or #each'
101
- end
102
-
103
- @stream = stream.respond_to?(:to_enum) ? stream.to_enum : stream
104
- @status_code = Integer(status_code || 200)
105
- header_hash = headers || {}
106
- @headers = header_hash.each_with_object({}) do |(key, value), memo|
107
- memo[String(key)] = String(value)
108
- end
109
-
110
- rebuild_native!
111
- end
112
-
113
- def to_native_response
114
- @native_response
115
- end
116
-
117
- private
118
-
119
- def rebuild_native!
120
- ensure_native!
121
- @native_response = Spikard::Native.build_streaming_response(@stream, @status_code, @headers)
122
- return unless @native_response
123
-
124
- @status_code = @native_response.status_code
125
- @headers = @native_response.headers
126
- end
127
-
128
- def ensure_native!
129
- return if defined?(Spikard::Native) && Spikard::Native.respond_to?(:build_streaming_response)
130
-
131
- raise 'Spikard native extension is not loaded'
132
- end
133
- end
134
-
135
- module Testing
136
- class Response # :nodoc: Lightweight response wrapper used by the test client.
137
- attr_reader :status_code, :headers, :body
138
-
139
- def initialize(payload)
140
- @status_code = payload[:status_code]
141
- @headers = payload[:headers] || {}
142
- @body = payload[:body]
143
- @body_text = payload[:body_text]
144
- end
145
-
146
- def status
147
- @status_code
148
- end
149
-
150
- def body_bytes
151
- @body || ''.b
152
- end
153
-
154
- def body_text
155
- @body_text || @body&.dup&.force_encoding(Encoding::UTF_8)
156
- end
157
-
158
- def text
159
- body_text
160
- end
161
-
162
- def json
163
- return nil if @body.nil? || @body.empty?
164
-
165
- JSON.parse(@body)
166
- end
167
-
168
- def bytes
169
- body_bytes.bytes
170
- end
171
- end
172
- end
173
- end