• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

piotrmurach / finite_machine / #411

29 Aug 2023 09:37PM UTC coverage: 73.326% (-25.0%) from 98.297%
#411

push

piotrmurach
Add Ruby 3.1, 3.2, JRuby 9.3 and 9.4 to GitHub CI

646 of 881 relevant lines covered (73.33%)

256.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.71
/lib/finite_machine/state_parser.rb
1
# frozen_string_literal: true
2

3
module FiniteMachine
1✔
4
  # A class responsible for converting transition arguments to states
5
  #
6
  # Used by {TransitionBuilder} to parse user input state transitions.
7
  #
8
  # @api private
9
  class StateParser
1✔
10
    NON_STATE_KEYS = %i[name if unless silent].freeze
1✔
11

12
    STATE_KEYS = %i[from to].freeze
1✔
13

14
    # Extract states from user defined attributes
15
    #
16
    # @example
17
    #   StateParser.parse({from: [:green, :blue], to: :red})
18
    #   # => {green: :red, green: :blue}
19
    #
20
    # @param [Proc] block
21
    #
22
    # @yield [Hash[Symbol]] the resolved states
23
    #
24
    # @return [Hash[Symbol]] the resolved states
25
    #
26
    # @api public
27
    def self.parse(attributes, &block)
1✔
28
      attrs  = ensure_only_states!(attributes)
4✔
29
      states = extract_states(attrs)
4✔
30
      block ? states.each(&block) : states
4✔
31
    end
32

33
    # Extract only states from attributes
34
    #
35
    # @return [Hash[Symbol]]
36
    #
37
    # @api private
38
    def self.ensure_only_states!(attrs)
1✔
39
      attributes = attrs.dup
4✔
40
      NON_STATE_KEYS.each { |key| attributes.delete(key) }
20✔
41
      raise_not_enough_transitions unless attributes.any?
4✔
42
      attributes
4✔
43
    end
44
    private_class_method :ensure_only_states!
1✔
45

46
    # Perform extraction of states from user supplied definitions
47
    #
48
    # @return [Hash[Symbol]] the resolved states
49
    #
50
    # @api private
51
    def self.extract_states(attrs)
1✔
52
      if contains_from_to_keys?(attrs)
4✔
53
        convert_from_to_attributes_to_states_hash(attrs)
×
54
      else
55
        convert_attributes_to_states_hash(attrs)
4✔
56
      end
57
    end
58
    private_class_method :extract_states
1✔
59

60
    # Check if attributes contain :from or :to key
61
    #
62
    # @example
63
    #   StateParser.contains_from_to_keys?({from: :green, to: :red})
64
    #   # => true
65
    #
66
    # @example
67
    #   StateParser.contains_from_to_keys?({:green => :red})
68
    #   # => false
69
    #
70
    # @return [Boolean]
71
    #
72
    # @api public
73
    def self.contains_from_to_keys?(attrs)
1✔
74
      STATE_KEYS.any? { |key| attrs.keys.include?(key) }
12✔
75
    end
76
    private_class_method :contains_from_to_keys?
1✔
77

78
    # Convert attrbiutes with :from, :to keys to states hash
79
    #
80
    # @return [Hash[Symbol]]
81
    #
82
    # @api private
83
    def self.convert_from_to_attributes_to_states_hash(attrs)
1✔
84
      Array(attrs[:from] || ANY_STATE).each_with_object({}) do |state, hash|
×
85
        hash[state] = attrs[:to] || state
×
86
      end
87
    end
88
    private_class_method :convert_from_to_attributes_to_states_hash
1✔
89

90
    # Convert collapsed attributes to states hash
91
    #
92
    # @example
93
    #   StateParser.convert_attributes_to_states_hash([:green, :red] => :yellow)
94
    #   # => {green: :yellow, red: :yellow}
95
    #
96
    # @param [Hash] attrs
97
    #   the attributes to convert to a simple hash
98
    #
99
    # @return [Hash[Symbol]]
100
    #
101
    # @api private
102
    def self.convert_attributes_to_states_hash(attrs)
1✔
103
      attrs.each_with_object({}) do |(k, v), hash|
4✔
104
        if k.respond_to?(:to_ary)
8✔
105
          k.each { |el| hash[el] = v }
×
106
        else
107
          hash[k] = v
8✔
108
        end
109
      end
110
    end
111
    private_class_method :convert_attributes_to_states_hash
1✔
112

113
    # Raise error when not enough transitions are provided
114
    #
115
    # @raise [NotEnoughTransitionsError]
116
    #   if the event has no transitions
117
    #
118
    # @return [nil]
119
    #
120
    # @api private
121
    def self.raise_not_enough_transitions
1✔
122
      raise NotEnoughTransitionsError, "please provide state transitions"
×
123
    end
124
    private_class_method :raise_not_enough_transitions
1✔
125
  end # StateParser
126
end # FiniteMachine
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc