state_gate 1.2.3
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 +7 -0
- data/lib/state_gate/builder/conflict_detection_methods.rb +134 -0
- data/lib/state_gate/builder/dynamic_module_creation_methods.rb +120 -0
- data/lib/state_gate/builder/scope_methods.rb +96 -0
- data/lib/state_gate/builder/state_methods.rb +289 -0
- data/lib/state_gate/builder/transition_methods.rb +142 -0
- data/lib/state_gate/builder/transition_validation_methods.rb +247 -0
- data/lib/state_gate/builder.rb +244 -0
- data/lib/state_gate/engine/configurator.rb +230 -0
- data/lib/state_gate/engine/errator.rb +63 -0
- data/lib/state_gate/engine/fixer.rb +75 -0
- data/lib/state_gate/engine/scoper.rb +66 -0
- data/lib/state_gate/engine/sequencer.rb +116 -0
- data/lib/state_gate/engine/stator.rb +225 -0
- data/lib/state_gate/engine/transitioner.rb +73 -0
- data/lib/state_gate/engine.rb +65 -0
- data/lib/state_gate/locale/builder_en.yml +14 -0
- data/lib/state_gate/locale/engine_en.yml +58 -0
- data/lib/state_gate/locale/state_gate_en.yml +5 -0
- data/lib/state_gate/rspec/allow_transitions_on.rb +259 -0
- data/lib/state_gate/rspec/have_states.rb +140 -0
- data/lib/state_gate/rspec.rb +6 -0
- data/lib/state_gate/type.rb +112 -0
- data/lib/state_gate.rb +193 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1a3a28a8136472a91cd7c75616e189dd2c92a7b57a0bc18645395a46c55ff906
|
4
|
+
data.tar.gz: c909cd3d3a1101056ff1fe33ea6b94e38008f26a093476d7aaeb4ac17847b1ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b9cae6865505456fafa9da807e8472eb148f8af294c7631690b28ebba7b01c20e93f27b2bbe3ccf16c7ec27ea872e604726323bebb7639c7e7db726eb4bdd34
|
7
|
+
data.tar.gz: 7c104de800820cedee61c177a44504e2e1f052aaf529fd4990d3407ae337a4004c925f12162762f2db66b34a96ecda327e06e0c96c4dfc299feef63885b0e6f6
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StateGate
|
4
|
+
class Builder
|
5
|
+
##
|
6
|
+
# = Description
|
7
|
+
#
|
8
|
+
# Multiple private methods providing error handling functionality for
|
9
|
+
# StateGate::Builder.
|
10
|
+
#
|
11
|
+
module ConflictDetectionMethods
|
12
|
+
|
13
|
+
# Private
|
14
|
+
# ======================================================================
|
15
|
+
private
|
16
|
+
|
17
|
+
# Check if a class method is already defined. Checks are made:
|
18
|
+
# 1 --> is it an ActiveRecord dangerous method?
|
19
|
+
# 2 --> is it an ActiveRecord defined class method?
|
20
|
+
# 3 --> is it a singleton method of the klass?
|
21
|
+
# 4 --> is it defined within any of the klass ancestors?
|
22
|
+
#
|
23
|
+
# If found, raise an error giving a guide to where the method has been defined
|
24
|
+
#
|
25
|
+
def detect_class_method_conflict!(method_name)
|
26
|
+
defining_klass = _active_record_protected_method?(method_name) ||
|
27
|
+
_klass_singleton_method?(method_name) ||
|
28
|
+
_klass_ancestor_singleton_method?(method_name)
|
29
|
+
|
30
|
+
return unless defining_klass
|
31
|
+
|
32
|
+
raise_conflict_error method_name, type: 'a class', source: defining_klass
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
# Check an instance method is already defined. Checks are made:
|
38
|
+
# 1 --> is it an ActiveRecord dangerous method?
|
39
|
+
# 2 --> is it an ActiveRecord defined instance method?
|
40
|
+
# 3 --> is it an instance method of the klass?
|
41
|
+
# 4 --> is it defined within any of the klass ancestors?
|
42
|
+
#
|
43
|
+
# If found, raise an error giving a guide to where the method has been defined
|
44
|
+
#
|
45
|
+
def detect_instance_method_conflict!(method_name)
|
46
|
+
defining_klass = _active_record_protected_method?(method_name) ||
|
47
|
+
_klass_instance_method?(method_name) ||
|
48
|
+
_klass_ancestor_instance_method?(method_name)
|
49
|
+
|
50
|
+
return unless defining_klass
|
51
|
+
|
52
|
+
raise_conflict_error method_name, source: defining_klass
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# Raise a StateGate::ConflictError with a details message of the problem
|
58
|
+
#
|
59
|
+
# = Message
|
60
|
+
#
|
61
|
+
# StateGate for Klass#attribute will generate a class
|
62
|
+
# method 'statuses', which is already defined by ActiveRecord.
|
63
|
+
#
|
64
|
+
def raise_conflict_error(method_name, type: 'an instance', source: 'ActiveRecord')
|
65
|
+
fail StateGate::ConflictError, I18n.t('state_gate.builder.conflict_err',
|
66
|
+
klass: @klass,
|
67
|
+
attribute: @attribute,
|
68
|
+
type: type,
|
69
|
+
method_name: method_name,
|
70
|
+
source: source)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# Check if the method is an ActiveRecord dangerous method name
|
76
|
+
#
|
77
|
+
def _active_record_protected_method?(method_name) #:nodoc:
|
78
|
+
'ActiveRecord' if _dangerous_method_names.include?(method_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
# Check if the method is a singleton method of the klass
|
84
|
+
#
|
85
|
+
def _klass_singleton_method?(method_name) #:nodoc:
|
86
|
+
@klass.name if @klass.singleton_methods(false).include?(method_name.to_sym)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
# Check if the method is an ancestral singleton method of the klass
|
92
|
+
#
|
93
|
+
def _klass_ancestor_singleton_method?(method_name) #:nodoc:
|
94
|
+
return nil unless @klass.respond_to?(method_name)
|
95
|
+
|
96
|
+
@klass.singleton_class
|
97
|
+
.ancestors
|
98
|
+
.select { |a| a.instance_methods(false).include?(method_name.to_sym) }
|
99
|
+
.first
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
# Check if the method an instance method of the klass
|
105
|
+
#
|
106
|
+
def _klass_instance_method?(method_name) #:nodoc:
|
107
|
+
@klass.instance_methods(false).include?(method_name.to_sym) ? @klass.name : nil
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
# Check if the method is an ancestral singleton method of the klass
|
113
|
+
#
|
114
|
+
def _klass_ancestor_instance_method?(method_name) #:nodoc:
|
115
|
+
return nil unless @klass.instance_methods.include?(method_name.to_sym)
|
116
|
+
|
117
|
+
@klass.ancestors
|
118
|
+
.select { |a| a.instance_methods(false).include?(method_name.to_sym) }
|
119
|
+
.first
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
# returns an array of dagerous methods names found in
|
125
|
+
# ActiveRecord::AttributeMethods::RESTRICTED_CLASS_METHODS (which is called
|
126
|
+
# BLACKLISTED_CLASS_METHODS in 5.0 and 5.1)
|
127
|
+
#
|
128
|
+
def _dangerous_method_names
|
129
|
+
%w[private public protected allocate new name parent superclass]
|
130
|
+
end
|
131
|
+
|
132
|
+
end # ConflictDetectionMethods
|
133
|
+
end # Builder
|
134
|
+
end # StateGate
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StateGate
|
4
|
+
class Builder
|
5
|
+
##
|
6
|
+
# = Description
|
7
|
+
#
|
8
|
+
# Multiple private methods enabling StateGate::Builder to dynamically
|
9
|
+
# generate module, instance and class helper methods.
|
10
|
+
#
|
11
|
+
module DynamicModuleCreationMethods
|
12
|
+
|
13
|
+
# Private
|
14
|
+
# ======================================================================
|
15
|
+
private
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# = Dynamic Module Creation
|
20
|
+
# ======================================================================
|
21
|
+
|
22
|
+
# Dynamically generated module to hold the StateGate helper methods. This
|
23
|
+
# keeps a clear distinction between the state machine helper methods and the klass'
|
24
|
+
# own methods.
|
25
|
+
#
|
26
|
+
# The module is named after the class and is created if needed, or reused if exisitng.
|
27
|
+
#
|
28
|
+
# Note:
|
29
|
+
# the module is named "<klass>::StateGate_HelperMethods"
|
30
|
+
#
|
31
|
+
def _helper_methods_module
|
32
|
+
@_helper_methods_module ||= begin
|
33
|
+
if @klass.const_defined?('StateGate_HelperMethods')
|
34
|
+
"#{@klass}::StateGate_HelperMethods".constantize
|
35
|
+
else
|
36
|
+
@klass.const_set('StateGate_HelperMethods', Module.new)
|
37
|
+
mod = "#{@klass}::StateGate_HelperMethods".constantize
|
38
|
+
@klass.include mod
|
39
|
+
mod
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Method Re-defined Detection
|
46
|
+
# ======================================================================
|
47
|
+
|
48
|
+
|
49
|
+
# Adds the hook method :method_added to the Klass, detecting any new method
|
50
|
+
# definitions for an attribute already defined as a StateGate.
|
51
|
+
#
|
52
|
+
# If a matching method is discoverd, it adds a warning to logger, if defined,
|
53
|
+
# otherwise it outputs the warning to STDOUT via `puts`
|
54
|
+
#
|
55
|
+
# method_name - the name of the newly defined method.
|
56
|
+
#
|
57
|
+
# Note
|
58
|
+
#
|
59
|
+
# This method is added last so it does not trigger when StateGate adds
|
60
|
+
# the attribute methods.
|
61
|
+
#
|
62
|
+
# meta
|
63
|
+
#
|
64
|
+
# * loop though each state machine attribute.
|
65
|
+
# * does the new defined method use 'attr' or 'attr='?
|
66
|
+
# * if so then record an error logger if denied, othewise use `puts`
|
67
|
+
#
|
68
|
+
def _generate_method_redefine_detection # rubocop:disable Metrics/MethodLength
|
69
|
+
@klass.instance_eval(%(
|
70
|
+
def method_added(method_name)
|
71
|
+
stateables.keys.each do |attr_name|
|
72
|
+
if method_name&.to_s == attr_name ||
|
73
|
+
method_name&.to_s == "\#{attr_name}="
|
74
|
+
|
75
|
+
msg = "WARNING! \#{self.name}#\#{attr_name} is a defined StateGate and"
|
76
|
+
msg += " redefining :\#{method_name} may cause conflict."
|
77
|
+
|
78
|
+
logger ? logger.warn(msg) : puts("\n\n\#{msg}\n\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
super(method_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
), __FILE__, __LINE__ - 15)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
# Method Creation
|
90
|
+
# ======================================================================
|
91
|
+
|
92
|
+
# Add an Class helper method to the _helper_methods_module
|
93
|
+
#
|
94
|
+
# method_name - a String name for the method, needed to check for conflicts
|
95
|
+
# file - a String file name for error reporting
|
96
|
+
# line - a String or Integer line number for error reporting
|
97
|
+
# method_body - a String to bhe evaluates in the module
|
98
|
+
#
|
99
|
+
def add__klass__helper_method(method_name, file, line, method_body)
|
100
|
+
detect_class_method_conflict!(method_name)
|
101
|
+
@klass.instance_eval(method_body, file, line)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
# Add an instance helper method to the _helper_methods_module
|
107
|
+
#
|
108
|
+
# method_name - a String name for the method, needed to check for conflicts
|
109
|
+
# file - a String file name for error reporting
|
110
|
+
# line - a String or Integer line number for error reporting
|
111
|
+
# method_body - a String to bhe evaluates in the module
|
112
|
+
#
|
113
|
+
def add__instance__helper_method(method_name, file, line, method_body)
|
114
|
+
detect_instance_method_conflict!(method_name)
|
115
|
+
_helper_methods_module.module_eval(method_body, file, line)
|
116
|
+
end
|
117
|
+
|
118
|
+
end # DynamicModuleCreationMethods
|
119
|
+
end # Builder
|
120
|
+
end # StateGate
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StateGate
|
4
|
+
class Builder
|
5
|
+
##
|
6
|
+
# = Description
|
7
|
+
#
|
8
|
+
# Multiple private methods enabling StateGate::Builder to generate
|
9
|
+
# scopes for each state.
|
10
|
+
#
|
11
|
+
# * fetch all records with the given state:
|
12
|
+
# Klass.active # => Klass.where(state: :active)
|
13
|
+
#
|
14
|
+
# * fetch all records without the given state:
|
15
|
+
# Klass.not_active # => Klass.where.not(state: :active)
|
16
|
+
#
|
17
|
+
# * fetch all records with the supplied states:
|
18
|
+
# Klass.with_statuses(:pending, :active) # => Klass.where(state: [:pending, :active])
|
19
|
+
#
|
20
|
+
module ScopeMethods
|
21
|
+
|
22
|
+
# = Private
|
23
|
+
# ======================================================================
|
24
|
+
private
|
25
|
+
|
26
|
+
|
27
|
+
# Add scopes to the klass for filtering by state
|
28
|
+
#
|
29
|
+
# Note:
|
30
|
+
# The scope name is a concatenation of <prefix><state name><suffix>
|
31
|
+
#
|
32
|
+
def generate_scope_methods
|
33
|
+
return unless @engine.include_scopes?
|
34
|
+
|
35
|
+
_add__klass__state_scopes
|
36
|
+
_add__klass__not_state_scopes
|
37
|
+
_add__klass__with_attrs_scope
|
38
|
+
|
39
|
+
_add__klass__with_attrs_scope(@alias) if @alias
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
# ======================================================================
|
45
|
+
# Klass methods
|
46
|
+
# ======================================================================
|
47
|
+
|
48
|
+
# Add a klass method that scopes records to the specified state.
|
49
|
+
# eg:
|
50
|
+
# Klass.active # => ActiveRecord::Relation
|
51
|
+
# Klass.active_status # => ActiveRecord::Relation
|
52
|
+
#
|
53
|
+
def _add__klass__state_scopes
|
54
|
+
attr_name = @attribute
|
55
|
+
|
56
|
+
@engine.states.each do |state|
|
57
|
+
scope_name = @engine.scope_name_for_state(state)
|
58
|
+
detect_class_method_conflict! scope_name
|
59
|
+
@klass.scope(scope_name, -> { where(attr_name => state) })
|
60
|
+
end # each state
|
61
|
+
end # _add__klass__state_scopes
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
# Add a klass method that scopes records to those without the specified state.
|
66
|
+
# eg:
|
67
|
+
# Klass.not_active # => ActiveRecord::Relation
|
68
|
+
# Klass.not_active_status # => ActiveRecord::Relation
|
69
|
+
#
|
70
|
+
def _add__klass__not_state_scopes
|
71
|
+
attr_name = @attribute
|
72
|
+
|
73
|
+
@engine.states.each do |state|
|
74
|
+
scope_name = @engine.scope_name_for_state(state)
|
75
|
+
detect_class_method_conflict! "not_#{scope_name}"
|
76
|
+
@klass.scope "not_#{scope_name}", -> { where.not(attr_name => state) }
|
77
|
+
end # each state
|
78
|
+
end # _add__klass__not_state_scopes
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
# Add a klass method that scopes records to the given states.
|
83
|
+
# eg:
|
84
|
+
# Klass.with_statuses(:active, :pending) # => ActiveRecord::Relation
|
85
|
+
#
|
86
|
+
def _add__klass__with_attrs_scope(method_name = @attribute)
|
87
|
+
attr_name = @attribute
|
88
|
+
method_name = "with_#{method_name.to_s.pluralize}"
|
89
|
+
|
90
|
+
detect_class_method_conflict! method_name
|
91
|
+
@klass.scope method_name, ->(states) { where(attr_name => Array(states)) }
|
92
|
+
end # _add__klass__with_attrs_scope
|
93
|
+
|
94
|
+
end # LockingMethods
|
95
|
+
end # Builder
|
96
|
+
end # StateGate
|
@@ -0,0 +1,289 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StateGate
|
4
|
+
class Builder
|
5
|
+
##
|
6
|
+
# = Description
|
7
|
+
#
|
8
|
+
# Multiple private methods enabling StateGate::Builder to generate
|
9
|
+
# state functionality.
|
10
|
+
#
|
11
|
+
# * query the class for all state:
|
12
|
+
# Klass.statuses # => [:pending, :active, :archived]
|
13
|
+
#
|
14
|
+
# * query the class for the human names of all state:
|
15
|
+
# Klass.human_statuses # => ['Pending Activation', 'Active', 'Archived']
|
16
|
+
#
|
17
|
+
# * query the class for an Array of human names/state names for use in a select form:
|
18
|
+
# Klass.statuses_for_select
|
19
|
+
# # => [['Pending Activation', 'pending'],["Active', 'active'], ['Archived','archived']]
|
20
|
+
#
|
21
|
+
# * list all attribute states:
|
22
|
+
# .status_states # => [:pending, :active, :archived]
|
23
|
+
#
|
24
|
+
# * list all human names for the attribute states:
|
25
|
+
# .status_human_names # => ['Pending Activation', 'Active', 'Archived']
|
26
|
+
#
|
27
|
+
# * list the human name for the attribute state:
|
28
|
+
# .human_status # => 'Pending Activation'
|
29
|
+
#
|
30
|
+
# * is a particular state set:
|
31
|
+
# .pending? # => false
|
32
|
+
# .active? # => true
|
33
|
+
# .archived? # => false
|
34
|
+
#
|
35
|
+
# * is a particular state not set:
|
36
|
+
# .not_pending? # => true
|
37
|
+
# .not_active? # => false
|
38
|
+
# .not_archived? # => true
|
39
|
+
#
|
40
|
+
# * list the allowed transitions for the current state.
|
41
|
+
# .status_transitions # => [:suspended, :archived]
|
42
|
+
#
|
43
|
+
module StateMethods
|
44
|
+
|
45
|
+
# Private
|
46
|
+
# ======================================================================
|
47
|
+
private
|
48
|
+
|
49
|
+
# Add Class and instance methods that allow querying states
|
50
|
+
#
|
51
|
+
def generate_state_methods
|
52
|
+
add_state_attribute_methods
|
53
|
+
add_state_alias_methods
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
# add attribute methods
|
59
|
+
#
|
60
|
+
def add_state_attribute_methods
|
61
|
+
_add__klass__attrs
|
62
|
+
_add__klass__human_attrs
|
63
|
+
_add__klass__attrs_for_select
|
64
|
+
|
65
|
+
_add__instance__attrs
|
66
|
+
_add__instance__human_attrs
|
67
|
+
_add__instance__human_attr
|
68
|
+
_add__instance__state?
|
69
|
+
_add__instance__not_state?
|
70
|
+
_add__instance__attrs_for_select
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# add alias methods
|
76
|
+
#
|
77
|
+
def add_state_alias_methods
|
78
|
+
return unless @alias
|
79
|
+
|
80
|
+
_add__klass__attrs(@alias)
|
81
|
+
_add__klass__human_attrs(@alias)
|
82
|
+
_add__klass__attrs_for_select(@alias)
|
83
|
+
|
84
|
+
_add__instance__attrs(@alias)
|
85
|
+
_add__instance__human_attrs(@alias)
|
86
|
+
_add__instance__attrs_for_select(@alias)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
# ======================================================================
|
92
|
+
# Class Merthods
|
93
|
+
# ======================================================================
|
94
|
+
|
95
|
+
# Adds a Class method to return an Array of the defined states for the attribute
|
96
|
+
# eg:
|
97
|
+
# Klass.statuses # => [:pending, :active, :suspended, :archived]
|
98
|
+
#
|
99
|
+
def _add__klass__attrs(method_name = @attribute)
|
100
|
+
method_name = method_name.to_s.pluralize
|
101
|
+
|
102
|
+
add__klass__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
103
|
+
def #{method_name}
|
104
|
+
stateables[:#{@attribute}].states
|
105
|
+
end
|
106
|
+
))
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
# Adds a Class method to return an Array of the human names of the defined states
|
112
|
+
# for the attribute
|
113
|
+
# eg:
|
114
|
+
# Klass.human_statuses # => ['Pending Activation', 'Active',
|
115
|
+
# 'Suspended by Admin', 'Archived']
|
116
|
+
#
|
117
|
+
def _add__klass__human_attrs(method_name = @attribute)
|
118
|
+
method_name = "human_#{method_name.to_s.pluralize}"
|
119
|
+
|
120
|
+
add__klass__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
121
|
+
def #{method_name}
|
122
|
+
stateables[:#{@attribute}].human_states
|
123
|
+
end
|
124
|
+
))
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
# Adds a Class method to return an Array of the human and state names for the
|
130
|
+
# attribute, suitable for using in a form select statement.
|
131
|
+
#
|
132
|
+
# sorted - if TRUE, the array is sorted in alphabetical order by human name
|
133
|
+
# otherwise it is in the order specified
|
134
|
+
#
|
135
|
+
# Klass.statuses_for_select # => [ ['Pending Activation', 'pending'],
|
136
|
+
# ['Active', 'active'],
|
137
|
+
# ['Suspended by Admin', 'suspended',
|
138
|
+
# ['Archived', 'archived'] ]
|
139
|
+
#
|
140
|
+
# Klass.statuses_for_select(true) # => [ ['Active', 'active'],
|
141
|
+
# ['Pending Activation', 'pending'],
|
142
|
+
# ['Suspended by Admin', 'suspended',
|
143
|
+
# ['Archived', 'archived'] ]
|
144
|
+
#
|
145
|
+
# Note:
|
146
|
+
# States should NEVER be set from direct user selection. This method is
|
147
|
+
# intended for use within search forms, where the user may filter by state.
|
148
|
+
#
|
149
|
+
def _add__klass__attrs_for_select(method_name = @attribute)
|
150
|
+
method_name = "#{method_name.to_s.pluralize}_for_select"
|
151
|
+
|
152
|
+
add__klass__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
153
|
+
def #{method_name}(sorted = false)
|
154
|
+
stateables[:#{@attribute}].states_for_select(sorted)
|
155
|
+
end
|
156
|
+
))
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
# ======================================================================
|
162
|
+
# Instance Methods
|
163
|
+
# ======================================================================
|
164
|
+
|
165
|
+
# Adds an Instance method to return Array of the defined states for the attribute
|
166
|
+
# eg:
|
167
|
+
# .statuses # => [:pending, :active, :suspended, :archived]
|
168
|
+
#
|
169
|
+
def _add__instance__attrs(method_name = @attribute)
|
170
|
+
method_name = method_name.to_s.pluralize
|
171
|
+
|
172
|
+
add__instance__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
173
|
+
def #{method_name}
|
174
|
+
stateables[:#{@attribute}].states
|
175
|
+
end
|
176
|
+
))
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
|
181
|
+
# Adds an Instance method to return an Array of the human names for the attribute
|
182
|
+
# eg:
|
183
|
+
# .status_human_states # => ['Pending Activation', 'Active',
|
184
|
+
# 'Suspended by Admin', 'Archived']
|
185
|
+
#
|
186
|
+
def _add__instance__human_attrs(method_name = @attribute)
|
187
|
+
method_name = "human_#{method_name.to_s.pluralize}"
|
188
|
+
|
189
|
+
add__instance__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
190
|
+
def #{method_name}
|
191
|
+
stateables[:#{@attribute}].human_states
|
192
|
+
end
|
193
|
+
))
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
# Adds an Instance method to return the human name for the attribute's state
|
199
|
+
# eg:
|
200
|
+
# .human_status # => 'Suspended by Admin'
|
201
|
+
#
|
202
|
+
def _add__instance__human_attr(method_name = @attribute)
|
203
|
+
method_name = "human_#{method_name.to_s}"
|
204
|
+
|
205
|
+
add__instance__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
206
|
+
def #{method_name}
|
207
|
+
stateables[:#{@attribute}].human_state_for(#{@attribute})
|
208
|
+
end
|
209
|
+
))
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
|
214
|
+
# Adds an Instance method for each state, returning TRUE if the state is set
|
215
|
+
# eg:
|
216
|
+
# --> when :active
|
217
|
+
# .active? # => true
|
218
|
+
# .archived? # => false
|
219
|
+
#
|
220
|
+
def _add__instance__state?
|
221
|
+
@engine.states.each do |state|
|
222
|
+
method_name = "#{@engine.scope_name_for_state(state)}?"
|
223
|
+
|
224
|
+
add__instance__helper_method(method_name, __FILE__, __LINE__ - 3, %(
|
225
|
+
def #{method_name}
|
226
|
+
self[:#{@attribute}] == :#{state}.to_s
|
227
|
+
end
|
228
|
+
))
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
# Adds an Instance method for each state, returning TRUE if the state is not set.
|
235
|
+
# eg:
|
236
|
+
# --> when :active
|
237
|
+
# .not_active? # => false
|
238
|
+
# .not_archived? # => true
|
239
|
+
#
|
240
|
+
# def _add__instance__not_state?
|
241
|
+
# attr_name = @attribute
|
242
|
+
#
|
243
|
+
def _add__instance__not_state?
|
244
|
+
@engine.states.each do |state|
|
245
|
+
method_name = "not_#{@engine.scope_name_for_state(state)}?"
|
246
|
+
|
247
|
+
add__instance__helper_method(method_name, __FILE__, __LINE__ - 3, %(
|
248
|
+
def #{method_name}
|
249
|
+
self[:#{@attribute}] != :#{state}.to_s
|
250
|
+
end
|
251
|
+
))
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
# Adds a, Instance method to return an Array of the human and state names for the
|
258
|
+
# attribute, suitable for using in a form select statement.
|
259
|
+
#
|
260
|
+
# sorted - if TRUE, the array is sorted in alphabetical order by human name
|
261
|
+
# otherwise it is in the order specified
|
262
|
+
#
|
263
|
+
# .statuses_for_select # => [ ['Pending Activation', 'pending'],
|
264
|
+
# ['Active', 'active'],
|
265
|
+
# ['Suspended by Admin', 'suspended',
|
266
|
+
# ['Archived', 'archived'] ]
|
267
|
+
#
|
268
|
+
# .statuses_for_select(true) # => [ ['Active', 'active'],
|
269
|
+
# ['Pending Activation', 'pending'],
|
270
|
+
# ['Suspended by Admin', 'suspended',
|
271
|
+
# ['Archived', 'archived'] ]
|
272
|
+
#
|
273
|
+
# Note:
|
274
|
+
# States should NEVER be set from direct user selection. This method is
|
275
|
+
# intended for use within search forms, where the user may filter by state.
|
276
|
+
#
|
277
|
+
def _add__instance__attrs_for_select(method_name = @attribute)
|
278
|
+
method_name = "#{method_name.to_s.pluralize}_for_select"
|
279
|
+
|
280
|
+
add__instance__helper_method(method_name, __FILE__, __LINE__ - 2, %(
|
281
|
+
def #{method_name}(sorted = false)
|
282
|
+
stateables[:#{@attribute}].states_for_select(sorted)
|
283
|
+
end
|
284
|
+
))
|
285
|
+
end
|
286
|
+
|
287
|
+
end # StateMethods
|
288
|
+
end # Builder
|
289
|
+
end # StateGate
|