kirei 0.1.0 → 0.2.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: cea2982351607eeafadd7e8ee3bb3ce13ba2c7e1e37eed22fa2f5a124b625022
4
- data.tar.gz: 84a47be77945fa7a2faf5950441290fde74293388467729f2eb52c1c6041d29b
3
+ metadata.gz: 8a37402ff7c216a16e3cc2a279d477e91d7ed8c40fd58169a8c598e47efd90d7
4
+ data.tar.gz: 1fd249f44b20dc1dfba0ace6ea52f6de6f67ee47e4c2a7f511438ceafba4985f
5
5
  SHA512:
6
- metadata.gz: 3b420ea6cbfce60bf741bfd85cbfeb310dfd71031519a6d5fcc330bff788d24a0df1ef7268abbdbe49a2f373f4ff5df413dc6cf1b83589f96a6f8867164ffcef
7
- data.tar.gz: 061a15d94de3e73dcd8d41db48601bfa5390d59aa1438c322d590ec3c33db9da4dd38d1f9258bf37a13c248ded433883a6da9ff52d41ee5873ca13b0cda414ca
6
+ metadata.gz: d6d29c323e8b3438a0c9775f2b92312182a709443aa04843145bfed76226d71e2d78081361b178dc6293dfefa75571d525981b5ed84fee2664c93a880eb5f6fa
7
+ data.tar.gz: 6b33ff256a0a199333d789c81ff70f4dca49225069f0c854fe0f4cfec4c6a68c918a6966b92c93ef76aebd14117029c5f3e7ad36d65d7d74033c5d7a0f905ab4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
+ # Changelog
2
+
1
3
  ## [Unreleased]
2
4
 
3
5
  ## [0.1.0] - 2023-09-02
4
6
 
5
7
  - Initial release
8
+ - added base model
9
+ - added database connection
10
+ - WIP: basic cli to scaffold a new project
11
+
12
+ ## [0.2.0] - 2023-09-02
13
+
14
+ - added routing
15
+ - added base controller
16
+ - added database tasks (create, drop, migrate, rollback, generate migration)
data/README.md CHANGED
@@ -49,6 +49,10 @@ bundle exec kirei new "MyApp"
49
49
 
50
50
  ### Quick Start
51
51
 
52
+ Find a test app in the [spec/test_app](spec/test_app) directory. It is a fully functional example of a Kirei app.
53
+
54
+ #### Models
55
+
52
56
  All models must inherit from `T::Struct` and include `Kirei::BaseModel`. They must implement `id` which must hold the primary key of the table. The primary key must be named `id` and be of type `T.any(String, Integer)`.
53
57
 
54
58
  ```ruby
@@ -86,6 +90,93 @@ first_user = User.resolve_first(query) # T.nilable(User)
86
90
  first_user = User.from_hash(query.first.stringify_keys)
87
91
  ```
88
92
 
93
+ #### Database Migrations
94
+
95
+ Read the [Sequel Migrations](https://github.com/jeremyevans/sequel/blob/5.78.0/doc/schema_modification.rdoc) documentation for detailed information.
96
+
97
+ ```ruby
98
+ Sequel.migration do
99
+ up do
100
+ create_table(:airports) do
101
+ primary_key :id
102
+ String :name, null: false
103
+ end
104
+ end
105
+
106
+ down do
107
+ drop_table(:airports)
108
+ end
109
+ end
110
+ ```
111
+
112
+ Applying migrations:
113
+
114
+ ```shell
115
+ # create the database
116
+ bundle exec rake db:create
117
+
118
+ # drop the database
119
+ bundle exec rake db:drop
120
+
121
+ # apply all pending migrations
122
+ bundle exec rake db:migrate
123
+
124
+ # roll back the last n migration
125
+ STEPS=1 bundle exec rake db:rollback
126
+
127
+ # run db/seeds.rb to seed the database
128
+ bundle exec rake db:migrate
129
+
130
+ # scaffold a new migration file
131
+ bundle exec rake 'db:migration[CreateAirports]'
132
+ ```
133
+
134
+ #### Routing
135
+
136
+ Define routes anywhere in your app; by convention, they are defined in `config/routes.rb`:
137
+
138
+ ```ruby
139
+ # config/routes.rb
140
+
141
+ Kirei::Router.add_routes([
142
+ Kirei::Router::Route.new(
143
+ verb: "GET",
144
+ path: "/livez",
145
+ controller: Controllers::Health,
146
+ action: "livez",
147
+ ),
148
+
149
+ Kirei::Router::Route.new(
150
+ verb: "GET",
151
+ path: "/airports",
152
+ controller: Controllers::Airports,
153
+ action: "index",
154
+ ),
155
+ ])
156
+ ```
157
+
158
+ #### Controllers
159
+
160
+ Controllers can be defined anywhere; by convention, they are defined in the `app/controllers` directory:
161
+
162
+ ```ruby
163
+ module Controllers
164
+ class Airports < Kirei::BaseController
165
+ extend T::Sig
166
+
167
+ sig { returns(Kirei::Middleware::RackResponseType) }
168
+ def index
169
+ airports = Airport.all
170
+
171
+ # or use a serializer
172
+ data = Oj.dump(airports.map(&:serialize))
173
+
174
+ render(status: 200, body: data)
175
+ end
176
+ end
177
+ end
178
+ ```
179
+
89
180
  ## Contributions
90
181
 
91
182
  We welcome contributions from the community. Before starting work on a major feature, please get in touch with us either via email or by opening an issue on GitHub. "Major feature" means anything that changes user-facing features or significant changes to the codebase itself.
data/kirei.gemspec CHANGED
@@ -45,13 +45,12 @@ Gem::Specification.new do |spec|
45
45
 
46
46
  # Utilities
47
47
  spec.add_dependency "oj", "~> 3.0"
48
- spec.add_dependency "rake", "~> 13.0"
49
48
  spec.add_dependency "sorbet-runtime", "~> 0.5"
50
49
  spec.add_dependency "tzinfo-data", "~> 1.0" # for containerized environments, e.g. on AWS ECS
51
50
 
52
51
  # Web server & routing
53
- spec.add_dependency "sinatra", "~> 3.0"
54
- spec.add_dependency "sinatra-contrib", "~> 3.0"
52
+ spec.add_dependency "puma", "~> 6.0"
53
+ spec.add_dependency "rack", "~> 3.0"
55
54
 
56
55
  # Database (Postgres)
57
56
  spec.add_dependency "pg", "~> 1.0"
data/lib/boot.rb CHANGED
@@ -15,8 +15,7 @@ require "bundler/setup"
15
15
  require "logger"
16
16
  require "sorbet-runtime"
17
17
  require "oj"
18
- require "sinatra"
19
- require "sinatra/namespace" # from sinatra-contrib
18
+ require "rack"
20
19
  require "pg"
21
20
  require "sequel" # "sequel_pg" is auto-required by "sequel"
22
21
 
@@ -25,6 +25,9 @@ module Cli
25
25
  "db/migrate",
26
26
  "db/seeds",
27
27
 
28
+ "lib",
29
+ "lib/tasks",
30
+
28
31
  "sorbet",
29
32
  "sorbet/rbi",
30
33
  "sorbet/rbi/shims",
@@ -9,7 +9,11 @@ module Cli
9
9
  def self.call(app_name:)
10
10
  BaseDirectories.call
11
11
  Files::App.call(app_name)
12
+ Files::ConfigRu.call(app_name)
13
+ Files::DbRake.call(app_name)
12
14
  Files::Irbrc.call
15
+ Files::Rakefile.call
16
+ Files::Routes.call
13
17
 
14
18
  Kirei::Logger.logger.info(
15
19
  "Kirei app '#{app_name}' scaffolded successfully!",
@@ -10,11 +10,33 @@ module Cli
10
10
  end
11
11
 
12
12
  def self.content(app_name)
13
+ snake_case_app_name = app_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
14
+
13
15
  <<~RUBY
14
16
  # typed: true
15
17
  # frozen_string_literal: true
16
18
 
17
- class #{app_name} < Kirei::Base
19
+ # First: check if all gems are installed correctly
20
+ require "bundler/setup"
21
+
22
+ # Second: load all gems
23
+ # we have runtime/production ("default") and development gems ("development")
24
+ Bundler.require(:default)
25
+ Bundler.require(:development) if ENV["RACK_ENV"] == "development"
26
+ Bundler.require(:test) if ENV["RACK_ENV"] == "test"
27
+
28
+ # Third: load all initializers
29
+ Dir[File.join(__dir__, "config/initializers", "*.rb")].each { require(_1) }
30
+
31
+ # Fourth: load all application code
32
+ Dir[File.join(__dir__, "app/**/*", "*.rb")].each { require(_1) }
33
+
34
+ # Fifth: load configs
35
+ Dir[File.join(__dir__, "config", "*.rb")].each { require(_1) }
36
+
37
+ class #{app_name} < Kirei::AppBase
38
+ # Kirei configuration
39
+ config.app_name = "#{snake_case_app_name}"
18
40
  end
19
41
  RUBY
20
42
  end
@@ -0,0 +1,34 @@
1
+ # typed: false
2
+
3
+ module Cli
4
+ module Commands
5
+ module NewApp
6
+ module Files
7
+ class ConfigRu
8
+ def self.call(app_name)
9
+ File.write("config.ru", content(app_name))
10
+ end
11
+
12
+ def self.content(app_name)
13
+ <<~RUBY
14
+ # typed: false
15
+ # frozen_string_literal: true
16
+
17
+ require_relative("app")
18
+
19
+ # Load middlewares here
20
+ use(Rack::Reloader, 0) if #{app_name}.environment == "development"
21
+
22
+ # Launch the app
23
+ run(#{app_name}.new)
24
+
25
+ # "use" all controllers
26
+ # store all routes in a global variable to render (localhost only)
27
+ # put "booted" statement
28
+ RUBY
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,178 @@
1
+ # typed: false
2
+
3
+ # rubocop:disable Metrics/ClassLength
4
+
5
+ module Cli
6
+ module Commands
7
+ module NewApp
8
+ module Files
9
+ class DbRake
10
+ def self.call(app_name)
11
+ # set db_name to snake_case version of app_name
12
+ db_name = app_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
13
+ File.write("lib/tasks/db.rake", content(app_name, db_name))
14
+ end
15
+
16
+ def self.content(app_name, db_name)
17
+ <<~RUBY
18
+ # typed: false
19
+
20
+ # run on the database server once:
21
+ #
22
+ # CREATE DATABASE #{db_name}_${environment};
23
+
24
+ require_relative "../../app"
25
+
26
+ namespace :db do
27
+ # RACK_ENV=development bundle exec rake db:create
28
+ desc "Create the database"
29
+ task :create do
30
+ envs = ENV.key?("RACK_ENV") ? [ENV.fetch("RACK_ENV")] : %w[development test]
31
+ envs.each do |env|
32
+ ENV["RACK_ENV"] = env
33
+ db_name = "#{db_name}_#{env}"
34
+ puts("Creating database \#{db_name}...")
35
+
36
+ reset_memoized_class_level_instance_vars(#{app_name})
37
+ url = #{app_name}.default_db_url.dup # frozen string
38
+ url.gsub!(db_name, "postgres")
39
+ puts("Connecting to \#{url.gsub(%r{://.*@}, "_REDACTED_")}")
40
+ db = Sequel.connect(url)
41
+
42
+ begin
43
+ db.execute("CREATE DATABASE \#{db_name}")
44
+ puts("Created database \#{db_name}.")
45
+ rescue Sequel::DatabaseError, PG::DuplicateDatabase
46
+ puts("Database \#{db_name} already exists, skipping.")
47
+ end
48
+ end
49
+ end
50
+
51
+ desc "Drop the database"
52
+ task :drop do
53
+ envs = ENV.key?("RACK_ENV") ? [ENV.fetch("RACK_ENV")] : %w[development test]
54
+ envs.each do |env|
55
+ ENV["RACK_ENV"] = env
56
+ db_name = "#{db_name}_\#{env}"
57
+ puts("Dropping database \#{db_name}...")
58
+
59
+ reset_memoized_class_level_instance_vars(#{app_name})
60
+ url = #{app_name}.default_db_url.dup # frozen string
61
+ url.gsub!(db_name, "postgres")
62
+ puts("Connecting to \#{url.gsub(%r{://.*@}, "_REDACTED_")}")
63
+ db = Sequel.connect(url)
64
+
65
+ begin
66
+ db.execute("DROP DATABASE \#{db_name} (FORCE)")
67
+ puts("Dropped database \#{db_name}.")
68
+ rescue Sequel::DatabaseError, PG::DuplicateDatabase
69
+ puts("Database \#{db_name} does not exists, nothing to drop.")
70
+ end
71
+ end
72
+ end
73
+
74
+ desc "Run migrations"
75
+ task :migrate do
76
+ Sequel.extension(:migration)
77
+ envs = ENV.key?("RACK_ENV") ? [ENV.fetch("RACK_ENV")] : %w[development test]
78
+ envs.each do |env|
79
+ ENV["RACK_ENV"] = env
80
+ db_name = "#{db_name}_\#{env}"
81
+ reset_memoized_class_level_instance_vars(#{app_name})
82
+ db = Sequel.connect(#{app_name}.default_db_url)
83
+ Sequel::Migrator.run(db, File.join(#{app_name}.root, "db/migrate"))
84
+ current_version = db[:schema_migrations].order(:filename).last[:filename].to_i
85
+ puts "Migrated \#{db_name} to version \#{current_version}!"
86
+ end
87
+ end
88
+
89
+ desc "Rollback the last migration"
90
+ task :rollback do
91
+ envs = ENV.key?("RACK_ENV") ? [ENV.fetch("RACK_ENV")] : %w[development test]
92
+ Sequel.extension(:migration)
93
+ envs.each do |env|
94
+ ENV["RACK_ENV"] = env
95
+ db_name = "#{db_name}_\#{env}"
96
+ reset_memoized_class_level_instance_vars(#{app_name})
97
+ db = Sequel.connect(#{app_name}.default_db_url)
98
+
99
+ steps = (ENV["STEPS"] || 1).to_i + 1
100
+ versions = db[:schema_migrations].order(:filename).all
101
+
102
+ if versions[-steps].nil?
103
+ puts "No more migrations to rollback"
104
+ else
105
+ target_version = versions[-steps][:filename].to_i
106
+
107
+ Sequel::Migrator.run(db, File.join(#{app_name}.root, "db/migrate"), target: target_version)
108
+ puts "Rolled back \#{db_name} \#{steps} steps to version \#{target_version}"
109
+ end
110
+ end
111
+ end
112
+
113
+ desc "Seed the database"
114
+ task :seed do
115
+ load File.join(#{app_name}.root, "db/seeds.rb")
116
+ end
117
+
118
+ desc "Generate a new migration file"
119
+ task :migration, [:name] do |_t, args|
120
+ require "fileutils"
121
+ require "time"
122
+
123
+ # Ensure the migrations directory exists
124
+ migrations_dir = File.join(#{app_name}.root, "db/migrate")
125
+ FileUtils.mkdir_p(migrations_dir)
126
+
127
+ # Generate the migration number
128
+ migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S")
129
+
130
+ # Sanitize and format the migration name
131
+ formatted_name = args[:name].to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
132
+
133
+ # Combine them to create the filename
134
+ filename = "\#{migration_number}_\#{formatted_name}.rb"
135
+ file_path = File.join(migrations_dir, filename)
136
+
137
+ # Define the content of the migration file
138
+ content = <<~MIGRATION
139
+ # typed: strict
140
+ # frozen_string_literal: true
141
+
142
+ Sequel.migration do
143
+ up do
144
+ # your code here
145
+ end
146
+
147
+ down do
148
+ # your code here
149
+ end
150
+ end
151
+ MIGRATION
152
+
153
+ # Write the migration file
154
+ File.write(file_path, content)
155
+
156
+ puts "Generated migration: db/migrate/\#{filename}"
157
+ end
158
+ end
159
+
160
+ def reset_memoized_class_level_instance_vars(app)
161
+ %i[
162
+ @default_db_name
163
+ @default_db_url
164
+ @raw_db_connection
165
+ ].each do |ivar|
166
+ app.remove_instance_variable(ivar) if app.instance_variable_defined?(ivar)
167
+ end
168
+ end
169
+
170
+ RUBY
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ # rubocop:enable Metrics/ClassLength
@@ -16,10 +16,10 @@ module Cli
16
16
  # Kirei needs to know where the root of the project is
17
17
  APP_ROOT = File.expand_path(__dir__)
18
18
 
19
- ENV['RACK_ENV'] ||= 'development'
20
- ENV['APP_VERSION'] ||= (ENV['GIT_SHA'] ||= `git rev-parse --short HEAD`.to_s.chomp.freeze)
21
- require('dotenv/load') if %w[test development].include?(ENV['RACK_ENV'])
22
- require_relative('app')
19
+ ENV["RACK_ENV"] ||= "development"
20
+ ENV["APP_VERSION"] ||= (ENV["GIT_SHA"] ||= `git rev-parse --short HEAD`.to_s.chomp.freeze)
21
+ require("dotenv/load") if %w[test development].include?(ENV["RACK_ENV"])
22
+ require_relative("app")
23
23
  RUBY
24
24
  end
25
25
  end
@@ -0,0 +1,27 @@
1
+ # typed: false
2
+
3
+ module Cli
4
+ module Commands
5
+ module NewApp
6
+ module Files
7
+ class Rakefile
8
+ def self.call
9
+ File.write("Rakefile", content)
10
+ end
11
+
12
+ def self.content
13
+ <<~RUBY
14
+ # typed: false
15
+ # frozen_string_literal: true
16
+
17
+ require "rake"
18
+
19
+ Dir.glob("lib/tasks/**/*.rake").each { import(_1) }
20
+
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # typed: false
2
+
3
+ module Cli
4
+ module Commands
5
+ module NewApp
6
+ module Files
7
+ class Routes
8
+ def self.call
9
+ File.write("config/routes.rb", content)
10
+ end
11
+
12
+ def self.content
13
+ <<~RUBY
14
+ # typed: strict
15
+ # frozen_string_literal: true
16
+
17
+ Kirei::Router.add_routes([
18
+ # Kirei::Router::Route.new(
19
+ # verb: "GET",
20
+ # path: "/livez",
21
+ # controller: Controllers::HealthController,
22
+ # action: "livez",
23
+ # )
24
+ ])
25
+ RUBY
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -9,8 +9,8 @@ module Cli
9
9
  case args[0]
10
10
  when "new"
11
11
  app_name = args[1] || "MyApp"
12
- # @TODO(lud, 31.12.2023): classify is from ActiveSupport -> remove this?
13
- app_name = app_name.gsub(/[-\s]/, "_").classify
12
+ app_name = app_name.gsub(/[-\s]/, "_")
13
+ app_name = app_name.split("_").map(&:capitalize).join if app_name.include?("_")
14
14
  NewApp::Execute.call(app_name: app_name)
15
15
  else
16
16
  Kirei::Logger.logger.info("Unknown command")
data/lib/kirei/app.rb ADDED
@@ -0,0 +1,72 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative("middleware")
5
+
6
+ # rubocop:disable Metrics/AbcSize, Layout/LineLength
7
+
8
+ module Kirei
9
+ class App
10
+ include Middleware
11
+ extend T::Sig
12
+
13
+ sig { params(params: T::Hash[String, T.untyped]).void }
14
+ def initialize(params: {})
15
+ @router = T.let(Router.instance, Router)
16
+ @params = T.let(params, T::Hash[String, T.untyped])
17
+ end
18
+
19
+ sig { returns(T::Hash[String, T.untyped]) }
20
+ attr_reader :params
21
+
22
+ sig { params(env: RackEnvType).returns(RackResponseType) }
23
+ def call(env)
24
+ http_verb = T.cast(env.fetch("REQUEST_METHOD"), String)
25
+ req_path = T.cast(env.fetch("REQUEST_PATH"), String)
26
+ # reject requests from unexpected hosts -> allow configuring allowed hosts in a `cors.rb` file
27
+ # ( offer a scaffold for this file )
28
+ # -> use https://github.com/cyu/rack-cors
29
+
30
+ route = Router.instance.get(http_verb, req_path)
31
+ return [404, {}, ["Not Found"]] if route.nil?
32
+
33
+ params = if route.verb == "GET"
34
+ query = T.cast(env.fetch("QUERY_STRING"), String)
35
+ query.split("&").to_h do |p|
36
+ k, v = p.split("=")
37
+ k = T.cast(k, String)
38
+ [k, v]
39
+ end
40
+ else
41
+ # TODO: based on content-type, parse the body differently
42
+ # build-in support for JSON & XML
43
+ body = T.cast(env.fetch("rack.input"), T.any(IO, StringIO))
44
+ res = Oj.load(body.read, Kirei::OJ_OPTIONS)
45
+ body.rewind # TODO: maybe don't rewind if we don't need to?
46
+ T.cast(res, T::Hash[String, T.untyped])
47
+ end
48
+
49
+ instance = route.controller.new(params: params)
50
+ instance.public_send(route.action)
51
+ end
52
+
53
+ sig do
54
+ params(
55
+ status: Integer,
56
+ body: String,
57
+ headers: T::Hash[String, String],
58
+ ).returns(RackResponseType)
59
+ end
60
+ def render(status:, body:, headers: {})
61
+ # merge default headers
62
+ # support a "type" to set content-type header? (or default to json, and users must set the header themselves for other types?)
63
+ [
64
+ status,
65
+ headers,
66
+ [body],
67
+ ]
68
+ end
69
+ end
70
+ end
71
+
72
+ # rubocop:enable Metrics/AbcSize, Layout/LineLength
@@ -1,8 +1,10 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require_relative("app")
5
+
4
6
  module Kirei
5
- class AppBase < ::Sinatra::Base
7
+ class AppBase < Kirei::App
6
8
  class << self
7
9
  extend T::Sig
8
10
 
@@ -1,14 +1,16 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require_relative("app")
5
+
4
6
  module Kirei
5
- class BaseController < Sinatra::Base
7
+ class BaseController < Kirei::App
6
8
  extend T::Sig
7
- register(Sinatra::Namespace)
9
+ # register(Sinatra::Namespace)
8
10
 
9
- before do
10
- Thread.current[:request_id] = request.env["HTTP_X_REQUEST_ID"].presence ||
11
- "req_#{AppBase.environment}_#{SecureRandom.uuid}"
12
- end
11
+ # before do
12
+ # Thread.current[:request_id] = request.env["HTTP_X_REQUEST_ID"].presence ||
13
+ # "req_#{AppBase.environment}_#{SecureRandom.uuid}"
14
+ # end
13
15
  end
14
16
  end
@@ -60,6 +60,10 @@ module Kirei
60
60
  def where(hash)
61
61
  end
62
62
 
63
+ sig { abstract.returns(T.untyped) }
64
+ def all
65
+ end
66
+
63
67
  sig { abstract.params(hash: T.untyped).returns(T.untyped) }
64
68
  def create(hash)
65
69
  end
@@ -123,6 +127,11 @@ module Kirei
123
127
  resolve(db.where(hash))
124
128
  end
125
129
 
130
+ sig { override.returns(T::Array[T.attached_class]) }
131
+ def all
132
+ resolve(db.all)
133
+ end
134
+
126
135
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
127
136
  # default values defined in the model are used, if omitted in the hash
128
137
  sig do
@@ -182,7 +191,7 @@ module Kirei
182
191
  # "strict" defaults to "false".
183
192
  sig do
184
193
  override.params(
185
- query: Sequel::Dataset,
194
+ query: T.any(Sequel::Dataset, T::Array[T::Hash[Symbol, T.untyped]]),
186
195
  strict: T.nilable(T::Boolean),
187
196
  ).returns(T::Array[T.attached_class])
188
197
  end
data/lib/kirei/logger.rb CHANGED
@@ -30,7 +30,9 @@ module Kirei
30
30
  #
31
31
  # NOTE: The log transformer must return an array of strings to allow emitting multiple lines per log event.
32
32
  #
33
- class Logger < Kirei::Base
33
+ class Logger
34
+ extend T::Sig
35
+
34
36
  FILTERED = "[FILTERED]"
35
37
 
36
38
  @instance = T.let(nil, T.nilable(Kirei::Logger))
@@ -0,0 +1,32 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Kirei
5
+ module Middleware
6
+ # https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md#rack-3-upgrade-guide
7
+ RackResponseType = T.type_alias do
8
+ [
9
+ Integer,
10
+ T::Hash[String, String], # in theory, the values are allowed to be arrays of integers for binary representations
11
+ T.any(T::Array[String], Proc),
12
+ ]
13
+ end
14
+
15
+ RackEnvType = T.type_alias do
16
+ T::Hash[
17
+ String,
18
+ T.any(
19
+ T::Array[T.untyped],
20
+ IO,
21
+ T::Boolean,
22
+ String,
23
+ Numeric,
24
+ TCPSocket,
25
+ Puma::Client,
26
+ StringIO,
27
+ Puma::Configuration,
28
+ )
29
+ ]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require("singleton")
5
+
6
+ module Kirei
7
+ #
8
+ # Usage:
9
+ #
10
+ # Router.add_routes([
11
+ # Route.new(
12
+ # verb: "GET",
13
+ # path: "/livez",
14
+ # controller: Controllers::HealthController,
15
+ # action: "livez",
16
+ # ),
17
+ # ])
18
+ #
19
+ class Router
20
+ extend T::Sig
21
+ include ::Singleton
22
+
23
+ class Route < T::Struct
24
+ const :verb, String
25
+ const :path, String
26
+ const :controller, T.class_of(BaseController)
27
+ const :action, String
28
+ end
29
+
30
+ RoutesHash = T.type_alias do
31
+ T::Hash[String, Route]
32
+ end
33
+
34
+ sig { void }
35
+ def initialize
36
+ @routes = T.let({}, RoutesHash)
37
+ end
38
+
39
+ sig { returns(RoutesHash) }
40
+ attr_reader :routes
41
+
42
+ sig do
43
+ params(
44
+ verb: String,
45
+ path: String,
46
+ ).returns(T.nilable(Route))
47
+ end
48
+ def get(verb, path)
49
+ key = "#{verb} #{path}"
50
+ routes[key]
51
+ end
52
+
53
+ sig { params(routes: T::Array[Route]).void }
54
+ def self.add_routes(routes)
55
+ routes.each do |route|
56
+ key = "#{route.verb} #{route.path}"
57
+ instance.routes[key] = route
58
+ end
59
+ end
60
+ end
61
+ 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.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kirei
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ludwig Reinmiedl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-02 00:00:00.000000000 Z
11
+ date: 2024-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.0'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '13.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '13.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: sorbet-runtime
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -67,21 +53,21 @@ dependencies:
67
53
  - !ruby/object:Gem::Version
68
54
  version: '1.0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: sinatra
56
+ name: puma
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
59
  - - "~>"
74
60
  - !ruby/object:Gem::Version
75
- version: '3.0'
61
+ version: '6.0'
76
62
  type: :runtime
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
66
  - - "~>"
81
67
  - !ruby/object:Gem::Version
82
- version: '3.0'
68
+ version: '6.0'
83
69
  - !ruby/object:Gem::Dependency
84
- name: sinatra-contrib
70
+ name: rack
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - "~>"
@@ -159,16 +145,22 @@ files:
159
145
  - lib/cli/commands/new_app/base_directories.rb
160
146
  - lib/cli/commands/new_app/execute.rb
161
147
  - lib/cli/commands/new_app/files/app.rb
148
+ - lib/cli/commands/new_app/files/config_ru.rb
149
+ - lib/cli/commands/new_app/files/db_rake.rb
162
150
  - lib/cli/commands/new_app/files/irbrc.rb
151
+ - lib/cli/commands/new_app/files/rakefile.rb
152
+ - lib/cli/commands/new_app/files/routes.rb
163
153
  - lib/cli/commands/start.rb
164
154
  - lib/kirei.rb
155
+ - lib/kirei/app.rb
165
156
  - lib/kirei/app_base.rb
166
- - lib/kirei/base.rb
167
157
  - lib/kirei/base_controller.rb
168
158
  - lib/kirei/base_model.rb
169
159
  - lib/kirei/config.rb
170
160
  - lib/kirei/helpers.rb
171
161
  - lib/kirei/logger.rb
162
+ - lib/kirei/middleware.rb
163
+ - lib/kirei/router.rb
172
164
  - lib/kirei/version.rb
173
165
  - sorbet/rbi/shims/base_model.rbi
174
166
  homepage: https://github.com/swiknaba/kirei
@@ -192,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
184
  - !ruby/object:Gem::Version
193
185
  version: '0'
194
186
  requirements: []
195
- rubygems_version: 3.5.3
187
+ rubygems_version: 3.5.6
196
188
  signing_key:
197
189
  specification_version: 4
198
190
  summary: Kirei is a strictly typed Ruby micro/REST-framework for building scaleable
data/lib/kirei/base.rb DELETED
@@ -1,8 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module Kirei
5
- class Base
6
- extend T::Sig
7
- end
8
- end