Module: Clickwrap::Registration

Defined in:
lib/clickwrap/registration.rb

Overview

The authentication adapters: how an account and the evidence that authorized creating it commit together.

Both are thin conveniences over Clickwrap.register!, and both are deliberately EXPLICIT — a line you write in your controller, not a hidden after_create callback and not a monkey patch. That matters because this is the exact place two real applications got it wrong in the same way: the account was already persisted, the evidence write failed, and the exception was rescued. The result was a live account with no record of what its owner had agreed to, and nothing anywhere said so.

Signup is also modeled honestly. At first render there is no persisted actor, so the presentation binds to a short-lived registration flow rather than to a fictional authenticated user, and the receipt records account_registration attribution instead of implying a session that did not exist.

Defined Under Namespace

Modules: ClassMethods, DeviseAdapter

Constant Summary collapse

REFUSALS =

Everything that counts as a REFUSAL of one registration attempt — a person on a stale render, an unticked control, a validation the account failed — as opposed to an infrastructure failure (EventWriteFailed and friends), which is never dressed up as validation and always escapes.

[
  Clickwrap::SubmissionInvalid,
  Clickwrap::PresentationInvalid,
  Clickwrap::AnswerInvalid,
  Clickwrap::RegistrationFailed,
  ActiveRecord::RecordInvalid
].freeze

Class Method Summary collapse

Class Method Details

.absorb_refusal(error, resource:, clickwrap_errors:) ⇒ Object

Translates one refusal into the same human sentences everywhere: inline beside the control it belongs to (through the controller's clickwrap_errors) and once on the resource's :base so the page's error rollup announces it. The Devise adapter and the hand-rolled-door helper (register_with_clickwrap without the bang) both come through here, so every door in an application refuses in identical language by construction — a door cannot forget a rescue it never writes.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/clickwrap/registration.rb', line 44

def self.absorb_refusal(error, resource:, clickwrap_errors:)
  case error
  when Clickwrap::AnswerInvalid
    if error.statement_key.present?
      clickwrap_errors[error.statement_key.to_s] = I18n.t("clickwrap.errors.required_statement")
    end
    resource.errors.add(:base, I18n.t("clickwrap.errors.required_statement")) if resource.errors.empty?
  when Clickwrap::SubmissionInvalid, Clickwrap::PresentationInvalid
    # A missing, stale, expired, or swapped presentation is a person on an
    # old render (or a cached form with no presentation at all) — not an
    # application error, and never a raw 500 in front of a person.
    resource.errors.add(:base, I18n.t("clickwrap.errors.presentation_no_longer_valid")) if resource.errors.empty?
  when Clickwrap::RegistrationFailed, ActiveRecord::RecordInvalid
    resource.errors.add(:base, error.message) if resource.errors.empty?
  else
    raise error
  end

  error
end

.included(base) ⇒ Object



33
34
35
# File 'lib/clickwrap/registration.rb', line 33

def self.included(base)
  base.extend(ClassMethods)
end

.perform(policy_key, prospective_actor:, http_request: nil, submission: nil, tenant: nil, locale: nil, registration_flow_id: nil, &block) ⇒ Object

The primitive both adapters compose. A host with its own registration service can call this directly and get the same guarantees.

Raises:

  • (ArgumentError)


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/clickwrap/registration.rb', line 96

def self.perform(policy_key, prospective_actor:, http_request: nil, submission: nil,
                 tenant: nil, locale: nil, registration_flow_id: nil, &block)
  raise ArgumentError, "register_with_clickwrap needs a block that persists the account" unless block

  Clickwrap.register!(
    policy_key,
    prospective_actor: prospective_actor,
    http_request: http_request,
    submission: submission,
    tenant: tenant,
    locale: locale,
    registration_flow_id: registration_flow_id,
    &block
  )
end