kino 0.1.0-aarch64-linux

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.
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Kino configuration.
4
+ # Generated by `kino --init`.
5
+ #
6
+ # Every setting below is shown with its default value, commented out:
7
+ # the file is a valid no-op until you uncomment something. Precedence:
8
+ # explicit Server.new kwargs / CLI flags > this file > built-in defaults.
9
+
10
+ ## Network
11
+
12
+ # Address to listen on. Use "0.0.0.0" to accept non-local connections.
13
+ # bind "127.0.0.1"
14
+
15
+ # Port to listen on. 0 picks an ephemeral port (readable via server.port).
16
+ # The `kino` CLI defaults this to 9292 when nothing else sets it.
17
+ # port 9292
18
+
19
+ # TLS termination (rustls, in Rust; never blocks a Ruby thread).
20
+ # Values are file paths or inline PEM strings. ALPN is http/1.1.
21
+ # tls cert: "config/certs/server.pem", key: "config/certs/server.key"
22
+
23
+ ## Topology
24
+
25
+ # Puma-style two-level topology: `workers` × `threads`.
26
+ #
27
+ # In :ractor mode, `workers` is the number of worker Ractors: true
28
+ # multi-core parallelism for Ruby CPU work, one per core is a good start.
29
+ # In :threaded mode the same total (workers × threads) runs as plain
30
+ # Threads on the main ractor.
31
+
32
+ # Defaults to the number of CPU cores (Etc.nprocessors).
33
+ # workers 8
34
+
35
+ # Threads per worker. Threads inside one ractor share its lock, so they
36
+ # only add concurrency where handlers block on I/O (database calls, HTTP).
37
+ # CPU-bound apps gain nothing past 1 (and pay a lock-handoff tax: threads 1
38
+ # measured +17% on fast handlers). I/O-heavy apps want more SLOTS overall -
39
+ # in :ractor mode prefer raising `workers` over `threads` (slots are cheap,
40
+ # no fork memory): 32 workers x 1 thread beat 8x3 by +35% on waits.
41
+ # threads 3
42
+
43
+ ## Dispatch mode
44
+ #
45
+ # :auto: :ractor when the app is Ractor-shareable, else :threaded
46
+ # (with a warning). Note: a Class used as a Rack app always
47
+ # counts as "shareable" even if calling it touches unshareable
48
+ # state; force :threaded for those.
49
+ # :ractor: require a Ractor-shareable app; raises
50
+ # Kino::UnshareableAppError otherwise. The app must capture
51
+ # nothing mutable: frozen middleware, Ractor.shareable_proc
52
+ # endpoints.
53
+ # :threaded: run ANY Rack app (Rails included) on a classic thread pool.
54
+ # mode :auto
55
+
56
+ ## Backpressure
57
+
58
+ # Bounded request queue between the Rust front-end and Ruby workers.
59
+ # When it stays full past queue_timeout, clients get an immediate 503
60
+ # instead of waiting forever.
61
+ # queue_depth 1024
62
+
63
+ # Seconds a request may wait for queue space before the 503.
64
+ # queue_timeout 1.0
65
+
66
+ # Seconds the app gets to produce a response before the client receives a
67
+ # 504 instead. Off by default (nil = wait forever). The handler is NOT
68
+ # killed - its late response is dropped and its slot stays busy until it
69
+ # returns, so size this above your slowest legitimate endpoint.
70
+ # request_timeout 30
71
+
72
+ # Requests a worker may grab per queue visit. Values above 1 squeeze more
73
+ # throughput out of uniformly fast handlers, but add head-of-line blocking
74
+ # behind slow ones and stretch the effective queue depth - leave at 1
75
+ # unless your handlers are all sub-millisecond.
76
+ # batch 1
77
+
78
+ # EXPERIMENTAL lane dispatch: per-worker queues with awake-preferring
79
+ # assignment and work stealing. Cuts per-request wakeups for uniformly
80
+ # fast handlers; semantics under overload are slightly different (per-lane
81
+ # caps with brief dispatcher retries instead of one global queue).
82
+ # lanes false
83
+
84
+ # Native access log: one line per request to stdout, written by a
85
+ # Rust-side flusher thread - request threads never block on the log.
86
+ #
87
+ # On color terminals lines are tinted by status class (2xx green,
88
+ # 3xx yellow, 4xx maroon, 5xx bright red). This is the SERVER's view - it
89
+ # includes the 503 rejections your app never sees - and it interleaves
90
+ # cleanly with your app's own log (e.g. Rails') on stdout. See also
91
+ # Kino::Logger for routing the app log through the same async sink.
92
+ #
93
+ # Try enabling it in the development environment.
94
+ # log_requests false
95
+
96
+ ## Lifecycle
97
+
98
+ # Graceful-shutdown drain deadline in seconds: in-flight requests get this
99
+ # long to finish; past it, their clients receive 500s and workers are
100
+ # reaped. A second INT/TERM force-exits immediately.
101
+ # shutdown_timeout 30
102
+
103
+ # Write the master PID here on start; removed on graceful shutdown.
104
+ # pidfile "tmp/pids/kino.pid"
105
+
106
+ ## Runtime
107
+
108
+ # Threads for the tokio (Rust I/O) runtime. Default (nil) lets tokio use
109
+ # one per core: right for I/O-heavy apps. For CPU-heavy apps this is a
110
+ # real lever: `tokio_threads 1` + `threads 1` measured +26% on a pure-CPU
111
+ # benchmark (every spare thread is Ruby work you didn't run).
112
+ # tokio_threads 4
113
+
114
+ ## App
115
+
116
+ # Rackup file the `kino` CLI loads (positional CLI argument wins).
117
+ # rackup "config.ru"
118
+
119
+ # Sets RACK_ENV (unless already set) before the app is loaded by the CLI.
120
+ # environment "production"
121
+
122
+ ## Rails
123
+ #
124
+ # Rails runs on Kino TODAY in :threaded mode; uncomment for a Rails app:
125
+ #
126
+ # mode :threaded
127
+ # environment "production"
128
+ # threads 5 # match your database pool size
129
+ #
130
+ # Recommended Rails-side settings to pair with Kino:
131
+ # - config.eager_load = true and no code reloading (production defaults):
132
+ # Kino's workers serve concurrently; lazy class loading under
133
+ # concurrency is slow and, in ractor mode, unsafe.
134
+ # - Database pool >= workers × threads (config/database.yml `pool:`).
135
+ # - Rails.logger goes to stdout/stderr or a thread-safe device.
136
+ #
137
+ # Rails main is being ractorized, but
138
+ # Rails.application still captures unshareable state at boot; known
139
+ # blockers are documented in Kino's README. Track rails/rails main; when
140
+ # Ractor.make_shareable(Rails.application) succeeds, `mode :ractor` here
141
+ # is all you'll need to change.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kino
4
+ # The gem version (single source of truth; ext/kino/Cargo.toml syncs).
5
+ VERSION = "0.1.0"
6
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kino
4
+ # @private
5
+ # The request loop. Identical for threaded and ractor modes.
6
+ #
7
+ # The default (batch 1) hot path allocates one Hash per request: the env
8
+ # arrives with the native request handle embedded under "kino.request",
9
+ # and the common complete-body response rides the fused
10
+ # respond_and_take_one call: ~one FFI crossing per request, no arrays.
11
+ #
12
+ # batch > 1 trades fairness for throughput: a worker grabs up to that
13
+ # many already-queued requests per crossing, adding head-of-line blocking
14
+ # behind slow handlers and stretching effective queue depth.
15
+ module Worker
16
+ RACK_INPUT = "rack.input"
17
+ KINO_REQUEST = "kino.request"
18
+
19
+ module_function
20
+
21
+ def run(server_id, worker_id, app, batch_size = 1)
22
+ if batch_size <= 1
23
+ env = Native.take_one(server_id, worker_id)
24
+ env = handle_one(env, server_id, worker_id, app) while env
25
+ else
26
+ batch = Native.take_batch(server_id, worker_id, batch_size)
27
+ batch = process(batch, server_id, worker_id, app, batch_size) while batch
28
+ end
29
+ end
30
+
31
+ # serve() returns this when the response did NOT ride a fused
32
+ # respond-and-take (streaming body or app error) and the caller must
33
+ # take the next request itself. Frozen: worker ractors read it.
34
+ NOT_FUSED = Object.new.freeze
35
+
36
+ # Handle one request; returns the next env (fused take) or nil.
37
+ def handle_one(env, server_id, worker_id, app)
38
+ result = serve(env, app) do |request, status, headers, chunks|
39
+ request.respond_and_take_one(server_id, worker_id, status, headers, chunks)
40
+ end
41
+ result.equal?(NOT_FUSED) ? Native.take_one(server_id, worker_id) : result
42
+ end
43
+
44
+ # Handle every env in the batch; returns the next batch (the last
45
+ # simple response rides the fused respond_and_take) or nil on shutdown.
46
+ def process(batch, server_id, worker_id, app, batch_size)
47
+ last = batch.size - 1
48
+ batch.each_with_index do |env, index|
49
+ result = serve(env, app) do |request, status, headers, chunks|
50
+ if index == last
51
+ request.respond_and_take(server_id, worker_id, batch_size,
52
+ status, headers, chunks)
53
+ else
54
+ request.send_simple(status, headers, chunks)
55
+ NOT_FUSED
56
+ end
57
+ end
58
+ return result if index == last && !result.equal?(NOT_FUSED)
59
+ end
60
+ Native.take_batch(server_id, worker_id, batch_size)
61
+ end
62
+
63
+ # Run one request through the app. Complete bodies are yielded so the
64
+ # caller picks plain vs fused delivery (the block's return value passes
65
+ # through after the body is closed); streaming bodies are delivered
66
+ # here and return NOT_FUSED. App errors must never kill the worker;
67
+ # hard crashes (Exception) are the supervisor's job; and `abort` does
68
+ # the right thing whether or not the response head already went out.
69
+ def serve(env, app)
70
+ request = env[KINO_REQUEST]
71
+ env[RACK_INPUT] ||= Input.new(request)
72
+ status, headers, body = app.call(env)
73
+
74
+ if body.respond_to?(:to_ary)
75
+ result = yield(request, status.to_i, headers, join_chunks(body.to_ary))
76
+ body.close if body.respond_to?(:close)
77
+ result
78
+ else
79
+ deliver_streaming(request, status.to_i, headers, body, env[RACK_INPUT])
80
+ NOT_FUSED
81
+ end
82
+ rescue => e
83
+ Native.log_error("#{e.class}: #{e.message}")
84
+ request.abort
85
+ NOT_FUSED
86
+ end
87
+
88
+ def deliver_streaming(request, status, headers, body, input)
89
+ request.send_headers(status, headers)
90
+ if body.respond_to?(:call) && !body.respond_to?(:each)
91
+ # Rack 3 streaming body: the app drives a full-duplex stream whose
92
+ # read side is the request's existing rack.input (a fresh Input
93
+ # here would strand anything the app already buffered from it).
94
+ stream = Stream.new(request, input)
95
+ begin
96
+ body.call(stream)
97
+ ensure
98
+ stream.close
99
+ end
100
+ else
101
+ # Enumerable body: chunked transfer unless the app set content-length.
102
+ begin
103
+ body.each { |chunk| request.write_chunk(chunk) }
104
+ ensure
105
+ request.finish
106
+ body.close if body.respond_to?(:close)
107
+ end
108
+ end
109
+ end
110
+
111
+ def join_chunks(chunks)
112
+ # Single-chunk bodies (the common case) skip the join copy entirely:
113
+ # the native layer reads raw bytes, so encoding doesn't matter.
114
+ return chunks.first || "" if chunks.size <= 1
115
+
116
+ joined = (+"").force_encoding(Encoding::BINARY)
117
+ chunks.each { |chunk| joined << chunk.b }
118
+ joined
119
+ end
120
+
121
+ private_class_method :handle_one, :process, :serve, :deliver_streaming,
122
+ :join_chunks
123
+ end
124
+ end
data/lib/kino.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "kino/version"
4
+ require "kino/kino"
5
+
6
+ # A high-performance Ractor web server for Ruby 4.0+: Rack 3-based, with
7
+ # a Rust (tokio + hyper) front-end and Ractor-parallel Ruby workers, plus
8
+ # a threaded fallback mode for apps (such as Rails) that are not
9
+ # Ractor-shareable.
10
+ #
11
+ # The public API is {Kino::Server}, {Kino::Configuration}, {Kino::Check},
12
+ # {Kino::Logger}, and {Kino.sleep}; the `kino` executable is implemented
13
+ # by {Kino::CLI}.
14
+ module Kino
15
+ # Base class for all errors raised by Kino.
16
+ class Error < StandardError; end
17
+
18
+ # Raised when mode: :ractor is forced but the app is not Ractor-shareable.
19
+ class UnshareableAppError < Error; end
20
+
21
+ # High-resolution sleep that bypasses the VM timer (whose wakeups inside
22
+ # non-main ractors are coarse: ~+2.5ms on Linux). Sleeps on the OS clock
23
+ # with the GVL released; chunked so Thread#kill and shutdown interrupts
24
+ # are honored between chunks.
25
+ #
26
+ # @param seconds [Numeric] how long to sleep; must be non-negative
27
+ # @return [nil]
28
+ # @raise [ArgumentError] for negative or non-numeric durations
29
+ def self.sleep(seconds)
30
+ remaining = Float(seconds)
31
+ raise ArgumentError, "sleep duration must be non-negative" if remaining.negative?
32
+
33
+ remaining = Native.sleep_chunk(remaining) while remaining.positive?
34
+ nil
35
+ end
36
+ end
37
+
38
+ require_relative "kino/cli"
39
+ require_relative "kino/logger"
40
+ require_relative "kino/check"
41
+ require_relative "kino/input"
42
+ require_relative "kino/null_input"
43
+ require_relative "kino/errors_stream"
44
+ require_relative "kino/stream"
45
+ require_relative "kino/configuration"
46
+ require_relative "kino/worker"
47
+ require_relative "kino/ractor_supervisor"
48
+ require_relative "kino/server"
49
+
50
+ # Hand the frozen shareable singletons to the native layer: it sets them
51
+ # straight into each request's env, so the worker loop allocates neither
52
+ # an errors stream nor (for bodyless requests) an input object.
53
+ Kino::Native.register_defaults(Kino::ErrorsStream::INSTANCE, Kino::NullInput::INSTANCE)
data/sig/kino.rbs ADDED
@@ -0,0 +1,178 @@
1
+ # Public interfaces only. Internals (Kino::Worker, Kino::RactorSupervisor,
2
+ # Kino::Input, Kino::NullInput, Kino::ErrorsStream, Kino::Stream,
3
+ # Kino::Native) are deliberately unsigned: they are implementation detail
4
+ # and may change without notice.
5
+ module Kino
6
+ VERSION: String
7
+
8
+ # Any Rack 3 application: responds to #call(env) with [status, headers, body].
9
+ type rack_app = untyped
10
+
11
+ type stats_hash = Hash[Symbol, untyped]
12
+
13
+ class Error < StandardError
14
+ end
15
+
16
+ # Raised when mode: :ractor is forced but the app is not Ractor-shareable.
17
+ class UnshareableAppError < Error
18
+ end
19
+
20
+ # High-resolution sleep on the OS clock with the GVL released.
21
+ def self.sleep: (Numeric seconds) -> nil
22
+
23
+ class Server
24
+ attr_reader port: Integer?
25
+ attr_reader mode: Symbol
26
+ attr_reader bind: String
27
+
28
+ def tls?: () -> bool
29
+
30
+ # Settings precedence: explicit kwargs > config_file DSL > defaults.
31
+ def initialize: (rack_app app, ?config_file: String?, **untyped options) -> void
32
+
33
+ def start: () -> self
34
+
35
+ # Graceful shutdown: drain to the deadline, then abort stragglers.
36
+ def shutdown: (?timeout: Numeric?) -> nil
37
+
38
+ def wait: () -> untyped
39
+
40
+ # Live snapshot: config echo plus native counters (queued, in_flight,
41
+ # served, rejected, timeouts, respawns, lane_depths when lanes are on).
42
+ def stats: () -> stats_hash
43
+
44
+ # Production entry point: start, banner, signal traps, block until done.
45
+ def self.run: (rack_app app, **untyped opts) -> Server
46
+
47
+ # INT/TERM drain gracefully (second signal force-exits); USR1 prints stats.
48
+ def self.trap_signals: (Server server) -> void
49
+ end
50
+
51
+ class Configuration
52
+ DEFAULTS: Hash[Symbol, untyped]
53
+ SETTINGS: Array[Symbol]
54
+ SAMPLE_TEMPLATE: String
55
+
56
+ # The fully commented sample config (see `kino --init`).
57
+ def self.sample: () -> String
58
+
59
+ # Write the sample config to path; raises Kino::Error if it exists
60
+ # unless force. Returns the path.
61
+ def self.write_sample: (String path, ?force: bool) -> String
62
+
63
+ def initialize: () -> void
64
+
65
+ def []: (Symbol key) -> untyped
66
+
67
+ # Raises ArgumentError for keys outside DEFAULTS.
68
+ def set: (Symbol key, untyped value) -> untyped
69
+
70
+ def set?: (Symbol key) -> bool
71
+
72
+ # Evaluate a Puma-style Ruby DSL config file into this configuration.
73
+ def load_file: (String path) -> self
74
+
75
+ def merge!: (Hash[Symbol, untyped] options) -> self
76
+
77
+ def to_h: () -> Hash[Symbol, untyped]
78
+
79
+ # to_h minus the CLI-only keys (:rackup, :environment); what Server.new
80
+ # accepts.
81
+ def server_options: () -> Hash[Symbol, untyped]
82
+
83
+ # The config-file DSL: one method per directive.
84
+ class DSL
85
+ def initialize: (Configuration config) -> void
86
+
87
+ def bind: (String host) -> untyped
88
+ def port: (int port) -> untyped
89
+ def workers: (int count) -> untyped
90
+ def threads: (int count) -> untyped
91
+ def mode: (Symbol | String mode) -> untyped
92
+ def queue_depth: (int depth) -> untyped
93
+ def queue_timeout: (Numeric seconds) -> untyped
94
+ def request_timeout: (Numeric? seconds) -> untyped
95
+ def batch: (int count) -> untyped
96
+ def lanes: (boolish enabled) -> untyped
97
+ def log_requests: (boolish enabled) -> untyped
98
+ def shutdown_timeout: (Numeric seconds) -> untyped
99
+ def tokio_threads: (int count) -> untyped
100
+ def tls: (cert: String, key: String) -> untyped
101
+ def environment: (String | Symbol env) -> untyped
102
+ def pidfile: (String path) -> untyped
103
+ def rackup: (String path) -> untyped
104
+ end
105
+ end
106
+
107
+ # The `kino` executable plus startup presentation shared with Server.run.
108
+ module CLI
109
+ MOTD: String
110
+
111
+ # Parse argv, then init/check/serve. Returns the process exit status
112
+ # (-v and -h print and exit in place per optparse convention).
113
+ def self.start: (Array[String] argv) -> Integer
114
+
115
+ # True when output to io may use ANSI styling.
116
+ def self.color?: (?IO io) -> bool
117
+
118
+ # Wrap text in an SGR code, resetting at the end; plain on non-color io.
119
+ def self.paint: (String code, String text, ?io: IO) -> String
120
+
121
+ def self.dim: (String text, ?io: IO) -> String
122
+
123
+ def self.bold: (String text, ?io: IO) -> String
124
+
125
+ def self.red: (String text, ?io: IO) -> String
126
+
127
+ # The banner with a vertical grayscale gradient.
128
+ def self.motd: (?color: bool) -> String
129
+
130
+ # One-line stats dump (the SIGUSR1 handler's output).
131
+ def self.stats_line: (stats_hash stats) -> String
132
+
133
+ # The two banner halves around Server#start.
134
+ def self.opening_credits: () -> void
135
+ def self.action!: (Server server) -> void
136
+
137
+ # Print a bold "Fin." when the process ends (idempotent).
138
+ def self.fin_at_exit: () -> void
139
+ end
140
+
141
+ # The shareability doctor behind `kino --check`.
142
+ module Check
143
+ MAX_FINDINGS: Integer
144
+ MAX_NODES: Integer
145
+
146
+ class Finding < ::Struct[untyped]
147
+ attr_accessor path: String
148
+ attr_accessor message: String
149
+
150
+ def to_s: () -> String
151
+ end
152
+
153
+ def self.report: (rack_app app) -> { shareable: bool, findings: Array[Finding] }
154
+
155
+ # Pretty-printed report; true when the app is ractor-ready.
156
+ def self.print_report: (rack_app app, ?io: IO) -> bool
157
+ end
158
+
159
+ # A ::Logger writing through the native async sink.
160
+ class Logger < ::Logger
161
+ # path: a file (created/appended) or nil for stdout.
162
+ def initialize: (?String? path, **untyped options) -> void
163
+
164
+ # The raw IO-like device: frozen and Ractor-shareable, one device can
165
+ # serve every worker.
166
+ class Device
167
+ def initialize: (?String? path) -> void
168
+
169
+ def write: (untyped message) -> void
170
+
171
+ alias << write
172
+
173
+ def close: () -> void
174
+
175
+ def reopen: (*untyped) -> self
176
+ end
177
+ end
178
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kino
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: aarch64-linux
6
+ authors:
7
+ - Yaroslav Markin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logger
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rbs
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: standard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.50'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.50'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.9'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.9'
125
+ description: 'A high-performance Ractor web server for Ruby 4.0+: Rack 3-based, with
126
+ a Rust tokio/hyper front-end and Ractor-parallel Ruby workers and threaded fallback
127
+ mode.'
128
+ email:
129
+ - yaroslav@markin.net
130
+ executables:
131
+ - kino
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - ".yardopts"
136
+ - CHANGELOG.md
137
+ - LICENSE.txt
138
+ - README.md
139
+ - doc/README.md
140
+ - doc/architecture.md
141
+ - doc/benchmarks.md
142
+ - doc/rails-on-ractors.md
143
+ - doc/why-kino.md
144
+ - exe/kino
145
+ - lib/kino.rb
146
+ - lib/kino/check.rb
147
+ - lib/kino/cli.rb
148
+ - lib/kino/configuration.rb
149
+ - lib/kino/errors_stream.rb
150
+ - lib/kino/input.rb
151
+ - lib/kino/kino.so
152
+ - lib/kino/logger.rb
153
+ - lib/kino/null_input.rb
154
+ - lib/kino/ractor_supervisor.rb
155
+ - lib/kino/server.rb
156
+ - lib/kino/stream.rb
157
+ - lib/kino/templates/kino.rb.tt
158
+ - lib/kino/version.rb
159
+ - lib/kino/worker.rb
160
+ - sig/kino.rbs
161
+ homepage: https://github.com/yaroslav/kino
162
+ licenses:
163
+ - MIT
164
+ metadata:
165
+ allowed_push_host: https://rubygems.org
166
+ homepage_uri: https://github.com/yaroslav/kino
167
+ source_code_uri: https://github.com/yaroslav/kino
168
+ changelog_uri: https://github.com/yaroslav/kino/blob/main/CHANGELOG.md
169
+ documentation_uri: https://rubydoc.info/gems/kino
170
+ rubygems_mfa_required: 'true'
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '4.0'
180
+ - - "<"
181
+ - !ruby/object:Gem::Version
182
+ version: 4.1.dev
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ requirements: []
189
+ rubygems_version: 3.5.23
190
+ signing_key:
191
+ specification_version: 4
192
+ summary: High-performance Ractor web server for Ruby.
193
+ test_files: []