concerns_on_rails 1.28.9 → 1.30.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 +4 -4
- data/CHANGELOG.md +164 -0
- data/README.md +169 -43
- data/lib/concerns_on_rails/controllers/cacheable.rb +44 -10
- data/lib/concerns_on_rails/controllers/cursor_paginatable.rb +43 -7
- data/lib/concerns_on_rails/controllers/filterable.rb +159 -10
- data/lib/concerns_on_rails/controllers/idempotentable.rb +76 -18
- data/lib/concerns_on_rails/controllers/includable.rb +8 -3
- data/lib/concerns_on_rails/controllers/localizable.rb +42 -6
- data/lib/concerns_on_rails/controllers/paginatable.rb +7 -13
- data/lib/concerns_on_rails/controllers/throttleable.rb +20 -3
- data/lib/concerns_on_rails/controllers/timezoneable.rb +24 -1
- data/lib/concerns_on_rails/controllers/webhook_verifiable.rb +79 -24
- data/lib/concerns_on_rails/models/activatable.rb +43 -14
- data/lib/concerns_on_rails/models/addressable.rb +83 -13
- data/lib/concerns_on_rails/models/aliasable.rb +43 -0
- data/lib/concerns_on_rails/models/anonymizable.rb +230 -11
- data/lib/concerns_on_rails/models/auditable.rb +20 -4
- data/lib/concerns_on_rails/models/counter_cacheable.rb +106 -29
- data/lib/concerns_on_rails/models/duplicable.rb +101 -2
- data/lib/concerns_on_rails/models/encryptable.rb +81 -3
- data/lib/concerns_on_rails/models/expirable.rb +59 -22
- data/lib/concerns_on_rails/models/hashable.rb +35 -5
- data/lib/concerns_on_rails/models/lockable.rb +7 -4
- data/lib/concerns_on_rails/models/monetizable.rb +15 -18
- data/lib/concerns_on_rails/models/normalizable.rb +17 -5
- data/lib/concerns_on_rails/models/publishable.rb +36 -12
- data/lib/concerns_on_rails/models/sanitizable.rb +17 -4
- data/lib/concerns_on_rails/models/schedulable.rb +55 -13
- data/lib/concerns_on_rails/models/searchable.rb +8 -1
- data/lib/concerns_on_rails/models/sequenceable.rb +36 -3
- data/lib/concerns_on_rails/models/soft_deletable.rb +31 -24
- data/lib/concerns_on_rails/models/stateable.rb +17 -17
- data/lib/concerns_on_rails/models/storable.rb +18 -0
- data/lib/concerns_on_rails/models/taggable.rb +36 -3
- data/lib/concerns_on_rails/models/tokenizable.rb +7 -3
- data/lib/concerns_on_rails/support/address_data.rb +1 -2
- data/lib/concerns_on_rails/support/affix.rb +120 -1
- data/lib/concerns_on_rails/support/hooked_write.rb +149 -0
- data/lib/concerns_on_rails/support/html_sanitizers.rb +66 -0
- data/lib/concerns_on_rails/support/masker.rb +9 -4
- data/lib/concerns_on_rails/support/money.rb +127 -2
- data/lib/concerns_on_rails/support/numeric_operand.rb +186 -0
- data/lib/concerns_on_rails/support/scalar_param.rb +23 -0
- data/lib/concerns_on_rails/support/sequence_calculator.rb +42 -3
- data/lib/concerns_on_rails/support/unique_retry.rb +12 -2
- data/lib/concerns_on_rails/version.rb +1 -1
- data/lib/concerns_on_rails.rb +2 -0
- metadata +10 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01db17beb47c485890bfd3c755086624432941547fdd77ecc52536164a2a3404
|
|
4
|
+
data.tar.gz: 8aeca6a8b1e4d901a8d1b128a75ff27f25f5f85138034819b75ed66cfb16cc4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7615ea7a35f68bffb71e55975a9ad3097d683287dd1cddc81d4d0397343fba1d38289a8589db573ebe3a5f310ed5e2ddf43f621082f6bc16f56587402594df0e
|
|
7
|
+
data.tar.gz: '093ac4ae4b2de63d42722de0f75b23ce9423cc8528fa6c1768e849a515079d501b5df095e64c54227cd967d355ce302c63d55fe4467b7903083770ff38115adc'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,169 @@
|
|
|
1
1
|
<!-- CHANGELOG.md -->
|
|
2
2
|
|
|
3
|
+
## 1.30.0 (2026-09-27)
|
|
4
|
+
|
|
5
|
+
The fixes from the 2026-09-23 whole-gem audit of 1.29.0: 37 bugs (8 HIGH) across six PRs
|
|
6
|
+
(#109–#114). It is a minor release, not a patch, because a few fixes add an option or a method,
|
|
7
|
+
and several change observable behaviour. Read **Changed** before upgrading. No new
|
|
8
|
+
concerns and no migrations. Every fix ships with a regression spec that fails on 1.29.0.
|
|
9
|
+
2146 examples, 0 failures (2121 on Rails 6.0), green on PostgreSQL and MySQL.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **Throttleable: counters that use the default name reset once on upgrade.** The default
|
|
13
|
+
rule name changes from `"rule<n>"` to `"<DeclaringController>#rule<n>"`, so the cache key
|
|
14
|
+
changes, and so does the `rule` field of `rate_limited.concerns_on_rails`. Rules with an
|
|
15
|
+
explicit `name:` are not affected. (#109)
|
|
16
|
+
- **WebhookVerifiable: rule lookup is by specificity.** A rule that names the action beats any
|
|
17
|
+
catch-all, whichever class declared either. The most-derived class wins, then declaration
|
|
18
|
+
order. Such actions no longer inherit a parent catch-all's `replay:`/`tolerance:`/scheme, so
|
|
19
|
+
declare those on the subclass rule if you still want them. A catch-all declared before
|
|
20
|
+
specific rules no longer shadows them. `commit_webhook_replay_claim` is now private. (#109)
|
|
21
|
+
- **Cacheable**: POST/PUT/PATCH/DELETE responses, and statuses other than
|
|
22
|
+
200/203/204/206/304, no longer receive the rule's positive `Cache-Control`. `no_store` is
|
|
23
|
+
unchanged. `Vary` merging is now case-insensitive, leaves `Vary: *` alone, and keeps Rails'
|
|
24
|
+
own `Vary: Accept`. (#109)
|
|
25
|
+
- **Idempotentable: an unreachable store returns 503 `idempotency_store_unavailable`**
|
|
26
|
+
(with `Retry-After`) instead of a permanent 409 `idempotency_conflict`. The action still
|
|
27
|
+
does not run. Unreachable means both claims failed and both reads returned nil. (#109)
|
|
28
|
+
- **Filterable: numeric operands are exact, never truncated.** On an integer column,
|
|
29
|
+
`?stock=5.5` now matches nothing (it used to match 5), and `?stock=1e1` matches 10 (it used
|
|
30
|
+
to match 1). `?stock_lt=<huge>` returns every non-NULL row. One garbage member fails a whole
|
|
31
|
+
`in`/`not_in` list closed, and garbage fails `not` closed too. A JSON `true` or boolean
|
|
32
|
+
against a numeric column fails closed. `type: :integer` on a float/decimal column no longer
|
|
33
|
+
truncates. A numeric `type:` on a `with:` lambda pre-casts strictly, and fails closed
|
|
34
|
+
without calling the lambda. Operands over 100 characters are refused. (#113)
|
|
35
|
+
- **Sequenceable (STI)**: declared on an STI **base** class, all subclasses now share one
|
|
36
|
+
table-wide counter. They used to each start at 1 and collide on a shared unique index.
|
|
37
|
+
**Upgrade note:** a subclass that *inherited* a base declaration and numbered per type now
|
|
38
|
+
jumps once to the table-wide MAX + 1. Declare `scope: :type` to keep per-type numbering, and
|
|
39
|
+
index `(type, <column>)`. `next_<field>` under `scope: :type` previews the receiving class's
|
|
40
|
+
sequence. (#111)
|
|
41
|
+
- **Hooked verbs** (Publishable, SoftDeletable, Expirable, Activatable, Anonymizable,
|
|
42
|
+
Stateable): a hook's `ActiveRecord::Rollback` now vetoes the write for real. The verb
|
|
43
|
+
returns `false`, and the `*_all` verbs raise `RecordNotSaved`. The exception is
|
|
44
|
+
`anonymize_all!`, which skips the vetoed record and doesn't count it. After any aborted
|
|
45
|
+
write, the written attributes return to their pre-call in-memory values. (#114)
|
|
46
|
+
- **Publishable**: with a boolean column, `Post.published.new` no longer pre-sets the flag.
|
|
47
|
+
The scope is now `("published" = TRUE)`, or `<> FALSE` on Rails 6.0. (#114)
|
|
48
|
+
- **Expirable**: `expire!`/`expire_all` with nil or a blank time now mean "now" (they used to
|
|
49
|
+
mean "never expires"). An unparseable time raises `ArgumentError`. (#114)
|
|
50
|
+
- **Expirable / Activatable / Schedulable**: `prefix:`/`suffix:` now also define affixed
|
|
51
|
+
predicates (`term_active?`, `window_expired?`, …). The macro raises if one would shadow a
|
|
52
|
+
different column's query method. (#114)
|
|
53
|
+
- **Auditable**: new entries store `"at"` with microseconds. Second-precision entries still
|
|
54
|
+
parse. (#111)
|
|
55
|
+
- **Hashable**: a `:custom` alphabet with duplicate characters is de-duplicated, with a
|
|
56
|
+
deprecation warning (it will raise in 2.0). Fewer than 2 distinct characters raises. (#111)
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
- **Idempotentable**: `on_store_unavailable:` (`:reject` default, or `:proceed` to run the
|
|
60
|
+
action without deduplication). (#109)
|
|
61
|
+
- **Anonymizable**: `slug:` (`:auto` default, `true`, `false`). It rewrites a friendly_id slug
|
|
62
|
+
built from an anonymized column to a random slug, and deletes its history rows. (#110)
|
|
63
|
+
- **Taggable**: `Model.normalize_tags!` rewrites rows written around the callbacks (e.g.
|
|
64
|
+
`"ruby, rails"`) to the form `tagged_with` matches. It returns a count. (#112)
|
|
65
|
+
- **Support::UniqueRetry**: `savepoint:` runs each attempt in its own savepoint, so a retry
|
|
66
|
+
works inside a caller's transaction on PostgreSQL. (#110, #111)
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
- **Encryptable**: `Model.needs_reencryption` / `reencrypt_all!` now cover the whole table.
|
|
70
|
+
Rows hidden by a `default_scope` used to be skipped, and became undecryptable once the old
|
|
71
|
+
key was dropped. On a relation, both cover exactly that relation. On Rails 6.0–7.0,
|
|
72
|
+
`<field>_ciphertext` returns the stored ciphertext after a save, and `reencrypt!` works
|
|
73
|
+
right after a save. (#110)
|
|
74
|
+
- **Anonymizable**: erasure is never blocked by ciphertext that won't decrypt, and one bad
|
|
75
|
+
row no longer rolls back `anonymize_all!`. A slug built from erased PII is replaced in the
|
|
76
|
+
same UPDATE. (#110)
|
|
77
|
+
- **Storable**: `where_<key>` on an `encryptable` column raises instead of silently matching
|
|
78
|
+
nothing (or every row). (#110)
|
|
79
|
+
- **CounterCacheable**: a destroy decrements only when its DELETE removed a row, and reads
|
|
80
|
+
persisted values. `belongs_to primary_key:` is honoured. A child destroyed by its parent's
|
|
81
|
+
`dependent: :destroy` no longer decrements that parent, which also fixes
|
|
82
|
+
`StaleObjectError`. (#111)
|
|
83
|
+
- **Duplicable**: counter-cache columns are zeroed on the copy, so a deep copy counts its
|
|
84
|
+
children once. `duplicate!` re-reads the counters. (#111)
|
|
85
|
+
- **Sequenceable**: `assign_<field>!` restores the number when the save fails, so a retry
|
|
86
|
+
draws a fresh one. (#111)
|
|
87
|
+
- **Hashable / Tokenizable**: uniqueness prechecks see sibling STI rows, and
|
|
88
|
+
`regenerate_<field>!` retries work inside a caller's transaction. (#111)
|
|
89
|
+
- **Lockable / Addressable**: models load again when the schema is unreachable. (#111)
|
|
90
|
+
- **Aliasable**: `accepts_nested_attributes_for :alias` now updates and destroys existing
|
|
91
|
+
children. It used to save only new ones. (#112)
|
|
92
|
+
- **Sanitizable**: `with: :strip, on: :write` stores plain text (`"Tom & Jerry"`, not
|
|
93
|
+
`"Tom & Jerry"`). It stays idempotent and linear-time, and `sanitize_all!` repairs
|
|
94
|
+
double-escaped rows. (#112)
|
|
95
|
+
- **Addressable**: `if:`/`unless:` from later calls and subclasses are honoured. (#112)
|
|
96
|
+
- **Monetizable**: the writer reads the concern's own formatted output (`"$1,234.50"`).
|
|
97
|
+
Garbage, NaN, Infinity and oversized input cast to nil instead of raising. (#112)
|
|
98
|
+
- **Normalizable**: whitespace presets treat Unicode whitespace as whitespace. (#112)
|
|
99
|
+
- **Maskable**: `:email`/`:phone` fail closed. A malformed value gets the full mask instead of
|
|
100
|
+
being returned raw. (#112)
|
|
101
|
+
- **Taggable**: `taggable_by` raises on an empty `delimiter:`. (#112)
|
|
102
|
+
- **CursorPaginatable**: `max_per_page: 0` no longer lets a huge `per_page` overflow `LIMIT`
|
|
103
|
+
(a 500). Both paginators share `Support::ScalarParam.per_page`, with a 1,000,000 ceiling. A
|
|
104
|
+
cursor whose values can't be bound is a 400 `invalid_cursor`. (#113)
|
|
105
|
+
- **Filterable**: `1e99999999` on an unconstrained numeric column no longer costs ~1.3 s and
|
|
106
|
+
476 MB per request. `contains`/`starts_with` on non-string columns match nothing, instead of
|
|
107
|
+
a PostgreSQL 500. (#113)
|
|
108
|
+
- **Localizable / Timezoneable**: `rescue_from` handlers render under the resolved
|
|
109
|
+
locale/zone. `Accept-Language` ties keep header order, and `q` is case-insensitive. (#113)
|
|
110
|
+
- **Stateable**: the in-memory state and `<state>_at` are restored after a vetoed transition
|
|
111
|
+
or `RecordInvalid`. (#114)
|
|
112
|
+
- **Rails 6.0**: a hooked write aborted inside a caller's transaction no longer makes a
|
|
113
|
+
record created earlier in it INSERT twice. (#114)
|
|
114
|
+
|
|
115
|
+
### Internal
|
|
116
|
+
- New `Support::HookedWrite` (the one before-hook → write → after-hook path) and
|
|
117
|
+
`Support::NumericOperand` (Filterable's exact numeric reader). (#113, #114)
|
|
118
|
+
- Development dependencies: rubocop 1.91, simplecov 1.2, permittable 0.7 in the lockfile. (#106)
|
|
119
|
+
- CLAUDE.md describes the new behaviour. (#115)
|
|
120
|
+
|
|
121
|
+
## 1.29.0 (2026-09-21)
|
|
122
|
+
|
|
123
|
+
CI now runs **every Rails line the gemspec admits** — 6.0, 6.1, 7.0, 7.1, 7.2, 8.0 and 8.1 —
|
|
124
|
+
where it previously ran 7.0 through 8.0. Extending the matrix downwards found 201 failures
|
|
125
|
+
on the two new old lines and 0 on the new top line; all of them are fixed, and all seven
|
|
126
|
+
legs are green and blocking.
|
|
127
|
+
|
|
128
|
+
### Changed
|
|
129
|
+
- **Rails 5.x is no longer declared.** The `actionpack` / `activerecord` / `activesupport`
|
|
130
|
+
floor moves from `>= 5.0` to `>= 6.0`. This drops nothing that ever worked: with
|
|
131
|
+
`required_ruby_version = ">= 3.2.0"`, the `>= 5.0` half was unreachable by construction —
|
|
132
|
+
Ruby 3's keyword-argument separation breaks Active Record 5.2's own `create_table`
|
|
133
|
+
(`schema_statements.rb:290`), so no bundle could satisfy both. Verified rather than
|
|
134
|
+
assumed: on Ruby 3.2 the suite fails 1,302 examples on Rails 5.2, every one of them
|
|
135
|
+
inside the framework.
|
|
136
|
+
|
|
137
|
+
### Fixed
|
|
138
|
+
- **Controllers::Includable**: `?fields[table]=...` raised `LocalJumpError` ("no block
|
|
139
|
+
given") on Rails 6.0. `requested_fields` called the block-less `params[:fields].each_pair`
|
|
140
|
+
and chained `with_object`, but `ActionController::Parameters#each_pair` only began
|
|
141
|
+
returning an Enumerator in 6.1. It now iterates with a block, which behaves the same on
|
|
142
|
+
every line.
|
|
143
|
+
- **Models::Searchable**: the generated `search` scope raised
|
|
144
|
+
`ArgumentError: wrong number of arguments (given 2, expected 1)` for every call on Rails
|
|
145
|
+
6.0. Rails 6.0 forwards a scope's arguments through `define_method(name) { |*args| }`
|
|
146
|
+
with no `ruby2_keywords`, so under Ruby 3 the `ranked:` keyword reaches the body as a
|
|
147
|
+
trailing positional Hash. The scope now takes its options positionally and splats them
|
|
148
|
+
back out — same call signature, and an unknown key still raises.
|
|
149
|
+
|
|
150
|
+
### Documented
|
|
151
|
+
- Two features need a line newer than the 6.0 floor, and both now say so in the README's
|
|
152
|
+
compatibility table and in their own docs: `Controllers::Sortable`'s `nulls:` ordering
|
|
153
|
+
(Rails 6.1+, already raised at macro time) and `Models::Aliasable`'s **query side**
|
|
154
|
+
(Rails 6.1+ — resolving a `where`-hash key to an association, which is what makes Rails
|
|
155
|
+
alias the join, landed in 6.1; Aliasable's read/write side works on 6.0).
|
|
156
|
+
|
|
157
|
+
### Internal
|
|
158
|
+
- `gemfiles/rails_6.0.gemfile`, `rails_6.1.gemfile` and `rails_8.1.gemfile`; every gemfile
|
|
159
|
+
below 7.1 now pins `concurrent-ruby < 1.3.5`, which dropped its own `require "logger"`
|
|
160
|
+
that Active Support only started doing for itself in 7.1.
|
|
161
|
+
- The spec harness clears Active Record's schema cache and (on <= 6.1)
|
|
162
|
+
`ActiveSupport::Dependencies::Reference` between examples, gives anonymous spec models a
|
|
163
|
+
`model_name`, and gains a `min_rails:` metadata tag for examples whose feature is version
|
|
164
|
+
gated. Without the first two, specs that reuse table and model names across files saw the
|
|
165
|
+
PREVIOUS file's columns and classes on 6.0/6.1.
|
|
166
|
+
|
|
3
167
|
## 1.28.9 (2026-09-19)
|
|
4
168
|
|
|
5
169
|
CI now runs the matrix the gemspec actually claims: Rails 7.0–8.0 across Ruby 3.2/3.3/3.4,
|