state_gate 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|