kirei 0.6.3 → 0.8.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 +4 -4
- data/README.md +84 -0
- data/kirei.gemspec +3 -2
- data/lib/cli/commands/new_app/files/app.rb +3 -1
- data/lib/cli/commands/new_app/files/db_rake.rb +35 -17
- data/lib/cli/commands/start.rb +4 -0
- data/lib/kirei/config.rb +1 -1
- data/lib/kirei/controller.rb +33 -0
- data/lib/kirei/domain/entity.rb +22 -0
- data/lib/kirei/domain/value_object.rb +41 -0
- data/lib/kirei/routing/base.rb +20 -9
- data/lib/kirei/routing/router.rb +3 -0
- data/lib/kirei/services/array_comparison.rb +35 -0
- data/lib/kirei/version.rb +1 -1
- data/sorbet/rbi/shims/domain.rbi +16 -0
- metadata +22 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f72e562a33b62988fd08b474ad821c50c35e7439a2fe6eb0e5909b7310099ddc
|
4
|
+
data.tar.gz: c1550bab74543280e4314b1847c8cc2ac80c575e2175efdb2c57bddeb0cd7f95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff42246d1757114996bf8094d80b47ebe2945e1df8551837389cad6c8dc7b96138ac398131d3b6ad07c7c0360b7758a77a06c3557716f22a07e6e4f99085a8bb
|
7
|
+
data.tar.gz: c7ff2ebedf28620e1ec16774b3ff07eb4007fdc76c59c925aa9f3161c452075123e8e4ae17e2786c1a3016f81df0e0c93528813fb879a950a9cec41952351619
|
data/README.md
CHANGED
@@ -55,6 +55,10 @@ Find a test app in the [spec/test_app](spec/test_app) directory. It is a fully f
|
|
55
55
|
|
56
56
|
All models must inherit from `T::Struct` and include `Kirei::Model`. 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)`.
|
57
57
|
|
58
|
+
Kirei models are immutable by convention - all properties are defined using `const` and updating a record returns a new instance rather than mutating the original. This immutability, combined with strict typing, makes them naturally suitable for both traditional data-centric applications and domain-driven design approaches.
|
59
|
+
|
60
|
+
In a domain-driven design, `Kirei::Model` serves as the persistence layer, while domain concepts are expressed through `Entity` and `ValueObject`. The domain layer might combine or transform data from multiple models to match the domain's understanding, keeping the internal data structure separate from the public interface.
|
61
|
+
|
58
62
|
```ruby
|
59
63
|
class User < T::Struct
|
60
64
|
extend T::Sig
|
@@ -99,6 +103,61 @@ first_user = User.resolve_first(query) # T.nilable(User)
|
|
99
103
|
first_user = User.from_hash(query.first.stringify_keys)
|
100
104
|
```
|
101
105
|
|
106
|
+
#### Domain Objects
|
107
|
+
|
108
|
+
Kirei provides support for Domain-Driven Design patterns through `Kirei::Domain::Entity` and `Kirei::Domain::ValueObject`. Here's how to use them:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# An Entity is identified by its ID
|
112
|
+
class Flight < T::Struct
|
113
|
+
include Kirei::Domain::Entity
|
114
|
+
|
115
|
+
const :id, Integer
|
116
|
+
|
117
|
+
const :flight_number, String
|
118
|
+
const :departure_airport_id, Integer
|
119
|
+
const :arrival_airport_id, Integer
|
120
|
+
const :scheduled_departure_at, Time
|
121
|
+
const :status, String
|
122
|
+
|
123
|
+
sig { returns(T::Boolean) }
|
124
|
+
def can_board?
|
125
|
+
Time.now.utc <= scheduled_departure_at && status == 'on_time'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# A Value Object is identified by its attributes
|
130
|
+
# I.e. two Coordinates with the same lat/long will be equal
|
131
|
+
# regardless of object identity
|
132
|
+
class Coordinates < T::Struct
|
133
|
+
include Kirei::Domain::ValueObject
|
134
|
+
|
135
|
+
const :latitude, Float
|
136
|
+
const :longitude, Float
|
137
|
+
|
138
|
+
sig { returns(String) }
|
139
|
+
def to_s
|
140
|
+
"#{latitude},#{longitude}"
|
141
|
+
end
|
142
|
+
|
143
|
+
sig { params(other_coords: Coordinates).returns(Float) }
|
144
|
+
def distance_to(other_coords)
|
145
|
+
# Implement e.g. Haversine here.
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Usage example
|
150
|
+
coords = Coordinates.new(latitude: 37.6188, longitude: -122.3750)
|
151
|
+
coords2 = Coordinates.new(latitude: 37.6188, longitude: -122.3750)
|
152
|
+
|
153
|
+
coords == coords2 # true
|
154
|
+
|
155
|
+
flight = Flight.new(id: 123, flight_number: 'UA123', status: 'on_time', scheduled_departure_at: Time.now.utc)
|
156
|
+
flight2 = Flight.new(id: 123, flight_number: 'UA123', status: 'delayed', scheduled_departure_at: Time.now.utc)
|
157
|
+
|
158
|
+
flight == flight2 # true - same entity even with different status
|
159
|
+
```
|
160
|
+
|
102
161
|
#### Database Migrations
|
103
162
|
|
104
163
|
Read the [Sequel Migrations](https://github.com/jeremyevans/sequel/blob/5.78.0/doc/schema_modification.rdoc) documentation for detailed information.
|
@@ -226,6 +285,31 @@ module Airports
|
|
226
285
|
end
|
227
286
|
```
|
228
287
|
|
288
|
+
### Goes well with these gems
|
289
|
+
|
290
|
+
* [pagy](https://github.com/ddnexus/pagy) for pagination
|
291
|
+
* [argon2](https://github.com/technion/ruby-argon2) for password hashing
|
292
|
+
* [rack-session](https://github.com/rack/rack-session) for session management
|
293
|
+
|
294
|
+
### Middlewares
|
295
|
+
|
296
|
+
If you place custom middlewares under `config/middleware/*.rb`, they will be automatically loaded, see [app.rb](spec/test_app/app.rb) "load configs".
|
297
|
+
|
298
|
+
However, you still need to `use` them in the `config.ru` file, see the generated [config.ru](spec/test_app/config.ru) file, this gives you full control over the order in which middlewares are executed:
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
# Load middlewares here
|
302
|
+
use(Rack::Reloader, 0) if TestApp.environment == "development"
|
303
|
+
|
304
|
+
# add more custom middlewares here, e.g.
|
305
|
+
use(Middleware::Example)
|
306
|
+
|
307
|
+
# Launch the app
|
308
|
+
run(TestApp.new)
|
309
|
+
```
|
310
|
+
|
311
|
+
Middleware provided by a gem like [rack-session](https://github.com/rack/rack-session) must be added to the `config.ru` file as well if you want to use it.
|
312
|
+
|
229
313
|
## Contributions
|
230
314
|
|
231
315
|
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
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.description = <<~TXT
|
18
18
|
Kirei is a Ruby micro/REST-framework for building scalable and performant microservices.
|
19
19
|
It is built from the ground up to be clean and easy to use.
|
20
|
-
It is a Rack app, and uses Sorbet for typing, Sequel as an ORM, Zeitwerk for autoloading
|
20
|
+
It is a Rack app, and uses Sorbet for typing, Sequel as an ORM, and Zeitwerk for autoloading.
|
21
21
|
It strives to have zero magic and to be as explicit as possible.
|
22
22
|
TXT
|
23
23
|
spec.homepage = "https://github.com/swiknaba/kirei"
|
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
"kirei.gemspec",
|
32
32
|
".irbrc",
|
33
33
|
"lib/**/*",
|
34
|
-
# do not include RBIs for gems, because users might use different
|
34
|
+
# do not include RBIs for gems, because users might use different versions
|
35
35
|
"sorbet/rbi/dsl/**/*.rbi",
|
36
36
|
"sorbet/rbi/shims/**/*.rbi",
|
37
37
|
"LICENSE",
|
@@ -44,6 +44,7 @@ Gem::Specification.new do |spec|
|
|
44
44
|
spec.require_paths = ["lib"]
|
45
45
|
|
46
46
|
# Utilities
|
47
|
+
spec.add_dependency "logger", "~> 1.5" # for Ruby 3.5+
|
47
48
|
spec.add_dependency "oj", "~> 3.0"
|
48
49
|
spec.add_dependency "sorbet-runtime", "~> 0.5"
|
49
50
|
spec.add_dependency "statsd-instrument", "~> 3.0"
|
@@ -43,7 +43,9 @@ module Cli
|
|
43
43
|
loader.setup
|
44
44
|
|
45
45
|
# Fifth: load configs
|
46
|
-
Dir[File.join(__dir__, "config", "*.rb")].each
|
46
|
+
Dir[File.join(__dir__, "config", "**", "*.rb")].each do |cnf|
|
47
|
+
require(cnf) unless cnf.split("/").include?("initializers")
|
48
|
+
end
|
47
49
|
|
48
50
|
class #{app_name} < Kirei::App
|
49
51
|
# Kirei configuration
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: true
|
2
2
|
|
3
3
|
# rubocop:disable Metrics/ClassLength
|
4
4
|
|
@@ -166,33 +166,51 @@ module Cli
|
|
166
166
|
db = #{app_name}.raw_db_connection
|
167
167
|
model_file_name = args[:model_file_name]&.to_s
|
168
168
|
|
169
|
-
|
169
|
+
app_root_dir = TestApp.root
|
170
|
+
app_dir = File.join(TestApp.root, "app")
|
170
171
|
|
171
|
-
Dir.glob("app
|
172
|
+
Dir.glob("app/**/*.rb").each do |model_file|
|
172
173
|
next if !model_file_name.nil? && model_file == model_file_name
|
173
174
|
|
174
|
-
model_path = File.expand_path(model_file,
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
175
|
+
model_path = File.expand_path(model_file, app_root_dir)
|
176
|
+
loader = Zeitwerk::Registry.loaders.find { |l| l.tag == "app" }
|
177
|
+
|
178
|
+
full_path = File.expand_path(model_file, app_root_dir)
|
179
|
+
klass_constant_name = loader.inflector.camelize(File.basename(model_file, ".rb"), full_path)
|
180
|
+
|
181
|
+
#
|
182
|
+
# root namespaces in Zeitwerk are flattend, e.g. if "app/models" is a root namespace
|
183
|
+
# then a file "app/models/airport.rb" is loaded as "::Airport".
|
184
|
+
# if it weren't a root namespace, it would be "::Models::Airport".
|
185
|
+
#
|
186
|
+
root_dir_namespaces = loader.dirs.filter_map { |dir| dir == app_dir ? nil : Pathname.new(dir).relative_path_from(Pathname.new(app_dir)).to_s }
|
187
|
+
relative_path = Pathname.new(full_path).relative_path_from(Pathname.new(app_dir)).to_s
|
188
|
+
root_dir_of_model = root_dir_namespaces.find { |root_dir| relative_path.start_with?(root_dir) }
|
189
|
+
relative_path.sub!("\#{root_dir_of_model}/", "") unless root_dir_of_model.nil? || root_dir_of_model.empty?
|
190
|
+
|
191
|
+
namespace_parts = relative_path.split("/")
|
192
|
+
namespace_parts.pop
|
193
|
+
namespace_parts.map! { |part| loader.inflector.camelize(part, full_path) }
|
194
|
+
|
195
|
+
constant_name = "\#{namespace_parts.join('::')}::\#{klass_constant_name}"
|
196
|
+
|
197
|
+
model_klass = Object.const_get(constant_name)
|
198
|
+
next unless model_klass.respond_to?(:table_name)
|
179
199
|
|
180
200
|
table_name = model_klass.table_name
|
181
201
|
schema = db.schema(table_name)
|
182
202
|
|
183
203
|
schema_comments = format_schema_comments(table_name, schema)
|
204
|
+
file_content = File.read(model_path)
|
184
205
|
|
185
|
-
|
186
|
-
|
187
|
-
# Remove existing schema info comments if present
|
188
|
-
updated_contents = file_contents.sub(/# == Schema Info\\n(.*?)(\\n#\\n)?\\n(?=\\s*(?:class|module))/m, "")
|
206
|
+
file_content_without_schema_info = file_content.sub(/# == Schema Info\\n(.*?)(\\n#\\n)?\\n(?=\\s*(?:class|module))/m, "")
|
189
207
|
|
190
208
|
# Insert the new schema comments before the module/class definition
|
191
|
-
|
192
|
-
first_module_or_class =
|
193
|
-
|
209
|
+
first_module = namespace_parts.first
|
210
|
+
first_module_or_class = first_module.nil? ? "class \#{klass_constant_name}" : "module \#{first_module}"
|
211
|
+
modified_content = file_content_without_schema_info.sub(/(A|\\n)(\#{first_module_or_class})/m, "\\\\1\#{schema_comments}\\n\\n\\\\2")
|
194
212
|
|
195
|
-
File.write(model_path,
|
213
|
+
File.write(model_path, modified_content)
|
196
214
|
end
|
197
215
|
end
|
198
216
|
end
|
@@ -215,7 +233,7 @@ module Cli
|
|
215
233
|
type ||= info[:db_type]
|
216
234
|
null = info[:allow_null] ? 'null' : 'not null'
|
217
235
|
primary_key = info[:primary_key] ? ', primary key' : ''
|
218
|
-
lines << "# \#{name.to_s.ljust(20)}:\#{type}
|
236
|
+
lines << "# \#{name.to_s.ljust(20)}:\#{type.to_s.ljust(20)}\#{null}\#{primary_key}"
|
219
237
|
end
|
220
238
|
lines.join("\\n") + "\\n#"
|
221
239
|
end
|
data/lib/cli/commands/start.rb
CHANGED
@@ -12,6 +12,10 @@ module Cli
|
|
12
12
|
app_name = app_name.gsub(/[-\s]/, "_")
|
13
13
|
app_name = app_name.split("_").map(&:capitalize).join if app_name.include?("_")
|
14
14
|
NewApp::Execute.call(app_name: app_name)
|
15
|
+
when "test"
|
16
|
+
# for internal testing
|
17
|
+
app_name = args[1] || "TestApp"
|
18
|
+
# test single services here
|
15
19
|
else
|
16
20
|
Kirei::Logging::Logger.logger.info("Unknown command")
|
17
21
|
end
|
data/lib/kirei/config.rb
CHANGED
@@ -30,7 +30,7 @@ module Kirei
|
|
30
30
|
# must use "pg_json" to parse jsonb columns to hashes
|
31
31
|
#
|
32
32
|
# Source: https://github.com/jeremyevans/sequel/blob/5.75.0/lib/sequel/extensions/pg_json.rb
|
33
|
-
prop :db_extensions, T::Array[Symbol], default: %i[pg_json pg_array]
|
33
|
+
prop :db_extensions, T::Array[Symbol], default: %i[pg_json pg_array] # add "fiber_concurrency" by default, too?
|
34
34
|
prop :db_url, T.nilable(String)
|
35
35
|
# Extra or unknown properties present in the Hash do not raise exceptions at runtime
|
36
36
|
# unless the optional strict argument to from_hash is passed
|
data/lib/kirei/controller.rb
CHANGED
@@ -40,5 +40,38 @@ module Kirei
|
|
40
40
|
@after_hooks ||= T.let(Set.new, Routing::NilableHooksType)
|
41
41
|
@after_hooks.add(block) if block
|
42
42
|
end
|
43
|
+
|
44
|
+
sig { returns(String) }
|
45
|
+
def req_host
|
46
|
+
env.fetch("HTTP_HOST")
|
47
|
+
end
|
48
|
+
|
49
|
+
sig { returns(String) }
|
50
|
+
def req_domain
|
51
|
+
T.must(req_host.split(":").first).split(".").last(2).join(".")
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { returns(T.nilable(String)) }
|
55
|
+
def req_subdomain
|
56
|
+
parts = T.must(req_host.split(":").first).split(".")
|
57
|
+
return if parts.size <= 2
|
58
|
+
|
59
|
+
T.must(parts[0..-3]).join(".")
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { returns(Integer) }
|
63
|
+
def req_port
|
64
|
+
env.fetch("SERVER_PORT")&.to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { returns(T::Boolean) }
|
68
|
+
def req_ssl?
|
69
|
+
env.fetch("HTTPS", env.fetch("rack.url_scheme", "http")) == "https"
|
70
|
+
end
|
71
|
+
|
72
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
73
|
+
private def env
|
74
|
+
T.cast(@router.current_env, T::Hash[String, T.untyped])
|
75
|
+
end
|
43
76
|
end
|
44
77
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Kirei
|
5
|
+
module Domain
|
6
|
+
module Entity
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
sig { returns(T.class_of(T::Struct)) }
|
11
|
+
def class; super; end # rubocop:disable all
|
12
|
+
|
13
|
+
sig { params(other: T.nilable(Kirei::Domain::Entity)).returns(T::Boolean) }
|
14
|
+
def ==(other)
|
15
|
+
return false unless other.is_a?(Kirei::Domain::Entity)
|
16
|
+
return false unless instance_of?(other.class)
|
17
|
+
|
18
|
+
id == other.id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Kirei
|
5
|
+
module Domain
|
6
|
+
module ValueObject
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
sig { returns(T.class_of(T::Struct)) }
|
11
|
+
def class; super; end # rubocop:disable all
|
12
|
+
|
13
|
+
sig { params(other: T.untyped).returns(T::Boolean) }
|
14
|
+
def ==(other)
|
15
|
+
return false unless instance_of?(other.class)
|
16
|
+
|
17
|
+
instance_variables.all? do |var|
|
18
|
+
instance_variable_get(var) == other.instance_variable_get(var)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
sig do
|
23
|
+
params(
|
24
|
+
other: T.untyped,
|
25
|
+
array_mode: Kirei::Services::ArrayComparison::Mode,
|
26
|
+
).returns(T::Boolean)
|
27
|
+
end
|
28
|
+
def equal_with_array_mode?(other, array_mode: Kirei::Services::ArrayComparison::Mode::STRICT)
|
29
|
+
return false unless instance_of?(other.class)
|
30
|
+
|
31
|
+
instance_variables.all? do |var|
|
32
|
+
one = instance_variable_get(var)
|
33
|
+
two = other.instance_variable_get(var)
|
34
|
+
next one == two unless one.is_a?(Array)
|
35
|
+
|
36
|
+
Kirei::Services::ArrayComparison.call(one, two, mode: array_mode)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/kirei/routing/base.rb
CHANGED
@@ -35,10 +35,13 @@ module Kirei
|
|
35
35
|
# -> use https://github.com/cyu/rack-cors ?
|
36
36
|
#
|
37
37
|
|
38
|
-
|
38
|
+
lookup_verb = http_verb == Verb::HEAD ? Verb::GET : http_verb
|
39
|
+
route = router.get(lookup_verb, req_path)
|
39
40
|
return NOT_FOUND if route.nil?
|
40
41
|
|
41
|
-
|
42
|
+
router.current_env = env # expose the env to the controller
|
43
|
+
|
44
|
+
params = case http_verb
|
42
45
|
when Verb::GET
|
43
46
|
query = T.cast(env.fetch("QUERY_STRING"), String)
|
44
47
|
query.split("&").to_h do |p|
|
@@ -48,14 +51,15 @@ module Kirei
|
|
48
51
|
end
|
49
52
|
when Verb::POST, Verb::PUT, Verb::PATCH
|
50
53
|
# TODO: based on content-type, parse the body differently
|
51
|
-
#
|
54
|
+
# built-in support for JSON & XML
|
52
55
|
body = T.cast(env.fetch("rack.input"), T.any(IO, StringIO))
|
53
56
|
res = Oj.load(body.read, Kirei::OJ_OPTIONS)
|
54
57
|
body.rewind # TODO: maybe don't rewind if we don't need to?
|
55
58
|
T.cast(res, T::Hash[String, T.untyped])
|
56
|
-
|
57
|
-
Logging::Logger.logger.warn("Unsupported HTTP verb: #{http_verb.serialize} send to #{req_path}")
|
59
|
+
when Verb::HEAD, Verb::DELETE, Verb::OPTIONS, Verb::TRACE, Verb::CONNECT
|
58
60
|
{}
|
61
|
+
else
|
62
|
+
T.absurd(http_verb)
|
59
63
|
end
|
60
64
|
|
61
65
|
req_id = T.cast(env["HTTP_X_REQUEST_ID"], T.nilable(String))
|
@@ -84,10 +88,17 @@ module Kirei
|
|
84
88
|
}
|
85
89
|
Logging::Metric.inject_defaults(statsd_timing_tags)
|
86
90
|
|
87
|
-
status, headers, response_body =
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
+
status, headers, response_body = case http_verb
|
92
|
+
when Verb::HEAD, Verb::OPTIONS, Verb::TRACE, Verb::CONNECT
|
93
|
+
[200, {}, []]
|
94
|
+
when Verb::GET, Verb::POST, Verb::PUT, Verb::PATCH, Verb::DELETE
|
95
|
+
T.cast(
|
96
|
+
controller.new(params: params).public_send(route.action),
|
97
|
+
RackResponseType,
|
98
|
+
)
|
99
|
+
else
|
100
|
+
T.absurd(http_verb)
|
101
|
+
end
|
91
102
|
|
92
103
|
after_hooks = collect_hooks(controller, :after_hooks)
|
93
104
|
run_hooks(after_hooks)
|
data/lib/kirei/routing/router.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Kirei
|
5
|
+
module Services
|
6
|
+
class ArrayComparison
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
class Mode < T::Enum
|
10
|
+
enums do
|
11
|
+
STRICT = new("strict")
|
12
|
+
IGNORE_ORDER = new("ignore_order")
|
13
|
+
IGNORE_ORDER_AND_DUPLICATES = new("ignore_order_and_duplicates")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
sig do
|
18
|
+
params(
|
19
|
+
array_one: T::Array[T.untyped],
|
20
|
+
array_two: T::Array[T.untyped],
|
21
|
+
mode: Mode,
|
22
|
+
).returns(T::Boolean)
|
23
|
+
end
|
24
|
+
def self.call(array_one, array_two, mode: Mode::STRICT)
|
25
|
+
case mode
|
26
|
+
when Mode::STRICT then array_one == array_two
|
27
|
+
when Mode::IGNORE_ORDER then array_one.sort == array_two.sort
|
28
|
+
when Mode::IGNORE_ORDER_AND_DUPLICATES then array_one.to_set == array_two.to_set
|
29
|
+
else
|
30
|
+
T.absurd(mode)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/kirei/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kirei
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ludwig Reinmiedl
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: logger
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.5'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '1.5'
|
13
26
|
- !ruby/object:Gem::Dependency
|
14
27
|
name: oj
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,7 +152,7 @@ dependencies:
|
|
139
152
|
description: |
|
140
153
|
Kirei is a Ruby micro/REST-framework for building scalable and performant microservices.
|
141
154
|
It is built from the ground up to be clean and easy to use.
|
142
|
-
It is a Rack app, and uses Sorbet for typing, Sequel as an ORM, Zeitwerk for autoloading
|
155
|
+
It is a Rack app, and uses Sorbet for typing, Sequel as an ORM, and Zeitwerk for autoloading.
|
143
156
|
It strives to have zero magic and to be as explicit as possible.
|
144
157
|
email:
|
145
158
|
- lud@reinmiedl.com
|
@@ -169,6 +182,8 @@ files:
|
|
169
182
|
- lib/kirei/app.rb
|
170
183
|
- lib/kirei/config.rb
|
171
184
|
- lib/kirei/controller.rb
|
185
|
+
- lib/kirei/domain/entity.rb
|
186
|
+
- lib/kirei/domain/value_object.rb
|
172
187
|
- lib/kirei/errors/json_api_error.rb
|
173
188
|
- lib/kirei/errors/json_api_error_source.rb
|
174
189
|
- lib/kirei/helpers.rb
|
@@ -186,11 +201,13 @@ files:
|
|
186
201
|
- lib/kirei/routing/route.rb
|
187
202
|
- lib/kirei/routing/router.rb
|
188
203
|
- lib/kirei/routing/verb.rb
|
204
|
+
- lib/kirei/services/array_comparison.rb
|
189
205
|
- lib/kirei/services/result.rb
|
190
206
|
- lib/kirei/services/runner.rb
|
191
207
|
- lib/kirei/version.rb
|
192
208
|
- lib/tasks/routes.rake
|
193
209
|
- sorbet/rbi/shims/base_model.rbi
|
210
|
+
- sorbet/rbi/shims/domain.rbi
|
194
211
|
- sorbet/rbi/shims/ruby.rbi
|
195
212
|
homepage: https://github.com/swiknaba/kirei
|
196
213
|
licenses:
|
@@ -198,7 +215,6 @@ licenses:
|
|
198
215
|
metadata:
|
199
216
|
rubygems_mfa_required: 'true'
|
200
217
|
homepage_uri: https://github.com/swiknaba/kirei
|
201
|
-
post_install_message:
|
202
218
|
rdoc_options: []
|
203
219
|
require_paths:
|
204
220
|
- lib
|
@@ -213,8 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
213
229
|
- !ruby/object:Gem::Version
|
214
230
|
version: '0'
|
215
231
|
requirements: []
|
216
|
-
rubygems_version: 3.
|
217
|
-
signing_key:
|
232
|
+
rubygems_version: 3.6.7
|
218
233
|
specification_version: 4
|
219
234
|
summary: Kirei is a typed Ruby micro/REST-framework for building scalable and performant
|
220
235
|
microservices.
|