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.
@@ -8,58 +8,58 @@ module StateGate
8
8
  # Multiple private methods allowing StateGate::Builder to generate
9
9
  # attribute setter methods for transition validation.
10
10
  #
11
- # * initializing the attribute with +Class.new+ :
12
- # Klass.new(status: :active) # => ArgumentError
11
+ # - initializing the attribute with +Class.new+ :
12
+ # Klass.new(status: :active) #=> ArgumentError
13
13
  #
14
- # * initializing the attribute with +Class.create+ :
15
- # Klass.create(status: :active) # => ArgumentError
14
+ # - initializing the attribute with +Class.create+ :
15
+ # Klass.create(status: :active) #=> ArgumentError
16
16
  #
17
- # * initializing the attribute with <tt>Class.create!</tt> :
18
- # Klass.create!(status: :active) # => ArgumentError
17
+ # - initializing the attribute with <tt>Class.create!</tt> :
18
+ # Klass.create!(status: :active) #=> ArgumentError
19
19
  #
20
- # * setting the attribute with +attr=+ :
21
- # .status = :active # => :active
22
- # .status = :archived # => ArgumentError
20
+ # - setting the attribute with +attr=+ :
21
+ # .status = :active #=> :active
22
+ # .status = :archived #=> ArgumentError
23
23
  #
24
- # * setting the attribute with <tt>[:attr]=</tt> :
25
- # [:status] = :active # => :active
26
- # [:status] = :archived # => ArgumentError
24
+ # - setting the attribute with <tt>[:attr]=</tt> :
25
+ # [:status] = :active #=> :active
26
+ # [:status] = :archived #=> ArgumentError
27
27
  #
28
- # * setting the attribute with <tt>attributes=</tt> :
29
- # .attrubutes = {status: :active} # => :active
30
- # .attributes = {status: :archived } # => ArgumentError
28
+ # - setting the attribute with <tt>attributes=</tt> :
29
+ # .attrubutes = {status: :active} #=> :active
30
+ # .attributes = {status: :archived } #=> ArgumentError
31
31
  #
32
- # * setting the attribute with <tt>assign_attributes</tt> :
33
- # .assign_attrubutes(status: :active) # => :active
34
- # .assign_attributes(status: :archived) # => ArgumentError
32
+ # - setting the attribute with <tt>assign_attributes</tt> :
33
+ # .assign_attrubutes(status: :active) #=> :active
34
+ # .assign_attributes(status: :archived) #=> ArgumentError
35
35
  #
36
- # * updating the attribute with <tt>Class.update</tt> :
37
- # Klass.update(instance.id, status: :active) # => :active
38
- # Klass.update(instance.id, status: :archived) # => ArgumentError
36
+ # - updating the attribute with <tt>Class.update</tt> :
37
+ # Klass.update(instance.id, status: :active) #=> :active
38
+ # Klass.update(instance.id, status: :archived) #=> ArgumentError
39
39
  #
40
- # * updating the attribute with <tt>.update</tt> :
41
- # .update(status: :active) # => :active
42
- # .update(status: :archived) # => ArgumentError
40
+ # - updating the attribute with <tt>.update</tt> :
41
+ # .update(status: :active) #=> :active
42
+ # .update(status: :archived) #=> ArgumentError
43
43
  #
44
- # * updating the attribute with <tt>.update_column</tt> :
45
- # .update_column(:status, :active) # => :active
46
- # .update_column(:status, :archived) # => ArgumentError
44
+ # - updating the attribute with <tt>.update_column</tt> :
45
+ # .update_column(:status, :active) #=> :active
46
+ # .update_column(:status, :archived) #=> ArgumentError
47
47
  #
48
- # * updating the attribute with <tt>.update_columns</tt> :
49
- # .update_columns(status: :active) # => :active
50
- # .update_columns(status: :archived) # => ArgumentError
48
+ # - updating the attribute with <tt>.update_columns</tt> :
49
+ # .update_columns(status: :active) #=> :active
50
+ # .update_columns(status: :archived) #=> ArgumentError
51
51
  #
52
- # * updating the attribute with <tt>.write_attribute</tt> :
53
- # .write_attribute(:status, :active) # => :active
54
- # .write_attribute(:status, :archived) # => ArgumentError
52
+ # - updating the attribute with <tt>.write_attribute</tt> :
53
+ # .write_attribute(:status, :active) #=> :active
54
+ # .write_attribute(:status, :archived) #=> ArgumentError
55
55
  #
56
56
  #
57
57
  # === | Forcing a change
58
58
  #
59
59
  # To force a status change that would otherwise be prohibited, preceed the
60
60
  # new state with +force_+ :
61
- # .status = :archived # => ArgumentError
62
- # .status = :force_archived # => :archived
61
+ # .status = :archived #=> ArgumentError
62
+ # .status = :force_archived #=> :archived
63
63
  #
64
64
  module TransitionValidationMethods
65
65
 
@@ -69,19 +69,20 @@ module StateGate
69
69
 
70
70
 
71
71
 
72
+ ##
72
73
  # Add prepended instance methods to the klass that catch all methods for
73
74
  # updating the attribute and validated the new value is an allowed transition
74
75
  #
75
- # Note:
76
- # These methods are only added if the engine has an
77
- # include_transition_validations? status on initialisation
76
+ # @note
77
+ # These methods are only added if the engine has an
78
+ # include_transition_validations? status on initialisation
78
79
  #
79
- # Note:
80
- # The three methods "<atrr>=(val)", "write_attribute(<attr>, val)" and
81
- # "update_columns(<attr>: val)" cover all the possibilities of setting the
82
- # attribute through ActiveRecord.
80
+ # @note
81
+ # The three methods "<atrr>=(val)", "write_attribute(<attr>, val)" and
82
+ # "update_columns(<attr>: val)" cover all the possibilities of setting the
83
+ # attribute through ActiveRecord.
83
84
  #
84
- def generate_transition_validation_methods
85
+ def _generate_transition_validation_methods
85
86
  return if @engine.transitionless?
86
87
 
87
88
  _prepend__attribute_equals
@@ -96,13 +97,14 @@ module StateGate
96
97
  # Prepend Module
97
98
  # ======================================================================
98
99
 
100
+ ##
99
101
  # Dynamically generated module to hold the validation setter methods
100
102
  # and is pre-pended to the class.
101
103
  #
102
104
  # A new module is create if it doesn't already exist.
103
105
  #
104
- # Note:
105
- # the module is named "StateGate::<klass>TranstionValidationMethods"
106
+ # @note
107
+ # the module is named "StateGate::<klass>TranstionValidationMethods"
106
108
  #
107
109
  def _transition_validation_module # rubocop:disable Metrics/MethodLength
108
110
  @_transition_validation_module ||= begin
@@ -125,16 +127,18 @@ module StateGate
125
127
  # Instance methods
126
128
  # ======================================================================
127
129
 
130
+ ##
128
131
  # Adds a method to overwrite the attribute :<attr>=(val) setter, raising an error
129
132
  # if the supplied value is not a valid transition
130
- # eg:
131
- # .status = :archived # => ArgumentError
132
- # .status - :active # => :active
133
+ #
134
+ # @example
135
+ # .status = :archived #=> ArgumentError
136
+ # .status - :active #=> :active
133
137
  #
134
138
  # ==== actions
135
139
  #
136
- # + assert it's a valid transition
137
- # + call super
140
+ # - assert it's a valid transition
141
+ # - call super
138
142
  #
139
143
  def _prepend__attribute_equals
140
144
  attr_name = @attribute
@@ -150,18 +154,20 @@ module StateGate
150
154
 
151
155
 
152
156
 
157
+ ##
153
158
  # Adds a method to overwrite the instance :write_attribute(attr, val) setter,
154
159
  # raising an error if the supplied value is not a valid transition
155
- # eg:
156
- # .write_attribute(:status, :archived) # => ArgumentError
157
- # .write_attribute(:status, :active) # => :active
160
+ #
161
+ # @example
162
+ # .write_attribute(:status, :archived) #=> ArgumentError
163
+ # .write_attribute(:status, :active) #=> :active
158
164
  #
159
165
  # ==== actions
160
166
  #
161
- # + loop through each attribute
162
- # + get the base attribute name from any alias used
163
- # + assert it's a valid transition
164
- # + call super
167
+ # - loop through each attribute
168
+ # - get the base attribute name from any alias used
169
+ # - assert it's a valid transition
170
+ # - call super
165
171
  #
166
172
  def _prepend__write_attribute
167
173
  return if _transition_validation_module.method_defined?(:write_attribute)
@@ -180,18 +186,20 @@ module StateGate
180
186
 
181
187
 
182
188
 
189
+ ##
183
190
  # Adds a method to overwrite the instance :update_columns(attr: val) setter,
184
191
  # raising an error if the supplied value is not a valid transition
185
- # eg:
186
- # .update_columns(status: :archived) # => ArgumentError
187
- # .update_columns(status: :active) # => :active
192
+ #
193
+ # @example
194
+ # .update_columns(status: :archived) #=> ArgumentError
195
+ # .update_columns(status: :active) #=> :active
188
196
  #
189
197
  # ==== actions
190
198
  #
191
- # + loop through each attribute
192
- # + get the base attribute name from any alias used
193
- # + assert it's a valid transition
194
- # + call super
199
+ # - loop through each attribute
200
+ # - get the base attribute name from any alias used
201
+ # - assert it's a valid transition
202
+ # - call super
195
203
  #
196
204
  def _prepend__update_columns # rubocop:disable Metrics/MethodLength
197
205
  return if _transition_validation_module.method_defined?(:update_columns)
@@ -215,10 +223,12 @@ module StateGate
215
223
 
216
224
 
217
225
 
218
- # Prepends an :itialize method to ensure the attribute is not set on initializing
226
+ ##
227
+ #Prepends an :itialize method to ensure the attribute is not set on initializing
219
228
  # a new instance unless :forced.
220
229
  #
221
- # Klass.new(status: :archived) # => ArgumentError
230
+ # @example
231
+ # Klass.new(status: :archived) #=> ArgumentError
222
232
  #
223
233
  def _prepend__initialize # rubocop:disable Metrics/MethodLength
224
234
  return if _transition_validation_module.method_defined?(:initialize)
@@ -14,11 +14,11 @@ module StateGate
14
14
  #
15
15
  # Both Class and Instance methods are generated for:
16
16
  #
17
- # * state interaction
18
- # * state sequences
19
- # * state scopes
20
- # * transition interaction
21
- # * transition validation
17
+ # - state interaction
18
+ # - state sequences
19
+ # - state scopes
20
+ # - transition interaction
21
+ # - transition validation
22
22
  #
23
23
  class Builder
24
24
 
@@ -35,24 +35,23 @@ module StateGate
35
35
  # ======================================================================
36
36
  private
37
37
 
38
+ ##
38
39
  # Initialize the Builder, creating the state gate and generating all the
39
40
  # Class and Instace helper methods on the sumbitted klass.
40
41
  #
41
- # [:klass]
42
+ # @param [Class] klass
42
43
  # The class containing the attribute to be cast as a state gate
43
- # (Class | required)
44
44
  #
45
- # [:attribute_name]
46
- # The name of the klass attribute to use for the state gate
47
- # (Symbol | required)
45
+ # @param [Symbol] attribute_name
46
+ # The name of the database attribute to use for the state gate
48
47
  #
49
- # [:config]
48
+ # @block config
50
49
  # The configuration block for the state gate.
51
- # (Block | required)
52
50
  #
53
- # StateGate::Builder.new(Klass, :attribute_name) do
51
+ # @example
52
+ # StateGate::Builder.new(Klass, :attribute_name) do
54
53
  # ... configuration ...
55
- # end
54
+ # end
56
55
  #
57
56
  def initialize(klass = nil, attribute_name = nil, &config)
58
57
  @klass = klass
@@ -70,19 +69,19 @@ module StateGate
70
69
  _cast_attribute_as_state_gate
71
70
 
72
71
  # generate the helper methods
73
- generate_helper_methods
72
+ _generate_helper_methods
74
73
  end
75
74
 
76
75
 
77
76
 
78
77
  # Generate the helper methods
79
78
  #
80
- def generate_helper_methods
79
+ def _generate_helper_methods
81
80
  # add the helper methods
82
- generate_scope_methods
83
- generate_state_methods
84
- generate_transition_methods
85
- generate_transition_validation_methods
81
+ _generate_scope_methods
82
+ _generate_state_methods
83
+ _generate_transition_methods
84
+ _generate_transition_validation_methods
86
85
 
87
86
  # warn if any state gate attribute methods are redefined
88
87
  _generate_method_redefine_detection
@@ -90,6 +89,7 @@ module StateGate
90
89
 
91
90
 
92
91
 
92
+ ##
93
93
  # Validate the klass to ensure it is a 'Class' and derived from ActiveRecord,
94
94
  # raising an error if not.
95
95
  #
@@ -100,15 +100,16 @@ module StateGate
100
100
 
101
101
 
102
102
 
103
+ ##
103
104
  # Parse the attribute name to ensure it is a valid input value and
104
105
  # detect if it's an attribute_alias.
105
106
  #
106
107
  # = meta
107
108
  #
108
- # * ensure it exists
109
- # * ensure it's a Symbol, to avoid string whitespace issues
110
- # * check is it's a registere attribute
111
- # * update @attribute & @alias
109
+ # - ensure it exists
110
+ # - ensure it's a Symbol, to avoid string whitespace issues
111
+ # - check is it's a registere attribute
112
+ # - update @attribute & @alias
112
113
  #
113
114
  def _parse_atttribute_name_for_alias
114
115
  if @attribute.nil?
@@ -125,6 +126,7 @@ module StateGate
125
126
 
126
127
 
127
128
 
129
+ ##
128
130
  # Validate we don't already have a state gate defined for the attribute,
129
131
  # raising an error if not.
130
132
  #
@@ -137,13 +139,14 @@ module StateGate
137
139
 
138
140
 
139
141
 
142
+ ##
140
143
  # Validate the attribute is a database String attribute,
141
144
  # raising an error if not.
142
145
  #
143
146
  # = meta
144
147
  #
145
- # * ensure it's mapped to a database column
146
- # * ensure it's a :string databse type
148
+ # - ensure it's mapped to a database column
149
+ # - ensure it's a :string databse type
147
150
  #
148
151
  def _assert_attribute_name_is_a_database_string_column
149
152
  if @klass.column_names.exclude?(@attribute.to_s)
@@ -157,10 +160,12 @@ module StateGate
157
160
 
158
161
 
159
162
 
163
+ ##
160
164
  # Builds a StateGate::Engine for the given attribute and add it to
161
165
  # the :stateables repository.
162
166
  #
163
- # config - the user generated configuration for the engine, including states,
167
+ # @block config
168
+ # the user generated configuration for the engine, including states,
164
169
  # transitions and optional settings
165
170
  #
166
171
  def _build_state_gate_engine(&config)
@@ -171,6 +176,7 @@ module StateGate
171
176
 
172
177
 
173
178
 
179
+ ##
174
180
  # Adds a :stateables class_attribute if it doesn't already exist and
175
181
  # initializes it to an empty Hash.
176
182
  #
@@ -178,17 +184,16 @@ module StateGate
178
184
  # created when generating state gates. The state gate attribute name is used
179
185
  # as the key.
180
186
  #
181
- # Example
182
- # Klass.stateables # => {
187
+ # @example
188
+ # Klass.stateables #=> {
183
189
  # status: <StateGate::Engine>,
184
190
  # account: <StateGate::Engine>
185
191
  # }
186
192
  #
187
- # Note
188
- #
189
- # The default empty Hash is set after the attribute is created to accommodate
190
- # ActiveRecord 5.0, even though ActiveRecord 6.0 allows it to be set within
191
- # the .class_attribute method.
193
+ # @note
194
+ # The default empty Hash is set after the attribute is created to accommodate
195
+ # ActiveRecord 5.0, even though ActiveRecord 6.0 allows it to be set within
196
+ # the .class_attribute method.
192
197
  #
193
198
  def _initialize_state_gate_repository
194
199
  return if @klass.methods(false).include?(:stateables)
@@ -199,6 +204,7 @@ module StateGate
199
204
 
200
205
 
201
206
 
207
+ ##
202
208
  # Builds a StateGate::Type with the custom states for the attribute,
203
209
  # then casts the attribute.
204
210
  #
@@ -208,9 +214,9 @@ module StateGate
208
214
  #
209
215
  # meta
210
216
  #
211
- # * retrieve the root attribute name if the supplied attribute is an alias.
212
- # * create a StateGate::Type with attributes states.
213
- # * overwrite the attribute, casting the type as the new StateGate::Type
217
+ # - retrieve the root attribute name if the supplied attribute is an alias.
218
+ # - create a StateGate::Type with attributes states.
219
+ # - overwrite the attribute, casting the type as the new StateGate::Type
214
220
  #
215
221
  def _cast_attribute_as_state_gate
216
222
  states = @engine.states
@@ -220,17 +226,21 @@ module StateGate
220
226
 
221
227
 
222
228
 
229
+ ##
223
230
  # Raise an ArgumentError for the given error, using I18n for the message.
224
231
  #
225
- # err - Symbol key for the I18n message
226
- #
227
- # args - Hash of attributes to pass to the message string.
232
+ # @param [Symbol] err
233
+ # the Symbol key for the I18n message
228
234
  #
229
- # [:klass] When true, args[:klass] will be updated with the 'KlassName'.
230
- # [:kattr] When true, args[:kattr] will be updated with 'KlassName#attribute'.
235
+ # @param [Hash] args
236
+ # Hash of attributes to pass to the message string.
231
237
  #
232
- # Example
238
+ # @option args :klass
239
+ # When present, args[:klass] will be updated with the 'KlassName'.
240
+ # @option args :kattr
241
+ # When true, args[:kattr] will be updated with 'KlassName#attribute'.
233
242
  #
243
+ # @example
234
244
  # err(:invalid_attribute_type_err, kattr: true)
235
245
  #
236
246
  def err(err, **args)