state_gate 1.2.3 → 1.3.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.
@@ -12,61 +12,61 @@ module StateGate
12
12
  #
13
13
  # Options include:
14
14
  #
15
- # === | state
16
- # Required name for the new state, supplied as a Symbol. The +state-gate+ requires
17
- # a minimum of two states to be defined.
15
+ # [state]
16
+ # Required name for the new state, supplied as a Symbol. The +state-gate+ requires
17
+ # a minimum of two states to be defined.
18
18
  # state :state_name
19
19
  #
20
20
  #
21
- # [:transitions_to]
21
+ # **_:transitions_to_**
22
22
  # An optional list of the other state that this state is allowed to change to.
23
23
  # state :state_1, transtions_to: [:state_2, :state_3, :state_4]
24
24
  # state :state_2, transtions_to: :state_4
25
25
  # state :state_3, transtions_to: :any
26
26
  # state :state_4
27
27
  #
28
- # [:human]
28
+ # **_:human_**
29
29
  # An optional String name to used when displaying gthe state in a view. If no
30
30
  # name is specified, it will default to +:state.titleized+.
31
31
  # state :state_1, transtions_to: [:state_2, :state_3], human: "My State"
32
32
  #
33
33
  #
34
- # === | default
35
- # Optional setting to specify the default state for a new object. The state name
36
- # is given as a Symbol.
34
+ # [default]
35
+ # Optional setting to specify the default state for a new object. The state name
36
+ # is given as a Symbol.
37
37
  # default :state_name
38
38
  #
39
39
  #
40
- # === | prefix
41
- # Optional setting to add a given Symbol before each state name when using Class Scopes.
42
- # This helps to differential between multiple attributes that have similar state names.
43
- # prefix :before # => Class.before_active
40
+ # [prefix]
41
+ # Optional setting to add a given Symbol before each state name when using Class Scopes.
42
+ # This helps to differential between multiple attributes that have similar state names.
43
+ # prefix :before #=> Class.before_active
44
44
  #
45
45
  #
46
- # === | suffix
47
- # Optional setting to add a given Symbol after each state name when using Class Scopes.
48
- # This helps to differential between multiple attributes that have similar state names.
49
- # suffix :after # => Class.active_after
46
+ # [suffix]
47
+ # Optional setting to add a given Symbol after each state name when using Class Scopes.
48
+ # This helps to differential between multiple attributes that have similar state names.
49
+ # suffix :after #=> Class.active_after
50
50
  #
51
51
  #
52
- # === | make_sequential
53
- # Optional setting to automatically add transitions from each state to both the
54
- # preceeding and following states.
52
+ # [make_sequential]
53
+ # Optional setting to automatically add transitions from each state to both the
54
+ # preceeding and following states.
55
55
  # make_sequential
56
56
  #
57
- # [:one_way]
58
- # Option to restrict the generated transitions to one directtion only: from each
59
- # state to the follow state.
60
- # make_sequential :one_way
57
+ # **_:one_way_**
58
+ # Option to restrict the generated transitions to one directtion only: from each
59
+ # state to the follow state.
60
+ # make_sequential :one_way
61
61
  #
62
- # [:loop]
63
- # Option to add transitions from the last state to the first and, unless +:one_way+
64
- # is specified, also from the first state to the last.
65
- # make_sequential :one_way, :loop
62
+ # **_:loop_**
63
+ # Option to add transitions from the last state to the first and, unless +:one_way+
64
+ # is specified, also from the first state to the last.
65
+ # make_sequential :one_way, :loop
66
66
  #
67
67
  #
68
- # === | no_scopes
69
- # Optional setting to disable the generation of Class Scope helpers methods.
68
+ # [no_scopes]
69
+ # Optional setting to disable the generation of Class Scope helpers methods.
70
70
  # no_scopes
71
71
  #
72
72
  module Configurator
@@ -81,53 +81,61 @@ module StateGate
81
81
  # ======================================================================
82
82
 
83
83
 
84
+ ##
84
85
  # Execute the provided configuration.
85
86
  #
87
+ # @block config
88
+ # the given configuration
89
+ #
86
90
  # ==== actions
87
91
  #
88
- # + create sequence links and transitions
89
- # + create scope names
90
- # + remove duplicate transitions for each state
92
+ # - create sequence links and transitions
93
+ # - create scope names
94
+ # - remove duplicate transitions for each state
91
95
  #
92
- # + verify there are multiple valid state names
93
- # + verify all transitions lead to existing states
94
- # + verify each state, except the default, can be reached from a transition
96
+ # - verify there are multiple valid state names
97
+ # - verify all transitions lead to existing states
98
+ # - verify each state, except the default, can be reached from a transition
95
99
  #
96
- def parse_configuration(&config)
97
- exec_configuration(&config)
100
+ def _parse_configuration(&config)
101
+ _exec_configuration(&config)
98
102
 
99
103
  generate_sequences
100
104
  generate_scope_names
101
105
 
102
- assert_states_are_valid
103
- assert_transitions_exist
104
- assert_uniq_transitions
105
- assert_any_has_been_expanded
106
- assert_all_transitions_are_states
107
- assert_all_states_are_reachable
106
+ _assert_states_are_valid
107
+ _assert_transitions_exist
108
+ _assert_uniq_transitions
109
+ _assert_any_has_been_expanded
110
+ _assert_all_transitions_are_states
111
+ _assert_all_states_are_reachable
108
112
  end
109
113
 
110
114
 
111
115
 
116
+ ##
112
117
  # Run the configuration commands.
113
118
  #
119
+ # @block config
120
+ # the given configuration
121
+ #
114
122
  # ==== actions
115
123
  #
116
- # + create sequence links and transitions
117
- # + create scope names
118
- # + remove duplicate transitions for each state
124
+ # - create sequence links and transitions
125
+ # - create scope names
126
+ # - remove duplicate transitions for each state
119
127
  #
120
- # + verify there are multiple valid state names
121
- # + verify all transitions lead to existing states
122
- # + verify each state, except the default, can be reached from a transition
128
+ # - verify there are multiple valid state names
129
+ # - verify all transitions lead to existing states
130
+ # - verify each state, except the default, can be reached from a transition
123
131
  #
124
- def exec_configuration(&config)
132
+ def _exec_configuration(&config)
125
133
  instance_exec(&config)
126
134
  rescue NameError => e
127
135
  err_command = e.to_s.gsub('undefined local variable or method `', '')
128
136
  .split("'")
129
137
  .first
130
- cerr :bad_command, cmd: err_command
138
+ _cerr :bad_command, cmd: err_command
131
139
  end
132
140
 
133
141
 
@@ -136,21 +144,22 @@ module StateGate
136
144
  # Assertions
137
145
  # ======================================================================
138
146
 
147
+ ##
139
148
  # Ensure there are enough states and the default is a valid state, setting
140
149
  # the default to the first state if required.
141
150
  #
142
- def assert_states_are_valid
151
+ def _assert_states_are_valid
143
152
  state_names = @states.keys
144
153
 
145
154
  # are there states
146
- cerr(:states_missing_err) if state_names.blank?
155
+ _cerr(:states_missing_err) if state_names.blank?
147
156
 
148
157
  # is there more than one state
149
- cerr(:single_state_err) if state_names.one?
158
+ _cerr(:single_state_err) if state_names.one?
150
159
 
151
160
  # set the deafult state if needed, otherwise check it is a valid state
152
161
  if @default
153
- cerr(:default_state_err) unless state_names.include?(@default)
162
+ _cerr(:default_state_err) unless state_names.include?(@default)
154
163
  else
155
164
  @default = state_names.first
156
165
  end
@@ -158,11 +167,12 @@ module StateGate
158
167
 
159
168
 
160
169
 
170
+ ##
161
171
  # Ensure that transitions have been specified. If not, then add the transitions
162
172
  # to allow every stater to transition to another state and flag the engine as
163
173
  # transitionless, so we don't add any validation methods.
164
174
  #
165
- def assert_transitions_exist
175
+ def _assert_transitions_exist
166
176
  return if @states.map { |_state, opts| opts[:transitions_to] }.uniq.flatten.any?
167
177
 
168
178
  @transitionless = true
@@ -173,39 +183,42 @@ module StateGate
173
183
 
174
184
 
175
185
 
186
+ ##
176
187
  # Ensure that there is only one of reach transition
177
188
  #
178
- def assert_uniq_transitions
189
+ def _assert_uniq_transitions
179
190
  @states.each { |_state, opts| opts[:transitions_to].uniq! }
180
191
  end
181
192
 
182
193
 
183
194
 
195
+ ##
184
196
  # Ensure that the :any transition is expanded or raise an exception
185
197
  # if it's included with other transitions
186
- def assert_any_has_been_expanded
198
+ def _assert_any_has_been_expanded
187
199
  @states.each do |state_name, opts|
188
200
  if opts[:transitions_to] == [:any]
189
201
  @states[state_name][:transitions_to] = @states.keys - [state_name]
190
202
 
191
203
  elsif opts[:transitions_to].include?(:any)
192
- cerr(:any_transition_err, state: state_name, kattr: true)
204
+ _cerr(:any_transition_err, state: state_name, kattr: true)
193
205
  end
194
206
  end
195
207
  end
196
208
 
197
209
 
198
210
 
211
+ ##
199
212
  # Ensure all transitions are to valid states.
200
213
  #
201
214
  # Replaces transition to :any with a list of all states
202
215
  # Raises an exception if :any in included with a list of other transitions
203
216
  #
204
- def assert_all_transitions_are_states
217
+ def _assert_all_transitions_are_states
205
218
  @states.each do |state_name, opts|
206
219
  opts[:transitions_to].each do |transition|
207
220
  unless @states.keys.include?(transition)
208
- cerr(:transition_state_err, state: state_name, transition: transition, kattr: true)
221
+ _cerr(:transition_state_err, state: state_name, transition: transition, kattr: true)
209
222
  end
210
223
  end
211
224
  end
@@ -213,16 +226,17 @@ module StateGate
213
226
 
214
227
 
215
228
 
229
+ ##
216
230
  # Ensure there is a transition leading to every non-default state.
217
231
  #
218
- def assert_all_states_are_reachable
232
+ def _assert_all_states_are_reachable
219
233
  # is there a transition to every state except the default.
220
234
  transitions = @states.map { |_state, opts| opts[:transitions_to] }.flatten.uniq
221
235
  adrift_states = (@states.keys - transitions - [@default])
222
236
  return if adrift_states.blank?
223
237
 
224
238
  states = adrift_states.map { |s| ':' + s.to_s }.to_sentence
225
- cerr(:transitionless_states_err, states: states, kattr: true)
239
+ _cerr(:transitionless_states_err, states: states, kattr: true)
226
240
  end
227
241
 
228
242
  end # ConfigurationMethods
@@ -6,8 +6,8 @@ module StateGate
6
6
  # = Description
7
7
  #
8
8
  # Adds error reporting methods to a StateMachine::Engine
9
- # * all error messages are I18n configured
10
- # * method names are deliberately short to encourage code readability with
9
+ # - all error messages are I18n configured
10
+ # - method names are deliberately short to encourage code readability with
11
11
  # if/unless one-liners
12
12
  module Errator
13
13
 
@@ -15,24 +15,39 @@ module StateGate
15
15
  # ======================================================================
16
16
  private
17
17
 
18
+ ##
18
19
  # Format the given value and report an ArgumentError
19
20
  #
20
- def invalid_state_error(val)
21
+ # @param [String]
22
+ # the error message
23
+ #
24
+ # @raise [ArgumentError]
25
+ #
26
+ def _invalid_state_error(val)
21
27
  case val
22
28
  when NilClass
23
- aerr :invalid_state_err, val: "'nil'", kattr: true
29
+ _aerr :invalid_state_err, val: "'nil'", kattr: true
24
30
  when Symbol
25
- aerr :invalid_state_err, val: ":#{val}", kattr: true
31
+ _aerr :invalid_state_err, val: ":#{val}", kattr: true
26
32
  else
27
- aerr :invalid_state_err, val: "'#{val&.to_s}'", kattr: true
33
+ _aerr :invalid_state_err, val: "'#{val&.to_s}'", kattr: true
28
34
  end
29
35
  end
30
36
 
31
37
 
32
38
 
39
+ ##
33
40
  # Report a ConfigurationError, including the Klass#attr variable.
34
41
  #
35
- def cerr(err, **args)
42
+ # @param [Symbol] err
43
+ # the I18n error message key
44
+ #
45
+ # @param [Hash] args
46
+ # arguments to be included in the error
47
+ #
48
+ # @raise [ConfigurationError]
49
+ #
50
+ def _cerr(err, **args)
36
51
  args[:kattr] = "#{@klass}##{@attribute}" if args[:kattr] == true
37
52
  key = "state_gate.engine.config.#{err}"
38
53
  fail ConfigurationError, I18n.t(key, **args)
@@ -40,9 +55,18 @@ module StateGate
40
55
 
41
56
 
42
57
 
58
+ ##
43
59
  # Report a RuntimeError, including the Klass#attr variable.
44
60
  #
45
- def rerr(err, **args)
61
+ # @param [Symbol] err
62
+ # the I18n error message key
63
+ #
64
+ # @param [Hash] args
65
+ # arguments to be included in the error
66
+ #
67
+ # @raise [RuntimeError]
68
+ #
69
+ def _rerr(err, **args)
46
70
  args[:kattr] = "#{@klass}##{@attribute}" if args[:kattr] == true
47
71
  key = "state_gate.engine.#{err}"
48
72
  fail I18n.t(key, **args)
@@ -50,9 +74,18 @@ module StateGate
50
74
 
51
75
 
52
76
 
77
+ ##
53
78
  # Report an ArgumentError, including the Klass#attr variable.
54
79
  #
55
- def aerr(err, **args)
80
+ # @param [Symbol] err
81
+ # the I18n error message key
82
+ #
83
+ # @param [Hash] args
84
+ # arguments to be included in the error
85
+ #
86
+ # @raise [ArgumentError]
87
+ #
88
+ def _aerr(err, **args)
56
89
  args[:kattr] = "#{@klass}##{@attribute}" if args[:kattr] == true
57
90
  key = "state_gate.engine.#{err}"
58
91
  fail ArgumentError, I18n.t(key, **args)
@@ -13,15 +13,19 @@ module StateGate
13
13
  # Configuration Methods
14
14
  # ======================================================================
15
15
 
16
+ ##
16
17
  # A phrase to add before state names when using Class Scopes.
17
18
  # This helps differential attributes that have similar state names.
18
- # (Symbol | optional)
19
19
  #
20
- # prefix :before # => Class.before_active
20
+ # @param [Symbol] val
21
+ # the prefix to use
22
+ #
23
+ # @example
24
+ # prefix :before #=> Class.before_active
21
25
  #
22
26
  def prefix(val = nil)
23
- cerr(:prefix_type_err, kattr: true) unless val.is_a?(Symbol)
24
- cerr(:prefix_multiple_err, kattr: true) if @prefix
27
+ _cerr(:prefix_type_err, kattr: true) unless val.is_a?(Symbol)
28
+ _cerr(:prefix_multiple_err, kattr: true) if @prefix
25
29
  @prefix = "#{val.to_s.downcase}_"
26
30
  end # prefix
27
31
 
@@ -29,13 +33,16 @@ module StateGate
29
33
 
30
34
  # A phrase to add before state names when using Class Scopes.
31
35
  # This helps differential attributes that have similar state names.
32
- # (Symbol | optional)
33
36
  #
34
- # suffix :after # => Class.active_after
37
+ # @param [Symbol] val
38
+ # the suffix to use
39
+ #
40
+ # @example
41
+ # suffix :after #=> Class.active_after
35
42
  #
36
43
  def suffix(val = nil)
37
- cerr(:suffix_type_err, kattr: true) unless val.is_a?(Symbol)
38
- cerr(:suffix_multiple_err, kattr: true) if @suffix
44
+ _cerr(:suffix_type_err, kattr: true) unless val.is_a?(Symbol)
45
+ _cerr(:suffix_multiple_err, kattr: true) if @suffix
39
46
  @suffix = "_#{val.to_s.downcase}"
40
47
  end # suffix
41
48
 
@@ -49,8 +56,9 @@ module StateGate
49
56
  # Returns the defined prefix for the state_gate, or an empty string if no
50
57
  # prefix has been defined.
51
58
  #
52
- # .state_prefix # => 'my_prefix'
53
- # .state_prefix # => ''
59
+ # @example
60
+ # .state_prefix #=> 'my_prefix'
61
+ # .state_prefix #=> ''
54
62
  #
55
63
  def state_prefix
56
64
  @prefix
@@ -62,8 +70,9 @@ module StateGate
62
70
  # Returns the defined suffix for the state_gate, or an empty string if no
63
71
  # suffix has been defined.
64
72
  #
65
- # .state_suffix # => 'my_suffix'
66
- # .state_suffix # => ''
73
+ # @example
74
+ # .state_suffix #=> 'my_suffix'
75
+ # .state_suffix #=> ''
67
76
  #
68
77
  def state_suffix
69
78
  @suffix
@@ -13,8 +13,10 @@ module StateGate
13
13
  # Configuration Methods
14
14
  # ======================================================================
15
15
 
16
+ ##
16
17
  # Disables the generation of Class Scope helpers methods
17
18
  #
19
+ # @example
18
20
  # no_scopes
19
21
  #
20
22
  def no_scopes
@@ -23,6 +25,7 @@ module StateGate
23
25
 
24
26
 
25
27
 
28
+ ##
26
29
  # Generate the scope name to use for each state.
27
30
  #
28
31
  def generate_scope_names
@@ -40,7 +43,8 @@ module StateGate
40
43
  ##
41
44
  # Returns TRUE if scope methods should be added to the model, otherwise FALSE.
42
45
  #
43
- # .include_scopes? # => true
46
+ # @example
47
+ # .include_scopes? #=> true
44
48
  #
45
49
  def include_scopes?
46
50
  !!@scopes
@@ -52,9 +56,13 @@ module StateGate
52
56
  # Returns the scope name for the given state. Scope names are generated by
53
57
  # concatenating the prefix, state name and suffix
54
58
  #
55
- # .scope_name_for_state(:active) # => 'active'
56
- # .scope_name_for_state(:pending) # => 'pending_status'
57
- # .scope_name_for_state(:archived) # => 'with_archived_status'
59
+ # @param [Symbol] state_name
60
+ # the state to generate the scope_name for
61
+ #
62
+ # Example
63
+ # .scope_name_for_state(:active) #=> 'active'
64
+ # .scope_name_for_state(:pending) #=> 'pending_status'
65
+ # .scope_name_for_state(:archived) #=> 'with_archived_status'
58
66
  #
59
67
  def scope_name_for_state(state_name = nil)
60
68
  state = assert_valid_state!(state_name)
@@ -15,16 +15,23 @@ module StateGate
15
15
  # Configuration Methods
16
16
  # ======================================================================
17
17
 
18
+ ##
18
19
  # Automatically add transitions from each state to the preceeding and following states.
19
20
  # make_sequential
20
21
  #
21
- # [:one_way]
22
+ # @param [Array[Symbol]] args
23
+ # the optional configuration arguments
24
+ #
25
+ # @option args [Symbol] :one_way
22
26
  # Only adds tranitions from each state to the follow state. (optional)
23
- # make_sequential :one_way
24
27
  #
25
- # [:loop]
28
+ # @option args [Symbol] :loop
26
29
  # Adds transitions from the last state to the first and from the first to the last
27
30
  # (unless also :one_way) (optional)
31
+ #
32
+ # @example
33
+ # make_sequential :one_way
34
+ # make_sequential :loop
28
35
  # make_sequential :one_way, :loop
29
36
  #
30
37
  def make_sequential(*args)
@@ -35,6 +42,7 @@ module StateGate
35
42
 
36
43
 
37
44
 
45
+ ##
38
46
  # Add sequence hooks if sequential requested.
39
47
  #
40
48
  def generate_sequences
@@ -47,6 +55,7 @@ module StateGate
47
55
 
48
56
 
49
57
 
58
+ ##
50
59
  # Add the previous sequential state
51
60
  #
52
61
  def add_previous_sequential_state
@@ -64,6 +73,7 @@ module StateGate
64
73
 
65
74
 
66
75
 
76
+ ##
67
77
  # Add the next sequential state
68
78
  #
69
79
  def add_next_sequential_state
@@ -79,6 +89,7 @@ module StateGate
79
89
 
80
90
 
81
91
 
92
+ ##
82
93
  # Add the first and last transitions to complete the sequential loop.
83
94
  #
84
95
  def loop_sequence
@@ -103,9 +114,11 @@ module StateGate
103
114
  # ======================================================================
104
115
 
105
116
  ##
106
- # return TRUE if the state_gate is sequential, otherwise FALSE.
117
+ # @return [Boolean]
118
+ # true if the state_gate is sequential, otherwise false.
107
119
  #
108
- # .sequential? # => TRUE
120
+ # @example
121
+ # .sequential? #=> TRUE
109
122
  #
110
123
  def sequential?
111
124
  !!@sequential