kirei 0.9.2 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f515625a7b5fda4fb1218d126c8c1c98c720186176d0b5eb3c01a2330de49017
4
- data.tar.gz: b47e63f5d3f9298523809f151040ed5fd78168ffb7616645a48b45f0b02ad74a
3
+ metadata.gz: 1926e641bc2fa378b9444e97f33c7aef0bd97ce3c277a38fc572f12d501acb77
4
+ data.tar.gz: 2cdaff92504ea20fac445d791da1f9557fe3fbe6f4f780eab70c9022feacb3e1
5
5
  SHA512:
6
- metadata.gz: 6eb332b813b427ecfaca012b17b3ba18729f7569fdb08544ee65bd5b52e6e20671e1c0185abee8dfd6d191ed4ce22d001bb1c2c2d8281bd96a2809ea151f7447
7
- data.tar.gz: 35d98ccc9bd0f1bc9d81425229925c02e666a9d973d956bdd57ed181384c53e890feb30ae24e4225cc4ed5ea68e173d86cc1163d60e5075f74f60802a2dd50de
6
+ metadata.gz: 4111679c635210bd6a9389c839c2665e5fea9a1e4bd3e6186725e89022a8964cf7a2dc8c2b5347eaf76962b327a7c0a77243cfc7c6ac214e01f59592a08a132f
7
+ data.tar.gz: 8aaf9c748f52d790cc2be69cb29087c96f1ad1001ee629555b5727d93770a1b73be72fbb62a8d1b3993a3ef75676832463b401cb265e5dd18a277d9706b9fc68
data/README.md CHANGED
@@ -6,7 +6,7 @@ Kirei's main advantages over other frameworks are its strict typing, low memory
6
6
 
7
7
  > Kirei (きれい) is a Japanese adjective that primarily means "beautiful" or "pretty." It can also be used to describe something that is "clean" or "neat."
8
8
 
9
- 👉 AI-generated wiki available on [DeepWiki.com](https://deepwiki.com/swiknaba/kirei).
9
+ 👉 AI-generated wiki available on [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/swiknaba/kirei)
10
10
 
11
11
  ## Why another Ruby framework?
12
12
 
@@ -44,7 +44,10 @@ module Cli
44
44
 
45
45
  # Fifth: load configs
46
46
  Dir[File.join(__dir__, "config", "**", "*.rb")].each do |cnf|
47
- require(cnf) unless cnf.split("/").include?("initializers")
47
+ next if cnf.split("/").include?("initializers")
48
+ next if cnf.end_with?("puma.rb") # Puma config uses DSL only available when loaded by Puma
49
+
50
+ require(cnf)
48
51
  end
49
52
 
50
53
  class #{app_name} < Kirei::App
@@ -5,10 +5,9 @@ module Cli
5
5
  module NewApp
6
6
  module Files
7
7
  class Routes
8
- def self.call(app_name)
8
+ def self.call(_app_name)
9
9
  File.write("config/routes.rb", router)
10
10
  File.write("app/controllers/base.rb", base_controller)
11
- File.write("app/controllers/health.rb", health_controller(app_name))
12
11
  end
13
12
 
14
13
  def self.router
@@ -17,16 +16,7 @@ module Cli
17
16
  # frozen_string_literal: true
18
17
 
19
18
  module Kirei::Routing
20
- Router.add_routes(
21
- [
22
- Route.new(
23
- verb: Verb::GET,
24
- path: "/livez",
25
- controller: Controllers::Health,
26
- action: "livez",
27
- ),
28
- ],
29
- )
19
+ Router.add_health_routes!
30
20
  end
31
21
  RUBY
32
22
  end
@@ -43,24 +33,6 @@ module Cli
43
33
  end
44
34
  RUBY
45
35
  end
46
-
47
- def self.health_controller(app_name)
48
- <<~RUBY
49
- # typed: strict
50
- # frozen_string_literal: true
51
-
52
- module Controllers
53
- class Health < Base
54
- sig { returns(T.anything) }
55
- def livez
56
- #{app_name}.config.logger.info("Health check")
57
- #{app_name}.config.logger.info(params.inspect)
58
- render(#{app_name}.version, status: 200)
59
- end
60
- end
61
- end
62
- RUBY
63
- end
64
36
  end
65
37
  end
66
38
  end
@@ -0,0 +1,63 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Kirei
5
+ module Controllers
6
+ #
7
+ # Built-in health check controller implementing Kubernetes API health endpoints.
8
+ #
9
+ # Provides three endpoints following the Kubernetes convention:
10
+ # - /livez — Liveness probe. Indicates the process is alive.
11
+ # - /readyz — Readiness probe. Verifies downstream dependencies (DB) are reachable.
12
+ # - /healthz — Deprecated alias for /livez (deprecated since Kubernetes v1.16).
13
+ #
14
+ # Reference: https://kubernetes.io/docs/reference/using-api/health-checks/
15
+ #
16
+ class Health < Kirei::Controller
17
+ extend T::Sig
18
+
19
+ sig { returns(Routing::RackResponseType) }
20
+ def livez
21
+ info = {
22
+ "status" => "ok",
23
+ "version" => App.version,
24
+ "app_name" => App.config.app_name,
25
+ "environment" => App.environment,
26
+ "req_host" => request.host,
27
+ "req_port" => request.port,
28
+ "req_ssl" => request.ssl?,
29
+ }
30
+
31
+ Kirei::Logging::Logger.call(
32
+ level: Kirei::Logging::Level::INFO,
33
+ label: "Health check",
34
+ meta: info,
35
+ )
36
+
37
+ render_json(info, status: 200)
38
+ end
39
+ alias healthz livez
40
+
41
+ sig { returns(Routing::RackResponseType) }
42
+ def readyz
43
+ T.unsafe(App.raw_db_connection).execute("SELECT 1")
44
+
45
+ Kirei::Logging::Logger.call(
46
+ level: Kirei::Logging::Level::INFO,
47
+ label: "Readiness check",
48
+ meta: { "status" => "ok" },
49
+ )
50
+
51
+ render_json({ "status" => "ok" }, status: 200)
52
+ rescue Sequel::Error => e
53
+ Kirei::Logging::Logger.call(
54
+ level: Kirei::Logging::Level::ERROR,
55
+ label: "Readiness check failed",
56
+ meta: { "error" => e.message },
57
+ )
58
+
59
+ render_json({ "status" => "unavailable", "reason" => e.message }, status: 503)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -23,6 +23,7 @@ module Kirei
23
23
 
24
24
  sig { params(env: RackEnvType).returns(RackResponseType) }
25
25
  def call(env)
26
+ statsd_timing_tags = T.let({}, T::Hash[String, T.untyped])
26
27
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
27
28
  status = 500 # we use it in the "ensure" block, so we need to define early (Sorbet doesn't like `status ||= 418`)
28
29
 
@@ -62,7 +63,7 @@ module Kirei
62
63
  body.rewind # TODO: maybe don't rewind if we don't need to?
63
64
  T.cast(res, T::Hash[String, T.untyped])
64
65
  end
65
- when Verb::HEAD, Verb::DELETE, Verb::OPTIONS, Verb::TRACE, Verb::CONNECT
66
+ when Verb::HEAD, Verb::DELETE, Verb::OPTIONS
66
67
  {}
67
68
  else
68
69
  T.absurd(http_verb)
@@ -90,14 +91,11 @@ module Kirei
90
91
  },
91
92
  )
92
93
 
93
- statsd_timing_tags = {
94
- "controller" => controller.name,
95
- "route" => route.action,
96
- }
97
- Logging::Metric.inject_defaults(statsd_timing_tags)
94
+ statsd_timing_tags["controller"] = controller.name
95
+ statsd_timing_tags["route"] = route.action
98
96
 
99
97
  status, headers, response_body = case http_verb
100
- when Verb::HEAD, Verb::OPTIONS, Verb::TRACE, Verb::CONNECT
98
+ when Verb::HEAD, Verb::OPTIONS
101
99
  [200, {}, []]
102
100
  when Verb::GET, Verb::POST, Verb::PUT, Verb::PATCH, Verb::DELETE
103
101
  T.cast(
@@ -124,10 +122,35 @@ module Kirei
124
122
  headers,
125
123
  response_body,
126
124
  ]
125
+ rescue StandardError => e
126
+ status = 500
127
+
128
+ Kirei::Logging::Logger.call(
129
+ level: Kirei::Logging::Level::ERROR,
130
+ label: "Unhandled Exception",
131
+ meta: {
132
+ "error.class" => e.class.name,
133
+ "error.message" => e.message,
134
+ "error.backtrace" => e.backtrace&.first(10)&.join("\n"),
135
+ },
136
+ )
137
+
138
+ detail = if Kirei::App.environment == "development"
139
+ "#{e.class}: #{e.message}\n#{e.backtrace&.first(10)&.join("\n")}"
140
+ else
141
+ "An unexpected error occurred"
142
+ end
143
+
144
+ error = Errors::JsonApiError.new(code: "internal_server_error", detail: detail)
145
+ body = Oj.dump({ "errors" => [error.serialize] }, Kirei::OJ_OPTIONS)
146
+ response_body = [body]
147
+
148
+ [status, { "Content-Type" => "application/json; charset=utf-8" }, response_body]
127
149
  ensure
128
150
  stop = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
129
- if start # early return for 404
151
+ if start && statsd_timing_tags # early return for 404
130
152
  latency_in_ms = stop - start
153
+ Logging::Metric.inject_defaults(statsd_timing_tags)
131
154
  App.config.metrics_backend.measure("request", latency_in_ms, tags: statsd_timing_tags)
132
155
 
133
156
  Kirei::Logging::Logger.call(
@@ -85,6 +85,18 @@ module Kirei
85
85
  end
86
86
  end
87
87
 
88
+ # must be added manually => we don't want to magically add routes for the user
89
+ sig { void }
90
+ def self.add_health_routes!
91
+ add_routes(
92
+ [
93
+ Route.new(verb: Verb::GET, path: "/livez", controller: Kirei::Controllers::Health, action: "livez"),
94
+ Route.new(verb: Verb::GET, path: "/readyz", controller: Kirei::Controllers::Health, action: "readyz"),
95
+ Route.new(verb: Verb::GET, path: "/healthz", controller: Kirei::Controllers::Health, action: "healthz"),
96
+ ],
97
+ )
98
+ end
99
+
88
100
  # Matches a request path against registered dynamic routes.
89
101
  # Returns [Route, extracted_params] or nil.
90
102
  sig do
@@ -25,12 +25,6 @@ module Kirei
25
25
 
26
26
  # idempotent
27
27
  OPTIONS = new("OPTIONS")
28
-
29
- # idempotent
30
- TRACE = new("TRACE")
31
-
32
- # non-idempotent
33
- CONNECT = new("CONNECT")
34
28
  end
35
29
  end
36
30
  end
data/lib/kirei/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Kirei
5
- VERSION = "0.9.2"
5
+ VERSION = "0.10.0"
6
6
  end
data/lib/kirei.rb CHANGED
@@ -67,6 +67,4 @@ loader.eager_load
67
67
 
68
68
  Kirei.configure(&:itself)
69
69
 
70
- yjit_enabled = defined?(RubyVM::YJIT) ? RubyVM::YJIT.enabled? : false
71
-
72
- Kirei::Logging::Logger.logger.info("Kirei v#{Kirei::VERSION} booted; YJIT enabled: #{yjit_enabled}")
70
+ Kirei::Logging::Logger.logger.info("Kirei v#{Kirei::VERSION} booted")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kirei
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ludwig Reinmiedl
@@ -174,6 +174,7 @@ files:
174
174
  - lib/kirei/app.rb
175
175
  - lib/kirei/config.rb
176
176
  - lib/kirei/controller.rb
177
+ - lib/kirei/controllers/health.rb
177
178
  - lib/kirei/domain/entity.rb
178
179
  - lib/kirei/domain/value_object.rb
179
180
  - lib/kirei/errors/json_api_error.rb
@@ -226,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
226
227
  - !ruby/object:Gem::Version
227
228
  version: '0'
228
229
  requirements: []
229
- rubygems_version: 4.0.3
230
+ rubygems_version: 4.0.6
230
231
  specification_version: 4
231
232
  summary: Kirei is a typed Ruby micro/REST-framework for building scalable and performant
232
233
  microservices.