philiprehberger-state_machine 0.7.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9866b1db838f27de8926074d6ab88834f6a2dcf79738257ae626f1deab522386
4
- data.tar.gz: fd8688dc4a75f72ce8095c933b5461c15fe8949a40d09f969ecb750bb9f2cf21
3
+ metadata.gz: 885a98621c6e7398bd5ae50b824fd6cc439c5b009fb26bbd648cf8fe46205127
4
+ data.tar.gz: 211673e70044114428a3624a5372da6cb5e419bde101b7fd93a98b2d9a7b8244
5
5
  SHA512:
6
- metadata.gz: 3bf30e72a74ac2709efd90e113bfbe1b034bdbebb82efe6b5f06002502906c7344f48163834d229c5525c8020064a7204d10f620f2e576973bbbcca533b01fc3
7
- data.tar.gz: ea1aeeb97241ce622d202522fe26cb17ffeac1b52d4a2bb5ef927a55ae2d6d19b1755c2f11bd6a1bd215d25b0c0b56ca339d7572cf6534ae820ab4feea50a7f5
6
+ metadata.gz: 8d7e14bbf7d1d91d8052fb0ccb75d1097e928137d88d9965366acd924602299a89f83b7feb0306899b39aaeefdcad5dc00b575d11042b6985ad9249d319a93b7
7
+ data.tar.gz: c8463b4fede47754b81bf508b82cfaf6a71f84d825b2aaf047fac2f46b8da69de0d199b990206674772797d6f2851e234d7e43e562b314dab2a6858bddd16155
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
  ## [Unreleased]
9
9
 
10
+ ## [0.8.0] - 2026-05-08
11
+
12
+ ### Added
13
+ - `#can_transition_to?(state, **payload)` — returns `true` when any currently-fireable event would land the machine in `state`. Honours guards (a guarded-out transition reports `false`) and raises `ArgumentError` for unknown states. Useful for UI gating without hand-rolling the per-event lookup.
14
+
10
15
  ## [0.7.0] - 2026-04-24
11
16
 
12
17
  ### Added
data/README.md CHANGED
@@ -114,11 +114,13 @@ Query the state machine at runtime:
114
114
  ```ruby
115
115
  order = Order.new
116
116
 
117
- order.pending? # => true
118
- order.paid? # => false
119
- order.can_pay? # => true
120
- order.can_ship? # => false
121
- order.allowed_transitions # => [:pay]
117
+ order.pending? # => true
118
+ order.paid? # => false
119
+ order.can_pay? # => true
120
+ order.can_ship? # => false
121
+ order.allowed_transitions # => [:pay]
122
+ order.can_transition_to?(:paid) # => true
123
+ order.can_transition_to?(:shipped) # => false (no event leads there from :pending)
122
124
  ```
123
125
 
124
126
  ### State History
@@ -339,6 +341,7 @@ order.stuck? # => false
339
341
  | `#current_state` | Returns the current state as a symbol |
340
342
  | `#can_X?(**payload)` | Returns true if event X can fire from the current state (including guards) |
341
343
  | `#allowed_transitions(**payload)` | Returns an array of event names that can fire from the current state |
344
+ | `#can_transition_to?(state, **payload)` | Returns true if any currently-fireable event would land the machine in `state` (honours guards; raises ArgumentError for unknown states) |
342
345
  | `#X!(**payload)` | Fire event X with optional payload, or raise `InvalidTransition` |
343
346
  | `#X(**payload)` | Fire event X with optional payload, returns true on success, false on failure |
344
347
  | `#X?` | Returns true if current state is X |
@@ -251,6 +251,36 @@ module Philiprehberger
251
251
  true
252
252
  end.keys
253
253
  end
254
+
255
+ # Whether any currently-fireable event would land the machine in `state`.
256
+ # Honours guards (a guarded-out transition reports false). Useful for UI
257
+ # gating without hand-rolling the per-event lookup.
258
+ #
259
+ # @param state [Symbol] the candidate target state
260
+ # @param payload [Hash] keyword payload forwarded to guards that accept it
261
+ # @return [Boolean]
262
+ # @raise [ArgumentError] if `state` is not declared on the machine
263
+ klass.define_method(:can_transition_to?) do |state, **payload|
264
+ unless definition.all_states.include?(state)
265
+ raise ArgumentError, "Unknown state: #{state.inspect}"
266
+ end
267
+
268
+ definition.events.any? do |_name, transitions|
269
+ transition = transitions.find { |t| t.matches?(current_state) && t.to == state }
270
+ next false unless transition
271
+
272
+ if transition.guard
273
+ guard_result = if transition.guard.arity.zero?
274
+ instance_exec(&transition.guard)
275
+ else
276
+ instance_exec(**payload, &transition.guard)
277
+ end
278
+ next false unless guard_result
279
+ end
280
+
281
+ true
282
+ end
283
+ end
254
284
  end
255
285
  end
256
286
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module StateMachine
5
- VERSION = '0.7.0'
5
+ VERSION = '0.8.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-state_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-24 00:00:00.000000000 Z
11
+ date: 2026-05-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A minimal state machine for Ruby objects. Define states, events, transitions,
14
14
  guard conditions, and callbacks with a clean DSL. Works with any Ruby class — no