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.
@@ -15,27 +15,28 @@ module StateGate
15
15
 
16
16
  # Add a new +state+.
17
17
  #
18
- # [:state_name*]
19
- # The name for the new state. (Symbol | required)
18
+ # @param [Symbol, String] name
19
+ # The name for the new state. It must be converatbel to a Symbol
20
20
  # state :state_name
21
21
  #
22
+ # @param [Hash] opts
23
+ # configuration options for the given state
22
24
  #
23
- # [:transitions_to]
25
+ # @option opts [Hash] :transitions_to
24
26
  # A list of other state that this state may change to. (Array | optional)
25
27
  # state :state_name, transtions_to: [:state_1, :state_4, :state_5]
26
28
  #
27
- # [:human]
28
- # A display name for the state used in views.
29
- # (String | optoinal | default: state.titleized)
29
+ # @option opts [Hash] :human
30
+ # A display name for the state used in views, defaulting to state.titleized.
30
31
  # state :state_name, transtions_to: [:state_1, :state_4, :state_5], human: "My State"
31
32
  #
32
33
  def state(name, opts = {})
33
34
  name = StateGate.symbolize(name)
34
- assert_valid_state_name(name)
35
- assert_valid_opts(opts, name)
35
+ _assert_valid_state_name(name)
36
+ _assert_valid_opts(opts, name)
36
37
 
37
38
  # add the state
38
- @states[name] = state_template.merge({ human: opts[:human] || name.to_s.titleize })
39
+ @states[name] = _state_template.merge({ human: opts[:human] || name.to_s.titleize })
39
40
 
40
41
  # add the state transitions
41
42
  Array(opts[:transitions_to]).each do |transition|
@@ -45,14 +46,19 @@ module StateGate
45
46
 
46
47
 
47
48
 
49
+ ##
48
50
  # The name for the default state for a new object. (String | required)
49
51
  #
52
+ # @param [Symbol] default_state
53
+ # the state name to set as default
54
+ #
55
+ # @example
50
56
  # default :state_name
51
57
  #
52
- def default(val = nil)
53
- cerr(:default_type_err, kattr: true) unless val.is_a?(Symbol)
54
- cerr(:default_repeat_err, kattr: true) if @default
55
- @default = StateGate.symbolize(val)
58
+ def default(default_state = nil)
59
+ _cerr(:default_type_err, kattr: true) unless default_state.is_a?(Symbol)
60
+ _cerr(:default_repeat_err, kattr: true) if @default
61
+ @default = StateGate.symbolize(default_state)
56
62
  end
57
63
 
58
64
 
@@ -64,7 +70,8 @@ module StateGate
64
70
  ##
65
71
  # Returns an Array defined states.
66
72
  #
67
- # .states # => [:pending, :active, :suspended, :archived]
73
+ # @example
74
+ # .states #=> [:pending, :active, :suspended, :archived]
68
75
  #
69
76
  def states
70
77
  @states.keys
@@ -75,26 +82,30 @@ module StateGate
75
82
  ##
76
83
  # Ensures the given value is a valid state name.
77
84
  #
78
- # [value]
79
- # A String or Symbol state name.
85
+ # @param [String, Symbol] value
86
+ # the state name.
80
87
  #
81
- # .assert_valid_state!(:active) # => :active
82
- # .assert_valid_state!('PENDING') # => :pending
83
- # .assert_valid_state!(:dummy) # => ArgumentError
88
+ # @example
89
+ # .assert_valid_state!(:active) #=> :active
90
+ # .assert_valid_state!('PENDING') #=> :pending
91
+ # .assert_valid_state!(:dummy) #=> ArgumentError
84
92
  #
85
93
  #
86
- # [Note]
94
+ # @note
87
95
  # Valid state names preceeded with +force_+ are also allowed.
88
96
  #
89
- # .assert_valid_state!(:force_active) # => :force_active
97
+ # .assert_valid_state!(:force_active) #=> :force_active
90
98
  #
91
- # Returns the Symbol state name
92
- # Raises an exception if the value is not a valid state name.
99
+ # @return [Symbol]
100
+ # the Symbol state name
101
+ #
102
+ # @raise [ArgumentError]
103
+ # if the value is not a valid state name.
93
104
  #
94
105
  def assert_valid_state!(value)
95
106
  state_name = StateGate.symbolize(value)
96
107
  unforced_state = state_name.to_s.remove(/^force_/).to_sym
97
- invalid_state_error(value) unless @states.keys.include?(unforced_state)
108
+ _invalid_state_error(value) unless @states.keys.include?(unforced_state)
98
109
  state_name
99
110
  end
100
111
 
@@ -103,7 +114,8 @@ module StateGate
103
114
  ##
104
115
  # Returns an Array of the human display names for each defined state.
105
116
  #
106
- # human_states # => ['Pending Activation', 'Active', 'Suspended By Admin']
117
+ # @example
118
+ # human_states #=> ['Pending Activation', 'Active', 'Suspended By Admin']
107
119
  #
108
120
  def human_states
109
121
  @states.map { |_k, v| v[:human] }
@@ -114,8 +126,12 @@ module StateGate
114
126
  ##
115
127
  # Returns the human display name for a given state.
116
128
  #
117
- # .human_state_for(:pending) # => 'Panding Activation'
118
- # .human_state_for(:active) # => 'Active'
129
+ # @param [Symbol] state
130
+ # the state name
131
+ #
132
+ # @example
133
+ # .human_state_for(:pending) #=> 'Panding Activation'
134
+ # .human_state_for(:active) #=> 'Active'
119
135
  #
120
136
  def human_state_for(state)
121
137
  state_name = assert_valid_state!(state)
@@ -128,15 +144,20 @@ module StateGate
128
144
  # Return an Array of states, with their human display names, ready for
129
145
  # use in a form select.
130
146
  #
131
- # sorted - TRUE is the states should be sorted by human name, defaults to false
147
+ # @param [Boolean] sorted
148
+ # true is the states should be sorted by human name, defaults to false
132
149
  #
133
- # .states_for_select # => [['Pending Activation', 'pending'],
134
- # # ['Active', 'active'],
135
- # # ['Suspended by Admin', 'suspended']]
150
+ # @return [Array[Array[Strings]]]
151
+ # Array of state names with their human names
136
152
  #
137
- # .states_for_select(true) # => [['Active', 'active'],
138
- # # ['Pending Activation', 'pending'],
139
- # # ['Suspended by Admin', 'suspended']]
153
+ # @example
154
+ # .states_for_select #=> [['Pending Activation', 'pending'],
155
+ # ['Active', 'active'],
156
+ # ['Suspended by Admin', 'suspended']]
157
+ #
158
+ # .states_for_select(true) #=> [['Active', 'active'],
159
+ # ['Pending Activation', 'pending'],
160
+ # ['Suspended by Admin', 'suspended']]
140
161
  #
141
162
  def states_for_select(sorted = false)
142
163
  result = []
@@ -152,12 +173,14 @@ module StateGate
152
173
 
153
174
 
154
175
  ##
155
- # Return the raw states hash, allowing inspection of the core engine states.
176
+ # @return [Hash]
177
+ # a hash of states and their transitions & human names
156
178
  #
157
- # .raw_states # => { pending: { transitions_to: [:active],
158
- # # human: 'Pending Activation'},
159
- # # active: { transitions_to: [:pending],
160
- # # human: 'Active'}}
179
+ # @example
180
+ # .raw_states #=> { pending: { transitions_to: [:active],
181
+ # human: 'Pending Activation'},
182
+ # active: { transitions_to: [:pending],
183
+ # human: 'Active'}}
161
184
  #
162
185
  def raw_states
163
186
  @states
@@ -166,9 +189,11 @@ module StateGate
166
189
 
167
190
 
168
191
  ##
169
- # Returns the state_gate default state
192
+ # @return [String]
193
+ # the state_gate default state
170
194
  #
171
- # .default_state # => :pending
195
+ # @example
196
+ # .default_state #=> :pending
172
197
  #
173
198
  def default_state
174
199
  @default
@@ -183,8 +208,11 @@ module StateGate
183
208
 
184
209
 
185
210
 
186
- # return a Hash with the state keys defined.
187
- def state_template
211
+ ##
212
+ # @return [Hash]
213
+ # of the state keys defined.
214
+ #
215
+ def _state_template
188
216
  {
189
217
  transitions_to: [],
190
218
  previous_state: nil,
@@ -196,28 +224,45 @@ module StateGate
196
224
 
197
225
 
198
226
 
199
- # fail if the supplied name is not a symbol, already added
227
+ ##
228
+ # Raises an error fail if the supplied name is not a symbol, already added
200
229
  # or starts with 'not_' or 'force'.
201
230
  #
202
- def assert_valid_state_name(name)
203
- cerr(:state_type_err, kattr: true) unless name.is_a?(Symbol)
204
- cerr(:state_repeat_err, state: name, kattr: true) if @states.key?(name)
205
- cerr(:state_not_name_err, state: name, kattr: true) if name.to_s.start_with?('not_')
206
- cerr(:state_force_name_err, state: name, kattr: true) if name.to_s.start_with?('force_')
231
+ # @param [Symbol] name
232
+ # the state name to validate
233
+ #
234
+ # @raise [AygumentError]
235
+ # if the state name is invalid
236
+ #
237
+ def _assert_valid_state_name(name)
238
+ _cerr(:state_type_err, kattr: true) unless name.is_a?(Symbol)
239
+ _cerr(:state_repeat_err, state: name, kattr: true) if @states.key?(name)
240
+ _cerr(:state_not_name_err, state: name, kattr: true) if name.to_s.start_with?('not_')
241
+ _cerr(:state_force_name_err, state: name, kattr: true) if name.to_s.start_with?('force_')
207
242
  end
208
243
 
209
244
 
210
245
 
211
- # Fail if opts is not a hash, has non-symbol keys or non-symbol transitions.
246
+ ##
247
+ # raises an error if opts is not a hash, has non-symbol keys or non-symbol transitions.
248
+ #
249
+ # @param [Hash] opts
250
+ # an options hash
251
+ #
252
+ # @param [Symbol] name
253
+ # the state name
254
+ #
255
+ # @raise [ArgumentError]
256
+ # if any invalid options
212
257
  #
213
- def assert_valid_opts(opts, state_name)
258
+ def _assert_valid_opts(opts, state_name)
214
259
  unless opts.keys.reject { |k| k.is_a?(Symbol) }.blank?
215
- cerr(:state_opts_key_type_err, state: state_name, kattr: true)
260
+ _cerr(:state_opts_key_type_err, state: state_name, kattr: true)
216
261
  end
217
262
 
218
263
  return if Array(opts[:transitions_to]).reject { |k| k.is_a?(Symbol) }.blank?
219
264
 
220
- cerr(:transition_key_type_err, state: state_name, kattr: true)
265
+ _cerr(:transition_key_type_err, state: state_name, kattr: true)
221
266
  end
222
267
 
223
268
  end # Stater
@@ -10,10 +10,12 @@ module StateGate
10
10
  module Transitioner
11
11
 
12
12
  ##
13
- # Returns TRUE if every state can transition to every other state, rendering
14
- # transitions pointless.
13
+ # @return [Boolean]
14
+ # true if every state can transition to every other state, rendering
15
+ # transitions pointless.
15
16
  #
16
- # .transitionless? # => true
17
+ # @example
18
+ # .transitionless? #=> true
17
19
  #
18
20
  def transitionless?
19
21
  !!@transitionless
@@ -22,12 +24,14 @@ module StateGate
22
24
 
23
25
 
24
26
  ##
25
- # Returns a Hash of states and allowed transitions.
27
+ # @return [Hash]
28
+ # of states and allowed transitions.
26
29
  #
27
- # .transitions # => { pending: [:active],
28
- # # ativive: [:suspended, :archived],
29
- # # suspended: [:active, :archived],
30
- # # archived: [] }
30
+ # @example
31
+ # .transitions #=> { pending: [:active],
32
+ # ativive: [:suspended, :archived],
33
+ # suspended: [:active, :archived],
34
+ # archived: [] }
31
35
  #
32
36
  def transitions
33
37
  @transitions ||= begin
@@ -40,9 +44,14 @@ module StateGate
40
44
 
41
45
 
42
46
  ##
43
- # Return an Array of allowed transitions for the given state
47
+ # @param [Symbol, String] state
48
+ # the state name
44
49
  #
45
- # .transitions_for_state(:active) # => [:suspended, :archived]
50
+ # @return [Array]
51
+ # of allowed transitions for the given state
52
+ #
53
+ # @example
54
+ # .transitions_for_state(:active) #=> [:suspended, :archived]
46
55
  #
47
56
  def transitions_for_state(state)
48
57
  state_name = assert_valid_state!(state)
@@ -52,10 +61,15 @@ module StateGate
52
61
 
53
62
 
54
63
  ##
55
- # Returns TRUE if a transition is allowed, otherwise raises an exception.
64
+ # @return [true]
65
+ # if a transition is allowed
66
+ #
67
+ # @raise [ArgumentError]
68
+ # if a transition is invalid
56
69
  #
57
- # .assert_valid_transition!(:pending, :active) # => true
58
- # .assert_valid_transition!(:active, :pending) # => ArgumentError
70
+ # @example
71
+ # .assert_valid_transition!(:pending, :active) #=> true
72
+ # .assert_valid_transition!(:active, :pending) #=> ArgumentError
59
73
  #
60
74
  def assert_valid_transition!(current_state = nil, new_state = nil)
61
75
  from_state = assert_valid_state!(current_state)
@@ -65,7 +79,7 @@ module StateGate
65
79
  return true if to_state.to_s.start_with?('force_')
66
80
  return true if @states[from_state][:transitions_to].include?(to_state)
67
81
 
68
- aerr(:invalid_state_transition_err, from: from_state, to: to_state, kattr: true)
82
+ _aerr(:invalid_state_transition_err, from: from_state, to: to_state, kattr: true)
69
83
  end
70
84
 
71
85
  end # Sequencer
@@ -28,30 +28,43 @@ module StateGate
28
28
  # ======================================================================
29
29
  private
30
30
 
31
+ ##
31
32
  # Initialize the engine, setting the Class and attribute for the new engine
32
33
  # and parsing the provided configuration.
33
34
  #
35
+ # @param [Class] klass
36
+ # The class containing the attribute to be cast as a state gate
37
+ #
38
+ # @param [Symbol] attribute
39
+ # The name of the database attribute to use for the state gate
40
+ #
41
+ # @block config
42
+ # The configuration block for the state gate.
43
+ #
44
+ # @example
34
45
  # StateGate::Engine.new(MyKlass, :status) do
35
46
  # ... configuration ...
36
47
  # end
37
48
  #
38
49
  def initialize(klass, attribute, &config)
39
- aerror(:klass_type_err) unless klass.respond_to?(:to_s)
40
- aerror(:attribute_type_err) unless attribute.is_a?(Symbol)
41
- aerror(:missing_config_block_err) unless block_given?
50
+ _aerror(:klass_type_err) unless klass.respond_to?(:to_s)
51
+ _aerror(:attribute_type_err) unless attribute.is_a?(Symbol)
52
+ _aerror(:missing_config_block_err) unless block_given?
42
53
 
43
54
  @klass = klass
44
55
  @attribute = StateGate.symbolize(attribute)
45
56
 
46
- set_defaults
57
+ _set_defaults
47
58
 
48
- parse_configuration(&config)
59
+ _parse_configuration(&config)
49
60
  end
50
61
 
51
62
 
52
63
 
64
+ ##
53
65
  # Set the class variables with default values
54
- def set_defaults
66
+ #
67
+ def _set_defaults
55
68
  @states = {}
56
69
  @default = nil
57
70
  @prefix = nil
@@ -12,6 +12,8 @@ module StateGate
12
12
  #
13
13
  # This class is has an internal API for ActiveRecord and is not intended for public use.
14
14
  #
15
+ # @private
16
+ #
15
17
  class Type < ::ActiveModel::Type::String
16
18
 
17
19
  ##
@@ -19,7 +21,7 @@ module StateGate
19
21
  #
20
22
  def cast(value) # :nodoc:
21
23
  assert_valid_value(value)
22
- value.to_s.downcase.remove(/^force_/)
24
+ cast_value(value)
23
25
  end
24
26
 
25
27
 
@@ -47,6 +49,17 @@ module StateGate
47
49
 
48
50
 
49
51
 
52
+ ##
53
+ # Convert a nil DB value to the default state.
54
+ #
55
+ def deserialize(value) # :nodoc:
56
+ return value if value
57
+
58
+ klass.constantize.stateables[name].default_state
59
+ end
60
+
61
+
62
+
50
63
  ##
51
64
  # Raise an exception unless the value is both serializable and a legitimate state
52
65
  #
@@ -99,7 +112,7 @@ module StateGate
99
112
  attr_reader :klass, :name, :states
100
113
 
101
114
 
102
-
115
+ ##
103
116
  # initialize and set the class variables
104
117
  #
105
118
  def initialize(klass, name, states) # :nodoc:
@@ -108,5 +121,12 @@ module StateGate
108
121
  @states = states.map(&:to_s)
109
122
  end
110
123
 
124
+
125
+ ##
126
+ # convert the value to lowercase and remove and 'force_' prefix
127
+ #
128
+ def cast_value(value)
129
+ value.to_s.downcase.remove(/^force_/)
130
+ end
111
131
  end # class Type
112
132
  end # StateGate