Module: RailsOnboarding::ControllerHelpers
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/rails_onboarding/controller_helpers.rb
Instance Method Summary collapse
-
#advance_onboarding!(step_name) ⇒ Boolean
Complete the current onboarding step from a host-app controller.
- #needs_onboarding? ⇒ Boolean
- #on_onboarding_page? ⇒ Boolean
-
#onboarding_continue_available? ⇒ Boolean
Would following the banner's "Continue" link actually move the user?.
-
#onboarding_path ⇒ Object
The onboarding flow lives at the engine's mount root, so the raw engine helper returns it with a trailing slash ("/onboarding/") like any mounted engine root.
-
#onboarding_step_criteria_met?(step) ⇒ Boolean
Has this step's :complete_if been satisfied?.
-
#resolve_onboarding_step_path(path) ⇒ Object
Resolve a step's :path option against the host application's routes.
- #skip_onboarding_request? ⇒ Boolean
Instance Method Details
#advance_onboarding!(step_name) ⇒ Boolean
Complete the current onboarding step from a host-app controller.
Call this from the action that performs the step's real work (e.g. ProfilesController#create after a successful save), then redirect to onboarding_path to advance the user:
if advance_onboarding!(:profile)
redirect_to onboarding_path
else
redirect_to @profile
end
Deliberately a no-op unless the named step is the user's current step - so when the same action runs outside onboarding (the user edits their profile again next week), the controller behaves normally.
85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 85 def advance_onboarding!(step_name) return false unless user_signed_in? return false unless current_user.respond_to?(:needs_onboarding?) return false unless current_user.needs_onboarding? step = current_user.current_onboarding_step return false unless step && step[:name].to_sym == step_name.to_sym current_user.complete_onboarding_step!(step[:name]) true end |
#needs_onboarding? ⇒ Boolean
29 30 31 32 33 34 35 36 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 29 def needs_onboarding? return false unless user_signed_in? return false if on_onboarding_page? return false if skip_onboarding_request? return false if self.class.skip_onboarding_for_action?(action_name) current_user.needs_onboarding? end |
#on_onboarding_page? ⇒ Boolean
38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 38 def on_onboarding_page? # Deliberately reads the engine proxy, not the onboarding_path method - # in engine controllers the engine's own url_helper shadows ours, so # relying on method resolution here would be ambiguous. Chomping makes # the match segment-aware: the engine root (with or without its # trailing slash) and anything under it count, but sibling host paths # that merely share the prefix (/onboarding_help when mounted at # /onboarding) do not. root = rails_onboarding.onboarding_path.chomp("/") return true if request.path == root || request.path.start_with?("#{root}/") on_current_step_page? end |
#onboarding_continue_available? ⇒ Boolean
Would following the banner's "Continue" link actually move the user?
Anywhere else in the app, yes: /onboarding routes them to the current step. Standing on the step's own page it depends - /onboarding re-checks :complete_if and only advances once it passes. Until then it resolves the step's path and redirects straight back to the page the user is already on, so the link renders but the click does nothing visible. The banner asks this before offering it.
The comparison is deliberately an exact path match rather than on_current_step_page?, which also treats any other action on the same controller as "on the step page". That breadth is right for the loop guard - it keeps the guard off the PATCH that completes the step - but wrong here: /profiles/123 is a different page from a step pointing at /profiles/123/edit, and Continue really does move the user between them.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 138 def onboarding_continue_available? return false unless user_signed_in? return false unless current_user.respond_to?(:current_onboarding_step) step = current_user.current_onboarding_step # No :path means the step renders a gem template at /onboarding, which is # always somewhere other than the host page showing this banner. return true unless step.is_a?(Hash) && step[:path] # The resolved route may carry a query string or be a full URL; # request.path never does. resolved_path = URI.parse(resolve_onboarding_step_path(step[:path]).to_s).path return true unless request.path == resolved_path onboarding_step_criteria_met?(step) rescue StandardError => e # An unresolvable :path means the engine can't redirect to it either - it # falls back to rendering a gem template, so Continue still goes # somewhere. Offer it. Rails.logger.warn("RailsOnboarding: could not resolve step path for the banner link: #{e.class} - #{e.}") true end |
#onboarding_path ⇒ Object
The onboarding flow lives at the engine's mount root, so the raw engine helper returns it with a trailing slash ("/onboarding/") like any mounted engine root. Serve host apps the canonical slash-less form for links and redirects.
62 63 64 65 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 62 def onboarding_path path = rails_onboarding.onboarding_path.chomp("/") path.empty? ? "/" : path end |
#onboarding_step_criteria_met?(step) ⇒ Boolean
Has this step's :complete_if been satisfied?
A buggy :complete_if must not brick onboarding - if it raises, treat the step as not yet complete instead of letting the engine's shared StandardError handler redirect back to /onboarding (which would re-raise on arrival, redirecting again in an endless browser loop).
114 115 116 117 118 119 120 121 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 114 def onboarding_step_criteria_met?(step) return false unless step.is_a?(Hash) && step[:complete_if].is_a?(Proc) step[:complete_if].call(current_user) rescue StandardError => e Rails.logger.error("RailsOnboarding: complete_if for step '#{step[:name]}' raised #{e.class} - #{e.}") false end |
#resolve_onboarding_step_path(path) ⇒ Object
Resolve a step's :path option against the host application's routes. Symbols/Strings are sent to the main_app route proxy; Procs are instance_exec'd in the controller context, so a zero-arg lambda can use main_app, current_user, params, etc.:
path: :new_profile_path
path: -> { main_app.new_post_path(from: "onboarding") }
104 105 106 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 104 def resolve_onboarding_step_path(path) path.is_a?(Proc) ? instance_exec(&path) : main_app.public_send(path) end |
#skip_onboarding_request? ⇒ Boolean
52 53 54 55 56 |
# File 'lib/rails_onboarding/controller_helpers.rb', line 52 def skip_onboarding_request? request.xhr? || request.format.json? || request.path.start_with?("/api") end |