rails_onboarding 0.6.1 → 0.7.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: b5f1d40794e4959c807fd7fd7077c81c60821cbf8e1bd79fd69089543e718790
4
- data.tar.gz: 3ce1ebd746fc540b491460bf1c0c7e2dee36709182bee72beab9a6c5e4b56ec0
3
+ metadata.gz: c0a6325415445d0fcf87f3513dcfda910866b9bbbf93b8edde3e2c904af7ee15
4
+ data.tar.gz: 4db74bd332e05a95ba79404499c314c4dbb251511d075ef1b56746559a96963b
5
5
  SHA512:
6
- metadata.gz: ac1a9a964c82891f05512236f1930c753082efa29e16f34621da582f962cabdd7761611c4538f4857a6ffc08f5388c123f8837b3923aba3d2392b6d72c178ce2
7
- data.tar.gz: ed7976e143fc227f068324ac48c6d93e01acad26a3fcd6a445599fd628411b0b22f8353a9f5c87085929cc07025b18e956391c6d7973cb9742aab6dd4e522cf3
6
+ metadata.gz: 4a3f455ac868d10a4cc011f434f5e2f8ab4e7b72c9769ebbfccd7ad2a0af78af0dc132d23771bd899926a05075ab025e1c53e2d4dacd8df329220e43b55899e9
7
+ data.tar.gz: 5a6d4ab7899f89c143754b9c2c5d85efb44efaeb5e8a7c0f1a0df9dd009961cf39614f91085f4d51de8ccfb76eacf85d56a205c857b5d4298af984e4b82b14b0
data/README.md CHANGED
@@ -537,7 +537,11 @@ current_user.complete_onboarding!
537
537
  # Skip onboarding
538
538
  current_user.skip_onboarding!
539
539
 
540
- # Restart onboarding
540
+ # Reset onboarding state (clears milestones too; pass clear_milestones: false to keep them)
541
+ current_user.reset_onboarding!
542
+
543
+ # Restart onboarding - resets and walks the user through the steps again, even
544
+ # if their data already satisfies every :complete_if
541
545
  current_user.restart_onboarding!
542
546
  ```
543
547
 
@@ -1016,7 +1020,9 @@ RailsOnboarding::Templates.apply('saas')
1016
1020
  - `go_back!` - Move to previous step
1017
1021
  - `complete_onboarding!` - Mark onboarding as complete
1018
1022
  - `skip_onboarding!` - Skip onboarding
1019
- - `restart_onboarding!` - Restart from beginning
1023
+ - `reset_onboarding!(clear_milestones: true)` - Clear onboarding state
1024
+ - `restart_onboarding!` - Reset and re-walk the flow (replay mode)
1025
+ - `replaying_onboarding?` - Is the user re-walking the flow?
1020
1026
 
1021
1027
  #### Progress Methods
1022
1028
  - `onboarding_progress` - Returns completion percentage (0-100)
@@ -61,7 +61,19 @@ module RailsOnboarding
61
61
  @user = user_class.find(params[:id])
62
62
 
63
63
  @user.restart_onboarding!
64
- flash[:notice] = "Onboarding restarted for user #{@user.id}"
64
+
65
+ # Without the replay columns a restart can't stop :complete_if from
66
+ # auto-advancing an established user straight back to "completed",
67
+ # which looks like the button did nothing. Say so rather than claiming
68
+ # a restart that won't survive their next page load.
69
+ flash[:notice] = if !user_class.respond_to?(:onboarding_replay_supported?) ||
70
+ user_class.onboarding_replay_supported?
71
+ "Onboarding restarted for user #{@user.id}"
72
+ else
73
+ "Onboarding restarted for user #{@user.id}. Steps whose completion criteria " \
74
+ "are already met will be skipped - run the add_onboarding_replay_to_users " \
75
+ "migration to walk this user through them again."
76
+ end
65
77
  redirect_to admin_user_path(@user)
66
78
  rescue StandardError => e
67
79
  flash[:alert] = "Error restarting onboarding: #{e.message}"
@@ -55,6 +55,12 @@ module RailsOnboarding
55
55
  )
56
56
  end
57
57
 
58
+ # Record that the user has now been shown this step. That's what lets
59
+ # :complete_if advance them past it when they come back, and it is what
60
+ # makes a restart re-walk a flow the user's data already satisfies.
61
+ # No-op outside a replay.
62
+ mark_step_replayed(@current_step)
63
+
58
64
  # Steps that own a real page in the host app: route there instead of
59
65
  # rendering a gem template, so the existing controller/view do the work.
60
66
  if @current_step[:path]
@@ -231,7 +237,11 @@ module RailsOnboarding
231
237
  def advance_completed_steps
232
238
  awarded_milestones = []
233
239
  RailsOnboarding.configuration.total_steps.times do
234
- break unless @current_step && step_criteria_met?(@current_step)
240
+ break unless @current_step && onboarding_step_criteria_met?(@current_step)
241
+ # During a replay, a satisfied :complete_if is not enough on its own -
242
+ # the user has to have been shown the step again first, or a restart
243
+ # would auto-advance them straight back to "completed".
244
+ break unless step_replayed?(@current_step)
235
245
 
236
246
  if defined?(RailsOnboarding::MilestoneService)
237
247
  milestones = RailsOnboarding::MilestoneService.check_onboarding_step_milestones(
@@ -258,17 +268,19 @@ module RailsOnboarding
258
268
  set_step
259
269
  end
260
270
 
261
- # A buggy :complete_if must not brick onboarding - if it raises, treat the
262
- # step as not yet complete instead of letting the shared StandardError
263
- # handler redirect back to /onboarding (which would re-raise on arrival,
264
- # redirecting again in an endless browser loop).
265
- def step_criteria_met?(step)
266
- return false unless step[:complete_if].is_a?(Proc)
271
+ # Replay bookkeeping. Both tolerate a host User that predates replay
272
+ # support (or isn't Onboardable at all), falling back to the ordinary
273
+ # auto-advance behaviour.
274
+ def step_replayed?(step)
275
+ return true unless current_user.respond_to?(:onboarding_step_replayed?)
267
276
 
268
- step[:complete_if].call(current_user)
269
- rescue StandardError => e
270
- Rails.logger.error("RailsOnboarding: complete_if for step '#{step[:name]}' raised #{e.class} - #{e.message}")
271
- false
277
+ current_user.onboarding_step_replayed?(step[:name])
278
+ end
279
+
280
+ def mark_step_replayed(step)
281
+ return unless current_user.respond_to?(:mark_onboarding_step_replayed!)
282
+
283
+ current_user.mark_onboarding_step_replayed!(step[:name])
272
284
  end
273
285
 
274
286
  # Redirect to the host-app page that owns the current step. Returns false
@@ -18,6 +18,8 @@ module RailsOnboarding
18
18
  # - milestones_achieved: text (serialized JSON array)
19
19
  # - milestone_points: integer
20
20
  # - last_milestone_at: datetime
21
+ # - onboarding_replay_started_at: datetime
22
+ # - onboarding_replay_steps: text (serialized JSON array)
21
23
 
22
24
  # Fix for Rails 8: Use the new serialize syntax
23
25
  if columns_hash["feature_tooltips_shown"]&.type == :text
@@ -28,6 +30,10 @@ module RailsOnboarding
28
30
  serialize :milestones_achieved, coder: JSON
29
31
  end
30
32
 
33
+ if columns_hash["onboarding_replay_steps"]&.type == :text
34
+ serialize :onboarding_replay_steps, coder: JSON
35
+ end
36
+
31
37
  # Association with analytics events (only if ActiveRecord is available)
32
38
  if respond_to?(:has_many)
33
39
  has_many :analytics_events,
@@ -43,6 +49,19 @@ module RailsOnboarding
43
49
  end
44
50
  end
45
51
 
52
+ class_methods do
53
+ # Replay mode needs two columns that predate no host app: installs from
54
+ # before it existed simply don't have them. Everything replay-related
55
+ # degrades to the old auto-advance behaviour when they're missing rather
56
+ # than raising on a column that isn't there.
57
+ def onboarding_replay_supported?
58
+ return false unless respond_to?(:column_names)
59
+
60
+ column_names.include?("onboarding_replay_started_at") &&
61
+ column_names.include?("onboarding_replay_steps")
62
+ end
63
+ end
64
+
46
65
  # Determines if the user needs to go through onboarding
47
66
  #
48
67
  # This method checks the configuration setting and user state to determine
@@ -163,9 +182,11 @@ module RailsOnboarding
163
182
 
164
183
  def complete_onboarding!(session_id: nil, completion_time: nil)
165
184
  persist_and_track!(
166
- onboarding_completed: true,
167
- onboarding_completed_at: Time.current,
168
- onboarding_current_step: nil
185
+ {
186
+ onboarding_completed: true,
187
+ onboarding_completed_at: Time.current,
188
+ onboarding_current_step: nil
189
+ }.merge(replay_reset_attributes)
169
190
  ) do
170
191
  AnalyticsEvent.track_onboarding_completed(
171
192
  user: self,
@@ -179,10 +200,12 @@ module RailsOnboarding
179
200
 
180
201
  def skip_onboarding!(session_id: nil)
181
202
  persist_and_track!(
182
- onboarding_completed: true,
183
- onboarding_completed_at: Time.current,
184
- onboarding_skipped: true,
185
- onboarding_current_step: nil
203
+ {
204
+ onboarding_completed: true,
205
+ onboarding_completed_at: Time.current,
206
+ onboarding_skipped: true,
207
+ onboarding_current_step: nil
208
+ }.merge(replay_reset_attributes)
186
209
  ) do
187
210
  AnalyticsEvent.track_onboarding_completed(
188
211
  user: self,
@@ -193,14 +216,35 @@ module RailsOnboarding
193
216
  end
194
217
  end
195
218
 
196
- def reset_onboarding!
197
- update!(
219
+ # Clear onboarding state so the user starts over.
220
+ #
221
+ # Milestones are cleared by default. achieve_milestone! is idempotent
222
+ # (it returns false for anything already in milestones_achieved), so
223
+ # leaving them behind means a user sent back through the flow can never
224
+ # re-earn them and finishes with no milestone notice at all. Pass
225
+ # clear_milestones: false to keep them - worth doing where a host app's
226
+ # milestones record real achievements rather than onboarding progress.
227
+ #
228
+ # Any in-flight replay is cancelled: a bare reset drops the user back to
229
+ # the default behaviour, where a step whose :complete_if already passes is
230
+ # advanced past automatically. Use restart_onboarding! to reset *and*
231
+ # replay.
232
+ #
233
+ # @param clear_milestones [Boolean] also wipe milestones, points and
234
+ # last_milestone_at (default: true)
235
+ # @return [Boolean] true
236
+ def reset_onboarding!(clear_milestones: true)
237
+ attributes = {
198
238
  onboarding_completed: false,
199
239
  onboarding_completed_at: nil,
200
240
  onboarding_skipped: false,
201
241
  onboarding_current_step: nil,
202
242
  feature_tooltips_shown: {}
203
- )
243
+ }
244
+ attributes.merge!(milestone_reset_attributes) if clear_milestones
245
+ attributes.merge!(replay_reset_attributes)
246
+
247
+ update!(attributes)
204
248
  end
205
249
 
206
250
  # Feature tooltips
@@ -432,11 +476,18 @@ module RailsOnboarding
432
476
  true
433
477
  end
434
478
 
435
- def restart_onboarding!(session_id: nil)
479
+ # Reset and immediately re-enter the flow at step one.
480
+ #
481
+ # Unlike a bare reset this turns replay mode on, so a user whose host-app
482
+ # data already satisfies every :complete_if walks the steps again instead
483
+ # of being auto-advanced straight back to "completed" on their next visit
484
+ # to /onboarding. See replaying_onboarding?.
485
+ def restart_onboarding!(session_id: nil, clear_milestones: true)
436
486
  previous_step = onboarding_current_step
437
487
  was_completed = onboarding_completed
438
488
 
439
- reset_onboarding!
489
+ reset_onboarding!(clear_milestones: clear_milestones)
490
+ start_onboarding_replay!
440
491
  start_onboarding!(session_id: session_id)
441
492
 
442
493
  AnalyticsEvent.track_custom_event(
@@ -447,6 +498,63 @@ module RailsOnboarding
447
498
  )
448
499
  end
449
500
 
501
+ # Replay mode
502
+ #
503
+ # A reset clears onboarding's own bookkeeping, but it cannot clear the
504
+ # host-app data a step's :complete_if reads. For an established user every
505
+ # predicate is still satisfied, so /onboarding advances through the whole
506
+ # flow in a single redirect and the reset looks like it did nothing.
507
+ #
508
+ # While a replay is active, :complete_if is no longer enough on its own -
509
+ # the user also has to have been shown the step again. That makes a
510
+ # restart walk the real pages a second time without touching the data
511
+ # those predicates read.
512
+
513
+ # Is this user currently re-walking the flow?
514
+ #
515
+ # False once they finish, so a completed user is never stuck in replay.
516
+ def replaying_onboarding?
517
+ return false unless self.class.onboarding_replay_supported?
518
+ return false if onboarding_completed?
519
+
520
+ onboarding_replay_started_at.present?
521
+ end
522
+
523
+ # @return [Boolean] false when the host app hasn't run the replay migration
524
+ def start_onboarding_replay!
525
+ return false unless self.class.onboarding_replay_supported?
526
+
527
+ update!(onboarding_replay_started_at: Time.current, onboarding_replay_steps: [])
528
+ true
529
+ end
530
+
531
+ def end_onboarding_replay!
532
+ return false unless self.class.onboarding_replay_supported?
533
+
534
+ update!(replay_reset_attributes)
535
+ true
536
+ end
537
+
538
+ # Has the user been shown this step since the current replay began?
539
+ #
540
+ # True whenever no replay is active, so callers can gate auto-advance on
541
+ # it unconditionally and get the ordinary behaviour outside a replay.
542
+ def onboarding_step_replayed?(step_name)
543
+ return true unless replaying_onboarding?
544
+
545
+ (onboarding_replay_steps || []).map(&:to_s).include?(step_name.to_s)
546
+ end
547
+
548
+ # Record that the user has now seen this step. No-op outside a replay.
549
+ def mark_onboarding_step_replayed!(step_name)
550
+ return false unless replaying_onboarding?
551
+ return false if onboarding_step_replayed?(step_name)
552
+
553
+ self.onboarding_replay_steps = (onboarding_replay_steps || []) + [ step_name.to_s ]
554
+ save!
555
+ true
556
+ end
557
+
450
558
  # API-friendly aliases and helper methods
451
559
  alias_method :complete_step, :complete_onboarding_step!
452
560
  alias_method :skip_step, :skip_onboarding_step!
@@ -552,6 +660,26 @@ module RailsOnboarding
552
660
  # triggers) must never fire for a state change that didn't actually get
553
661
  # persisted - update!/save! raise on failure, so if that happens, this
554
662
  # method never reaches the block.
663
+ # Milestone columns arrive in a separate migration, so a host app can be
664
+ # running without them. Only reset what actually exists.
665
+ def milestone_reset_attributes
666
+ attributes = {}
667
+ attributes[:milestones_achieved] = [] if onboarding_column?("milestones_achieved")
668
+ attributes[:milestone_points] = 0 if onboarding_column?("milestone_points")
669
+ attributes[:last_milestone_at] = nil if onboarding_column?("last_milestone_at")
670
+ attributes
671
+ end
672
+
673
+ def replay_reset_attributes
674
+ return {} unless self.class.onboarding_replay_supported?
675
+
676
+ { onboarding_replay_started_at: nil, onboarding_replay_steps: [] }
677
+ end
678
+
679
+ def onboarding_column?(name)
680
+ self.class.respond_to?(:column_names) && self.class.column_names.include?(name)
681
+ end
682
+
555
683
  def persist_and_track!(attributes = nil)
556
684
  if attributes
557
685
  update!(attributes)
@@ -34,11 +34,15 @@
34
34
  </span>
35
35
  </div>
36
36
  <div class="onboarding-banner-actions">
37
- <%# For :path steps, /onboarding re-checks complete_if and either
38
- advances or routes back to the step's page - so "Continue" is
39
- always the right link. %>
40
- <%= link_to "Continue setup →", onboarding_root,
41
- class: "onboarding-banner-continue" %>
37
+ <%# /onboarding re-checks complete_if and either advances or routes back
38
+ to the step's page. That makes "Continue" the right link from
39
+ anywhere else in the app - but on the step's own page, with the
40
+ criteria not yet met, it redirects straight back here and the click
41
+ does nothing visible. Offer it only when it leads somewhere. %>
42
+ <% if !defined?(onboarding_continue_available?) || onboarding_continue_available? %>
43
+ <%= link_to "Continue setup →", onboarding_root,
44
+ class: "onboarding-banner-continue" %>
45
+ <% end %>
42
46
  <% if step[:skippable] %>
43
47
  <%= button_to "Skip this step", rails_onboarding.skip_onboarding_path,
44
48
  method: :post, class: "onboarding-banner-skip" %>
@@ -60,6 +60,11 @@ module RailsOnboarding
60
60
  "db/migrate/add_robustness_fields_to_users.rb"
61
61
  end
62
62
 
63
+ def copy_onboarding_replay_migration
64
+ migration_template "add_onboarding_replay_to_users.rb",
65
+ "db/migrate/add_onboarding_replay_to_users.rb"
66
+ end
67
+
63
68
  def copy_initializer
64
69
  template "rails_onboarding.rb", "config/initializers/rails_onboarding.rb"
65
70
  end
@@ -87,6 +92,7 @@ module RailsOnboarding
87
92
  "add_milestone_tracking_to_users.rb",
88
93
  "add_onboarding_indexes.rb",
89
94
  "add_robustness_fields_to_users.rb.tt",
95
+ "add_onboarding_replay_to_users.rb",
90
96
  "rails_onboarding.rb",
91
97
  "onboarding.css",
92
98
  "README"
@@ -0,0 +1,22 @@
1
+ class AddOnboardingReplayToUsers < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def up
3
+ # Replay mode: when set, the user is walking the flow again after a
4
+ # restart, and a step's :complete_if is not allowed to advance them past a
5
+ # step they have not been shown yet. See Onboardable#replaying_onboarding?.
6
+ #
7
+ # No DB-level default on the text column: MySQL/MariaDB reject a literal
8
+ # DEFAULT on TEXT, and Onboardable already treats nil as an empty array.
9
+ unless column_exists?(:users, :onboarding_replay_started_at)
10
+ add_column :users, :onboarding_replay_started_at, :datetime
11
+ end
12
+
13
+ unless column_exists?(:users, :onboarding_replay_steps)
14
+ add_column :users, :onboarding_replay_steps, :text
15
+ end
16
+ end
17
+
18
+ def down
19
+ remove_column :users, :onboarding_replay_steps if column_exists?(:users, :onboarding_replay_steps)
20
+ remove_column :users, :onboarding_replay_started_at if column_exists?(:users, :onboarding_replay_started_at)
21
+ end
22
+ end
@@ -4,7 +4,7 @@ module RailsOnboarding
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- helper_method :needs_onboarding?, :onboarding_path
7
+ helper_method :needs_onboarding?, :onboarding_path, :onboarding_continue_available?
8
8
  end
9
9
 
10
10
  # Configuration at the controller level
@@ -105,6 +105,59 @@ module RailsOnboarding
105
105
  path.is_a?(Proc) ? instance_exec(&path) : main_app.public_send(path)
106
106
  end
107
107
 
108
+ # Has this step's :complete_if been satisfied?
109
+ #
110
+ # A buggy :complete_if must not brick onboarding - if it raises, treat the
111
+ # step as not yet complete instead of letting the engine's shared
112
+ # StandardError handler redirect back to /onboarding (which would re-raise
113
+ # on arrival, redirecting again in an endless browser loop).
114
+ def onboarding_step_criteria_met?(step)
115
+ return false unless step.is_a?(Hash) && step[:complete_if].is_a?(Proc)
116
+
117
+ step[:complete_if].call(current_user)
118
+ rescue StandardError => e
119
+ Rails.logger.error("RailsOnboarding: complete_if for step '#{step[:name]}' raised #{e.class} - #{e.message}")
120
+ false
121
+ end
122
+
123
+ # Would following the banner's "Continue" link actually move the user?
124
+ #
125
+ # Anywhere else in the app, yes: /onboarding routes them to the current
126
+ # step. Standing on the step's own page it depends - /onboarding re-checks
127
+ # :complete_if and only advances once it passes. Until then it resolves the
128
+ # step's path and redirects straight back to the page the user is already
129
+ # on, so the link renders but the click does nothing visible. The banner
130
+ # asks this before offering it.
131
+ #
132
+ # The comparison is deliberately an exact path match rather than
133
+ # on_current_step_page?, which also treats any other action on the same
134
+ # controller as "on the step page". That breadth is right for the loop
135
+ # guard - it keeps the guard off the PATCH that completes the step - but
136
+ # wrong here: /profiles/123 is a different page from a step pointing at
137
+ # /profiles/123/edit, and Continue really does move the user between them.
138
+ def onboarding_continue_available?
139
+ return false unless user_signed_in?
140
+ return false unless current_user.respond_to?(:current_onboarding_step)
141
+
142
+ step = current_user.current_onboarding_step
143
+ # No :path means the step renders a gem template at /onboarding, which is
144
+ # always somewhere other than the host page showing this banner.
145
+ return true unless step.is_a?(Hash) && step[:path]
146
+
147
+ # The resolved route may carry a query string or be a full URL;
148
+ # request.path never does.
149
+ resolved_path = URI.parse(resolve_onboarding_step_path(step[:path]).to_s).path
150
+ return true unless request.path == resolved_path
151
+
152
+ onboarding_step_criteria_met?(step)
153
+ rescue StandardError => e
154
+ # An unresolvable :path means the engine can't redirect to it either - it
155
+ # falls back to rendering a gem template, so Continue still goes
156
+ # somewhere. Offer it.
157
+ Rails.logger.warn("RailsOnboarding: could not resolve step path for the banner link: #{e.class} - #{e.message}")
158
+ true
159
+ end
160
+
108
161
  private
109
162
 
110
163
  # True when the current request is for the page a :path-based step points
@@ -167,7 +167,9 @@ module RailsOnboarding
167
167
  "feature_tooltips_shown" => [ :jsonb, :text ],
168
168
  "milestones_achieved" => [ :text, :jsonb ],
169
169
  "milestone_points" => :integer,
170
- "last_milestone_at" => :datetime
170
+ "last_milestone_at" => :datetime,
171
+ "onboarding_replay_started_at" => :datetime,
172
+ "onboarding_replay_steps" => [ :text, :jsonb ]
171
173
  }
172
174
 
173
175
  # Check required columns
@@ -1,3 +1,3 @@
1
1
  module RailsOnboarding
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_onboarding
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Lewis
@@ -234,6 +234,7 @@ files:
234
234
  - lib/generators/rails_onboarding/templates/add_analytics_to_rails_onboarding.rb
235
235
  - lib/generators/rails_onboarding/templates/add_milestone_tracking_to_users.rb
236
236
  - lib/generators/rails_onboarding/templates/add_onboarding_indexes.rb
237
+ - lib/generators/rails_onboarding/templates/add_onboarding_replay_to_users.rb
237
238
  - lib/generators/rails_onboarding/templates/add_onboarding_to_users.rb
238
239
  - lib/generators/rails_onboarding/templates/add_robustness_fields_to_users.rb.tt
239
240
  - lib/generators/rails_onboarding/templates/create_rails_onboarding_flows.rb
@@ -281,7 +282,7 @@ licenses:
281
282
  metadata:
282
283
  allowed_push_host: https://rubygems.org
283
284
  homepage_uri: https://github.com/bunnahabhain/rails_onboarding
284
- source_code_uri: https://github.com/bunnahabhain/rails_onboarding/tree/v0.6.1
285
+ source_code_uri: https://github.com/bunnahabhain/rails_onboarding/tree/v0.7.0
285
286
  changelog_uri: https://github.com/bunnahabhain/rails_onboarding/blob/master/docs/CHANGELOG.md
286
287
  rdoc_options: []
287
288
  require_paths: