otori 0.1.0 → 0.2.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: 8918f6c816e04317efede4302afa940ac592e92f6a4e8499bd81f9adbee536f0
4
- data.tar.gz: c2659d173281ef798d54c52bcd2967dab83d971e3eb662db1ba214565824365a
3
+ metadata.gz: dad6a295086cd2eb7abd60f81e264cde80726e6802deec8e185b3d6ddd2bd082
4
+ data.tar.gz: b1fe5bab67f24f249b9b45055fa0a632774b99fa93fe0c156235014c9bdf1416
5
5
  SHA512:
6
- metadata.gz: fbfb505eb0362b440bdbd0b86f53395dc4f3c1ff059e6446d2d7c8466c93c27f25e9b05889c62962292315bda17fcd09b5b98c434ed613e8bc5858fdf713ca89
7
- data.tar.gz: a975548f46af0ec0ceb9d59fb73ef20a175211016156253414676e292f4c08d4b86fa7208aab98ed92ca42b61fc54e97b71f1ddc519dcebeaa61d6fc6b968216
6
+ metadata.gz: a275bbc1f3a7ce4a3f8ae0b6a09b0835aeeca30713254819f747e3e3b06f0c2e8a875a6ca604a8df396ada100ce9518238e8575ec1a06a3bb06e2bec5abe8b5d
7
+ data.tar.gz: 7fee9a413435e3efcf65f5196fdffe168c664935c8e1390f06545cb9f650571e47e031f04f3f9f2649f01a0b62a67229a9e7071199bdc2903c3dfe1543e625f1
data/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-09-17
11
+
12
+ ### Added
13
+
14
+ - `Otori::Rails`, an optional adapter for Rails apps. Provides a
15
+ `honeypot` controller class macro that registers a `before_action`
16
+ (halting with `:no_content` by default, or running a user-supplied
17
+ block), and a `honeypot_field` / `honeypot_signals` view-helper pair.
18
+ Loaded via `require "otori/rails"`; auto-hooks via a Railtie when
19
+ present.
20
+
21
+ ## [0.1.0] - 2026-05-22
22
+
10
23
  ### Added
11
24
 
12
25
  - Initial scaffolding inspired by the
data/README.md CHANGED
@@ -3,8 +3,11 @@
3
3
  [![CI](https://codeberg.org/fluck/otori/actions/workflows/ci.yml/badge.svg)](https://codeberg.org/fluck/otori/actions?workflow=ci.yml)
4
4
  [![Version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fcodeberg.org%2Fapi%2Fv1%2Frepos%2Ffluck%2Fotori%2Ftags&query=%24%5B0%5D.name&label=version)](https://codeberg.org/fluck/otori/tags)
5
5
 
6
- Invisible captcha spam protection for any Rack-based Ruby app, with an opt-in
7
- Hanami adapter.
6
+ Invisible honeypot spam protection for Hanami, Sinatra, Roda, Rails, and any
7
+ other Rack-based Ruby app. Think of it as a framework-agnostic
8
+ [invisible_captcha](https://github.com/markets/invisible_captcha) with a
9
+ first-class Hanami adapter and an extra layer of JavaScript-based input
10
+ detection.
8
11
 
9
12
  _Otori_ (囮) is the Japanese word for "decoy", historically a bird used in
10
13
  hunting to draw others in.
@@ -26,6 +29,29 @@ or flag submissions at a chosen _human rating_ threshold.
26
29
  > [Codeberg](https://codeberg.org/fluck/otori). The [GitHub
27
30
  > repo](https://github.com/flucksite/otori) is just a mirror.
28
31
 
32
+ ## How it compares
33
+
34
+ ### otori
35
+
36
+ - Frameworks: Hanami, Sinatra, Roda, Rails, any Rack app
37
+ - Hidden field + timing check + JS input signals
38
+ - Active (new in 2026)
39
+
40
+ ### [invisible_captcha](https://github.com/markets/invisible_captcha)
41
+
42
+ - Frameworks: Rails only
43
+ - Hidden field + timing check
44
+ - Actively maintained
45
+
46
+ ### [rack-honeypot](https://github.com/sunlightlabs/rack-honeypot)
47
+
48
+ - Frameworks: any Rack app
49
+ - Hidden field only
50
+ - Dormant since 2016
51
+
52
+ If you're on Rails and don't need the JS signals layer, `invisible_captcha` is
53
+ the more battle-tested choice. Otori exists for everything else.
54
+
29
55
  ## Installation
30
56
 
31
57
  Add this to your Gemfile:
@@ -174,46 +200,54 @@ In the view:
174
200
 
175
201
  ### Rails
176
202
 
177
- Rails is well served by [invisible_captcha](https://github.com/markets/invisible_captcha)
178
- when all you need is a hidden field and a timing check. Use this gem in Rails
179
- if you want the input-signals rating on top.
203
+ Load the Rails adapter from your Gemfile so the Railtie auto-hooks the
204
+ controller macros and view helpers:
180
205
 
181
206
  ```ruby
182
- # app/helpers/application_helper.rb
183
- module ApplicationHelper
184
- def honeypot_field(name, **attrs)
185
- Otori.field(name, session: session, **attrs).html_safe
186
- end
207
+ gem "otori", require: "otori/rails"
208
+ ```
187
209
 
188
- def honeypot_signals(**attrs)
189
- Otori.signals_field(**attrs).html_safe
190
- end
191
- end
210
+ In a form template:
211
+
212
+ ```erb
213
+ <%= honeypot_field("user[website]") %>
214
+ <%= honeypot_signals %>
192
215
  ```
193
216
 
217
+ Guard the receiving action with the `honeypot` class macro:
218
+
194
219
  ```ruby
195
- # app/controllers/sign_ups_controller.rb
196
220
  class SignUpsController < ApplicationController
197
- before_action :check_honeypot, only: :create
221
+ honeypot :website, wait: 2, only: :create
198
222
 
199
223
  def create
200
224
  # ...
201
225
  end
226
+ end
227
+ ```
202
228
 
203
- private
229
+ When the honeypot is tripped, the request halts with `:no_content` by default.
230
+ To customize the response, pass a block:
204
231
 
205
- def check_honeypot
206
- return unless Otori.caught?(
207
- "user[website]",
208
- params: params.to_unsafe_h,
209
- session: session
210
- )
232
+ ```ruby
233
+ honeypot :website, only: :create do
234
+ redirect_to root_path, notice: "Moving on..."
235
+ end
236
+ ```
211
237
 
212
- head :no_content
213
- end
238
+ To act on the input-signals rating inside the action:
239
+
240
+ ```ruby
241
+ def create
242
+ rating = Otori.signals_rating(params)
243
+ # weight into your own scoring, or reject below a threshold
214
244
  end
215
245
  ```
216
246
 
247
+ The adapter is a thin wrapper over the framework-agnostic core; if you prefer
248
+ to wire it up manually, `Otori.caught?` and `Otori.field` still work fine
249
+ inside a `before_action` and a helper method.
250
+
217
251
  ## Configuration
218
252
 
219
253
  ```ruby
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../otori"
4
+
5
+ module Otori
6
+ module Rails
7
+ module ControllerMacros
8
+ def honeypot(name, wait: nil, only: nil, except: nil, &on_caught)
9
+ filter_options = {}
10
+ filter_options[:only] = only if only
11
+ filter_options[:except] = except if except
12
+
13
+ before_action(**filter_options) do
14
+ next unless Otori.caught?(
15
+ name,
16
+ params:,
17
+ session:,
18
+ wait:
19
+ )
20
+
21
+ if on_caught
22
+ instance_exec(&on_caught)
23
+ else
24
+ head :no_content
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ module Helpers
31
+ def honeypot_field(name, **attrs)
32
+ Otori.field(name, session: session, **attrs).html_safe
33
+ end
34
+
35
+ def honeypot_signals(**attrs)
36
+ Otori.signals_field(**attrs).html_safe
37
+ end
38
+ end
39
+
40
+ if defined?(::Rails::Railtie)
41
+ class Railtie < ::Rails::Railtie
42
+ initializer "otori.action_controller" do
43
+ ActiveSupport.on_load(:action_controller) do
44
+ extend Otori::Rails::ControllerMacros
45
+ end
46
+ end
47
+
48
+ initializer "otori.action_view" do
49
+ ActiveSupport.on_load(:action_view) do
50
+ include Otori::Rails::Helpers
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/otori/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Otori
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: otori
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wout
@@ -29,6 +29,7 @@ files:
29
29
  - lib/otori/error.rb
30
30
  - lib/otori/form.rb
31
31
  - lib/otori/hanami.rb
32
+ - lib/otori/rails.rb
32
33
  - lib/otori/signals.rb
33
34
  - lib/otori/validator.rb
34
35
  - lib/otori/version.rb
@@ -54,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
55
  - !ruby/object:Gem::Version
55
56
  version: '0'
56
57
  requirements: []
57
- rubygems_version: 4.0.11
58
+ rubygems_version: 4.0.19
58
59
  specification_version: 4
59
60
  summary: Invisible honeypot spam protection for Rack apps, with a Hanami adapter.
60
61
  test_files: []