spikard 0.3.6 → 0.6.1

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +674 -659
  4. data/ext/spikard_rb/Cargo.toml +17 -17
  5. data/ext/spikard_rb/extconf.rb +13 -10
  6. data/ext/spikard_rb/src/lib.rs +6 -6
  7. data/lib/spikard/app.rb +405 -386
  8. data/lib/spikard/background.rb +27 -27
  9. data/lib/spikard/config.rb +396 -396
  10. data/lib/spikard/converters.rb +13 -13
  11. data/lib/spikard/handler_wrapper.rb +113 -113
  12. data/lib/spikard/provide.rb +214 -214
  13. data/lib/spikard/response.rb +173 -173
  14. data/lib/spikard/schema.rb +243 -243
  15. data/lib/spikard/sse.rb +111 -111
  16. data/lib/spikard/streaming_response.rb +44 -44
  17. data/lib/spikard/testing.rb +256 -221
  18. data/lib/spikard/upload_file.rb +131 -131
  19. data/lib/spikard/version.rb +5 -5
  20. data/lib/spikard/websocket.rb +59 -59
  21. data/lib/spikard.rb +43 -43
  22. data/sig/spikard.rbs +366 -366
  23. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
  24. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -0
  25. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -0
  26. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
  27. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
  28. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -0
  29. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -0
  30. data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -0
  31. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -0
  32. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -0
  33. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -0
  34. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
  35. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -0
  36. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -0
  37. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -0
  38. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -0
  39. data/vendor/crates/spikard-core/Cargo.toml +40 -40
  40. data/vendor/crates/spikard-core/src/bindings/mod.rs +3 -3
  41. data/vendor/crates/spikard-core/src/bindings/response.rs +133 -133
  42. data/vendor/crates/spikard-core/src/debug.rs +127 -63
  43. data/vendor/crates/spikard-core/src/di/container.rs +702 -726
  44. data/vendor/crates/spikard-core/src/di/dependency.rs +273 -273
  45. data/vendor/crates/spikard-core/src/di/error.rs +118 -118
  46. data/vendor/crates/spikard-core/src/di/factory.rs +534 -538
  47. data/vendor/crates/spikard-core/src/di/graph.rs +506 -545
  48. data/vendor/crates/spikard-core/src/di/mod.rs +192 -192
  49. data/vendor/crates/spikard-core/src/di/resolved.rs +405 -411
  50. data/vendor/crates/spikard-core/src/di/value.rs +281 -283
  51. data/vendor/crates/spikard-core/src/errors.rs +69 -39
  52. data/vendor/crates/spikard-core/src/http.rs +415 -153
  53. data/vendor/crates/spikard-core/src/lib.rs +29 -29
  54. data/vendor/crates/spikard-core/src/lifecycle.rs +1186 -422
  55. data/vendor/crates/spikard-core/src/metadata.rs +389 -0
  56. data/vendor/crates/spikard-core/src/parameters.rs +2525 -722
  57. data/vendor/crates/spikard-core/src/problem.rs +344 -310
  58. data/vendor/crates/spikard-core/src/request_data.rs +1154 -189
  59. data/vendor/crates/spikard-core/src/router.rs +510 -249
  60. data/vendor/crates/spikard-core/src/schema_registry.rs +183 -183
  61. data/vendor/crates/spikard-core/src/type_hints.rs +304 -304
  62. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +696 -0
  63. data/vendor/crates/spikard-core/src/{validation.rs → validation/mod.rs} +457 -699
  64. data/vendor/crates/spikard-http/Cargo.toml +62 -68
  65. data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -0
  66. data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -0
  67. data/vendor/crates/spikard-http/src/auth.rs +296 -247
  68. data/vendor/crates/spikard-http/src/background.rs +1860 -249
  69. data/vendor/crates/spikard-http/src/bindings/mod.rs +3 -3
  70. data/vendor/crates/spikard-http/src/bindings/response.rs +1 -1
  71. data/vendor/crates/spikard-http/src/body_metadata.rs +8 -8
  72. data/vendor/crates/spikard-http/src/cors.rs +1005 -490
  73. data/vendor/crates/spikard-http/src/debug.rs +128 -63
  74. data/vendor/crates/spikard-http/src/di_handler.rs +1668 -423
  75. data/vendor/crates/spikard-http/src/handler_response.rs +901 -190
  76. data/vendor/crates/spikard-http/src/handler_trait.rs +838 -228
  77. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +290 -284
  78. data/vendor/crates/spikard-http/src/lib.rs +534 -529
  79. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +230 -149
  80. data/vendor/crates/spikard-http/src/lifecycle.rs +1193 -428
  81. data/vendor/crates/spikard-http/src/middleware/mod.rs +560 -285
  82. data/vendor/crates/spikard-http/src/middleware/multipart.rs +912 -86
  83. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +513 -147
  84. data/vendor/crates/spikard-http/src/middleware/validation.rs +768 -287
  85. data/vendor/crates/spikard-http/src/openapi/mod.rs +309 -309
  86. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +535 -190
  87. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1363 -308
  88. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +665 -195
  89. data/vendor/crates/spikard-http/src/query_parser.rs +793 -369
  90. data/vendor/crates/spikard-http/src/response.rs +720 -399
  91. data/vendor/crates/spikard-http/src/server/handler.rs +1650 -87
  92. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +234 -98
  93. data/vendor/crates/spikard-http/src/server/mod.rs +1593 -805
  94. data/vendor/crates/spikard-http/src/server/request_extraction.rs +789 -119
  95. data/vendor/crates/spikard-http/src/server/routing_factory.rs +629 -0
  96. data/vendor/crates/spikard-http/src/sse.rs +1409 -447
  97. data/vendor/crates/spikard-http/src/testing/form.rs +52 -14
  98. data/vendor/crates/spikard-http/src/testing/multipart.rs +64 -60
  99. data/vendor/crates/spikard-http/src/testing/test_client.rs +311 -285
  100. data/vendor/crates/spikard-http/src/testing.rs +406 -377
  101. data/vendor/crates/spikard-http/src/websocket.rs +1404 -324
  102. data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -0
  103. data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -0
  104. data/vendor/crates/spikard-http/tests/common/mod.rs +26 -0
  105. data/vendor/crates/spikard-http/tests/di_integration.rs +192 -0
  106. data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -0
  107. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -0
  108. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -0
  109. data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -0
  110. data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -0
  111. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -0
  112. data/vendor/crates/spikard-rb/Cargo.toml +48 -42
  113. data/vendor/crates/spikard-rb/build.rs +199 -8
  114. data/vendor/crates/spikard-rb/src/background.rs +63 -63
  115. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
  116. data/vendor/crates/spikard-rb/src/{config.rs → config/server_config.rs} +285 -294
  117. data/vendor/crates/spikard-rb/src/conversion.rs +554 -453
  118. data/vendor/crates/spikard-rb/src/di/builder.rs +100 -0
  119. data/vendor/crates/spikard-rb/src/{di.rs → di/mod.rs} +375 -409
  120. data/vendor/crates/spikard-rb/src/handler.rs +618 -625
  121. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
  122. data/vendor/crates/spikard-rb/src/lib.rs +1806 -2771
  123. data/vendor/crates/spikard-rb/src/lifecycle.rs +275 -274
  124. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
  125. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +442 -0
  126. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
  127. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -0
  128. data/vendor/crates/spikard-rb/src/server.rs +305 -283
  129. data/vendor/crates/spikard-rb/src/sse.rs +231 -231
  130. data/vendor/crates/spikard-rb/src/{test_client.rs → testing/client.rs} +538 -404
  131. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
  132. data/vendor/crates/spikard-rb/src/{test_sse.rs → testing/sse.rs} +143 -143
  133. data/vendor/crates/spikard-rb/src/testing/websocket.rs +608 -0
  134. data/vendor/crates/spikard-rb/src/websocket.rs +377 -233
  135. metadata +60 -13
  136. data/vendor/crates/spikard-http/src/parameters.rs +0 -1
  137. data/vendor/crates/spikard-http/src/problem.rs +0 -1
  138. data/vendor/crates/spikard-http/src/router.rs +0 -1
  139. data/vendor/crates/spikard-http/src/schema_registry.rs +0 -1
  140. data/vendor/crates/spikard-http/src/type_hints.rs +0 -1
  141. data/vendor/crates/spikard-http/src/validation.rs +0 -1
  142. data/vendor/crates/spikard-rb/src/test_websocket.rs +0 -221
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spikard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Na'aman Hirschfeld
@@ -9,6 +9,20 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rb_sys
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.9'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.9'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: websocket-client-simple
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -69,6 +83,22 @@ files:
69
83
  - sig/spikard.rbs
70
84
  - vendor/bundle/ruby/3.4.0/gems/diff-lcs-1.6.2/mise.toml
71
85
  - vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.10.0/build/buildkitd.toml
86
+ - vendor/crates/spikard-bindings-shared/Cargo.toml
87
+ - vendor/crates/spikard-bindings-shared/examples/config_extraction.rs
88
+ - vendor/crates/spikard-bindings-shared/src/config_extractor.rs
89
+ - vendor/crates/spikard-bindings-shared/src/conversion_traits.rs
90
+ - vendor/crates/spikard-bindings-shared/src/di_traits.rs
91
+ - vendor/crates/spikard-bindings-shared/src/error_response.rs
92
+ - vendor/crates/spikard-bindings-shared/src/handler_base.rs
93
+ - vendor/crates/spikard-bindings-shared/src/lib.rs
94
+ - vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs
95
+ - vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs
96
+ - vendor/crates/spikard-bindings-shared/src/response_builder.rs
97
+ - vendor/crates/spikard-bindings-shared/src/test_client_base.rs
98
+ - vendor/crates/spikard-bindings-shared/src/validation_helpers.rs
99
+ - vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs
100
+ - vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs
101
+ - vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs
72
102
  - vendor/crates/spikard-core/Cargo.toml
73
103
  - vendor/crates/spikard-core/src/bindings/mod.rs
74
104
  - vendor/crates/spikard-core/src/bindings/response.rs
@@ -85,14 +115,18 @@ files:
85
115
  - vendor/crates/spikard-core/src/http.rs
86
116
  - vendor/crates/spikard-core/src/lib.rs
87
117
  - vendor/crates/spikard-core/src/lifecycle.rs
118
+ - vendor/crates/spikard-core/src/metadata.rs
88
119
  - vendor/crates/spikard-core/src/parameters.rs
89
120
  - vendor/crates/spikard-core/src/problem.rs
90
121
  - vendor/crates/spikard-core/src/request_data.rs
91
122
  - vendor/crates/spikard-core/src/router.rs
92
123
  - vendor/crates/spikard-core/src/schema_registry.rs
93
124
  - vendor/crates/spikard-core/src/type_hints.rs
94
- - vendor/crates/spikard-core/src/validation.rs
125
+ - vendor/crates/spikard-core/src/validation/error_mapper.rs
126
+ - vendor/crates/spikard-core/src/validation/mod.rs
95
127
  - vendor/crates/spikard-http/Cargo.toml
128
+ - vendor/crates/spikard-http/examples/sse-notifications.rs
129
+ - vendor/crates/spikard-http/examples/websocket-chat.rs
96
130
  - vendor/crates/spikard-http/src/auth.rs
97
131
  - vendor/crates/spikard-http/src/background.rs
98
132
  - vendor/crates/spikard-http/src/bindings/mod.rs
@@ -115,38 +149,51 @@ files:
115
149
  - vendor/crates/spikard-http/src/openapi/parameter_extraction.rs
116
150
  - vendor/crates/spikard-http/src/openapi/schema_conversion.rs
117
151
  - vendor/crates/spikard-http/src/openapi/spec_generation.rs
118
- - vendor/crates/spikard-http/src/parameters.rs
119
- - vendor/crates/spikard-http/src/problem.rs
120
152
  - vendor/crates/spikard-http/src/query_parser.rs
121
153
  - vendor/crates/spikard-http/src/response.rs
122
- - vendor/crates/spikard-http/src/router.rs
123
- - vendor/crates/spikard-http/src/schema_registry.rs
124
154
  - vendor/crates/spikard-http/src/server/handler.rs
125
155
  - vendor/crates/spikard-http/src/server/lifecycle_execution.rs
126
156
  - vendor/crates/spikard-http/src/server/mod.rs
127
157
  - vendor/crates/spikard-http/src/server/request_extraction.rs
158
+ - vendor/crates/spikard-http/src/server/routing_factory.rs
128
159
  - vendor/crates/spikard-http/src/sse.rs
129
160
  - vendor/crates/spikard-http/src/testing.rs
130
161
  - vendor/crates/spikard-http/src/testing/form.rs
131
162
  - vendor/crates/spikard-http/src/testing/multipart.rs
132
163
  - vendor/crates/spikard-http/src/testing/test_client.rs
133
- - vendor/crates/spikard-http/src/type_hints.rs
134
- - vendor/crates/spikard-http/src/validation.rs
135
164
  - vendor/crates/spikard-http/src/websocket.rs
165
+ - vendor/crates/spikard-http/tests/background_behavior.rs
166
+ - vendor/crates/spikard-http/tests/common/handlers.rs
167
+ - vendor/crates/spikard-http/tests/common/mod.rs
168
+ - vendor/crates/spikard-http/tests/di_integration.rs
169
+ - vendor/crates/spikard-http/tests/doc_snippets.rs
170
+ - vendor/crates/spikard-http/tests/lifecycle_execution.rs
171
+ - vendor/crates/spikard-http/tests/multipart_behavior.rs
172
+ - vendor/crates/spikard-http/tests/server_config_builder.rs
173
+ - vendor/crates/spikard-http/tests/sse_behavior.rs
174
+ - vendor/crates/spikard-http/tests/websocket_behavior.rs
136
175
  - vendor/crates/spikard-rb/Cargo.toml
137
176
  - vendor/crates/spikard-rb/build.rs
138
177
  - vendor/crates/spikard-rb/src/background.rs
139
- - vendor/crates/spikard-rb/src/config.rs
178
+ - vendor/crates/spikard-rb/src/config/mod.rs
179
+ - vendor/crates/spikard-rb/src/config/server_config.rs
140
180
  - vendor/crates/spikard-rb/src/conversion.rs
141
- - vendor/crates/spikard-rb/src/di.rs
181
+ - vendor/crates/spikard-rb/src/di/builder.rs
182
+ - vendor/crates/spikard-rb/src/di/mod.rs
142
183
  - vendor/crates/spikard-rb/src/handler.rs
184
+ - vendor/crates/spikard-rb/src/integration/mod.rs
143
185
  - vendor/crates/spikard-rb/src/lib.rs
144
186
  - vendor/crates/spikard-rb/src/lifecycle.rs
187
+ - vendor/crates/spikard-rb/src/metadata/mod.rs
188
+ - vendor/crates/spikard-rb/src/metadata/route_extraction.rs
189
+ - vendor/crates/spikard-rb/src/runtime/mod.rs
190
+ - vendor/crates/spikard-rb/src/runtime/server_runner.rs
145
191
  - vendor/crates/spikard-rb/src/server.rs
146
192
  - vendor/crates/spikard-rb/src/sse.rs
147
- - vendor/crates/spikard-rb/src/test_client.rs
148
- - vendor/crates/spikard-rb/src/test_sse.rs
149
- - vendor/crates/spikard-rb/src/test_websocket.rs
193
+ - vendor/crates/spikard-rb/src/testing/client.rs
194
+ - vendor/crates/spikard-rb/src/testing/mod.rs
195
+ - vendor/crates/spikard-rb/src/testing/sse.rs
196
+ - vendor/crates/spikard-rb/src/testing/websocket.rs
150
197
  - vendor/crates/spikard-rb/src/websocket.rs
151
198
  homepage: https://github.com/Goldziher/spikard
152
199
  licenses:
@@ -1 +0,0 @@
1
- pub use spikard_core::parameters::*;
@@ -1 +0,0 @@
1
- pub use spikard_core::problem::*;
@@ -1 +0,0 @@
1
- pub use spikard_core::router::*;
@@ -1 +0,0 @@
1
- pub use spikard_core::schema_registry::*;
@@ -1 +0,0 @@
1
- pub use spikard_core::type_hints::*;
@@ -1 +0,0 @@
1
- pub use spikard_core::validation::*;
@@ -1,221 +0,0 @@
1
- //! WebSocket test client bindings for Ruby
2
-
3
- use magnus::prelude::*;
4
- use magnus::{Error, Ruby, Value, method};
5
- use serde_json::Value as JsonValue;
6
- use spikard_http::testing::{WebSocketConnection as RustWebSocketConnection, WebSocketMessage as RustWebSocketMessage};
7
- use std::cell::RefCell;
8
-
9
- use crate::server::GLOBAL_RUNTIME;
10
-
11
- /// Ruby wrapper for WebSocket test client
12
- #[derive(Default)]
13
- #[magnus::wrap(class = "Spikard::Native::WebSocketTestConnection", free_immediately)]
14
- pub struct WebSocketTestConnection {
15
- inner: RefCell<Option<RustWebSocketConnection>>,
16
- }
17
-
18
- impl WebSocketTestConnection {
19
- /// Create a new WebSocket test connection (public for lib.rs)
20
- pub(crate) fn new(inner: RustWebSocketConnection) -> Self {
21
- Self {
22
- inner: RefCell::new(Some(inner)),
23
- }
24
- }
25
-
26
- /// Send a text message
27
- fn send_text(&self, text: String) -> Result<(), Error> {
28
- let mut inner = self.inner.borrow_mut();
29
- let ws = inner
30
- .as_mut()
31
- .ok_or_else(|| Error::new(magnus::exception::runtime_error(), "WebSocket closed"))?;
32
-
33
- GLOBAL_RUNTIME.block_on(async {
34
- ws.send_text(text).await;
35
- });
36
-
37
- Ok(())
38
- }
39
-
40
- /// Send a JSON message
41
- fn send_json(ruby: &Ruby, this: &Self, obj: Value) -> Result<(), Error> {
42
- let json_value = ruby_to_json(ruby, obj)?;
43
- let mut inner = this.inner.borrow_mut();
44
- let ws = inner
45
- .as_mut()
46
- .ok_or_else(|| Error::new(ruby.exception_runtime_error(), "WebSocket closed"))?;
47
-
48
- GLOBAL_RUNTIME.block_on(async {
49
- ws.send_json(&json_value).await;
50
- });
51
-
52
- Ok(())
53
- }
54
-
55
- /// Receive a text message
56
- fn receive_text(&self) -> Result<String, Error> {
57
- let mut inner = self.inner.borrow_mut();
58
- let ws = inner
59
- .as_mut()
60
- .ok_or_else(|| Error::new(magnus::exception::runtime_error(), "WebSocket closed"))?;
61
-
62
- let text = GLOBAL_RUNTIME.block_on(async { ws.receive_text().await });
63
-
64
- Ok(text)
65
- }
66
-
67
- /// Receive and parse a JSON message
68
- fn receive_json(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
69
- let mut inner = this.inner.borrow_mut();
70
- let ws = inner
71
- .as_mut()
72
- .ok_or_else(|| Error::new(ruby.exception_runtime_error(), "WebSocket closed"))?;
73
-
74
- let json_value: JsonValue = GLOBAL_RUNTIME.block_on(async { ws.receive_json().await });
75
-
76
- json_to_ruby(ruby, &json_value)
77
- }
78
-
79
- /// Receive raw bytes
80
- fn receive_bytes(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
81
- let mut inner = this.inner.borrow_mut();
82
- let ws = inner
83
- .as_mut()
84
- .ok_or_else(|| Error::new(ruby.exception_runtime_error(), "WebSocket closed"))?;
85
-
86
- let bytes = GLOBAL_RUNTIME.block_on(async { ws.receive_bytes().await });
87
-
88
- Ok(ruby.str_from_slice(&bytes).as_value())
89
- }
90
-
91
- /// Receive a message and return WebSocketMessage
92
- fn receive_message(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
93
- let mut inner = this.inner.borrow_mut();
94
- let ws = inner
95
- .as_mut()
96
- .ok_or_else(|| Error::new(ruby.exception_runtime_error(), "WebSocket closed"))?;
97
-
98
- let msg = GLOBAL_RUNTIME.block_on(async { ws.receive_message().await });
99
-
100
- let ws_msg = WebSocketMessage::new(msg);
101
- Ok(ruby.obj_wrap(ws_msg).as_value())
102
- }
103
-
104
- /// Close the WebSocket connection
105
- fn close(&self) -> Result<(), Error> {
106
- self.inner.borrow_mut().take();
107
- Ok(())
108
- }
109
- }
110
-
111
- /// Ruby wrapper for WebSocket messages
112
- #[magnus::wrap(class = "Spikard::Native::WebSocketMessage", free_immediately)]
113
- pub struct WebSocketMessage {
114
- inner: RustWebSocketMessage,
115
- }
116
-
117
- impl WebSocketMessage {
118
- pub fn new(inner: RustWebSocketMessage) -> Self {
119
- Self { inner }
120
- }
121
-
122
- /// Get message as text if it's a text message
123
- fn as_text(&self) -> Result<Option<String>, Error> {
124
- Ok(self.inner.as_text().map(|s| s.to_string()))
125
- }
126
-
127
- /// Get message as JSON if it's a text message containing JSON
128
- fn as_json(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
129
- match this.inner.as_json() {
130
- Ok(value) => json_to_ruby(ruby, &value),
131
- Err(_) => Ok(ruby.qnil().as_value()),
132
- }
133
- }
134
-
135
- /// Get message as binary if it's a binary message
136
- fn as_binary(ruby: &Ruby, this: &Self) -> Result<Value, Error> {
137
- match this.inner.as_binary() {
138
- Some(bytes) => Ok(ruby.str_from_slice(bytes).as_value()),
139
- None => Ok(ruby.qnil().as_value()),
140
- }
141
- }
142
-
143
- /// Check if this is a close message
144
- fn is_close(&self) -> bool {
145
- self.inner.is_close()
146
- }
147
- }
148
-
149
- /// Helper to convert Ruby object to JSON
150
- fn ruby_to_json(ruby: &Ruby, value: Value) -> Result<JsonValue, Error> {
151
- let json_module = ruby.class_object().const_get::<_, magnus::RModule>("JSON")?;
152
- let json_str: String = json_module.funcall("generate", (value,))?;
153
- serde_json::from_str(&json_str).map_err(|e| {
154
- Error::new(
155
- magnus::exception::runtime_error(),
156
- format!("Failed to parse JSON: {}", e),
157
- )
158
- })
159
- }
160
-
161
- /// Helper to convert JSON to Ruby object
162
- fn json_to_ruby(ruby: &Ruby, value: &JsonValue) -> Result<Value, Error> {
163
- match value {
164
- JsonValue::Null => Ok(ruby.qnil().as_value()),
165
- JsonValue::Bool(b) => Ok(if *b {
166
- ruby.qtrue().as_value()
167
- } else {
168
- ruby.qfalse().as_value()
169
- }),
170
- JsonValue::Number(n) => {
171
- if let Some(i) = n.as_i64() {
172
- Ok(ruby.integer_from_i64(i).as_value())
173
- } else if let Some(u) = n.as_u64() {
174
- Ok(ruby.integer_from_i64(u as i64).as_value())
175
- } else if let Some(f) = n.as_f64() {
176
- Ok(ruby.float_from_f64(f).as_value())
177
- } else {
178
- Ok(ruby.qnil().as_value())
179
- }
180
- }
181
- JsonValue::String(s) => Ok(ruby.str_new(s).as_value()),
182
- JsonValue::Array(arr) => {
183
- let ruby_arr = ruby.ary_new();
184
- for item in arr {
185
- let ruby_val = json_to_ruby(ruby, item)?;
186
- ruby_arr.push(ruby_val)?;
187
- }
188
- Ok(ruby_arr.as_value())
189
- }
190
- JsonValue::Object(obj) => {
191
- let ruby_hash = ruby.hash_new();
192
- for (key, val) in obj {
193
- let ruby_val = json_to_ruby(ruby, val)?;
194
- ruby_hash.aset(ruby.str_new(key), ruby_val)?;
195
- }
196
- Ok(ruby_hash.as_value())
197
- }
198
- }
199
- }
200
-
201
- /// Initialize WebSocket test client bindings
202
- pub fn init(ruby: &Ruby, module: &magnus::RModule) -> Result<(), Error> {
203
- let native_module = module.define_module("Native")?;
204
-
205
- let ws_conn_class = native_module.define_class("WebSocketTestConnection", ruby.class_object())?;
206
- ws_conn_class.define_method("send_text", method!(WebSocketTestConnection::send_text, 1))?;
207
- ws_conn_class.define_method("send_json", method!(WebSocketTestConnection::send_json, 1))?;
208
- ws_conn_class.define_method("receive_text", method!(WebSocketTestConnection::receive_text, 0))?;
209
- ws_conn_class.define_method("receive_json", method!(WebSocketTestConnection::receive_json, 0))?;
210
- ws_conn_class.define_method("receive_bytes", method!(WebSocketTestConnection::receive_bytes, 0))?;
211
- ws_conn_class.define_method("receive_message", method!(WebSocketTestConnection::receive_message, 0))?;
212
- ws_conn_class.define_method("close", method!(WebSocketTestConnection::close, 0))?;
213
-
214
- let ws_msg_class = native_module.define_class("WebSocketMessage", ruby.class_object())?;
215
- ws_msg_class.define_method("as_text", method!(WebSocketMessage::as_text, 0))?;
216
- ws_msg_class.define_method("as_json", method!(WebSocketMessage::as_json, 0))?;
217
- ws_msg_class.define_method("as_binary", method!(WebSocketMessage::as_binary, 0))?;
218
- ws_msg_class.define_method("is_close", method!(WebSocketMessage::is_close, 0))?;
219
-
220
- Ok(())
221
- }