onboardable 1.0.1 → 1.1.1
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 +4 -4
- data/.rubocop.yml +0 -3
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +8 -6
- data/README.md +29 -48
- data/lib/onboardable/errors.rb +49 -3
- data/lib/onboardable/list/base.rb +59 -0
- data/lib/onboardable/list/builder.rb +91 -0
- data/lib/onboardable/list/navigation.rb +68 -0
- data/lib/onboardable/step.rb +52 -3
- data/lib/onboardable/utils/warnings.rb +18 -0
- data/lib/onboardable/version.rb +1 -1
- data/lib/onboardable.rb +32 -5
- data/sig/onboardable/errors.rbs +32 -0
- data/sig/onboardable/list/base.rbs +19 -0
- data/sig/onboardable/list/builder.rbs +28 -0
- data/sig/onboardable/{utils → list}/navigation.rbs +1 -1
- data/sig/onboardable/step.rbs +3 -0
- data/sig/onboardable/utils/warnings.rbs +9 -0
- data/sig/onboardable.rbs +5 -3
- metadata +14 -11
- data/lib/onboardable/list.rb +0 -38
- data/lib/onboardable/list_builder.rb +0 -36
- data/lib/onboardable/utils/navigation.rb +0 -43
- data/sig/onboardable/list.rbs +0 -17
- data/sig/onboardable/list_builder.rbs +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef1662a800c2b2c562af504026f4a84bd7a93c895236784e20e53659d7fdcaa3
|
4
|
+
data.tar.gz: 90df600da2944d056ca6d64891a311e4507f1227c2827b44485735451ccc329d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32e156cde48492f62a29a41b1c382c1be86bd5922b47ddcd32a5ca606127204002f8805da9c1adc087ea005c90a26a20de9081939617d9f623f9b5075fecd113
|
7
|
+
data.tar.gz: 3f8a70a79552741d71c0de140204220e66d70b5a43e5e7ce3c54c15b06900e55e15f5c840812c3463f3c10909bb693788e0aeaeb8039617a026b2fe2cef2bcc0
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
- Added `onboarding` class method to define the onboarding steps.
|
6
|
+
- Added [documentation link](https://rubydoc.info/gems/onboardable) to gemspec.
|
7
|
+
|
8
|
+
## [1.1.0] - 2024-05-21
|
9
|
+
|
10
|
+
- Introduced `step_from` method for adding steps from external sources.
|
11
|
+
- Added warn_about_override method to alert on step overrides.
|
12
|
+
- Added YARD documentation to the project for improved code documentation.
|
13
|
+
|
14
|
+
## [1.0.1] - 2024-05-09
|
15
|
+
|
5
16
|
- Added `first_step` and `last_step` methods to easily access
|
6
17
|
the boundaries of step lists.
|
7
18
|
- Added `progress` method for calculating onboarding completion percentage.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
onboardable (1.
|
4
|
+
onboardable (1.1.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -15,11 +15,12 @@ GEM
|
|
15
15
|
parser (3.3.1.0)
|
16
16
|
ast (~> 2.4.1)
|
17
17
|
racc
|
18
|
-
racc (1.
|
18
|
+
racc (1.8.0)
|
19
19
|
rainbow (3.1.1)
|
20
20
|
rake (13.2.1)
|
21
|
-
regexp_parser (2.9.
|
22
|
-
rexml (3.2.
|
21
|
+
regexp_parser (2.9.2)
|
22
|
+
rexml (3.2.8)
|
23
|
+
strscan (>= 3.0.9)
|
23
24
|
rspec (3.13.0)
|
24
25
|
rspec-core (~> 3.13.0)
|
25
26
|
rspec-expectations (~> 3.13.0)
|
@@ -33,7 +34,7 @@ GEM
|
|
33
34
|
diff-lcs (>= 1.2.0, < 2.0)
|
34
35
|
rspec-support (~> 3.13.0)
|
35
36
|
rspec-support (3.13.1)
|
36
|
-
rubocop (1.63.
|
37
|
+
rubocop (1.63.5)
|
37
38
|
json (~> 2.3)
|
38
39
|
language_server-protocol (>= 3.17.0)
|
39
40
|
parallel (~> 1.10)
|
@@ -69,6 +70,7 @@ GEM
|
|
69
70
|
simplecov_json_formatter (~> 0.1)
|
70
71
|
simplecov-html (0.12.3)
|
71
72
|
simplecov_json_formatter (0.1.4)
|
73
|
+
strscan (3.1.0)
|
72
74
|
unicode-display_width (2.5.0)
|
73
75
|
|
74
76
|
PLATFORMS
|
@@ -79,7 +81,7 @@ DEPENDENCIES
|
|
79
81
|
onboardable!
|
80
82
|
rake (~> 13.2, >= 13.2.1)
|
81
83
|
rspec (~> 3.13)
|
82
|
-
rubocop (~> 1.63, >= 1.63.
|
84
|
+
rubocop (~> 1.63, >= 1.63.5)
|
83
85
|
rubocop-performance (~> 1.21)
|
84
86
|
rubocop-rake (~> 0.6.0)
|
85
87
|
rubocop-rspec (~> 2.29, >= 2.29.2)
|
data/README.md
CHANGED
@@ -31,32 +31,31 @@ project as per the installation guide provided earlier.
|
|
31
31
|
|
32
32
|
### Basic Configuration
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
class, you would modify the class as follows:
|
34
|
+
To incorporate an onboarding process into your Ruby User class, start by
|
35
|
+
including the Onboardable module to add onboarding functionality. Then,
|
36
|
+
define the onboarding steps with the has_onboarding method, detailing each
|
37
|
+
step with helpful tooltips and descriptions. Here's how you can set it up:
|
39
38
|
|
40
39
|
```ruby
|
41
40
|
class User
|
42
41
|
include Onboardable
|
43
|
-
end
|
44
|
-
```
|
45
42
|
|
46
|
-
|
43
|
+
has_onboarding do
|
44
|
+
# Use the `step` method to steps with a name and optional data
|
45
|
+
step 'welcome', message: 'Welcome to your new account!'
|
46
|
+
step 'account_setup', task: 'Create credentials'
|
47
|
+
step 'confirmation'
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
# Use the `step_from` method to define steps from external providers
|
50
|
+
step_from ExternalStepProvider
|
51
|
+
end
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
class
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
step 'Complete Profile' # This step does not include specific action data
|
59
|
-
step 'Introduction Tour', description: 'Get to know your new workspace!'
|
54
|
+
# External class for providing a reusable onboarding step
|
55
|
+
class ExternalStepProvider
|
56
|
+
# Define class method to return an onboarding step
|
57
|
+
def self.to_onboarding_step
|
58
|
+
Onboardable::Step.new('external_step', info: 'This is an external step.')
|
60
59
|
end
|
61
60
|
end
|
62
61
|
```
|
@@ -79,7 +78,6 @@ that allow step navigation and state verification:
|
|
79
78
|
|
80
79
|
```ruby
|
81
80
|
onboarding = User.new.onboarding
|
82
|
-
# Initializes the onboarding process for a new user instance
|
83
81
|
```
|
84
82
|
|
85
83
|
1. **Navigating Through Steps**
|
@@ -93,11 +91,8 @@ that allow step navigation and state verification:
|
|
93
91
|
what's next or advance to it, updating the current step status.
|
94
92
|
|
95
93
|
```ruby
|
96
|
-
onboarding.next_step
|
97
|
-
#
|
98
|
-
|
99
|
-
onboarding.next_step!
|
100
|
-
# Advances to the next step, updating the current step
|
94
|
+
onboarding.next_step # Preview the next step
|
95
|
+
onboarding.next_step! # Advance to the next step
|
101
96
|
```
|
102
97
|
|
103
98
|
- **Previous Step**
|
@@ -106,11 +101,8 @@ that allow step navigation and state verification:
|
|
106
101
|
making changes or updates to revert to the previous step.
|
107
102
|
|
108
103
|
```ruby
|
109
|
-
onboarding.prev_step
|
110
|
-
#
|
111
|
-
|
112
|
-
onboarding.prev_step!
|
113
|
-
# Reverts to the previous step, updating the current step
|
104
|
+
onboarding.prev_step # Preview the previous step
|
105
|
+
onboarding.prev_step! # Move back to the previous step
|
114
106
|
```
|
115
107
|
|
116
108
|
1. **Check Step Position**
|
@@ -119,11 +111,8 @@ that allow step navigation and state verification:
|
|
119
111
|
to manage UI elements like 'Next' or 'Back' buttons appropriately.
|
120
112
|
|
121
113
|
```ruby
|
122
|
-
onboarding.first_step?
|
123
|
-
#
|
124
|
-
|
125
|
-
onboarding.last_step?
|
126
|
-
# Returns true if the current step is the last
|
114
|
+
onboarding.first_step? # Is the first step?
|
115
|
+
onboarding.last_step? # Is the last step?
|
127
116
|
```
|
128
117
|
|
129
118
|
1. **Monitor Progress**
|
@@ -132,8 +121,7 @@ that allow step navigation and state verification:
|
|
132
121
|
to provide users with an indication of how far they have progressed.
|
133
122
|
|
134
123
|
```ruby
|
135
|
-
onboarding.progress
|
136
|
-
# Returns the percentage of onboarding completion
|
124
|
+
onboarding.progress # Returns the completion percentage
|
137
125
|
```
|
138
126
|
|
139
127
|
1. **Access Current Step Details**
|
@@ -143,17 +131,10 @@ that allow step navigation and state verification:
|
|
143
131
|
the user complete tasks associated with the step.
|
144
132
|
|
145
133
|
```ruby
|
146
|
-
onboarding.current_step
|
147
|
-
#
|
148
|
-
|
149
|
-
onboarding.current_step.
|
150
|
-
# Returns the name of the current step
|
151
|
-
|
152
|
-
onboarding.current_step.data
|
153
|
-
# Returns the custom data associated with the step or an empty hash if not specified
|
154
|
-
|
155
|
-
onboarding.current_step.status
|
156
|
-
# Provides the current status or progress of the step
|
134
|
+
onboarding.current_step # Current step details
|
135
|
+
onboarding.current_step.name # Step name
|
136
|
+
onboarding.current_step.data # Step custom data
|
137
|
+
onboarding.current_step.status # Step status
|
157
138
|
```
|
158
139
|
|
159
140
|
1. **Complete the Onboarding Process**
|
data/lib/onboardable/errors.rb
CHANGED
@@ -1,33 +1,79 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Onboardable
|
4
|
+
# Base error class for Onboardable-related exceptions.
|
4
5
|
class Error < StandardError; end
|
5
6
|
|
6
|
-
|
7
|
+
# Error raised when a method is called on an object that does not define it.
|
8
|
+
class UndefinedMethodError < Error
|
9
|
+
# Initializes a new instance of UndefinedMethodError.
|
10
|
+
#
|
11
|
+
# @param klass [Class] The class that does not have the method defined.
|
12
|
+
# @param method [Symbol, String] The name of the method that is not defined.
|
13
|
+
def initialize(klass, method)
|
14
|
+
super("Method `#{method}` is not defined for `#{klass}`.")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Error raised when an object cannot be converted to a Step.
|
19
|
+
class StepConversionError < Error
|
20
|
+
# Initializes a new instance of StepConversionError.
|
21
|
+
#
|
22
|
+
# @param klass [Class] The class that failed to convert.
|
23
|
+
# @param step [Object] The object returned by the failed conversion.
|
24
|
+
def initialize(klass, step)
|
25
|
+
super("can't convert #{klass} to Step (#{klass}#to_onboarding_step gives #{step.class}).")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Error raised when an operation is attempted on an empty steps collection.
|
30
|
+
class EmptyStepsError < Error
|
31
|
+
# Initializes a new instance of EmptyStepsError.
|
32
|
+
# This error indicates that an operation requiring non-empty steps was attempted on an empty collection.
|
7
33
|
def initialize
|
8
|
-
super('Cannot be performed because the
|
34
|
+
super('Cannot be performed because the steps is empty.')
|
9
35
|
end
|
10
36
|
end
|
11
37
|
|
38
|
+
# Error raised when an invalid step is encountered within the onboarding process.
|
12
39
|
class InvalidStepError < Error
|
40
|
+
# Initializes a new InvalidStepError with details about the issue.
|
41
|
+
#
|
42
|
+
# @param step [String] The invalid step that triggered the error.
|
43
|
+
# @param expected_steps [Array<String>] The list of valid steps expected at this point.
|
13
44
|
def initialize(step, expected_steps)
|
14
45
|
super("Invalid step: `#{step}`. Must be one of: `#{expected_steps.join('`, `')}`.")
|
15
46
|
end
|
16
47
|
end
|
17
48
|
|
18
|
-
|
49
|
+
# Error raised when an invalid comparison result is encountered.
|
50
|
+
class ComparisonResultError < Error
|
51
|
+
# Initializes a new ComparisonResultError with details about the issue.
|
52
|
+
#
|
53
|
+
# @param comparison [Integer] The invalid comparison result that triggered the error.
|
54
|
+
# @param expected_comparisons [Array<Integer>] The list of valid comparison results expected.
|
19
55
|
def initialize(comparison, expected_comparisons)
|
20
56
|
super("Invalid comparison result: `#{comparison}`. Must be one of: #{expected_comparisons.join('`, `')}.")
|
21
57
|
end
|
22
58
|
end
|
23
59
|
|
60
|
+
# Error raised when attempting to navigate beyond the last step in the onboarding process.
|
24
61
|
class LastStepError < Error
|
62
|
+
# Initializes a new LastStepError indicating that the end of the step sequence has been reached.
|
63
|
+
#
|
64
|
+
# @param step [String] The last step that was attempted to be surpassed.
|
65
|
+
# @param expected_steps [Array<String>] The complete list of valid steps in the onboarding process.
|
25
66
|
def initialize(step, expected_steps)
|
26
67
|
super("Currently `#{step}` the last step. Available steps are: `#{expected_steps.join('`, `')}`.")
|
27
68
|
end
|
28
69
|
end
|
29
70
|
|
71
|
+
# Error raised when attempting to navigate before the first step in the onboarding process.
|
30
72
|
class FirstStepError < Error
|
73
|
+
# Initializes a new FirstStepError indicating that the beginning of the step sequence has been reached.
|
74
|
+
#
|
75
|
+
# @param step [String] The first step that was attempted to be preceded.
|
76
|
+
# @param expected_steps [Array<String>] The complete list of valid steps in the onboarding process.
|
31
77
|
def initialize(step, expected_steps)
|
32
78
|
super("Currently `#{step}` the first step. Available steps are: `#{expected_steps.join('`, `')}`.")
|
33
79
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Onboardable
|
4
|
+
module List
|
5
|
+
# The List class manages a sequence of steps in an onboarding process, tracking progress and current state.
|
6
|
+
class Base
|
7
|
+
include Navigation
|
8
|
+
|
9
|
+
# @return [Array<Step>] the steps in the list
|
10
|
+
attr_reader :steps
|
11
|
+
|
12
|
+
# @return [Step] the current step in the list
|
13
|
+
attr_reader :current_step
|
14
|
+
|
15
|
+
# Initializes a new instance of List with steps and a current step.
|
16
|
+
#
|
17
|
+
# @param steps [Array<Step>] An array of steps comprising the onboarding process.
|
18
|
+
# @param current_step [Step] The step currently active in the process.
|
19
|
+
def initialize(steps, current_step)
|
20
|
+
self.steps = steps
|
21
|
+
self.current_step = current_step
|
22
|
+
end
|
23
|
+
|
24
|
+
# Calculates and returns the onboarding progress as a percentage.
|
25
|
+
#
|
26
|
+
# @return [Float] The completion percentage of the onboarding process.
|
27
|
+
def progress
|
28
|
+
(step_index!(current_step).to_f / steps.size) * 100
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Sets and validates the steps array, ensuring it is an Array of Step objects.
|
34
|
+
#
|
35
|
+
# @param steps [Array<Step>] The steps to be assigned to the list.
|
36
|
+
def steps=(steps)
|
37
|
+
@steps = Array(Array.try_convert(steps)).freeze
|
38
|
+
end
|
39
|
+
|
40
|
+
# Updates the current step and recalibrates the status of all steps in the list.
|
41
|
+
#
|
42
|
+
# @param raw_current_step [Step] The new current step to set.
|
43
|
+
def current_step=(raw_current_step)
|
44
|
+
current_step_index = step_index!(raw_current_step)
|
45
|
+
steps.each_with_index { |step, index| step.update_status!(index <=> current_step_index) }
|
46
|
+
@current_step = steps.fetch(current_step_index)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Determines the index of a given step in the list, ensuring the step exists.
|
50
|
+
#
|
51
|
+
# @param raw_step [Step] The step for which the index is requested.
|
52
|
+
# @return [Integer] The index of the step within the list.
|
53
|
+
# @raise [InvalidStepError] Raises an error if the step does not exist in the list.
|
54
|
+
def step_index!(raw_step)
|
55
|
+
steps.index { |step| step == raw_step } || raise(InvalidStepError.new(raw_step.to_str, steps.map(&:to_str)))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Onboardable
|
4
|
+
module List
|
5
|
+
# The Builder class constructs and manages an onboarding step list, adding steps and building the final list.
|
6
|
+
class Builder
|
7
|
+
include Utils::Warnings
|
8
|
+
|
9
|
+
# @return [Hash] A hash where keys are step names and values are Step objects.
|
10
|
+
attr_reader :steps
|
11
|
+
|
12
|
+
# @return [Step] The current step in the building process, defaulting to the first added step.
|
13
|
+
attr_accessor :current_step
|
14
|
+
|
15
|
+
# Initializes a new instance of ListBuilder.
|
16
|
+
def initialize
|
17
|
+
self.steps = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a new Step object and adds it to the builder.
|
21
|
+
#
|
22
|
+
# @param name [String] The name of the step.
|
23
|
+
# @param data [Hash] The data associated with the step.
|
24
|
+
# @return [Step] The created step.
|
25
|
+
def create_step(name, data = {})
|
26
|
+
Step.new(name, data).tap { |step| add_step(step) }
|
27
|
+
end
|
28
|
+
alias step create_step
|
29
|
+
|
30
|
+
# Converts a class to a Step object and adds it to the builder.
|
31
|
+
#
|
32
|
+
# @param klass [Class] The class to be converted to a step.
|
33
|
+
# @raise [UndefinedMethodError] if the conversion method is not defined for the class.
|
34
|
+
def create_step_from!(klass)
|
35
|
+
Step.try_convert(klass).tap do |step|
|
36
|
+
add_step(step || raise(UndefinedMethodError.new(klass, Step::CONVERSION_METHOD)))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
alias step_from create_step_from!
|
40
|
+
|
41
|
+
# Constructs a new List object from the steps added to the builder.
|
42
|
+
#
|
43
|
+
# @param current_step_name [String, nil] The name of the step to mark as current in the built list. Can be nil.
|
44
|
+
# @return [Base] A new List object initialized with the steps and the specified current step.
|
45
|
+
# @raise [EmptyStepsError] if no steps have been added to the builder.
|
46
|
+
def build!(current_step_name)
|
47
|
+
Base.new(convert_to_steps!, convert_to_step!(current_step_name || current_step.name))
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Adds a step to the builder.
|
53
|
+
#
|
54
|
+
# @param step [Step] The step to be added.
|
55
|
+
def add_step(step)
|
56
|
+
step.name.then do |name|
|
57
|
+
warn_about_override(name) if steps.key?(name)
|
58
|
+
steps[name] = step
|
59
|
+
end
|
60
|
+
|
61
|
+
self.current_step ||= step
|
62
|
+
end
|
63
|
+
|
64
|
+
# Assigns a hash of steps to the builder.
|
65
|
+
#
|
66
|
+
# @param raw_steps [Hash] The hash of steps to be assigned.
|
67
|
+
def steps=(raw_steps)
|
68
|
+
@steps = Hash(Hash.try_convert(raw_steps))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Converts the internal hash of steps to an array of Step objects.
|
72
|
+
#
|
73
|
+
# @return [Array<Step>] An array of steps.
|
74
|
+
# @raise [EmptyStepsError] Raises if there are no steps to convert.
|
75
|
+
def convert_to_steps!
|
76
|
+
raise EmptyStepsError if steps.empty?
|
77
|
+
|
78
|
+
steps.values
|
79
|
+
end
|
80
|
+
|
81
|
+
# Retrieves a Step object from the builder's steps based on the step name.
|
82
|
+
#
|
83
|
+
# @param name [String] The name of the step to be converted to a Step object.
|
84
|
+
# @return [Step] The corresponding Step object.
|
85
|
+
# @raise [InvalidStepError] Raises if the specified step name is not present in the steps hash.
|
86
|
+
def convert_to_step!(name)
|
87
|
+
steps[name] || raise(InvalidStepError.new(name, steps.keys))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Onboardable
|
4
|
+
module List
|
5
|
+
# The Navigation module provides methods for navigating through the steps of the onboarding process.
|
6
|
+
module Navigation
|
7
|
+
# Returns the next step in the onboarding process.
|
8
|
+
#
|
9
|
+
# @return [Step, nil] The next step in the list or nil if the current step is the last one.
|
10
|
+
def next_step
|
11
|
+
current_index = step_index!(current_step)
|
12
|
+
steps[current_index.next]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Moves the current step pointer to the next step in the onboarding process.
|
16
|
+
#
|
17
|
+
# @raise [LastStepError] if the current step is the last step and there is no next step to move to.
|
18
|
+
def next_step!
|
19
|
+
self.current_step = next_step || raise(LastStepError.new(current_step, steps.map(&:to_str)))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the previous step in the onboarding process.
|
23
|
+
#
|
24
|
+
# @return [Step, nil] The previous step in the list or nil if the current step is the first one.
|
25
|
+
def prev_step
|
26
|
+
current_index = step_index!(current_step)
|
27
|
+
current_index.positive? ? steps[current_index.pred] : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Moves the current step pointer to the previous step in the onboarding process.
|
31
|
+
#
|
32
|
+
# @raise [FirstStepError] if the current step is the first step and there is no previous step to move to.
|
33
|
+
def prev_step!
|
34
|
+
self.current_step = prev_step || raise(FirstStepError.new(current_step, steps.map(&:to_str)))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks if the specified step is the first step in the onboarding process.
|
38
|
+
#
|
39
|
+
# @param step [Step] The step to check (defaults to the current step if not specified).
|
40
|
+
# @return [Boolean] True if the specified step is the first step, false otherwise.
|
41
|
+
def first_step?(step = current_step)
|
42
|
+
step == first_step
|
43
|
+
end
|
44
|
+
|
45
|
+
# Checks if the specified step is the last step in the onboarding process.
|
46
|
+
#
|
47
|
+
# @param step [Step] The step to check (defaults to the current step if not specified).
|
48
|
+
# @return [Boolean] True if the specified step is the last step, false otherwise.
|
49
|
+
def last_step?(step = current_step)
|
50
|
+
step == last_step
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieves the first step in the onboarding process.
|
54
|
+
#
|
55
|
+
# @return [Step] The first step in the list.
|
56
|
+
def first_step
|
57
|
+
steps.fetch(0)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Retrieves the last step in the onboarding process.
|
61
|
+
#
|
62
|
+
# @return [Step] The last step in the list.
|
63
|
+
def last_step
|
64
|
+
steps.fetch(-1)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/onboardable/step.rb
CHANGED
@@ -1,56 +1,105 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Onboardable
|
4
|
+
# Represents a single step within an onboarding process, including its status and associated data.
|
4
5
|
class Step
|
6
|
+
CONVERSION_METHOD = :to_onboarding_step
|
7
|
+
|
5
8
|
PENDING_STATUS = :pending
|
6
9
|
CURRENT_STATUS = :current
|
7
10
|
COMPLETED_STATUS = :completed
|
11
|
+
|
8
12
|
DEFAULT_STATUS = PENDING_STATUS
|
13
|
+
|
9
14
|
STATUSES = [PENDING_STATUS, CURRENT_STATUS, COMPLETED_STATUS].freeze
|
10
15
|
|
11
|
-
|
16
|
+
class << self
|
17
|
+
def try_convert(value)
|
18
|
+
return unless value.respond_to?(CONVERSION_METHOD)
|
19
|
+
|
20
|
+
value.public_send(CONVERSION_METHOD).then do |step|
|
21
|
+
step.is_a?(Step) ? step : raise(StepConversionError.new(value, step))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String] the name of the step
|
27
|
+
attr_reader :name
|
28
|
+
|
29
|
+
# @return [Hash] custom data associated with the step
|
30
|
+
attr_reader :data
|
12
31
|
|
32
|
+
# @return [Symbol] the current status of the step
|
33
|
+
attr_reader :status
|
34
|
+
|
35
|
+
# Initializes a new Step with a name, optional custom data, and a default status.
|
36
|
+
#
|
37
|
+
# @param name [String] the name of the step
|
38
|
+
# @param data [Hash] the custom data associated with the step, defaults to an empty hash
|
13
39
|
def initialize(name, data = {})
|
14
40
|
self.name = name
|
15
41
|
self.data = data
|
16
|
-
|
17
42
|
self.status = DEFAULT_STATUS
|
18
43
|
end
|
19
44
|
|
20
45
|
STATUSES.each do |status_method|
|
46
|
+
# @!method {status_method}?
|
47
|
+
# Checks if the step is in a specific status.
|
48
|
+
#
|
49
|
+
# @return [Boolean] true if the step is currently in the {status_method} status, false otherwise.
|
21
50
|
define_method :"#{status_method}?" do
|
22
51
|
status == status_method
|
23
52
|
end
|
24
53
|
end
|
25
54
|
|
55
|
+
# Compares this step to another to determine if they are equivalent, based on the step name.
|
56
|
+
#
|
57
|
+
# @param other [Step] the step to compare with
|
58
|
+
# @return [Boolean] true if both steps have the same name, false otherwise
|
26
59
|
def ==(other)
|
27
60
|
to_str == other.to_str
|
28
61
|
end
|
29
62
|
|
63
|
+
# Provides a string representation of the step, using its name.
|
64
|
+
#
|
65
|
+
# @return [String] the name of the step
|
30
66
|
def to_str
|
31
67
|
name
|
32
68
|
end
|
33
69
|
|
70
|
+
# Updates the status of the step based on a specified comparison result.
|
71
|
+
#
|
72
|
+
# @param comparison_result [Integer] the result of a comparison with the current step (-1, 0, or 1)
|
73
|
+
# @raise [ComparisonResultError] if the comparison result is not -1, 0, or 1
|
34
74
|
def update_status!(comparison_result)
|
35
75
|
self.status = case comparison_result
|
36
76
|
when -1 then COMPLETED_STATUS
|
37
77
|
when 0 then CURRENT_STATUS
|
38
78
|
when 1 then PENDING_STATUS
|
39
79
|
else
|
40
|
-
raise
|
80
|
+
raise ComparisonResultError.new(comparison_result, [-1, 0, 1])
|
41
81
|
end
|
42
82
|
end
|
43
83
|
|
44
84
|
private
|
45
85
|
|
86
|
+
# Sets the name of the step, ensuring it is a valid String.
|
87
|
+
#
|
88
|
+
# @param raw_name [String] the raw name of the step
|
46
89
|
def name=(raw_name)
|
47
90
|
@name = String.new(String.try_convert(raw_name)).freeze
|
48
91
|
end
|
49
92
|
|
93
|
+
# Sets the custom data for the step, ensuring it is a valid Hash.
|
94
|
+
#
|
95
|
+
# @param raw_data [Hash] the raw custom data
|
50
96
|
def data=(raw_data)
|
51
97
|
@data = Hash(Hash.try_convert(raw_data)).freeze
|
52
98
|
end
|
53
99
|
|
100
|
+
# Sets the status of the step.
|
101
|
+
#
|
102
|
+
# @param status [Symbol] the new status of the step
|
54
103
|
attr_writer :status
|
55
104
|
end
|
56
105
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Onboardable
|
4
|
+
module Utils
|
5
|
+
# The Warnings module provides utility methods for issuing warnings.
|
6
|
+
module Warnings
|
7
|
+
private
|
8
|
+
|
9
|
+
# Issues a warning when a step with the same name already exists and will be overridden.
|
10
|
+
#
|
11
|
+
# @param name [String] The name of the step that will be overridden.
|
12
|
+
# @return [void]
|
13
|
+
def warn_about_override(name)
|
14
|
+
warn "Step `#{name}` already exists and will be overridden.", uplevel: 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/onboardable/version.rb
CHANGED
data/lib/onboardable.rb
CHANGED
@@ -1,32 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'onboardable/utils/navigation'
|
4
3
|
require_relative 'onboardable/errors'
|
5
|
-
require_relative 'onboardable/
|
6
|
-
require_relative 'onboardable/
|
4
|
+
require_relative 'onboardable/utils/warnings'
|
5
|
+
require_relative 'onboardable/list/navigation'
|
6
|
+
require_relative 'onboardable/list/builder'
|
7
|
+
require_relative 'onboardable/list/base'
|
7
8
|
require_relative 'onboardable/step'
|
8
9
|
require_relative 'onboardable/version'
|
9
10
|
|
11
|
+
# The Onboardable module provides a DSL for defining and navigating onboarding steps.
|
10
12
|
module Onboardable
|
13
|
+
# Initializes the Onboardable module when included in a class, extending it with class and instance methods.
|
14
|
+
#
|
15
|
+
# @param klass [Module] the class including the Onboardable module
|
16
|
+
# @return [untyped]
|
11
17
|
def self.included(klass)
|
12
18
|
klass.extend ClassMethods
|
13
19
|
klass.include InstanceMethods
|
14
20
|
end
|
15
21
|
|
22
|
+
# Class methods for managing the onboarding process, added to the class that includes the Onboardable module.
|
16
23
|
module ClassMethods
|
24
|
+
# Retrieves or initializes a ListBuilder for onboarding steps at the class level.
|
25
|
+
#
|
26
|
+
# @return [List::Builder] the ListBuilder associated with the class
|
17
27
|
def list_builder
|
18
|
-
@list_builder ||=
|
28
|
+
@list_builder ||= List::Builder.new
|
19
29
|
end
|
20
30
|
|
31
|
+
# Configures onboarding steps via a ListBuilder with a provided block.
|
32
|
+
#
|
33
|
+
# @yield [List::Builder] executes block in the context of List::Builder
|
34
|
+
# @return [Step] the current step in the building process
|
21
35
|
def list_builder=(&block)
|
22
36
|
list_builder.instance_eval(&block)
|
23
37
|
end
|
24
38
|
alias has_onboarding list_builder=
|
39
|
+
|
40
|
+
# Builds the onboarding list and optionally sets the current step.
|
41
|
+
#
|
42
|
+
# @param current_step_name [String, nil] the name of the current step, if specified
|
43
|
+
# @return [List::Base] the List built from the class's ListBuilder
|
44
|
+
def onboarding(current_step_name = nil)
|
45
|
+
list_builder.build!(current_step_name)
|
46
|
+
end
|
25
47
|
end
|
26
48
|
|
49
|
+
# Instance methods for onboarding navigation, added to classes including Onboardable.
|
27
50
|
module InstanceMethods
|
51
|
+
# Builds the onboarding list and optionally sets the current step.
|
52
|
+
#
|
53
|
+
# @param current_step_name [String, nil] the name of the current step, if specified
|
54
|
+
# @return [List::Base] the List built from the class's ListBuilder
|
28
55
|
def onboarding(current_step_name = nil)
|
29
|
-
self.class.
|
56
|
+
self.class.onboarding(current_step_name)
|
30
57
|
end
|
31
58
|
end
|
32
59
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Onboardable
|
2
|
+
class Error < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class UndefinedMethodError < Error
|
6
|
+
def initialize: (Class, Symbol | String) -> void
|
7
|
+
end
|
8
|
+
|
9
|
+
class StepConversionError < Error
|
10
|
+
def initialize: (Class, untyped) -> void
|
11
|
+
end
|
12
|
+
|
13
|
+
class EmptyStepsError < Error
|
14
|
+
def initialize: () -> void
|
15
|
+
end
|
16
|
+
|
17
|
+
class InvalidStepError < Error
|
18
|
+
def initialize: (String, Array[String]) -> void
|
19
|
+
end
|
20
|
+
|
21
|
+
class ComparisonResultError < Error
|
22
|
+
def initialize: (Integer, Array[Integer]) -> void
|
23
|
+
end
|
24
|
+
|
25
|
+
class LastStepError < Error
|
26
|
+
def initialize: (String, Array[String]) -> void
|
27
|
+
end
|
28
|
+
|
29
|
+
class FirstStepError < Error
|
30
|
+
def initialize: (String, Array[String]) -> void
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Onboardable
|
2
|
+
module List
|
3
|
+
class Base
|
4
|
+
attr_reader steps: Array[Step]
|
5
|
+
attr_reader current_step: Step
|
6
|
+
|
7
|
+
def initialize: (Array[Step], Step) -> instance
|
8
|
+
|
9
|
+
def progress: -> Float
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_writer steps: Array[Step]
|
14
|
+
attr_writer current_step: Step
|
15
|
+
|
16
|
+
def step_index!: (Step)-> Integer
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Onboardable
|
2
|
+
module List
|
3
|
+
class Builder
|
4
|
+
attr_reader steps: Hash[String, Step]
|
5
|
+
attr_accessor current_step: Step
|
6
|
+
|
7
|
+
def initialize: () -> Hash[String, Step]
|
8
|
+
|
9
|
+
def create_step: (String, Hash[Symbol, untyped]) -> Step
|
10
|
+
alias step create_step
|
11
|
+
|
12
|
+
def create_step_from!: (Class) -> Step
|
13
|
+
alias step_from create_step_from!
|
14
|
+
|
15
|
+
def build!: (String?) -> Base
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_writer steps: Hash[String, Step]
|
20
|
+
|
21
|
+
def add_step: (Step) -> Step
|
22
|
+
|
23
|
+
def convert_to_step!: (String) -> Step
|
24
|
+
|
25
|
+
def convert_to_steps!: -> Array[Step]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/sig/onboardable/step.rbs
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
module Onboardable
|
2
2
|
class Step
|
3
|
+
CONVERSION_METHOD: Symbol
|
3
4
|
PENDING_STATUS: Symbol
|
4
5
|
CURRENT_STATUS: Symbol
|
5
6
|
COMPLETED_STATUS: Symbol
|
6
7
|
DEFAULT_STATUS: Symbol
|
7
8
|
STATUSES: Array[Symbol]
|
8
9
|
|
10
|
+
def self.try_convert: -> Step?
|
11
|
+
|
9
12
|
attr_reader name: String
|
10
13
|
attr_reader data: Hash[Symbol, untyped]
|
11
14
|
attr_reader status: Symbol
|
data/sig/onboardable.rbs
CHANGED
@@ -3,13 +3,15 @@ module Onboardable
|
|
3
3
|
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
attr_reader list_builder:
|
6
|
+
attr_reader list_builder: List::Builder
|
7
7
|
|
8
|
-
def list_builder=: () { () ->
|
8
|
+
def list_builder=: () { () -> List::Builder } -> Step
|
9
9
|
alias has_onboarding list_builder=
|
10
|
+
|
11
|
+
def onboarding: (String?) -> List::Base
|
10
12
|
end
|
11
13
|
|
12
14
|
module InstanceMethods
|
13
|
-
def
|
15
|
+
def onboarding: (String?) -> List::Base
|
14
16
|
end
|
15
17
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: onboardable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Skrynnyk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
version: '1.63'
|
54
54
|
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 1.63.
|
56
|
+
version: 1.63.5
|
57
57
|
type: :development
|
58
58
|
prerelease: false
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -63,7 +63,7 @@ dependencies:
|
|
63
63
|
version: '1.63'
|
64
64
|
- - ">="
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 1.63.
|
66
|
+
version: 1.63.5
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: rubocop-performance
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,16 +144,19 @@ files:
|
|
144
144
|
- Rakefile
|
145
145
|
- lib/onboardable.rb
|
146
146
|
- lib/onboardable/errors.rb
|
147
|
-
- lib/onboardable/list.rb
|
148
|
-
- lib/onboardable/
|
147
|
+
- lib/onboardable/list/base.rb
|
148
|
+
- lib/onboardable/list/builder.rb
|
149
|
+
- lib/onboardable/list/navigation.rb
|
149
150
|
- lib/onboardable/step.rb
|
150
|
-
- lib/onboardable/utils/
|
151
|
+
- lib/onboardable/utils/warnings.rb
|
151
152
|
- lib/onboardable/version.rb
|
152
153
|
- sig/onboardable.rbs
|
153
|
-
- sig/onboardable/
|
154
|
-
- sig/onboardable/
|
154
|
+
- sig/onboardable/errors.rbs
|
155
|
+
- sig/onboardable/list/base.rbs
|
156
|
+
- sig/onboardable/list/builder.rbs
|
157
|
+
- sig/onboardable/list/navigation.rbs
|
155
158
|
- sig/onboardable/step.rbs
|
156
|
-
- sig/onboardable/utils/
|
159
|
+
- sig/onboardable/utils/warnings.rbs
|
157
160
|
homepage: https://github.com/dmrAnderson/onboardable
|
158
161
|
licenses:
|
159
162
|
- MIT
|
@@ -161,7 +164,7 @@ metadata:
|
|
161
164
|
homepage_uri: https://github.com/dmrAnderson/onboardable
|
162
165
|
changelog_uri: https://github.com/dmrAnderson/onboardable/blob/main/CHANGELOG.md
|
163
166
|
bug_tracker_uri: https://github.com/dmrAnderson/onboardable/issues
|
164
|
-
documentation_uri: https://
|
167
|
+
documentation_uri: https://rubydoc.info/gems/onboardable
|
165
168
|
rubygems_mfa_required: 'true'
|
166
169
|
post_install_message:
|
167
170
|
rdoc_options: []
|
data/lib/onboardable/list.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Onboardable
|
4
|
-
class List
|
5
|
-
include Utils::Navigation
|
6
|
-
|
7
|
-
attr_reader :steps, :current_step
|
8
|
-
|
9
|
-
def initialize(steps, current_step)
|
10
|
-
self.steps = steps
|
11
|
-
self.current_step = current_step
|
12
|
-
end
|
13
|
-
|
14
|
-
def progress
|
15
|
-
(step_index!(current_step).to_f / steps.size) * 100
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def steps=(raw_steps)
|
21
|
-
Array(Array.try_convert(raw_steps)).then do |converted_steps|
|
22
|
-
raise EmptyListError if converted_steps.empty?
|
23
|
-
|
24
|
-
@steps = converted_steps.freeze
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def current_step=(raw_current_step)
|
29
|
-
current_step_index = step_index!(raw_current_step)
|
30
|
-
steps.each_with_index { |step, index| step.update_status!(index <=> current_step_index) }
|
31
|
-
@current_step = steps.fetch(current_step_index)
|
32
|
-
end
|
33
|
-
|
34
|
-
def step_index!(raw_step)
|
35
|
-
steps.index { |step| step == raw_step } || raise(InvalidStepError.new(raw_step, steps.map(&:to_str)))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Onboardable
|
4
|
-
class ListBuilder
|
5
|
-
attr_reader :steps
|
6
|
-
attr_accessor :current_step
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
self.steps = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def add_step(name, data = {})
|
13
|
-
Step.new(name, data).tap do |step|
|
14
|
-
steps[name] = step
|
15
|
-
self.current_step ||= step
|
16
|
-
end
|
17
|
-
end
|
18
|
-
alias step add_step
|
19
|
-
|
20
|
-
def build!(current_step_name)
|
21
|
-
List.new(steps.values, convert_to_step!(current_step_name))
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def steps=(raw_steps)
|
27
|
-
@steps = Hash(Hash.try_convert(raw_steps))
|
28
|
-
end
|
29
|
-
|
30
|
-
def convert_to_step!(name)
|
31
|
-
return current_step unless name
|
32
|
-
|
33
|
-
steps[name] || raise(InvalidStepError.new(name, steps.keys))
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Onboardable
|
4
|
-
module Utils
|
5
|
-
module Navigation
|
6
|
-
def next_step
|
7
|
-
current_index = step_index!(current_step)
|
8
|
-
|
9
|
-
steps[current_index.next]
|
10
|
-
end
|
11
|
-
|
12
|
-
def next_step!
|
13
|
-
self.current_step = next_step || raise(LastStepError.new(current_step, steps))
|
14
|
-
end
|
15
|
-
|
16
|
-
def prev_step
|
17
|
-
current_index = step_index!(current_step)
|
18
|
-
|
19
|
-
current_index.positive? ? steps[current_index.pred] : nil
|
20
|
-
end
|
21
|
-
|
22
|
-
def prev_step!
|
23
|
-
self.current_step = prev_step || raise(FirstStepError.new(current_step, steps))
|
24
|
-
end
|
25
|
-
|
26
|
-
def first_step?(step = current_step)
|
27
|
-
step == first_step
|
28
|
-
end
|
29
|
-
|
30
|
-
def last_step?(step = current_step)
|
31
|
-
step == last_step
|
32
|
-
end
|
33
|
-
|
34
|
-
def first_step
|
35
|
-
steps.fetch(0)
|
36
|
-
end
|
37
|
-
|
38
|
-
def last_step
|
39
|
-
steps.fetch(-1)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/sig/onboardable/list.rbs
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module Onboardable
|
2
|
-
class List
|
3
|
-
attr_reader steps: Array[Step]
|
4
|
-
attr_reader current_step: Step
|
5
|
-
|
6
|
-
def initialize: (Array[Step], Step) -> instance
|
7
|
-
|
8
|
-
def progress: -> Float
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
attr_writer steps: Array[Step]
|
13
|
-
attr_writer current_step: Step
|
14
|
-
|
15
|
-
def step_index!: (Step)-> Integer
|
16
|
-
end
|
17
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Onboardable
|
2
|
-
class ListBuilder
|
3
|
-
attr_reader steps: Hash[String, Step]
|
4
|
-
attr_accessor current_step: Step
|
5
|
-
|
6
|
-
def initialize: () -> Hash[String, Step]
|
7
|
-
|
8
|
-
def add_step: (String, Hash[Symbol, untyped]) -> Step
|
9
|
-
alias step add_step
|
10
|
-
|
11
|
-
def build!: (String?) -> List
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
attr_writer steps: Hash[String, Step]
|
16
|
-
|
17
|
-
def convert_to_step!: (String?) -> Step
|
18
|
-
end
|
19
|
-
end
|