onboardable 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef1662a800c2b2c562af504026f4a84bd7a93c895236784e20e53659d7fdcaa3
4
- data.tar.gz: 90df600da2944d056ca6d64891a311e4507f1227c2827b44485735451ccc329d
3
+ metadata.gz: 5190ba93a0d09c427d6314bd1e7645ca8052406683fd9bc8f8ee90a7d0ba2386
4
+ data.tar.gz: 3aa3d7073ecba0ab36c136584a48c5296e40c98d80fdab15b95cf8de7d014040
5
5
  SHA512:
6
- metadata.gz: 32e156cde48492f62a29a41b1c382c1be86bd5922b47ddcd32a5ca606127204002f8805da9c1adc087ea005c90a26a20de9081939617d9f623f9b5075fecd113
7
- data.tar.gz: 3f8a70a79552741d71c0de140204220e66d70b5a43e5e7ce3c54c15b06900e55e15f5c840812c3463f3c10909bb693788e0aeaeb8039617a026b2fe2cef2bcc0
6
+ metadata.gz: 5b5248e51f51c217d8ddaeb2171b4a653bf291e1c02302701b1a02fae9175430ded3100e2e87e5f95514d9e1676cb159fc3c939f49a218f7f9b61cd9ba01b39d
7
+ data.tar.gz: 83a10458cd15031209a7ceeb7f8dec8a46de0c1af1b2328f8d142a41739c0983a62e4a11729761465f3a36dcdf0e9e3b90df20732de6d22d5a50cb91c48dc0ef
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.2.0] - 2024-05-25
6
+
7
+ - Added `#second_step?` method to check if the target step is the second step.
8
+ - Added `#prev_step?` method to check if the target step is the previous step.
9
+ - Added `#current_step?` method to check if the target step is the current step.
10
+
11
+ ## [1.1.1] - 2024-05-21
12
+
5
13
  - Added `onboarding` class method to define the onboarding steps.
6
14
  - Added [documentation link](https://rubydoc.info/gems/onboardable) to gemspec.
7
15
 
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.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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.0)
38
38
  json (~> 2.3)
39
39
  language_server-protocol (>= 3.17.0)
40
40
  parallel (~> 1.10)
@@ -81,7 +81,7 @@ 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)
85
85
  rubocop-performance (~> 1.21)
86
86
  rubocop-rake (~> 0.6.0)
87
87
  rubocop-rspec (~> 2.29, >= 2.29.2)
data/README.md CHANGED
@@ -41,19 +41,14 @@ project as per the installation guide provided earlier.
41
41
  include Onboardable
42
42
 
43
43
  has_onboarding do
44
- # Use the `step` method to steps with a name and optional data
45
44
  step 'welcome', message: 'Welcome to your new account!'
46
45
  step 'account_setup', task: 'Create credentials'
47
46
  step 'confirmation'
48
-
49
- # Use the `step_from` method to define steps from external providers
50
47
  step_from ExternalStepProvider
51
48
  end
52
49
  end
53
50
 
54
- # External class for providing a reusable onboarding step
55
51
  class ExternalStepProvider
56
- # Define class method to return an onboarding step
57
52
  def self.to_onboarding_step
58
53
  Onboardable::Step.new('external_step', info: 'This is an external step.')
59
54
  end
@@ -80,6 +75,15 @@ that allow step navigation and state verification:
80
75
  onboarding = User.new.onboarding
81
76
  ```
82
77
 
78
+ 1. **Check order of steps**
79
+
80
+ Determine the order of steps in the onboarding process to ensure
81
+ that the sequence is correct and that the steps are defined as expected.
82
+
83
+ ```ruby
84
+ onboarding.steps # Returns the list of steps
85
+ ```
86
+
83
87
  1. **Navigating Through Steps**
84
88
 
85
89
  Navigate through the onboarding steps using the navigation methods provided.
@@ -111,8 +115,13 @@ that allow step navigation and state verification:
111
115
  to manage UI elements like 'Next' or 'Back' buttons appropriately.
112
116
 
113
117
  ```ruby
114
- onboarding.first_step? # Is the first step?
115
- onboarding.last_step? # Is the last step?
118
+ step = onboarding.steps.sample # Random step from the list
119
+
120
+ onboarding.first_step?(step) # Is the first step?
121
+ onboarding.last_step?(step) # Is the last step?
122
+ onboarding.current_step?(step) # Is the current step?
123
+ onboarding.second_step?(step) # Is the second step?
124
+ onboarding.prev_step?(step) # Is the previous step?
116
125
  ```
117
126
 
118
127
  1. **Monitor Progress**
@@ -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,11 +30,10 @@ 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.
33
+ # @return [Step] The converted step.
34
+ # @raise [UndefinedMethodError] If the conversion method is not defined for the class.
34
35
  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
36
+ (Step.try_convert(klass) || undefined_method_error!(klass)).tap { |step| add_step(step) }
38
37
  end
39
38
  alias step_from create_step_from!
40
39
 
@@ -42,7 +41,7 @@ module Onboardable
42
41
  #
43
42
  # @param current_step_name [String, nil] The name of the step to mark as current in the built list. Can be nil.
44
43
  # @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.
44
+ # @raise [EmptyStepsError] If no steps have been added to the builder.
46
45
  def build!(current_step_name)
47
46
  Base.new(convert_to_steps!, convert_to_step!(current_step_name || current_step.name))
48
47
  end
@@ -52,13 +51,14 @@ module Onboardable
52
51
  # Adds a step to the builder.
53
52
  #
54
53
  # @param step [Step] The step to be added.
54
+ # @return [Step] The added step.
55
55
  def add_step(step)
56
- step.name.then do |name|
56
+ step.tap do
57
+ name = step.name
57
58
  warn_about_override(name) if steps.key?(name)
58
59
  steps[name] = step
60
+ self.current_step ||= step
59
61
  end
60
-
61
- self.current_step ||= step
62
62
  end
63
63
 
64
64
  # Assigns a hash of steps to the builder.
@@ -73,18 +73,24 @@ module Onboardable
73
73
  # @return [Array<Step>] An array of steps.
74
74
  # @raise [EmptyStepsError] Raises if there are no steps to convert.
75
75
  def convert_to_steps!
76
- raise EmptyStepsError if steps.empty?
77
-
78
- steps.values
76
+ steps.any? ? steps.values : raise(EmptyStepsError)
79
77
  end
80
78
 
81
79
  # Retrieves a Step object from the builder's steps based on the step name.
82
80
  #
83
81
  # @param name [String] The name of the step to be converted to a Step object.
84
82
  # @return [Step] The corresponding Step object.
85
- # @raise [InvalidStepError] Raises if the specified step name is not present in the steps hash.
83
+ # @raise [StepError] Raises if the specified step name is not present in the steps hash.
86
84
  def convert_to_step!(name)
87
- steps[name] || raise(InvalidStepError.new(name, steps.keys))
85
+ steps[name] || raise(StepError.new(name, steps.keys))
86
+ end
87
+
88
+ # Raises an UndefinedMethodError indicating that the conversion method is not defined for the class.
89
+ #
90
+ # @param klass [Class] The class that does not have the conversion method defined.
91
+ # @raise [UndefinedMethodError] Raises an error indicating the missing conversion method.
92
+ def undefined_method_error!(klass)
93
+ raise UndefinedMethodError.new(klass, Step::CONVERSION_METHOD)
88
94
  end
89
95
  end
90
96
  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.0'
5
5
  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
@@ -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.0
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-21 00:00:00.000000000 Z
11
+ date: 2024-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -50,20 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.63'
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: 1.63.5
53
+ version: '1.64'
57
54
  type: :development
58
55
  prerelease: false
59
56
  version_requirements: !ruby/object:Gem::Requirement
60
57
  requirements:
61
58
  - - "~>"
62
59
  - !ruby/object:Gem::Version
63
- version: '1.63'
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: 1.63.5
60
+ version: '1.64'
67
61
  - !ruby/object:Gem::Dependency
68
62
  name: rubocop-performance
69
63
  requirement: !ruby/object:Gem::Requirement