vicary 0.2.0 → 0.2.1

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: 030a648056336b3ad6905e692be55844a1a3fbffd16b73a5ef37bcfc275402cc
4
- data.tar.gz: 3753c57622c1b2ab6dfb82fa12f79eb862681dcc8377a8fe01094e471d235ad7
3
+ metadata.gz: 94923f9a64b909c5a6ab25e3d7dc1d8caa41843068e1ede77fe938905a1a5745
4
+ data.tar.gz: 43e9513288654b871a78115c0f90d86c4f51701944dc01e20df469a62991852d
5
5
  SHA512:
6
- metadata.gz: 37aa4e0e3ccd3bb7860f97125862e1f7b3b30e63594549f201fec0f42bd92e4883e34a4b0c01cffe0f631e46f6576f4dbfc0e56f89e8d30df9e844575b512eb8
7
- data.tar.gz: ae700f44fc829bb7431ffd26a20db4e3dc3c3e15ab0b4e047e5c57ff996b1ac701da0d9023ad2de6167b3047fff929469b948068c77421633ba8935a741a616f
6
+ metadata.gz: 888df67ec5e7842da4d43a9398ea28edcb6552f47c84fada77bc9912e3dcbc4a56f1ea4f2b19c67dc0c5701eefc9fef6b6e671e7b29f2c034c92fc7d5dc0e129
7
+ data.tar.gz: 650b3ca105ef6dc6801c0232606077bef713f3c84d55668183707c296233c9d6c6c3438e21dbb5d218457735782fb671c5bb97a7fd15d06242059649c46ca549
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # vicary (Ruby)
2
2
 
3
- The RubyGems front door. **Not published yet** — the gem name is claimed by a
4
- pending trusted publisher and the first release has not run.
3
+ The RubyGems front door. **Published** — [`vicary` on RubyGems](https://rubygems.org/gems/vicary),
4
+ via trusted publishing, so no API key lives in this repository.
5
5
 
6
6
  The detector, the data asset and the measured numbers are described in the
7
7
  [project README](https://github.com/bwthomas/vicary#readme). What lives here is a
@@ -10,7 +10,7 @@ conformance suite in [`conformance/`](../conformance): for every fixture frame i
10
10
  must produce **byte-identical output to the Python implementation, placeholder
11
11
  numbering included**.
12
12
 
13
- It clears that bar — 36 of 36 masking-required frames, 52 of 52 overall.
13
+ It clears that bar — 38 of 38 masking-required frames, 54 of 54 overall.
14
14
 
15
15
  ```ruby
16
16
  require "vicary"
@@ -31,7 +31,7 @@ Three layers, because each catches what the one above it cannot.
31
31
 
32
32
  | command | what it says |
33
33
  |---|---|
34
- | `rake conformance` | the scoreboard against the 52 frames — the final bar, and a coarse first one |
34
+ | `rake conformance` | the scoreboard against the 54 frames — the final bar, and a coarse first one |
35
35
  | `rake test` | the unit suites, including `primitives_test.rb`: forty-odd primitives over the shared corpus, which says *which brick* is crooked |
36
36
  | `rake parity` | gazetteer verdicts, name by name, against the Python reference |
37
37
  | `rake redaction_parity` | masked bytes against the Python reference, on prose no fixture contains |
@@ -119,7 +119,7 @@ module Vicary
119
119
  # The primitives spec — the layer underneath the frames.
120
120
  #
121
121
  # `frames.json` scores finished output, which is the right final bar and a
122
- # poor first one: a port with nothing implemented scores 0 of 36 and learns
122
+ # poor first one: a port with nothing implemented scores 0 of 38 and learns
123
123
  # nothing about which of the forty-odd primitives underneath is wrong.
124
124
  # `primitives.json` is that missing layer, generated from the Python
125
125
  # functions and byte-compared against a fresh export by
@@ -19,11 +19,12 @@ module Vicary
19
19
  #
20
20
  # **Order is the contract, not an optimisation.** The first pattern to claim a
21
21
  # span wins, and placeholder indices follow mint order, so reordering these
22
- # tables changes the output bytes even when it changes no verdict. Identity
23
- # runs first (an address line can otherwise swallow a surname); EMAIL before
24
- # PHONE; SSN and CARD before the generic digit runs; ZIP and AGE last, because
25
- # both are bare digits and would claim characters belonging to a phone, card or
26
- # address.
22
+ # tables changes the output bytes even when it changes no verdict. EMAIL and URL
23
+ # run first, because a school-issued address and a profile URL *contain* the
24
+ # writer's name and both are anchored too tightly to take one out of prose;
25
+ # identity next (an address line can otherwise swallow a surname); SSN and CARD
26
+ # before the generic digit runs; ZIP and AGE last, because both are bare digits
27
+ # and would claim characters belonging to a phone, card or address.
27
28
  #
28
29
  # ## Regex dialect
29
30
  #
@@ -141,13 +142,34 @@ module Vicary
141
142
  # Date of birth, explicitly labelled.
142
143
  DOB = %r{\b(?:date\s+of\s+birth|d\.?o\.?b\.?|born\s+on)\s*:?\s*\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b}i
143
144
 
144
- # (placeholder kind, pattern) in application order.
145
+ # The two structured patterns that run BEFORE identity interpolation, because
146
+ # their match text can legitimately *contain* the writer's own name:
147
+ # `first.last@district.org` and a profile URL ending in a name slug. Identity
148
+ # interpolation is a literal-name substitution, so running it first left these
149
+ # shredded rather than masked — `{NAME_2}.{NAME_1}{USERNAME_1}.k12.oh.us`
150
+ # instead of `{EMAIL_1}`, with the domain tail surviving in the clear and the
151
+ # span unrestorable on the round trip.
152
+ #
153
+ # Putting them first is safe in the direction that matters, and that asymmetry
154
+ # is the whole argument. Both are anchored on structure a name cannot supply —
155
+ # EMAIL needs an `@` and a dotted TLD, URL needs a scheme or a `www.` — so
156
+ # neither can reach into prose and take a bare surname out of it. Patterns
157
+ # that *could* still run after identity.
158
+ STRUCTURED_BEFORE_IDENTITY = [
159
+ ["EMAIL", EMAIL],
160
+ ["URL", URL_PATTERN],
161
+ ].freeze
162
+
163
+ # (placeholder kind, pattern) in application order, running AFTER identity
164
+ # interpolation.
145
165
  #
146
166
  # CARD is handled separately because it needs the Luhn gate; ZIP and AGE run
147
167
  # after it for the reason in the module docstring.
168
+ #
169
+ # Numbering is unaffected by which of these two tables a pattern sits in: the
170
+ # minter counts per kind, so `{EMAIL_1}` is the first email whether emails are
171
+ # matched before or after names.
148
172
  STRUCTURED = [
149
- ["EMAIL", EMAIL],
150
- ["URL", URL_PATTERN],
151
173
  ["US_SOCIAL_SECURITY_NUMBER", SSN],
152
174
  ["IP_ADDRESS", IP],
153
175
  ["PHONE", PHONE],
@@ -286,10 +308,15 @@ module Vicary
286
308
  masked = text
287
309
  n = 0
288
310
 
289
- # Identity patterns run FIRST: a name is the span most likely to be
311
+ # Identity patterns run early: a name is the span most likely to be
290
312
  # partially consumed by a looser pattern (an address line can swallow a
291
313
  # surname), and masking it first makes that impossible.
292
- (identity_patterns(identity) + STRUCTURED).each do |kind, pattern|
314
+ #
315
+ # Early, not first. Email and URL precede it, because those two are the
316
+ # patterns whose own match text contains a name — see
317
+ # STRUCTURED_BEFORE_IDENTITY for why that direction is the safe one.
318
+ patterns = STRUCTURED_BEFORE_IDENTITY + identity_patterns(identity) + STRUCTURED
319
+ patterns.each do |kind, pattern|
293
320
  masked, count = minter.substitute(kind, pattern, masked)
294
321
  n += count
295
322
  end
@@ -6,5 +6,5 @@ module Vicary
6
6
  # Shared across all three front doors on purpose: one detector, one number. A
7
7
  # gem 0.3.0 that corresponds to nothing on PyPI cannot be reasoned about, and
8
8
  # the parity claim is between *versions*, not between package names.
9
- VERSION = "0.2.0"
9
+ VERSION = "0.2.1"
10
10
  end
data/lib/vicary.rb CHANGED
@@ -17,7 +17,7 @@
17
17
  #
18
18
  # Three layers check that claim, and each catches what the one above it cannot:
19
19
  #
20
- # * `rake conformance` scores the 52 frames — the final bar, and a coarse first
20
+ # * `rake conformance` scores the 54 frames — the final bar, and a coarse first
21
21
  # one;
22
22
  # * `rake test` runs `test/primitives_test.rb`, forty-odd primitives over the
23
23
  # shared `primitives.json` corpus, which says *which brick is crooked*;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vicary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Thomas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-11 00:00:00.000000000 Z
11
+ date: 2026-08-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Finds the names a student writes about — classmates, teachers, relatives — and