Module: Turnstile
- Defined in:
- lib/turnstile.rb
Constant Summary collapse
- SITE_KEY =
raw_site- SECRET_KEY =
raw_secret- ENABLED =
(raw_enabled.nil? ? 'true' : raw_enabled.to_s) == 'true'
- VERIFY_URL =
'https://challenges.cloudflare.com/turnstile/v0/siteverify'
Class Method Summary collapse
- .enabled? ⇒ Boolean
-
.lazy_widget(options = {}) ⇒ Object
Lazy-loaded widget using Stimulus controller and IntersectionObserver Only loads Turnstile script when the container becomes visible in viewport.
-
.script_tag(nonce: nil) ⇒ Object
View helpers (called via helper_method).
-
.verify(response_token, remote_ip: nil) ⇒ Object
Server-side verification.
- .widget(options = {}) ⇒ Object
Class Method Details
.enabled? ⇒ Boolean
15 16 17 18 19 20 |
# File 'lib/turnstile.rb', line 15 def enabled? # Disable Turnstile entirely in development - production keys don't work on localhost return false if Rails.env.development? ENABLED && SITE_KEY.present? && SECRET_KEY.present? end |
.lazy_widget(options = {}) ⇒ Object
Lazy-loaded widget using Stimulus controller and IntersectionObserver
Only loads Turnstile script when the container becomes visible in viewport
Options:
- theme: 'light' (default), 'dark', or 'auto'
- size: 'normal' (default), 'flexible', or 'compact'
- root_margin: IntersectionObserver margin (default: '200px')
- class: Additional CSS classes for the container
Example:
<%= turnstile_lazy_widget(theme: 'dark') %>
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/turnstile.rb', line 44 def ( = {}) theme = .delete(:theme) || 'light' size = .delete(:size) || 'normal' root_margin = .delete(:root_margin) || '200px' css_class = .delete(:class) || '' attrs = { 'data-controller' => 'turnstile', 'data-turnstile-sitekey-value' => SITE_KEY, 'data-turnstile-theme-value' => theme, 'data-turnstile-size-value' => size, 'data-turnstile-root-margin-value' => root_margin } attrs['class'] = css_class if css_class.present? %(<div #{attrs.map { |k, v| %(#{k}="#{ERB::Util.html_escape(v)}") }.join(' ')}></div>).html_safe end |
.script_tag(nonce: nil) ⇒ Object
View helpers (called via helper_method)
23 24 25 |
# File 'lib/turnstile.rb', line 23 def script_tag(nonce: nil) %(<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer#{" nonce=\"#{ERB::Util.html_escape(nonce)}\"" if nonce}></script>).html_safe end |
.verify(response_token, remote_ip: nil) ⇒ Object
Server-side verification
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/turnstile.rb', line 64 def verify(response_token, remote_ip: nil) return true unless enabled? payload = { secret: SECRET_KEY, response: response_token } payload[:remoteip] = remote_ip if remote_ip uri = URI.parse(VERIFY_URL) res = Net::HTTP.post_form(uri, payload) body = begin JSON.parse(res.body) rescue StandardError { 'success' => false } end body['success'] == true end |
.widget(options = {}) ⇒ Object
27 28 29 30 |
# File 'lib/turnstile.rb', line 27 def ( = {}) attrs = { 'class' => 'cf-turnstile', 'data-sitekey' => SITE_KEY }.merge(.transform_keys { |k| "data-#{k}" }) %(<div #{attrs.map { |k, v| %(#{k}="#{ERB::Util.html_escape(v)}") }.join(' ')}></div>).html_safe end |