u-case 5.3.1 → 5.4.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: 936bb237d3decf217151d48e7f32339ac698d65bde22930a05e4b90e92d760ea
4
- data.tar.gz: e9ecc10a25c778a1393f803bd32ad6ebf7481c1a9144a4d0ac0fce843aed53d2
3
+ metadata.gz: 0dd8852d56fd022780ae18b61d849649c84080c4fe2a681aca031d3b0c43b139
4
+ data.tar.gz: 8d9196aee650c25efc8bedc701cbcc1f190e14ea4c8ca97ea6f2f22b3c09259c
5
5
  SHA512:
6
- metadata.gz: 5a17f2832229d00b96976ebd9d75053b2a0790625ebc8323e2d2def3d7f90d8aa78a255c834a53e7dcf88b578c9c7ba294a1097b317d0a0f74a579f1c29986a4
7
- data.tar.gz: 5a357a9a1cbcd6487c62259f85316304348171de648d1bdf35193b92a2952bfc9a51d98d4e16f0572eb7d4e64e12d1c511e161235b3a092176712abcc5e87157
6
+ metadata.gz: a7b9beee649d540efdbae0553139f09fc4a9053390db61c98c5fd0a3a81569ea7ab60a6db0218f7842f38eabd90f2f0bdec86c717ee473f5692143a1ae44746d
7
+ data.tar.gz: 1ade9a482e232dfdd5a1976aeb2e47f0b2a5f129bdb965911f7cfda233b2f99f0e34fae97a3402770700bb367a32ded882b1a4b7727c5aae2d7ad0680d863c32
data/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  > **Note:** This gem was originally published as `u-service` (versions 0.1.0 – 1.0.0) and renamed to `u-case` starting with `u-case 1.0.0` on 2019-09-15.
9
9
 
10
+ ## [5.4.0] - 2026-05-24
11
+ ### Added
12
+ - `Micro::Case.config.disable_runtime_checks` config (default `false`) to skip the gem's internal argument/contract checks for better performance in production. All checks are consolidated in `Micro::Case::Check::Enabled` (the default) and `Micro::Case::Check::Disabled` (no-ops with the same signature); the active module is swapped via `Micro::Case.check`. Measured throughput win is JIT-dependent: within noise on stock Ruby (no JIT), ~3–5% on Ruby 3.2 +YJIT, ~4–7% on Ruby 4.0 +PRISM (see `benchmarks/perfomance/runtime_checks/compare.rb`). Closes #45.
13
+ - `benchmarks/perfomance/runtime_checks/` — per-mode subprocess benchmark (`checks_enabled.rb`, `checks_disabled.rb`, `compare.rb`) demonstrating the toggle's perf effect across Ruby versions and JIT modes.
14
+
10
15
  ## [5.3.1] - 2026-05-23
11
16
  ### Added
12
17
  - This `CHANGELOG.md`, covering the full history of the gem (from `u-service 0.1.0` through `u-case 5.3.1`) following the [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) spec.
@@ -463,6 +468,7 @@ First release under the `u-case` name (renamed from `u-service`).
463
468
  - `Micro::Service::Result` with `Success`/`Failure` factories and helper methods for returning typed results from services.
464
469
  - Runtime dependency on `u-attributes` for service input declaration.
465
470
 
471
+ [5.4.0]: https://github.com/serradura/u-case/compare/v5.3.1...v5.4.0
466
472
  [5.3.1]: https://github.com/serradura/u-case/compare/v5.3.0...v5.3.1
467
473
  [5.3.0]: https://github.com/serradura/u-case/compare/v5.2.1...v5.3.0
468
474
  [5.2.1]: https://github.com/serradura/u-case/compare/v5.2.0...v5.2.1
data/CLAUDE.md CHANGED
@@ -93,6 +93,44 @@ Both files are user-facing — keep them in sync with the code:
93
93
  translations of each other and must stay in lockstep. If you change a
94
94
  documented API, update both READMEs in the same commit.
95
95
 
96
+ ## Internal argument checks live in `Micro::Case::Check`
97
+
98
+ Every internal argument/contract check that runs inside the gem (type
99
+ guards, "is this a `Micro::Case`?", "is this a `Symbol`?", "are these
100
+ flow args valid?", etc.) lives in `lib/micro/case/check.rb`, split across
101
+ two modules with **identical method signatures**:
102
+
103
+ - `Micro::Case::Check::Enabled` — the default; raises the curated
104
+ `Micro::Case::Error::*` exceptions.
105
+ - `Micro::Case::Check::Disabled` — no-ops (the matching method just
106
+ `return`s; passthrough methods return their input unchanged).
107
+
108
+ The active one is referenced as `Micro::Case.check`, swapped by
109
+ `config.disable_runtime_checks = true/false` (see PR #145 / issue #45).
110
+
111
+ ### When you add a new internal check, you must:
112
+
113
+ 1. **Add the method to BOTH modules.** Keep the signature identical.
114
+ The `Enabled` side does the real work; the `Disabled` side is a
115
+ no-op (or passthrough for `hash!`-style coercions).
116
+ 2. **Route the call site through `Micro::Case.check.<method>!(...)`.**
117
+ Don't `raise ... unless ...` inline — that bypasses the toggle and
118
+ leaks the check into the disabled-path performance budget.
119
+ 3. **Cover both modes in a test.** Mirror the pattern in
120
+ `test/micro/case/disable_runtime_checks_test.rb`: one test that the
121
+ `Enabled` side raises, one that the `Disabled` side does not.
122
+ 4. **Avoid extra allocation on the call site.** If the curated
123
+ exception needs dynamic params (a class name, a context string),
124
+ pass the raw strings/values into the check method and construct the
125
+ exception inside `Enabled` (only on the raise path). Don't build the
126
+ exception before calling — that defeats the perf rationale of the
127
+ `Disabled` side.
128
+
129
+ This is the only place where new gem-internal checks belong. Inline
130
+ `raise … unless …` inside the runtime call path is a regression of
131
+ this design — flag it during review and move the check into
132
+ `Micro::Case::Check`.
133
+
96
134
  ## Bumping the version
97
135
 
98
136
  1. Edit `lib/micro/case/version.rb` — change `Micro::Case::VERSION`. Follow
data/README.md CHANGED
@@ -27,7 +27,7 @@ The main project goals are:
27
27
  Version | Documentation
28
28
  --------- | -------------
29
29
  unreleased| https://github.com/serradura/u-case/blob/main/README.md
30
- 5.3.1 | https://github.com/serradura/u-case/blob/v5.x/README.md
30
+ 5.4.0 | https://github.com/serradura/u-case/blob/v5.x/README.md
31
31
  4.5.1 | https://github.com/serradura/u-case/blob/v4.x/README.md
32
32
 
33
33
  > **Note:** Você entende português? 🇧🇷&nbsp;🇵🇹 Verifique o [README traduzido em pt-BR](https://github.com/serradura/u-case/blob/main/README.pt-BR.md).
@@ -90,7 +90,7 @@ unreleased| https://github.com/serradura/u-case/blob/main/README.md
90
90
  | u-case | branch | ruby | activemodel | u-attributes |
91
91
  | ---------------- | ------ | -------- | -------------- | -------------- |
92
92
  | unreleased | main | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
93
- | 5.3.1 | v5.x | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
93
+ | 5.4.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
94
94
  | 5.1.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.7, < 4.0 |
95
95
  | 4.5.1 | v4.x | >= 2.2.0 | >= 3.2, <= 8.1 | >= 2.7, < 3.0 |
96
96
 
@@ -1256,9 +1256,22 @@ Micro::Case.config do |config|
1256
1256
  # - Calling `Micro::Cases.safe_flow(...)`
1257
1257
  # - Calling `Micro::Case::Result#on_exception`
1258
1258
  config.disable_safe_features = false
1259
+
1260
+ # Use to skip the gem's internal argument/contract checks (e.g., "is this a
1261
+ # Micro::Case?", "is the result type a Symbol?", "is the use case a kind of
1262
+ # Micro::Case?"). Set to `true` in production for a small performance boost
1263
+ # once your code paths are exercised by your test suite. The trade-off is that
1264
+ # incorrect usage will surface as confusing downstream errors instead of the
1265
+ # gem's curated ones (e.g. `Micro::Case::Error::InvalidUseCase`).
1266
+ config.disable_runtime_checks = false
1259
1267
  end
1260
1268
  ```
1261
1269
 
1270
+ All checks are consolidated in `Micro::Case::Check::Enabled` (the default).
1271
+ Toggling `disable_runtime_checks = true` swaps `Micro::Case.check` to
1272
+ `Micro::Case::Check::Disabled` — a module with the same signature whose
1273
+ methods are no-ops — so the validations themselves are not run on each call.
1274
+
1262
1275
  [⬆️ Back to Top](#table-of-contents-)
1263
1276
 
1264
1277
  ## Benchmarks
data/README.pt-BR.md CHANGED
@@ -27,7 +27,7 @@ Principais objetivos deste projeto:
27
27
  Versão | Documentação
28
28
  --------- | -------------
29
29
  unreleased| https://github.com/serradura/u-case/blob/main/README.md
30
- 5.3.1 | https://github.com/serradura/u-case/blob/v5.x/README.md
30
+ 5.4.0 | https://github.com/serradura/u-case/blob/v5.x/README.md
31
31
  4.5.1 | https://github.com/serradura/u-case/blob/v4.x/README.md
32
32
 
33
33
  ## Índice <!-- omit in toc -->
@@ -88,7 +88,7 @@ unreleased| https://github.com/serradura/u-case/blob/main/README.md
88
88
  | u-case | branch | ruby | activemodel | u-attributes |
89
89
  | ---------------- | ------ | -------- | -------------- | -------------- |
90
90
  | unreleased | main | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
91
- | 5.3.1 | v5.x | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
91
+ | 5.4.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.8, < 4.0 |
92
92
  | 5.1.0 | v5.x | >= 2.7 | >= 6.0 | >= 2.7, < 4.0 |
93
93
  | 4.5.1 | v4.x | >= 2.2.0 | >= 3.2, <= 8.1 | >= 2.7, < 3.0 |
94
94
 
@@ -1257,9 +1257,24 @@ Micro::Case.config do |config|
1257
1257
  # - Chamar `Micro::Cases.safe_flow(...)`
1258
1258
  # - Chamar `Micro::Case::Result#on_exception`
1259
1259
  config.disable_safe_features = false
1260
+
1261
+ # Use para pular as verificações internas de argumento/contrato da gem (por
1262
+ # exemplo, "isto é um Micro::Case?", "o tipo do resultado é um Symbol?",
1263
+ # "o use case é um tipo de Micro::Case?"). Defina `true` em produção para
1264
+ # um pequeno ganho de performance depois que seus caminhos de código já
1265
+ # estiverem cobertos pela sua suíte de testes. O custo é que usos
1266
+ # incorretos vão aparecer como erros confusos mais à frente, em vez dos
1267
+ # erros curados pela gem (ex.: `Micro::Case::Error::InvalidUseCase`).
1268
+ config.disable_runtime_checks = false
1260
1269
  end
1261
1270
  ```
1262
1271
 
1272
+ Todas as verificações estão consolidadas em `Micro::Case::Check::Enabled` (o
1273
+ padrão). Definir `disable_runtime_checks = true` troca `Micro::Case.check` por
1274
+ `Micro::Case::Check::Disabled` — um módulo com a mesma assinatura cujos
1275
+ métodos não fazem nada — de forma que as validações não são executadas a
1276
+ cada chamada.
1277
+
1263
1278
  [⬆️ Voltar para o índice](#índice-)
1264
1279
 
1265
1280
  ## Benchmarks
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro
4
+ class Case
5
+ module Check
6
+ module Enabled
7
+ extend self
8
+
9
+ def use_case_or_flow!(arg)
10
+ raise Error::InvalidUseCase unless ::Micro.case_or_flow?(arg)
11
+ end
12
+
13
+ def micro_case_instance!(arg)
14
+ raise Error::InvalidUseCase unless arg.is_a?(::Micro::Case)
15
+ end
16
+
17
+ def result_instance!(arg)
18
+ raise Error::InvalidResultInstance unless arg.is_a?(::Micro::Case::Result)
19
+ end
20
+
21
+ def result_not_defined!(is_defined)
22
+ raise Error::ResultIsAlreadyDefined if is_defined
23
+ end
24
+
25
+ def result_type!(type)
26
+ raise Error::InvalidResultType unless type.is_a?(Symbol)
27
+ end
28
+
29
+ def result_data!(data, is_success, type, use_case)
30
+ raise Error::InvalidResult.new(is_success, type, use_case) unless data
31
+ end
32
+
33
+ def expected_result!(result, context)
34
+ return if result.is_a?(::Micro::Case::Result)
35
+
36
+ raise Error::UnexpectedResult.new(context)
37
+ end
38
+
39
+ def expected_self_result!(actual, expected, context)
40
+ return if actual.equal?(expected)
41
+
42
+ raise Error::UnexpectedResult.new(context)
43
+ end
44
+
45
+ def then_use_case_or_flow!(arg, owner_label)
46
+ return if ::Micro.case_or_flow?(arg)
47
+
48
+ raise Error::InvalidInvocationOfTheThenMethod.new(owner_label)
49
+ end
50
+
51
+ def flow_use_cases!(use_cases)
52
+ raise Cases::Error::InvalidUseCases if use_cases.none?(&::Micro::Cases::Flow::IsAValidUseCase)
53
+ end
54
+
55
+ def map_args!(args)
56
+ raise Cases::Error::InvalidUseCases unless ::Micro::Cases::Map.const_get(:HasValidArgs, false)[args]
57
+ end
58
+
59
+ def hash!(arg)
60
+ Kind::Hash[arg]
61
+ end
62
+ end
63
+
64
+ module Disabled
65
+ extend self
66
+
67
+ def use_case_or_flow!(_arg); end
68
+ def micro_case_instance!(_arg); end
69
+ def result_instance!(_arg); end
70
+ def result_not_defined!(_is_defined); end
71
+ def result_type!(_type); end
72
+ def result_data!(_data, _is_success, _type, _use_case); end
73
+ def expected_result!(_result, _context); end
74
+ def expected_self_result!(_actual, _expected, _context); end
75
+ def then_use_case_or_flow!(_arg, _owner_label); end
76
+ def flow_use_cases!(_use_cases); end
77
+ def map_args!(_args); end
78
+ def hash!(arg); arg; end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -23,6 +23,18 @@ module Micro
23
23
  @disable_safe_features = false
24
24
  end
25
25
 
26
+ def disable_runtime_checks=(value)
27
+ @disable_runtime_checks = Kind::Boolean[value]
28
+
29
+ ::Micro::Case.check = @disable_runtime_checks ? ::Micro::Case::Check::Disabled : ::Micro::Case::Check::Enabled
30
+ end
31
+
32
+ def disable_runtime_checks
33
+ return @disable_runtime_checks if defined?(@disable_runtime_checks)
34
+
35
+ @disable_runtime_checks = false
36
+ end
37
+
26
38
  def enable_activemodel_validation=(value)
27
39
  return unless Kind::Boolean[value]
28
40
 
@@ -174,14 +174,14 @@ module Micro
174
174
  end
175
175
 
176
176
  def __set__(is_success, data, type, use_case)
177
- raise Error::InvalidResultType unless type.is_a?(Symbol)
178
- raise Error::InvalidUseCase unless use_case.is_a?(::Micro::Case)
177
+ ::Micro::Case.check.result_type!(type)
178
+ ::Micro::Case.check.micro_case_instance!(use_case)
179
179
 
180
180
  @__success, @type, @use_case = is_success, type, use_case
181
181
 
182
182
  @data = FetchData.call(data).freeze
183
183
 
184
- raise Micro::Case::Error::InvalidResult.new(is_success, type, use_case) unless @data
184
+ ::Micro::Case.check.result_data!(@data, is_success, type, use_case)
185
185
 
186
186
  @__accumulated_data.merge!(@data)
187
187
 
@@ -227,7 +227,7 @@ module Micro
227
227
  use_case_method = self.use_case.method(use_case)
228
228
  __call_method(use_case_method, attributes)
229
229
  else
230
- raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(use_case)
230
+ ::Micro::Case.check.then_use_case_or_flow!(use_case, 'Micro::Case::Result#')
231
231
 
232
232
  input = attributes.is_a?(Hash) ? self.data.merge(attributes) : self.data
233
233
 
@@ -244,9 +244,9 @@ module Micro
244
244
 
245
245
  result = fn.arity.zero? ? fn.call : fn.call(__fetch_accessible_attributes)
246
246
 
247
- return self if result === self
247
+ ::Micro::Case.check.expected_self_result!(result, self, "#{Result.name}##{expected}")
248
248
 
249
- raise Error::UnexpectedResult.new("#{Result.name}##{expected}")
249
+ self
250
250
  end
251
251
 
252
252
  def __call_method(methd, attributes = nil)
@@ -254,9 +254,9 @@ module Micro
254
254
 
255
255
  result = methd.arity.zero? ? methd.call : methd.call(**__fetch_accessible_attributes)
256
256
 
257
- return self if result === self
257
+ ::Micro::Case.check.expected_self_result!(result, self, "#{use_case.class.name}#method(:#{methd.name})")
258
258
 
259
- raise Error::UnexpectedResult.new("#{use_case.class.name}#method(:#{methd.name})")
259
+ self
260
260
  end
261
261
 
262
262
  def __success_type?(expected_type)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  class Case
5
- VERSION = '5.3.1'.freeze
5
+ VERSION = '5.4.0'.freeze
6
6
  end
7
7
  end
data/lib/micro/case.rb CHANGED
@@ -11,12 +11,18 @@ module Micro
11
11
  require 'micro/case/utils'
12
12
  require 'micro/case/error'
13
13
  require 'micro/case/result'
14
+ require 'micro/case/check'
14
15
  require 'micro/case/config'
15
16
  require 'micro/case/safe'
16
17
  require 'micro/case/strict'
17
18
 
18
19
  require 'micro/cases'
19
20
 
21
+ class << self
22
+ attr_accessor :check
23
+ end
24
+ self.check = Check::Enabled
25
+
20
26
  include Micro::Attributes
21
27
  include Micro::Attributes::Features::Accept
22
28
 
@@ -46,7 +52,7 @@ module Micro
46
52
  else
47
53
  return yield_self if !use_case && can_yield_self
48
54
 
49
- raise INVALID_INVOCATION_OF_THE_THEN_METHOD unless ::Micro.case_or_flow?(use_case)
55
+ ::Micro::Case.check.then_use_case_or_flow!(use_case, 'Micro::Case.')
50
56
 
51
57
  self.call.then(use_case)
52
58
  end
@@ -169,8 +175,8 @@ module Micro
169
175
  end
170
176
 
171
177
  def __set_result__(result)
172
- raise Error::InvalidResultInstance unless result.is_a?(Result)
173
- raise Error::ResultIsAlreadyDefined if defined?(@__result)
178
+ ::Micro::Case.check.result_instance!(result)
179
+ ::Micro::Case.check.result_not_defined!(defined?(@__result))
174
180
 
175
181
  @__result = result
176
182
 
@@ -180,7 +186,7 @@ module Micro
180
186
  private
181
187
 
182
188
  def call(use_case, defaults = Kind::Empty::HASH)
183
- raise Error::InvalidUseCase unless ::Micro.case_or_flow?(use_case)
189
+ ::Micro::Case.check.use_case_or_flow!(use_case)
184
190
 
185
191
  input =
186
192
  defaults.empty? ? attributes : attributes.merge(Utils::Hashes.stringify_keys(defaults))
@@ -211,9 +217,9 @@ module Micro
211
217
 
212
218
  result = call!
213
219
 
214
- return result if result.is_a?(Result)
220
+ ::Micro::Case.check.expected_result!(result, "#{self.class.name}#call!")
215
221
 
216
- raise Error::UnexpectedResult.new("#{self.class.name}#call!")
222
+ result
217
223
  end
218
224
 
219
225
  def __attributes_errors_present?
@@ -11,7 +11,7 @@ module Micro
11
11
  def self.build(args)
12
12
  use_cases = Utils.map_use_cases(args)
13
13
 
14
- raise Error::InvalidUseCases if use_cases.none?(&IsAValidUseCase)
14
+ ::Micro::Case.check.flow_use_cases!(use_cases)
15
15
 
16
16
  new(use_cases)
17
17
  end
@@ -63,7 +63,7 @@ module Micro
63
63
  else
64
64
  return yield_self if !use_case && can_yield_self
65
65
 
66
- raise_invalid_invocation_of_the_then_method unless ::Micro.case_or_flow?(use_case)
66
+ ::Micro::Case.check.then_use_case_or_flow!(use_case, "#{self.class.name}#")
67
67
 
68
68
  self.call.then(use_case)
69
69
  end
@@ -10,7 +10,7 @@ module Micro
10
10
  attr_reader :use_cases
11
11
 
12
12
  def self.build(args)
13
- raise Error::InvalidUseCases unless HasValidArgs[args]
13
+ ::Micro::Case.check.map_args!(args)
14
14
 
15
15
  new(args)
16
16
  end
@@ -30,7 +30,7 @@ module Micro
30
30
  end
31
31
 
32
32
  def call(arg = {})
33
- hash = Kind::Hash[arg]
33
+ hash = ::Micro::Case.check.hash!(arg)
34
34
 
35
35
  use_cases.map(&GetUseCaseResult[hash])
36
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-case
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.1
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
@@ -118,6 +118,7 @@ files:
118
118
  - gemfiles/rails_8_1.gemfile
119
119
  - gemfiles/rails_edge.gemfile
120
120
  - lib/micro/case.rb
121
+ - lib/micro/case/check.rb
121
122
  - lib/micro/case/config.rb
122
123
  - lib/micro/case/error.rb
123
124
  - lib/micro/case/result.rb