onboardable 1.1.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef1662a800c2b2c562af504026f4a84bd7a93c895236784e20e53659d7fdcaa3
4
- data.tar.gz: 90df600da2944d056ca6d64891a311e4507f1227c2827b44485735451ccc329d
3
+ metadata.gz: 85ade5264e7e019c70f76d1ad5ccf9713257e596baaae4495eeaaa575146af2e
4
+ data.tar.gz: 17fb50df5dc091e8f8c1326c2b28dea7657e5f97efcc94d4ba3ab34ea41e242b
5
5
  SHA512:
6
- metadata.gz: 32e156cde48492f62a29a41b1c382c1be86bd5922b47ddcd32a5ca606127204002f8805da9c1adc087ea005c90a26a20de9081939617d9f623f9b5075fecd113
7
- data.tar.gz: 3f8a70a79552741d71c0de140204220e66d70b5a43e5e7ce3c54c15b06900e55e15f5c840812c3463f3c10909bb693788e0aeaeb8039617a026b2fe2cef2bcc0
6
+ metadata.gz: ea96934ba19039ad48f297314d1021aa9be880cbd324da0d7575ecd351bb61563193099180cf43e2da051053ba2f2cce8df06bc260206e2796ee30b6f383e040
7
+ data.tar.gz: 35db141793dec0c56ddbd667ac8e1d1bacb33dfcb032a83ace5bcc712ab4f3010504f66b838ac7f0feddd422b87eb494f5b9c2064d15c221babedb17ac941088
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.2.1] - 2024-06-01
6
+
7
+ - Added trusted publisher
8
+
9
+ ## [1.2.0] - 2024-05-25
10
+
11
+ - Added `#second_step?` method to check if the target step is the second step.
12
+ - Added `#prev_step?` method to check if the target step is the previous step.
13
+ - Added `#current_step?` method to check if the target step is the current step.
14
+
15
+ ## [1.1.1] - 2024-05-21
16
+
5
17
  - Added `onboarding` class method to define the onboarding steps.
6
18
  - Added [documentation link](https://rubydoc.info/gems/onboardable) to gemspec.
7
19
 
data/CODE_OF_CONDUCT.md CHANGED
@@ -2,18 +2,25 @@
2
2
 
3
3
  ## Our Pledge
4
4
 
5
- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
5
+ We as members, contributors, and leaders pledge to participate in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity and orientation.
6
10
 
7
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
11
+ We pledge to act and interact in ways that contribute to an open, welcoming,
12
+ diverse, inclusive, and healthy community.
8
13
 
9
14
  ## Our Standards
10
15
 
11
- Examples of behavior that contributes to a positive environment for our community include:
16
+ Examples of behavior that contributes to a positive environment for
17
+ our community includes:
12
18
 
13
19
  * Demonstrating empathy and kindness toward other people
14
20
  * Being respectful of differing opinions, viewpoints, and experiences
15
21
  * Giving and gracefully accepting constructive feedback
16
- * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
22
+ * Accepting responsibility and apologizing to those affected by our mistakes,
23
+ and learning from the experience
17
24
  * Focusing on what is best not just for us as individuals, but for the overall community
18
25
 
19
26
  Examples of unacceptable behavior include:
@@ -24,61 +31,90 @@ Examples of unacceptable behavior include:
24
31
  * Public or private harassment
25
32
  * Publishing others' private information, such as a physical or email
26
33
  address, without their explicit permission
27
- * Other conduct which could reasonably be considered inappropriate in a
34
+ * Other conduct that could reasonably be considered inappropriate in a
28
35
  professional setting
29
36
 
30
37
  ## Enforcement Responsibilities
31
38
 
32
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
39
+ Community leaders are responsible for clarifying and enforcing our standards of
40
+ acceptable behavior and will take appropriate and fair corrective action in response
41
+ to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
42
 
34
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
43
+ Community leaders have the right and responsibility to remove, edit, or reject
44
+ comments, commits, code, wiki edits, issues, and other contributions that are
45
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
46
+ decisions when appropriate.
35
47
 
36
48
  ## Scope
37
49
 
38
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
50
+ This Code of Conduct applies within all community spaces and also applies when an
51
+ individual is officially representing the community in public spaces. Examples of
52
+ representing our community includes using an official e-mail address, posting via
53
+ an official social media account, or acting as an appointed representative at an
54
+ online or offline event.
39
55
 
40
56
  ## Enforcement
41
57
 
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at skrynnyk.artem@coaxsoft.com. All complaints will be reviewed and investigated promptly and fairly.
58
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported
59
+ to the community leaders responsible for enforcement at skrynnyk.artem@coaxsoft.com.
60
+ All complaints will be reviewed and investigated promptly and fairly.
43
61
 
44
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
62
+ All community leaders are obligated to respect the privacy and
63
+ security of the reporter of any incident.
45
64
 
46
65
  ## Enforcement Guidelines
47
66
 
48
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
67
+ Community leaders will follow these Community Impact Guidelines in determining
68
+ the consequences for any action they deem in violation of this Code of Conduct:
49
69
 
50
70
  ### 1. Correction
51
71
 
52
- **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
72
+ **Community Impact**: Use of inappropriate language or other behavior
73
+ deemed unprofessional or unwelcome in the community.
53
74
 
54
- **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
75
+ **Consequence**: A private, written warning from community leaders,
76
+ providing clarity around the nature of the violation and an explanation
77
+ of why the behavior was inappropriate. A public apology may be requested.
55
78
 
56
79
  ### 2. Warning
57
80
 
58
81
  **Community Impact**: A violation through a single incident or series of actions.
59
82
 
60
- **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
83
+ **Consequence**: A warning with consequences for continued behavior.
84
+ No interaction with the people involved, including unsolicited interaction
85
+ with those enforcing the Code of Conduct, for a specified period of time.
86
+ This includes avoiding interactions in community spaces as well as external
87
+ channels like social media. Violating these terms may lead to a temporary or
88
+ permanent ban.
61
89
 
62
90
  ### 3. Temporary Ban
63
91
 
64
- **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
92
+ **Community Impact**: A serious violation of community standards,
93
+ including sustained inappropriate behavior.
65
94
 
66
- **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
95
+ **Consequence**: A temporary ban from any sort of interaction or public
96
+ communication with the community for a specified period of time.
97
+ No public or private interaction with the people involved, including
98
+ unsolicited interaction with those enforcing the Code of Conduct, is
99
+ allowed during this period. Violating these terms may lead to a permanent ban.
67
100
 
68
101
  ### 4. Permanent Ban
69
102
 
70
- **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
103
+ **Community Impact**: Demonstrating a pattern of violation of community standards,
104
+ including sustained inappropriate behavior, harassment of an individual, or
105
+ aggression toward or disparagement of classes of individuals.
71
106
 
72
107
  **Consequence**: A permanent ban from any sort of public interaction within the community.
73
108
 
74
109
  ## Attribution
75
110
 
76
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
- available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
111
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
112
+ version 2.0, available at <https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
78
113
 
79
- Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
114
+ Community Impact Guidelines were inspired
115
+ by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
116
 
81
117
  [homepage]: https://www.contributor-covenant.org
82
118
 
83
119
  For answers to common questions about this code of conduct, see the FAQ at
84
- https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
120
+ <https://www.contributor-covenant.org/faq>. Translations are available at <https://www.contributor-covenant.org/translations>.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- onboardable (1.1.1)
4
+ onboardable (1.2.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -12,7 +12,7 @@ GEM
12
12
  json (2.7.2)
13
13
  language_server-protocol (3.17.0.3)
14
14
  parallel (1.24.0)
15
- parser (3.3.1.0)
15
+ parser (3.3.2.0)
16
16
  ast (~> 2.4.1)
17
17
  racc
18
18
  racc (1.8.0)
@@ -34,7 +34,7 @@ GEM
34
34
  diff-lcs (>= 1.2.0, < 2.0)
35
35
  rspec-support (~> 3.13.0)
36
36
  rspec-support (3.13.1)
37
- rubocop (1.63.5)
37
+ rubocop (1.64.1)
38
38
  json (~> 2.3)
39
39
  language_server-protocol (>= 3.17.0)
40
40
  parallel (~> 1.10)
@@ -81,11 +81,11 @@ DEPENDENCIES
81
81
  onboardable!
82
82
  rake (~> 13.2, >= 13.2.1)
83
83
  rspec (~> 3.13)
84
- rubocop (~> 1.63, >= 1.63.5)
84
+ rubocop (~> 1.64, >= 1.64.1)
85
85
  rubocop-performance (~> 1.21)
86
86
  rubocop-rake (~> 0.6.0)
87
87
  rubocop-rspec (~> 2.29, >= 2.29.2)
88
88
  simplecov (~> 0.22.0)
89
89
 
90
90
  BUNDLED WITH
91
- 2.5.10
91
+ 2.5.11
data/README.md CHANGED
@@ -18,7 +18,7 @@ Install the gem and add to the application's Gemfile by executing:
18
18
  bundle add onboardable
19
19
  ```
20
20
 
21
- If bundler is not being used to manage dependencies, install the gem by executing:
21
+ If the bundler is not being used to manage dependencies, install the gem by executing:
22
22
 
23
23
  ```shell
24
24
  gem install onboardable
@@ -29,36 +29,31 @@ gem install onboardable
29
29
  First, ensure that the Onboardable gem is installed and properly set up in your
30
30
  project as per the installation guide provided earlier.
31
31
 
32
- ### Basic Configuration
33
-
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:
38
-
39
- ```ruby
40
- class User
41
- include Onboardable
42
-
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'
48
-
49
- # Use the `step_from` method to define steps from external providers
50
- step_from ExternalStepProvider
51
- end
52
- end
53
-
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.')
59
- end
60
- end
61
- ```
32
+ ### Defining Onboarding Steps
33
+
34
+ To incorporate an onboarding process into your `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:
38
+
39
+ ```ruby
40
+ class User
41
+ include Onboardable
42
+
43
+ has_onboarding do
44
+ step 'welcome', message: 'Welcome to your new account!'
45
+ step 'account_setup', task: 'Create credentials'
46
+ step 'confirmation'
47
+ step_from ExternalStepProvider
48
+ end
49
+ end
50
+
51
+ class ExternalStepProvider
52
+ def self.to_onboarding_step
53
+ Onboardable::Step.new 'external_step', info: 'This is an external step.'
54
+ end
55
+ end
56
+ ```
62
57
 
63
58
  ### Using the Onboarding Steps
64
59
 
@@ -73,13 +68,36 @@ that allow step navigation and state verification:
73
68
 
74
69
  1. **Initialize Onboarding Process**
75
70
 
76
- Instantiate the onboarding process when a user object is created.
77
- This sets up the initial step based on the defined onboarding flow.
71
+ - Instantiate the onboarding process when a `User` object is created.
72
+ This sets up the initial step based on the defined onboarding flow.
78
73
 
79
74
  ```ruby
80
75
  onboarding = User.new.onboarding
81
76
  ```
82
77
 
78
+ - For a more detailed setup, use the `Onboardable::List::Builder` to
79
+ create a custom onboarding flow with specific steps and details.
80
+
81
+ ```ruby
82
+ builder = Onboardable::List::Builder.new
83
+
84
+ builder.create_step 'welcome', message: 'Welcome to your new account!'
85
+ builder.create_step 'account_setup', task: 'Create credentials'
86
+ builder.create_step 'confirmation'
87
+ builder.create_step_from ExternalStepProvider
88
+
89
+ onboarding = builder.build
90
+ ```
91
+
92
+ 1. **Check the order of steps**
93
+
94
+ Determine the order of steps in the onboarding process to ensure
95
+ that the sequence is correct and that the steps are defined as expected.
96
+
97
+ ```ruby
98
+ onboarding.steps # Returns the list of steps
99
+ ```
100
+
83
101
  1. **Navigating Through Steps**
84
102
 
85
103
  Navigate through the onboarding steps using the navigation methods provided.
@@ -111,8 +129,13 @@ that allow step navigation and state verification:
111
129
  to manage UI elements like 'Next' or 'Back' buttons appropriately.
112
130
 
113
131
  ```ruby
114
- onboarding.first_step? # Is the first step?
115
- onboarding.last_step? # Is the last step?
132
+ step = onboarding.steps.sample # Random step from the list
133
+
134
+ onboarding.first_step?(step) # Is the first step?
135
+ onboarding.last_step?(step) # Is the last step?
136
+ onboarding.current_step?(step) # Is the current step?
137
+ onboarding.second_step?(step) # Is the second step?
138
+ onboarding.prev_step?(step) # Is the previous step?
116
139
  ```
117
140
 
118
141
  1. **Monitor Progress**
@@ -128,7 +151,7 @@ that allow step navigation and state verification:
128
151
 
129
152
  Retrieve details about the current step, which can include the name,
130
153
  custom data, and status, to display appropriate information or help
131
- the user complete tasks associated with the step.
154
+ the user completes tasks associated with the step.
132
155
 
133
156
  ```ruby
134
157
  onboarding.current_step # Current step details
@@ -36,8 +36,8 @@ module Onboardable
36
36
  end
37
37
 
38
38
  # Error raised when an invalid step is encountered within the onboarding process.
39
- class InvalidStepError < Error
40
- # Initializes a new InvalidStepError with details about the issue.
39
+ class StepError < Error
40
+ # Initializes a new StepError with details about the issue.
41
41
  #
42
42
  # @param step [String] The invalid step that triggered the error.
43
43
  # @param expected_steps [Array<String>] The list of valid steps expected at this point.
@@ -78,4 +78,15 @@ module Onboardable
78
78
  super("Currently `#{step}` the first step. Available steps are: `#{expected_steps.join('`, `')}`.")
79
79
  end
80
80
  end
81
+
82
+ # Error raised when an invalid status is encountered for a step.
83
+ class StepStatusError < Error
84
+ # Initializes a new StepStatusError with details about the issue.
85
+ #
86
+ # @param status [Symbol] The invalid status that triggered the error.
87
+ # @param expected_statuses [Array<Symbol>] The list of valid statuses expected.
88
+ def initialize(status, expected_statuses)
89
+ super("Invalid status: `#{status}`. Must be one of: `#{expected_statuses.join('`, `')}`.")
90
+ end
91
+ end
81
92
  end
@@ -6,10 +6,10 @@ module Onboardable
6
6
  class Base
7
7
  include Navigation
8
8
 
9
- # @return [Array<Step>] the steps in the list
9
+ # @return [Array<Step>] The steps in the list.
10
10
  attr_reader :steps
11
11
 
12
- # @return [Step] the current step in the list
12
+ # @return [Step] The current step in the list.
13
13
  attr_reader :current_step
14
14
 
15
15
  # Initializes a new instance of List with steps and a current step.
@@ -25,7 +25,7 @@ module Onboardable
25
25
  #
26
26
  # @return [Float] The completion percentage of the onboarding process.
27
27
  def progress
28
- (step_index!(current_step).to_f / steps.size) * 100
28
+ (step_index(current_step).to_f / steps.size) * 100
29
29
  end
30
30
 
31
31
  private
@@ -33,6 +33,7 @@ module Onboardable
33
33
  # Sets and validates the steps array, ensuring it is an Array of Step objects.
34
34
  #
35
35
  # @param steps [Array<Step>] The steps to be assigned to the list.
36
+ # @return [Array<Step>] The assigned steps.
36
37
  def steps=(steps)
37
38
  @steps = Array(Array.try_convert(steps)).freeze
38
39
  end
@@ -40,19 +41,38 @@ module Onboardable
40
41
  # Updates the current step and recalibrates the status of all steps in the list.
41
42
  #
42
43
  # @param raw_current_step [Step] The new current step to set.
44
+ # @return [Step] The updated current step.
43
45
  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)
46
+ step_index(raw_current_step).then do |index|
47
+ update_each_step_status(index)
48
+ @current_step = steps.fetch(index)
49
+ end
47
50
  end
48
51
 
49
52
  # Determines the index of a given step in the list, ensuring the step exists.
50
53
  #
51
54
  # @param raw_step [Step] The step for which the index is requested.
52
55
  # @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
+ def step_index(raw_step)
57
+ steps.index { |step| step == raw_step } || step_error!(raw_step)
58
+ end
59
+
60
+ # Updates the status of each step in the list based on the current step index.
61
+ #
62
+ # @param current_step_index [Integer] The index of the current step in the list.
63
+ # @return [Array<Step>] The updated steps array.
64
+ def update_each_step_status(current_step_index)
65
+ steps.each_with_index do |step, index|
66
+ step.update_status!(index <=> current_step_index)
67
+ end
68
+ end
69
+
70
+ # Raises a StepError indicating an invalid step was encountered.
71
+ #
72
+ # @param raw_step [Step] The step that triggered the error.
73
+ # @raise [StepError] Raises an error if the step does not exist in the list.
74
+ def step_error!(raw_step)
75
+ raise StepError.new(raw_step.to_str, steps.map(&:to_str))
56
76
  end
57
77
  end
58
78
  end
@@ -30,20 +30,17 @@ module Onboardable
30
30
  # Converts a class to a Step object and adds it to the builder.
31
31
  #
32
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
33
+ # @return [Step] The converted step.
34
+ def create_step_from(klass)
35
+ (Step.try_convert(klass) || undefined_method_error!(klass)).tap { |step| add_step(step) }
38
36
  end
39
- alias step_from create_step_from!
37
+ alias step_from create_step_from
40
38
 
41
39
  # Constructs a new List object from the steps added to the builder.
42
40
  #
43
41
  # @param current_step_name [String, nil] The name of the step to mark as current in the built list. Can be nil.
44
42
  # @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)
43
+ def build(current_step_name)
47
44
  Base.new(convert_to_steps!, convert_to_step!(current_step_name || current_step.name))
48
45
  end
49
46
 
@@ -52,13 +49,14 @@ module Onboardable
52
49
  # Adds a step to the builder.
53
50
  #
54
51
  # @param step [Step] The step to be added.
52
+ # @return [Step] The added step.
55
53
  def add_step(step)
56
54
  step.name.then do |name|
57
55
  warn_about_override(name) if steps.key?(name)
58
56
  steps[name] = step
59
57
  end
60
58
 
61
- self.current_step ||= step
59
+ step.tap { self.current_step = step }
62
60
  end
63
61
 
64
62
  # Assigns a hash of steps to the builder.
@@ -73,18 +71,24 @@ module Onboardable
73
71
  # @return [Array<Step>] An array of steps.
74
72
  # @raise [EmptyStepsError] Raises if there are no steps to convert.
75
73
  def convert_to_steps!
76
- raise EmptyStepsError if steps.empty?
77
-
78
- steps.values
74
+ steps.any? ? steps.values : raise(EmptyStepsError)
79
75
  end
80
76
 
81
77
  # Retrieves a Step object from the builder's steps based on the step name.
82
78
  #
83
79
  # @param name [String] The name of the step to be converted to a Step object.
84
80
  # @return [Step] The corresponding Step object.
85
- # @raise [InvalidStepError] Raises if the specified step name is not present in the steps hash.
81
+ # @raise [StepError] Raises if the specified step name is not present in the steps hash.
86
82
  def convert_to_step!(name)
87
- steps[name] || raise(InvalidStepError.new(name, steps.keys))
83
+ steps[name] || raise(StepError.new(name, steps.keys))
84
+ end
85
+
86
+ # Raises an UndefinedMethodError indicating that the conversion method is not defined for the class.
87
+ #
88
+ # @param klass [Class] The class that does not have the conversion method defined.
89
+ # @raise [UndefinedMethodError] Raises an error indicating the missing conversion method.
90
+ def undefined_method_error!(klass)
91
+ raise UndefinedMethodError.new(klass, Step::CONVERSION_METHOD)
88
92
  end
89
93
  end
90
94
  end
@@ -8,30 +8,48 @@ module Onboardable
8
8
  #
9
9
  # @return [Step, nil] The next step in the list or nil if the current step is the last one.
10
10
  def next_step
11
- current_index = step_index!(current_step)
11
+ current_index = step_index(current_step)
12
12
  steps[current_index.next]
13
13
  end
14
14
 
15
+ # Checks if the specified step is the next step in the onboarding process.
16
+ #
17
+ # @param step [Step] The step to check.
18
+ # @return [Boolean] True if the specified step is the next step, false otherwise.
19
+ def next_step?(step)
20
+ next_step == step
21
+ end
22
+
15
23
  # Moves the current step pointer to the next step in the onboarding process.
16
24
  #
17
- # @raise [LastStepError] if the current step is the last step and there is no next step to move to.
25
+ # @return [Step] The next step in the list.
26
+ # @raise [LastStepError] If the current step is the last step and there is no next step to move to.
18
27
  def next_step!
19
- self.current_step = next_step || raise(LastStepError.new(current_step, steps.map(&:to_str)))
28
+ self.current_step = next_step || last_step_error!
20
29
  end
21
30
 
22
31
  # Returns the previous step in the onboarding process.
23
32
  #
24
33
  # @return [Step, nil] The previous step in the list or nil if the current step is the first one.
25
34
  def prev_step
26
- current_index = step_index!(current_step)
35
+ current_index = step_index(current_step)
27
36
  current_index.positive? ? steps[current_index.pred] : nil
28
37
  end
29
38
 
39
+ # Checks if the specified step is the previous step in the onboarding process.
40
+ #
41
+ # @param step [Step] The step to check.
42
+ # @return [Boolean] True if the specified step is the previous step, false otherwise.
43
+ def prev_step?(step)
44
+ prev_step == step
45
+ end
46
+
30
47
  # Moves the current step pointer to the previous step in the onboarding process.
31
48
  #
32
- # @raise [FirstStepError] if the current step is the first step and there is no previous step to move to.
49
+ # @return [Step] The previous step in the list.
50
+ # @raise [FirstStepError] If the current step is the first step and there is no previous step to move to.
33
51
  def prev_step!
34
- self.current_step = prev_step || raise(FirstStepError.new(current_step, steps.map(&:to_str)))
52
+ self.current_step = prev_step || first_step_error!
35
53
  end
36
54
 
37
55
  # Checks if the specified step is the first step in the onboarding process.
@@ -39,7 +57,7 @@ module Onboardable
39
57
  # @param step [Step] The step to check (defaults to the current step if not specified).
40
58
  # @return [Boolean] True if the specified step is the first step, false otherwise.
41
59
  def first_step?(step = current_step)
42
- step == first_step
60
+ first_step == step
43
61
  end
44
62
 
45
63
  # Checks if the specified step is the last step in the onboarding process.
@@ -47,7 +65,7 @@ module Onboardable
47
65
  # @param step [Step] The step to check (defaults to the current step if not specified).
48
66
  # @return [Boolean] True if the specified step is the last step, false otherwise.
49
67
  def last_step?(step = current_step)
50
- step == last_step
68
+ last_step == step
51
69
  end
52
70
 
53
71
  # Retrieves the first step in the onboarding process.
@@ -57,12 +75,36 @@ module Onboardable
57
75
  steps.fetch(0)
58
76
  end
59
77
 
78
+ # Checks if the specified step is the current step in the onboarding process.
79
+ #
80
+ # @param step [Step] The step to check.
81
+ # @return [Boolean] True if the specified step is the current step, false otherwise.
82
+ def current_step?(step)
83
+ current_step == step
84
+ end
85
+
60
86
  # Retrieves the last step in the onboarding process.
61
87
  #
62
88
  # @return [Step] The last step in the list.
63
89
  def last_step
64
90
  steps.fetch(-1)
65
91
  end
92
+
93
+ private
94
+
95
+ # Raises a FirstStepError indicating the current step is the first step in the onboarding process.
96
+ #
97
+ # @raise [FirstStepError] The error indicating the current step is the first step.
98
+ def first_step_error!
99
+ raise FirstStepError.new(current_step, steps.map(&:to_str))
100
+ end
101
+
102
+ # Raises a LastStepError indicating the current step is the last step in the onboarding process.
103
+ #
104
+ # @raise [LastStepError] The error indicating the current step is the last step.
105
+ def last_step_error!
106
+ raise LastStepError.new(current_step, steps.map(&:to_str))
107
+ end
66
108
  end
67
109
  end
68
110
  end
@@ -70,15 +70,14 @@ module Onboardable
70
70
  # Updates the status of the step based on a specified comparison result.
71
71
  #
72
72
  # @param comparison_result [Integer] the result of a comparison with the current step (-1, 0, or 1)
73
+ # @return [Symbol] the new status of the step
73
74
  # @raise [ComparisonResultError] if the comparison result is not -1, 0, or 1
74
75
  def update_status!(comparison_result)
75
76
  self.status = case comparison_result
76
77
  when -1 then COMPLETED_STATUS
77
78
  when 0 then CURRENT_STATUS
78
79
  when 1 then PENDING_STATUS
79
- else
80
- raise ComparisonResultError.new(comparison_result, [-1, 0, 1])
81
- end
80
+ else comparison_result_error!(comparison_result); end
82
81
  end
83
82
 
84
83
  private
@@ -90,6 +89,15 @@ module Onboardable
90
89
  @name = String.new(String.try_convert(raw_name)).freeze
91
90
  end
92
91
 
92
+ # Sets the status of the step.
93
+ #
94
+ # @param raw_status [Symbol] the new status of the step
95
+ def status=(raw_status)
96
+ STATUSES.include?(raw_status) || raise(StepStatusError.new(raw_status, STATUSES))
97
+
98
+ @status = raw_status
99
+ end
100
+
93
101
  # Sets the custom data for the step, ensuring it is a valid Hash.
94
102
  #
95
103
  # @param raw_data [Hash] the raw custom data
@@ -97,9 +105,12 @@ module Onboardable
97
105
  @data = Hash(Hash.try_convert(raw_data)).freeze
98
106
  end
99
107
 
100
- # Sets the status of the step.
108
+ # Raises an error for an invalid comparison result.
101
109
  #
102
- # @param status [Symbol] the new status of the step
103
- attr_writer :status
110
+ # @param comparison_result [Integer] the invalid comparison result
111
+ # @raise [ComparisonResultError] raises an error for an invalid comparison result
112
+ def comparison_result_error!(comparison_result)
113
+ raise ComparisonResultError.new(comparison_result, (-1..1).to_a)
114
+ end
104
115
  end
105
116
  end
@@ -9,7 +9,7 @@ module Onboardable
9
9
  # Issues a warning when a step with the same name already exists and will be overridden.
10
10
  #
11
11
  # @param name [String] The name of the step that will be overridden.
12
- # @return [void]
12
+ # @return [void] Returns nothing.
13
13
  def warn_about_override(name)
14
14
  warn "Step `#{name}` already exists and will be overridden.", uplevel: 1
15
15
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Onboardable
4
- VERSION = '1.1.1'
4
+ VERSION = '1.2.1'
5
5
  end
data/lib/onboardable.rb CHANGED
@@ -12,8 +12,8 @@ require_relative 'onboardable/version'
12
12
  module Onboardable
13
13
  # Initializes the Onboardable module when included in a class, extending it with class and instance methods.
14
14
  #
15
- # @param klass [Module] the class including the Onboardable module
16
- # @return [untyped]
15
+ # @param klass [Module] The class including the Onboardable module
16
+ # @return [untyped] The class including the Onboardable module.
17
17
  def self.included(klass)
18
18
  klass.extend ClassMethods
19
19
  klass.include InstanceMethods
@@ -23,15 +23,15 @@ module Onboardable
23
23
  module ClassMethods
24
24
  # Retrieves or initializes a ListBuilder for onboarding steps at the class level.
25
25
  #
26
- # @return [List::Builder] the ListBuilder associated with the class
26
+ # @return [List::Builder] The ListBuilder associated with the class.
27
27
  def list_builder
28
28
  @list_builder ||= List::Builder.new
29
29
  end
30
30
 
31
31
  # Configures onboarding steps via a ListBuilder with a provided block.
32
32
  #
33
- # @yield [List::Builder] executes block in the context of List::Builder
34
- # @return [Step] the current step in the building process
33
+ # @yield [List::Builder] Executes block in the context of List::Builder.
34
+ # @return [Step] The current step in the building process.
35
35
  def list_builder=(&block)
36
36
  list_builder.instance_eval(&block)
37
37
  end
@@ -39,10 +39,10 @@ module Onboardable
39
39
 
40
40
  # Builds the onboarding list and optionally sets the current step.
41
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
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
44
  def onboarding(current_step_name = nil)
45
- list_builder.build!(current_step_name)
45
+ list_builder.build(current_step_name)
46
46
  end
47
47
  end
48
48
 
@@ -50,8 +50,8 @@ module Onboardable
50
50
  module InstanceMethods
51
51
  # Builds the onboarding list and optionally sets the current step.
52
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
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.
55
55
  def onboarding(current_step_name = nil)
56
56
  self.class.onboarding(current_step_name)
57
57
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/onboardable/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'onboardable'
7
+ spec.version = Onboardable::VERSION
8
+ spec.authors = ['Artem Skrynnyk']
9
+ spec.email = ['skrynnyk.artem@coaxsoft.com']
10
+
11
+ spec.summary = 'Streamlines onboarding process integration'
12
+ spec.description = 'Provides tools for easy setup of custom onboarding to boost engagement.'
13
+ spec.homepage = 'https://github.com/dmrAnderson/onboardable'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.0.0'
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ # spec.metadata['source_code_uri'] = "TODO: Put your gem's public repo URL here."
21
+ spec.metadata['changelog_uri'] = 'https://github.com/dmrAnderson/onboardable/blob/main/CHANGELOG.md'
22
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/dmrAnderson/onboardable/issues'
23
+ spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/onboardable'
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(__dir__) do
28
+ `git ls-files -z`.split("\x0").reject do |f|
29
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|spec)/|\.(?:git|rspec|rubocop))})
30
+ end
31
+ end
32
+
33
+ spec.bindir = 'exe'
34
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ['lib']
36
+
37
+ # Uncomment to register a new dependency of your gem
38
+ # spec.add_dependency "example-gem", "~> 1.0"
39
+
40
+ # For more information and examples about making a new gem, check out our
41
+ # guide at: https://bundler.io/guides/creating_gem.html
42
+ spec.add_development_dependency 'rake', '~> 13.2', '>= 13.2.1'
43
+ spec.add_development_dependency 'rspec', '~> 3.13'
44
+ spec.add_development_dependency 'rubocop', '~> 1.64', '>= 1.64.1'
45
+ spec.add_development_dependency 'rubocop-performance', '~> 1.21'
46
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6.0'
47
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.29', '>= 2.29.2'
48
+ spec.add_development_dependency 'simplecov', '~> 0.22.0'
49
+
50
+ spec.metadata['rubygems_mfa_required'] = 'true'
51
+ end
@@ -14,7 +14,7 @@ module Onboardable
14
14
  def initialize: () -> void
15
15
  end
16
16
 
17
- class InvalidStepError < Error
17
+ class StepError < Error
18
18
  def initialize: (String, Array[String]) -> void
19
19
  end
20
20
 
@@ -29,4 +29,8 @@ module Onboardable
29
29
  class FirstStepError < Error
30
30
  def initialize: (String, Array[String]) -> void
31
31
  end
32
+
33
+ class StepStatusError < Error
34
+ def initialize: (Symbol, Array[Symbol]) -> void
35
+ end
32
36
  end
@@ -13,7 +13,11 @@ module Onboardable
13
13
  attr_writer steps: Array[Step]
14
14
  attr_writer current_step: Step
15
15
 
16
- def step_index!: (Step)-> Integer
16
+ def step_error!: (Step) -> void
17
+
18
+ def step_index: (Step)-> Integer
19
+
20
+ def update_each_step_status: (Integer) -> Array[Step]
17
21
  end
18
22
  end
19
23
  end
@@ -9,10 +9,10 @@ module Onboardable
9
9
  def create_step: (String, Hash[Symbol, untyped]) -> Step
10
10
  alias step create_step
11
11
 
12
- def create_step_from!: (Class) -> Step
13
- alias step_from create_step_from!
12
+ def create_step_from: (Class) -> Step
13
+ alias step_from create_step_from
14
14
 
15
- def build!: (String?) -> Base
15
+ def build: (String?) -> Base
16
16
 
17
17
  private
18
18
 
@@ -23,6 +23,8 @@ module Onboardable
23
23
  def convert_to_step!: (String) -> Step
24
24
 
25
25
  def convert_to_steps!: -> Array[Step]
26
+
27
+ def undefined_method_error!: (Class) -> void
26
28
  end
27
29
  end
28
30
  end
@@ -3,12 +3,24 @@ module Onboardable
3
3
  module Navigation
4
4
  def next_step: () -> Step?
5
5
  def next_step!: () -> Step
6
+ def next_step?: (Step) -> bool
7
+
6
8
  def prev_step: () -> Step?
9
+ def prev_step?: (Step) -> bool
7
10
  def prev_step!: () -> Step
8
- def first_step?: (?Step) -> bool
11
+
9
12
  def first_step: -> Step
10
- def last_step?: (?Step) -> bool
13
+ def first_step?: (?Step) -> bool
14
+
11
15
  def last_step: -> Step
16
+ def last_step?: (?Step) -> bool
17
+
18
+ def current_step?: (Step) -> bool
19
+
20
+ private
21
+
22
+ def first_step_error!: -> void
23
+ def last_step_error!: -> void
12
24
  end
13
25
  end
14
26
  end
@@ -28,5 +28,7 @@ module Onboardable
28
28
  attr_writer name: String
29
29
  attr_writer data: Hash[Symbol, untyped]
30
30
  attr_writer status: Symbol
31
+
32
+ def comparison_result_error!: (Integer) -> void
31
33
  end
32
34
  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.1.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artem Skrynnyk
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-21 00:00:00.000000000 Z
11
+ date: 2024-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -50,20 +50,20 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.63'
53
+ version: '1.64'
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: 1.63.5
56
+ version: 1.64.1
57
57
  type: :development
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '1.63'
63
+ version: '1.64'
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: 1.63.5
66
+ version: 1.64.1
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rubocop-performance
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -133,8 +133,6 @@ executables: []
133
133
  extensions: []
134
134
  extra_rdoc_files: []
135
135
  files:
136
- - ".rspec"
137
- - ".rubocop.yml"
138
136
  - CHANGELOG.md
139
137
  - CODE_OF_CONDUCT.md
140
138
  - Gemfile
@@ -150,6 +148,7 @@ files:
150
148
  - lib/onboardable/step.rb
151
149
  - lib/onboardable/utils/warnings.rb
152
150
  - lib/onboardable/version.rb
151
+ - onboardable.gemspec
153
152
  - sig/onboardable.rbs
154
153
  - sig/onboardable/errors.rbs
155
154
  - sig/onboardable/list/base.rbs
@@ -166,7 +165,7 @@ metadata:
166
165
  bug_tracker_uri: https://github.com/dmrAnderson/onboardable/issues
167
166
  documentation_uri: https://rubydoc.info/gems/onboardable
168
167
  rubygems_mfa_required: 'true'
169
- post_install_message:
168
+ post_install_message:
170
169
  rdoc_options: []
171
170
  require_paths:
172
171
  - lib
@@ -181,8 +180,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
180
  - !ruby/object:Gem::Version
182
181
  version: '0'
183
182
  requirements: []
184
- rubygems_version: 3.5.10
185
- signing_key:
183
+ rubygems_version: 3.5.9
184
+ signing_key:
186
185
  specification_version: 4
187
186
  summary: Streamlines onboarding process integration
188
187
  test_files: []
data/.rspec DELETED
@@ -1,4 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
4
- --order rand
data/.rubocop.yml DELETED
@@ -1,11 +0,0 @@
1
- require:
2
- - rubocop-performance
3
- - rubocop-rspec
4
- - rubocop-rake
5
-
6
- AllCops:
7
- NewCops: enable
8
- TargetRubyVersion: 3.0.0
9
-
10
- Gemspec/DevelopmentDependencies:
11
- EnforcedStyle: gemspec