Module: Clickwrap::FormBuilderExtensions
- Defined in:
- lib/clickwrap/form_builder_extensions.rb
Overview
form.clickwrap and form.clickwrap_fields, mixed into the standard Rails
form builder by the engine so the one-line happy path works inside an
ordinary form_with.
Both methods do the same three things: ask the server-owned presenter for a presentation, render the reference partial with it, and put the signed presentation token in the form. The difference is only who renders the submit button.
form.clickwrap is the recommended one because it renders the controls AND
the action as a single presentation. The call to action recorded in the
manifest is then, by construction, the words on the button the person can
actually press — there is no second place for that string to live and drift.
What this helper deliberately never renders is a hidden field carrying a decision the server owns: no IP address, no browser user-agent, no geolocation, no policy version, no document digest, no validity window, no retention rule, no subject binding. All of those are resolved server-side and rechecked at submit. A form field is not a safe place to keep a security decision, and a client that tries to send one gets a loud failure from Clickwrap::Submission rather than a quiet acceptance.
Instance Method Summary collapse
-
#clickwrap(policy_key, submit:, actor: NOT_GIVEN, subject: nil, tenant: NOT_GIVEN, acting_for: nil, locale: nil, capture_channel: nil, errors: nil, combined: true, **html_options) ⇒ Object
The strongest path, and the one the README leads with:.
-
#clickwrap_fields(policy_key, submit_button_text:, actor: NOT_GIVEN, subject: nil, tenant: NOT_GIVEN, acting_for: nil, locale: nil, capture_channel: nil, errors: nil, combined: true, **html_options, &block) ⇒ Object
The split API, for design systems that render the action somewhere this helper cannot reach:.
-
#clickwrap_submit(**options) ⇒ Object
The DRY split-form action.
-
#submit(value = nil, options = {}) ⇒ Object
When a split integration uses Rails' ordinary form.submit, compare the button Rails actually rendered with the words already signed into the presentation, refused in every environment rather than left as a development log.
Instance Method Details
#clickwrap(policy_key, submit:, actor: NOT_GIVEN, subject: nil, tenant: NOT_GIVEN, acting_for: nil, locale: nil, capture_channel: nil, errors: nil, combined: true, **html_options) ⇒ Object
The strongest path, and the one the README leads with:
<%= form.clickwrap :signup, submit: "Create account" %>
<%= form.clickwrap :signup,
actor: current_user,
subject: @organization,
locale: I18n.locale,
submit: {
text: "Create organization",
class: "button button--primary",
data: { turbo_submits_with: "Creating…" }
} %>
submit: takes a String, or a Hash of text: plus any ordinary HTML
options for the button. Everything else in **html_options decorates the
wrapper element, so a design system can hang its own classes on the block.
What it renders, by default, is ONE line:
[ ] I agree to the Terms of Service and I acknowledge the Privacy Policy.
…whenever the policy's statements are all ordinary, required,
default-worded agreements and acknowledgments. Anything the sentence
cannot honestly absorb keeps a control of its own below it, and a policy
with nothing composable is untouched. combined: false asks for the
itemized shape regardless — one boolean, no style registry — and it
reaches the PRESENTER rather than the template, so the manifest signs the
shape that was actually offered.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/clickwrap/form_builder_extensions.rb', line 61 def clickwrap(policy_key, submit:, actor: NOT_GIVEN, subject: nil, tenant: NOT_GIVEN, acting_for: nil, locale: nil, capture_channel: nil, errors: nil, combined: true, **) clickwrap_reserve_form_presentation!(policy_key) text, = clickwrap_split_submit(submit) presentation = clickwrap_present( policy_key, submit_button_text: text, actor: actor, subject: subject, tenant: tenant, acting_for: acting_for, locale: locale, capture_channel: capture_channel, combined: combined ) clickwrap_render_fields( presentation, submit: { text: text, options: }, errors: errors, html_options: ) end |
#clickwrap_fields(policy_key, submit_button_text:, actor: NOT_GIVEN, subject: nil, tenant: NOT_GIVEN, acting_for: nil, locale: nil, capture_channel: nil, errors: nil, combined: true, **html_options, &block) ⇒ Object
The split API, for design systems that render the action somewhere this helper cannot reach:
<%= form.clickwrap_fields :signup, submit_button_text: "Create account" %>
<%= form.clickwrap_submit %>
The manifest records the call to action the person was offered, so the
wording is declared here. form.clickwrap_submit reuses it without a
second string. An ordinary form.submit "Create account" is supported too,
but Clickwrap verifies that it says exactly what the manifest says.
A design system that renders its OWN button markup — not form.submit,
so not something Clickwrap can check — takes the block form and reads the
wording off the presentation instead of retyping it:
<%= form.clickwrap_fields :signup, submit_button_text: "Create account" do |clickwrap| %>
<button class="btn btn--primary" data-turbo-submits-with="Creating…">
<%= clickwrap.submit_button_text %>
</button>
<% end %>
The block is yielded the whole Presenter::Result — submit_button_text,
statements, policy_key, locale — and its output is rendered inside
the same wrapper, after the controls. This is the shape that makes drift
impossible rather than merely detected: there is one string, it is the
signed one, and nothing has to compare two copies of it afterwards.
submit: on form.clickwrap and submit_button_text: here are a
deliberate pair, not a duplication. submit: says "render the button
too"; submit_button_text: says "bind these exact words into the
manifest, and I will render the button myself".
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/clickwrap/form_builder_extensions.rb', line 117 def clickwrap_fields(policy_key, submit_button_text:, actor: NOT_GIVEN, subject: nil, tenant: NOT_GIVEN, acting_for: nil, locale: nil, capture_channel: nil, errors: nil, combined: true, **, &block) clickwrap_reserve_form_presentation!(policy_key) presentation = clickwrap_present( policy_key, submit_button_text: , actor: actor, subject: subject, tenant: tenant, acting_for: acting_for, locale: locale, capture_channel: capture_channel, combined: combined ) # A block that renders the action itself has already single-sourced the # wording off the signed presentation, so there is no second copy left to # verify — and arming the `form.submit` check would then reject a form # that never called `form.submit` at all. @clickwrap_expected_submit_button_text = presentation. unless block clickwrap_render_fields( presentation, submit: nil, errors: errors, html_options: , after: (@template.capture(presentation, &block) if block) ) end |
#clickwrap_submit(**options) ⇒ Object
The DRY split-form action. clickwrap_fields already declared and signed
the exact wording, so this helper renders that same wording without asking
the host to repeat it:
<%= form.clickwrap_fields :signup, submit_button_text: "Create account" %>
<%= form.clickwrap_submit class: "button" %>
Ordinary form.submit "Create account" remains supported and is checked
below. This helper simply removes the second string and therefore removes
the possibility of drift by construction.
158 159 160 161 162 163 164 165 166 |
# File 'lib/clickwrap/form_builder_extensions.rb', line 158 def clickwrap_submit(**) unless @clickwrap_expected_submit_button_text raise ConfigurationError, "form.clickwrap_submit needs form.clickwrap_fields earlier in the same form. " \ "The fields declare the exact call to action that Clickwrap signs into evidence." end submit(@clickwrap_expected_submit_button_text, ) end |
#submit(value = nil, options = {}) ⇒ Object
When a split integration uses Rails' ordinary form.submit, compare the
button Rails actually rendered with the words already signed into the
presentation, refused in every environment rather than left as a
development log. Honest bound: this hook covers form.submit — a raw
175 176 177 178 179 |
# File 'lib/clickwrap/form_builder_extensions.rb', line 175 def submit(value = nil, = {}) html = super (html) if @clickwrap_expected_submit_button_text html end |