servus 0.5.0 → 0.5.2

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: 2f382ed3d92dd577c93965ae207c00d28cac6dfddf452cf22fb5dafcd69d56eb
4
- data.tar.gz: f5a4394a33f5a3061d3c5746fdd058eb457cb29baa2f0adb9cc97c0679f5c158
3
+ metadata.gz: 15cec1ebe831437428e84374b7b7a83cc34c1f4cbcdc13bfe1c847eca5c1c9b3
4
+ data.tar.gz: 2b371d1069a549fab5af3de5aca80d8be029850cc0c1e3378e7643c742b917dd
5
5
  SHA512:
6
- metadata.gz: 5ccb1adc1b0bd4b7ff9f8b754571be541bbf7e3b9246746fe73c9e1259ad4b6f6e3a5a4d6c07d3b9f30cb8ddda94dfc2cad7c78fb6dc31fb17440c1d3a19b6be
7
- data.tar.gz: 0107e7e4d770913a5e86de5c496791e6311c913dc6b2bdad03417b78a2e66be6d50c2fc5d213f87b51162ac4a15d3c6c9dcb9b323267a9db9d0b222099f60245
6
+ metadata.gz: a66d2a07c3c564148e612fac96ab22e350c9483938226a495cc7b97b3b96ad979a4dba26b3bc2b5090bb3fc4c3c5016647f186f34cbe93bdc9f8f0f329205572
7
+ data.tar.gz: 56d23b1caac58e6d1c62062b80ecd85bb50784f034709fb5228d742e5de0c76e6a096bf59d45d67d84b0e024e2a0ba7f8f283db2027e543aa5543ad383eee2b3
@@ -33,12 +33,15 @@ module Servus
33
33
  # Declares an event that this service will emit.
34
34
  #
35
35
  # Events are automatically emitted when the service completes with the specified
36
- # trigger condition (:success, :failure, or :error). Use the `with` option to
37
- # provide a custom payload builder, or pass a block.
36
+ # trigger condition (:success, :failure, or :error). Use the `with` option or a
37
+ # block to provide a custom payload builder. Use `if` or `unless` to gate emission
38
+ # on a runtime condition.
38
39
  #
39
40
  # @param event_name [Symbol] the name of the event to emit
40
- # @param on [Symbol] when to emit (:success, :failure, or :error)
41
- # @param with [Symbol, nil] optional instance method name for building the payload
41
+ # @param on [Symbol] when to emit (:success, :failure, or :error!)
42
+ # @option options [Symbol, nil] :with instance method name for building the payload
43
+ # @option options [Proc, Symbol, nil] :if condition proc or method name; event only emits when truthy
44
+ # @option options [Proc, Symbol, nil] :unless condition proc or method name; event only emits when falsy
42
45
  # @yield [result] optional block for building the payload
43
46
  # @yieldparam result [Servus::Support::Response] the service result
44
47
  # @yieldreturn [Hash] the event payload
@@ -67,6 +70,22 @@ module Servus
67
70
  # end
68
71
  # end
69
72
  #
73
+ # @example Conditional emission with if: lambda
74
+ # class CreateUser < Servus::Base
75
+ # emits :premium_user_created, on: :success, if: ->(result) { result.data[:plan] == :premium }
76
+ # end
77
+ #
78
+ # @example Conditional emission with unless: method reference
79
+ # class CreateUser < Servus::Base
80
+ # emits :user_created, on: :success, unless: :suppressed?
81
+ #
82
+ # private
83
+ #
84
+ # def suppressed?(result)
85
+ # result.data[:suppressed]
86
+ # end
87
+ # end
88
+ #
70
89
  # @note Best Practice: Services should typically emit ONE event per trigger
71
90
  # that represents their core concern. Multiple downstream reactions should
72
91
  # be coordinated by Event classes, not by emitting multiple events
@@ -87,7 +106,7 @@ module Servus
87
106
  #
88
107
  # @see Servus::Events::Bus
89
108
  # @see Servus::Event
90
- def emits(event_name, on:, with: nil, &block)
109
+ def emits(event_name, on:, **options, &block)
91
110
  valid_triggers = %i[success failure error!]
92
111
 
93
112
  unless valid_triggers.include?(on)
@@ -95,10 +114,7 @@ module Servus
95
114
  end
96
115
 
97
116
  @event_emissions ||= { success: [], failure: [], error!: [] }
98
- @event_emissions[on] << {
99
- event_name: event_name,
100
- payload_builder: block || with
101
- }
117
+ @event_emissions[on] << build_emission(event_name, options, block)
102
118
  end
103
119
 
104
120
  # Returns all event emissions declared for this service.
@@ -116,6 +132,17 @@ module Servus
116
132
  def emissions_for(trigger)
117
133
  event_emissions[trigger] || []
118
134
  end
135
+
136
+ private
137
+
138
+ def build_emission(event_name, options, block)
139
+ {
140
+ event_name: event_name,
141
+ if_condition: options[:if],
142
+ unless_condition: options[:unless],
143
+ payload_builder: block || options[:with]
144
+ }
145
+ end
119
146
  end
120
147
 
121
148
  # Emits events for a specific trigger with the given result.
@@ -126,6 +153,8 @@ module Servus
126
153
  # @api private
127
154
  def emit_events_for(trigger, result)
128
155
  self.class.emissions_for(trigger).each do |emission|
156
+ next unless emission_condition_met?(emission, result)
157
+
129
158
  payload = build_event_payload(emission, result)
130
159
  validate_event_payload!(emission[:event_name], payload)
131
160
  Servus::Events::Bus.emit(emission[:event_name], payload)
@@ -133,7 +162,35 @@ module Servus
133
162
  end
134
163
 
135
164
  # Instance methods for emitting events during service execution
136
- private
165
+
166
+ # Returns true when all declared conditions on the emission pass.
167
+ #
168
+ # @param emission [Hash] the emission configuration
169
+ # @param result [Servus::Support::Response] the service result
170
+ # @return [Boolean]
171
+ # @api private
172
+ def emission_condition_met?(emission, result)
173
+ if_condition = emission[:if_condition]
174
+ unless_condition = emission[:unless_condition]
175
+
176
+ return false if if_condition && !evaluate_emission_condition(if_condition, result)
177
+ return false if unless_condition && evaluate_emission_condition(unless_condition, result)
178
+
179
+ true
180
+ end
181
+
182
+ # Evaluates a single emission condition — either a Proc/lambda or a Symbol method reference.
183
+ #
184
+ # Both forms receive the result object so conditions can inspect result.data,
185
+ # result.error, result.success?, etc.
186
+ #
187
+ # @param condition [Proc, Symbol] the condition to evaluate
188
+ # @param result [Servus::Support::Response] the service result
189
+ # @return [Object] truthy or falsy value
190
+ # @api private
191
+ def evaluate_emission_condition(condition, result)
192
+ condition.is_a?(Proc) ? instance_exec(result, &condition) : send(condition, result)
193
+ end
137
194
 
138
195
  # Validates the payload against the Event class's schema registered for the event.
139
196
  #
@@ -159,8 +216,7 @@ module Servus
159
216
  builder = emission[:payload_builder]
160
217
 
161
218
  if builder.is_a?(Proc)
162
- # Block-based payload builder
163
- builder.call(result)
219
+ instance_exec(result, &builder)
164
220
  elsif builder.is_a?(Symbol)
165
221
  # Method-based payload builder
166
222
  send(builder, result)
@@ -55,6 +55,11 @@ RSpec::Matchers.define :emit_event do |event_class_or_symbol|
55
55
  "got: #{@matching_event[:payload].inspect}"
56
56
  end
57
57
  end
58
+
59
+ failure_message_when_negated do
60
+ "expected event :#{@event_name} not to be emitted, but it was.\n" \
61
+ "Payload: #{@matching_event[:payload].inspect}"
62
+ end
58
63
  end
59
64
 
60
65
  # Matcher for asserting service invocation
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Servus
4
- VERSION = '0.5.0'
4
+ VERSION = '0.5.2'
5
5
  end
data/lib/servus.rb CHANGED
@@ -4,8 +4,7 @@
4
4
  require 'json-schema'
5
5
  require 'active_support'
6
6
  require 'active_support/core_ext/class/attribute'
7
- require 'active_model_serializers'
8
-
7
+ require 'active_support/core_ext/hash/indifferent_access'
9
8
  # Servus namespace
10
9
  module Servus; end
11
10
 
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Scholl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-07 00:00:00.000000000 Z
11
+ date: 2026-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: active_model_serializers
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 0.10.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 0.10.0
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: activesupport
29
15
  requirement: !ruby/object:Gem::Requirement