spikard 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +626 -553
- data/ext/spikard_rb/Cargo.toml +17 -16
- data/ext/spikard_rb/extconf.rb +10 -10
- data/ext/spikard_rb/src/lib.rs +6 -6
- data/lib/spikard/app.rb +374 -328
- data/lib/spikard/background.rb +27 -27
- data/lib/spikard/config.rb +396 -396
- data/lib/spikard/converters.rb +85 -85
- data/lib/spikard/handler_wrapper.rb +116 -116
- data/lib/spikard/provide.rb +228 -0
- data/lib/spikard/response.rb +109 -109
- data/lib/spikard/schema.rb +243 -243
- data/lib/spikard/sse.rb +111 -111
- data/lib/spikard/streaming_response.rb +21 -21
- data/lib/spikard/testing.rb +221 -220
- data/lib/spikard/upload_file.rb +131 -131
- data/lib/spikard/version.rb +5 -5
- data/lib/spikard/websocket.rb +59 -59
- data/lib/spikard.rb +43 -42
- data/sig/spikard.rbs +349 -336
- data/vendor/bundle/ruby/3.3.0/gems/diff-lcs-1.6.2/mise.toml +5 -0
- metadata +23 -5
data/lib/spikard/response.rb
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Spikard
|
|
4
|
-
# Response object returned from route handlers.
|
|
5
|
-
# Mirrors the Python/Node response helpers so the native layer
|
|
6
|
-
# can extract status, headers, and JSON-serialisable content.
|
|
7
|
-
class Response
|
|
8
|
-
attr_accessor :content
|
|
9
|
-
attr_reader :status_code, :headers
|
|
10
|
-
|
|
11
|
-
def initialize(content: nil, body: nil, status_code: 200, headers: nil, content_type: nil)
|
|
12
|
-
@content = content.nil? ? body : content
|
|
13
|
-
self.status_code = status_code
|
|
14
|
-
self.headers = headers
|
|
15
|
-
set_header('content-type', content_type) if content_type
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def status
|
|
19
|
-
@status_code
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def status_code=(value)
|
|
23
|
-
@status_code = Integer(value)
|
|
24
|
-
rescue ArgumentError, TypeError
|
|
25
|
-
raise ArgumentError, 'status_code must be an integer'
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def headers=(value)
|
|
29
|
-
@headers = normalize_headers(value)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def set_header(name, value)
|
|
33
|
-
@headers[name.to_s] = value.to_s
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def set_cookie(name, value, **options)
|
|
37
|
-
raise ArgumentError, 'cookie name required' if name.nil? || name.empty?
|
|
38
|
-
|
|
39
|
-
header_value = ["#{name}=#{value}", *cookie_parts(options)].join('; ')
|
|
40
|
-
set_header('set-cookie', header_value)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def cookie_parts(options)
|
|
46
|
-
[
|
|
47
|
-
options[:max_age] && "Max-Age=#{Integer(options[:max_age])}",
|
|
48
|
-
options[:domain] && "Domain=#{options[:domain]}",
|
|
49
|
-
"Path=#{options.fetch(:path, '/') || '/'}",
|
|
50
|
-
options[:secure] ? 'Secure' : nil,
|
|
51
|
-
options[:httponly] ? 'HttpOnly' : nil,
|
|
52
|
-
options[:samesite] && "SameSite=#{options[:samesite]}"
|
|
53
|
-
].compact
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def normalize_headers(value)
|
|
57
|
-
case value
|
|
58
|
-
when nil
|
|
59
|
-
{}
|
|
60
|
-
when Hash
|
|
61
|
-
value.each_with_object({}) do |(key, val), acc|
|
|
62
|
-
acc[key.to_s] = val.to_s
|
|
63
|
-
end
|
|
64
|
-
else
|
|
65
|
-
raise ArgumentError, 'headers must be a Hash'
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
module Testing
|
|
71
|
-
# Lightweight wrapper around native response hashes.
|
|
72
|
-
class Response
|
|
73
|
-
attr_reader :status_code, :headers, :body
|
|
74
|
-
|
|
75
|
-
def initialize(payload)
|
|
76
|
-
@status_code = payload[:status_code]
|
|
77
|
-
@headers = payload[:headers] || {}
|
|
78
|
-
@body = payload[:body]
|
|
79
|
-
@body_text = payload[:body_text]
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def status
|
|
83
|
-
@status_code
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def body_bytes
|
|
87
|
-
@body || ''.b
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def body_text
|
|
91
|
-
@body_text || @body&.dup&.force_encoding(Encoding::UTF_8)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def text
|
|
95
|
-
body_text
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def json
|
|
99
|
-
return nil if @body.nil? || @body.empty?
|
|
100
|
-
|
|
101
|
-
JSON.parse(@body)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def bytes
|
|
105
|
-
body_bytes.bytes
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spikard
|
|
4
|
+
# Response object returned from route handlers.
|
|
5
|
+
# Mirrors the Python/Node response helpers so the native layer
|
|
6
|
+
# can extract status, headers, and JSON-serialisable content.
|
|
7
|
+
class Response
|
|
8
|
+
attr_accessor :content
|
|
9
|
+
attr_reader :status_code, :headers
|
|
10
|
+
|
|
11
|
+
def initialize(content: nil, body: nil, status_code: 200, headers: nil, content_type: nil)
|
|
12
|
+
@content = content.nil? ? body : content
|
|
13
|
+
self.status_code = status_code
|
|
14
|
+
self.headers = headers
|
|
15
|
+
set_header('content-type', content_type) if content_type
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def status
|
|
19
|
+
@status_code
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def status_code=(value)
|
|
23
|
+
@status_code = Integer(value)
|
|
24
|
+
rescue ArgumentError, TypeError
|
|
25
|
+
raise ArgumentError, 'status_code must be an integer'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def headers=(value)
|
|
29
|
+
@headers = normalize_headers(value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def set_header(name, value)
|
|
33
|
+
@headers[name.to_s] = value.to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def set_cookie(name, value, **options)
|
|
37
|
+
raise ArgumentError, 'cookie name required' if name.nil? || name.empty?
|
|
38
|
+
|
|
39
|
+
header_value = ["#{name}=#{value}", *cookie_parts(options)].join('; ')
|
|
40
|
+
set_header('set-cookie', header_value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def cookie_parts(options)
|
|
46
|
+
[
|
|
47
|
+
options[:max_age] && "Max-Age=#{Integer(options[:max_age])}",
|
|
48
|
+
options[:domain] && "Domain=#{options[:domain]}",
|
|
49
|
+
"Path=#{options.fetch(:path, '/') || '/'}",
|
|
50
|
+
options[:secure] ? 'Secure' : nil,
|
|
51
|
+
options[:httponly] ? 'HttpOnly' : nil,
|
|
52
|
+
options[:samesite] && "SameSite=#{options[:samesite]}"
|
|
53
|
+
].compact
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def normalize_headers(value)
|
|
57
|
+
case value
|
|
58
|
+
when nil
|
|
59
|
+
{}
|
|
60
|
+
when Hash
|
|
61
|
+
value.each_with_object({}) do |(key, val), acc|
|
|
62
|
+
acc[key.to_s] = val.to_s
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
raise ArgumentError, 'headers must be a Hash'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module Testing
|
|
71
|
+
# Lightweight wrapper around native response hashes.
|
|
72
|
+
class Response
|
|
73
|
+
attr_reader :status_code, :headers, :body
|
|
74
|
+
|
|
75
|
+
def initialize(payload)
|
|
76
|
+
@status_code = payload[:status_code]
|
|
77
|
+
@headers = payload[:headers] || {}
|
|
78
|
+
@body = payload[:body]
|
|
79
|
+
@body_text = payload[:body_text]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def status
|
|
83
|
+
@status_code
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def body_bytes
|
|
87
|
+
@body || ''.b
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def body_text
|
|
91
|
+
@body_text || @body&.dup&.force_encoding(Encoding::UTF_8)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def text
|
|
95
|
+
body_text
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def json
|
|
99
|
+
return nil if @body.nil? || @body.empty?
|
|
100
|
+
|
|
101
|
+
JSON.parse(@body)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def bytes
|
|
105
|
+
body_bytes.bytes
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|