public_code 0.3.0 → 0.3.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: 85226caa9d5bbc68eedc7aecda5105a079c13733853522736aa1fb04f6d99477
4
- data.tar.gz: 5bfe2a1075d98348dad53b5b569d86e21c16938908f91ef08ea97015076df351
3
+ metadata.gz: fbf9421a17e6726374e177c5cc9580777fc403a08a07e85b3950c67d507be428
4
+ data.tar.gz: 90ac5dfcc08bdd8939bc80f5b56f22aaf498a6740ac6869e89f8ed4c6daa988d
5
5
  SHA512:
6
- metadata.gz: 7b25139d8cc38a6ef010df8bd0167022f39e35469d89a80c026386603224d23128d21079c193981c074bd4ff0bedf4212c826668568f36792b81479be1519b25
7
- data.tar.gz: 5909c90e24ef0e6dc33956b1454b20b7d0f37c47c327f3923649774da7a8a26a00712195359041436d9e7c06fbbcee5256d26cd1516c9308fa51e05cd3b96a57
6
+ metadata.gz: ec35ac1138505263d7debde048327e95786067fbb03f2310e220c3eac355e55a3ce7c2dfd919eb11fa8651c55c36e819c58b8a3da0388c008357c89bfa4155e8
7
+ data.tar.gz: 4a358f1a573c7976112006f38210015c4265ee6b252103cb0939525c0f511aff1d0a4ff282d1e676a3f8ed32057a26d3e16173d7a0f226db3452475acba7abe8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.3.1] - 2026-10-12
6
+
7
+ ### Added
8
+
9
+ - Claude Code locals for public_code, which `the_local` installs into an app that uses the gem.
10
+
5
11
  ## [0.3.0] - 2026-10-12
6
12
 
7
13
  ### Added
@@ -1,3 +1,3 @@
1
1
  module PublicCode
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: public_code-develop
3
+ description: Use PROACTIVELY for declaring a public code on a model, finding a record from a code in a URL, giving a record a new code, and testing that pages link to no numbered addresses — MUST BE USED instead of hand-rolling `to_param` overrides, token columns, `find_by!` lookups on a token column, or regexes over rendered links.
4
+ tools: Bash, Read, Write, Edit, Grep
5
+ scope: public codes — a random code in a record's addresses in place of its database id, the test that fails on numbered links, and the task that lists which models still use ids
6
+ ---
7
+
8
+ This local follows these steps exactly and invents none. Where a step names a
9
+ decision, it asks the developer and does not pick.
10
+
11
+ ## What public_code is
12
+
13
+ A gem that gives an Active Record model a random 24-character code of letters and
14
+ digits, used in the model's addresses in place of its database id. Fire this
15
+ local when a model should be addressed by code, when a controller loads a record
16
+ from an address, when a record's code has to be replaced, or when a test should
17
+ fail on any link that exposes an id. Adding the gem and the code column belongs
18
+ to **public_code-install**, and that must be done first.
19
+
20
+ ## Interface
21
+
22
+ - `has_public_code` — declared in a model, optionally with the column name
23
+ (default `:public_code`); gives each new record a code, makes the model's URL
24
+ helpers use the code, and adds the two entry points below.
25
+ - `find_by_public_code!` — called on the model with the code from an address;
26
+ returns the record or raises `ActiveRecord::RecordNotFound`.
27
+ - `record.regenerate_public_code` — gives a saved record a new code and saves it
28
+ at once; with a custom column the method is `regenerate_<column>`.
29
+ - `PublicCode::TestHelp` — a module included in an integration test class after
30
+ `require "public_code/test_help"`.
31
+ - `assert_no_numbered_links` — called in that test with a list of page paths;
32
+ visits each one and fails, naming every page and link, when a link or form on
33
+ it points at a numbered address.
34
+ - `PublicCode::TestHelp.numbered_links` — takes a list of addresses and returns
35
+ the ones that are numbered, for checking addresses that do not come from a
36
+ page.
37
+
38
+ ## How to use it
39
+
40
+ 1. Confirm the table has its code column. If not, hand off to
41
+ **public_code-install** and come back.
42
+
43
+ 2. Declare the code in the model, with the column name the migration used.
44
+
45
+ ```ruby
46
+ class Product < ApplicationRecord
47
+ has_public_code
48
+ end
49
+ ```
50
+
51
+ With another column name, pass it: `has_public_code :slug_code`. Declare it
52
+ once per model.
53
+
54
+ - A new record gets a code when it is created, or when it is built if the app
55
+ sets `ActiveRecord.generate_secure_token_on = :initialize`. A code already
56
+ set on the record is kept.
57
+ - `product_path(product)`, `url_for(product)`, `redirect_to product` and
58
+ `form_with model: product` now put the code where the id was. Do not
59
+ override `to_param`, and do not pass `product.id` to a route helper.
60
+
61
+ 3. Load the record from the address in each controller action that took an id.
62
+
63
+ ```ruby
64
+ def set_product
65
+ @product = Product.find_by_public_code!(params[:id])
66
+ end
67
+ ```
68
+
69
+ - Replace every `find(params[:id])`, and every nested `params[:product_id]`
70
+ lookup, for that model.
71
+ - Scope it as the action already scopes its lookups, for example
72
+ `Current.team.products.find_by_public_code!(params[:id])`.
73
+ - It raises `ActiveRecord::RecordNotFound`, so a missing code is a 404 as
74
+ before. Once the column exists, an id in the address is not found.
75
+ - Do not change the route parameter name. It stays `:id`.
76
+
77
+ 4. Give a record a new code only where the developer asks for one, for example
78
+ after a code was shared by mistake. Ask the developer whether the old address
79
+ should stop working, since it does.
80
+
81
+ ```ruby
82
+ product.regenerate_public_code
83
+ ```
84
+
85
+ It raises when the record is invalid. Redirect to the record's new address
86
+ afterwards.
87
+
88
+ 5. Add an integration test that fails on numbered links.
89
+
90
+ ```ruby
91
+ require "test_helper"
92
+ require "public_code/test_help"
93
+
94
+ class ProductAddressesTest < ActionDispatch::IntegrationTest
95
+ include PublicCode::TestHelp
96
+
97
+ test "no product page links to a numbered address" do
98
+ product = products(:one)
99
+ assert_no_numbered_links([ "/products", "/products/#{product.public_code}" ], safe: [ "page" ])
100
+ end
101
+ end
102
+ ```
103
+
104
+ - Ask the developer which pages to list. Each must render for the test as
105
+ written, so sign in first in the test if the pages need it.
106
+ - Ask the developer which query parameters hold a number that is not an id,
107
+ such as `page` or `per`, and pass those names in `safe:`.
108
+ - It checks every `a[href]` and `form[action]`. An address counts as numbered
109
+ when any path segment is all digits, or any query value is all digits and its
110
+ name is not in `safe:`.
111
+ - Addresses that do not start with `/`, start with `//`, `/assets/` or
112
+ `/rails/active_storage/` are not checked.
113
+
114
+ 6. For addresses built outside a page, such as in a mailer or a JSON response,
115
+ check them directly.
116
+
117
+ ```ruby
118
+ assert_empty PublicCode::TestHelp.numbered_links([ product_path(product) ], safe: [ "page" ])
119
+ ```
120
+
121
+ 7. Run the tests. A failure names each page and the link on it that still uses an
122
+ id. Fix the link by passing the record, not its id, to the route helper.
123
+
124
+ ## Conventions
125
+
126
+ - Always declare the code with the same column name the migration created.
127
+ - Always look up an addressed record with `find_by_public_code!`, never with
128
+ `find` or `find_by!(id:)`.
129
+ - Never put a record's `id` in a link, form action or redirect.
130
+ - Never write a code by hand. Codes come from the gem.
131
+ - Before the table has its column, the model stays addressed and found by id.
132
+ That is expected while a migration is pending, and is not a bug to fix here.
133
+ - `assert_no_numbered_links` works only in a test that can make requests and read
134
+ the response, such as `ActionDispatch::IntegrationTest`.
135
+ - Out of scope here: adding the gem, the migration that adds and fills the code
136
+ column, and checking which models are still addressed by id. Those are
137
+ **public_code-install**'s.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: public_code-info
3
+ description: Use to learn what public_code offers — a random code in a record's addresses in place of its database id, the test that fails on numbered links, and the task that lists which models still use ids.
4
+ tools: Read
5
+ scope: public codes — a random code in a record's addresses in place of its database id, the test that fails on numbered links, and the task that lists which models still use ids
6
+ ---
7
+
8
+ This local explains what public_code is and the words it uses. It makes no
9
+ changes and gives no steps.
10
+
11
+ ## What public_code is
12
+
13
+ public_code gives an Active Record model a random code that stands in for its
14
+ database id wherever a person or an outside caller sees the record: page
15
+ addresses, API and MCP addresses, and links sent out. Every address Rails builds
16
+ for the record carries the code, and the record is found from that code, so no
17
+ address shows an id and no one can step through records by counting.
18
+
19
+ Reach for it when an app or a gem exposes records at addresses and the ids in
20
+ those addresses should not be visible or guessable. It also gives an
21
+ integration test that fails when a page still links to a numbered address, and a
22
+ task that reports which models are still addressed by id.
23
+
24
+ ## Interface
25
+
26
+ This local documents no commands. Two other locals own the whole surface:
27
+
28
+ - **public_code-install** owns adding the gem to an app or gem, generating codes
29
+ for existing records in a migration, and the task that reports which models
30
+ are addressed by code or by id.
31
+ - **public_code-develop** owns declaring a code on a model, finding a record from
32
+ the code in an address, giving a record a new code, and the integration test
33
+ helper that checks pages for numbered links.
34
+
35
+ ## How to use it
36
+
37
+ - To add public_code to an app or gem, or to check across an app which models
38
+ still use ids, go to **public_code-install**.
39
+ - To give a model a code, look records up by code, or test that pages carry no
40
+ numbered links, go to **public_code-develop**.
41
+
42
+ ## Conventions
43
+
44
+ - **Code** — a random string of 24 letters and digits, stored in its own column
45
+ with a unique index. The column is named after the gem by default, and a model
46
+ may name its own.
47
+ - **Address** — any path or link a person or outside caller sees. Codes are for
48
+ addresses only.
49
+ - **Id** — the database id. It stays the internal key records point at each
50
+ other with, and it never changes.
51
+ - **Addressed by code / addressed by id** — whether a model's table has its code
52
+ column yet. A model that declares a code but whose table has no column is still
53
+ addressed and found by id, so a gem can declare codes before every app that
54
+ uses it has migrated. Once the column exists, an all-digit address is not
55
+ found.
56
+ - **Not found** — a lookup by code raises the standard Active Record not-found
57
+ error when no record among those searched has that code, so a lookup limited
58
+ to one client's records never finds another client's.
59
+ - **Numbered link** — a link or form on one of the app's own pages whose path has
60
+ an all-digit segment, or whose query has an all-digit value. Links to other
61
+ sites, asset files and stored files are never counted.
62
+ - **Safe query names** — query parameter names, such as a page number, whose
63
+ all-digit values are allowed and not counted as numbered links.
64
+ - **Strict mode** — the status report run so that it fails, naming each model
65
+ still addressed by id, for a deploy or CI step that requires every migration to
66
+ have run.
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: public_code-install
3
+ description: Use to hook public_code into a project — adding the gem, a migration that adds the code column and fills it with `PublicCode.generate`, and checking with `bin/rails public_code:status` which models are addressed by code or by id.
4
+ tools: Bash, Read, Edit
5
+ scope: public codes — a random code in a record's addresses in place of its database id, the test that fails on numbered links, and the task that lists which models still use ids
6
+ ---
7
+
8
+ This local follows these steps exactly and invents none. Where a step names a
9
+ decision, it asks the developer and does not pick.
10
+
11
+ ## What public_code is
12
+
13
+ A gem that gives an Active Record model a random 24-character code of letters and
14
+ digits to use in its addresses in place of its database id; hook it into a Rails
15
+ app or a gem whose records are shown at addresses where ids should not be seen.
16
+
17
+ ## Interface
18
+
19
+ - `gem "public_code"` — the Gemfile line, or a runtime dependency in a gem's
20
+ gemspec, that adds the gem; it needs Ruby 3.3 or later and Active Record 7.1 or
21
+ later, and loads itself into Active Record with no initializer.
22
+ - `PublicCode.generate` — returns a new random 24-character code; a migration
23
+ calls it to fill the code column for records that already exist.
24
+ - `bin/rails public_code:status` — lists every model that declares a code and
25
+ whether it is addressed by code or by id; with `STRICT=1` it exits non-zero and
26
+ names each model still addressed by id.
27
+
28
+ ## How to use it
29
+
30
+ 1. Ask the developer whether the host is a Rails app or a gem whose models other
31
+ apps use. The steps differ at step 4.
32
+
33
+ 2. Add the gem.
34
+ - Rails app: add `gem "public_code"` to `Gemfile`, then run `bundle install`.
35
+ `Gemfile.lock` changes.
36
+ - Gem: add `spec.add_dependency "public_code"` to the host's `*.gemspec`, then
37
+ run `bundle install`. Ask the developer which version constraint to use.
38
+
39
+ 3. Ask the developer which tables get a code, and for each one whether the column
40
+ is named `public_code` (the default) or another name. The column name chosen
41
+ here must match the name given when the model declares its code.
42
+
43
+ 4. Write one migration per table. In a Rails app, create it with
44
+ `bin/rails generate migration AddPublicCodeTo<Models>` and fill it in as below.
45
+ In a gem, put the migration in the gem's own migrations directory, so each app
46
+ copies and runs it later, and name it in the gem's changelog.
47
+
48
+ ```ruby
49
+ class AddPublicCodeToProducts < ActiveRecord::Migration[8.1]
50
+ class Product < ActiveRecord::Base
51
+ self.table_name = "products"
52
+ end
53
+
54
+ def up
55
+ add_column :products, :public_code, :string
56
+ Product.find_each { |product| product.update_columns(public_code: PublicCode.generate) }
57
+ change_column_null :products, :public_code, false
58
+ add_index :products, :public_code, unique: true
59
+ end
60
+
61
+ def down
62
+ remove_column :products, :public_code
63
+ end
64
+ end
65
+ ```
66
+
67
+ - Replace `products`, `Product` and `public_code` with the table, a class name
68
+ for it, and the column name from step 3.
69
+ - Keep the class defined inside the migration. It reads and writes the table
70
+ without the app model's callbacks.
71
+ - Set the `Migration[...]` version to the host's Rails version.
72
+
73
+ 5. Declare the code on each model. That step and everything after it, finding
74
+ records by code and testing pages for numbered links, belongs to
75
+ **public_code-develop**. Hand off to it here.
76
+
77
+ 6. In a Rails app, run `bin/rails db:migrate`. `db/schema.rb` (or
78
+ `db/structure.sql`) changes. Restart any running server or console, since a
79
+ process started before the migration still treats the table as having no code
80
+ column.
81
+
82
+ 7. In a Rails app, run `bin/rails public_code:status` and confirm every model from
83
+ step 3 prints `addressed by code`.
84
+
85
+ 8. Ask the developer whether a deploy or CI step should require every model to be
86
+ addressed by code. If yes, add `STRICT=1 bin/rails public_code:status` to that
87
+ step, after migrations run.
88
+
89
+ ## Conventions
90
+
91
+ - `public_code:status` exists only in a Rails app. A gem host does not run it
92
+ itself; each app that uses the gem does.
93
+ - The task only lists models that already declare a code. A table with a code
94
+ column whose model declares none does not appear.
95
+ - A model that declares a code before its table has the column keeps being
96
+ addressed and found by id. That is how a gem can release codes before every app
97
+ has migrated, and why step 7 is the check that the switch happened.
98
+ - When an app upgrades a gem that has added a code migration, copy that migration
99
+ into the app, run it, restart, and rerun step 7.
100
+ - Out of scope here: declaring the code on a model, finding a record by code,
101
+ giving a record a new code, and the numbered-link test helper. Those are
102
+ **public_code-develop**'s.
@@ -0,0 +1,21 @@
1
+ scope: public codes — a random code in a record's addresses in place of its database id, the test that fails on numbered links, and the task that lists which models still use ids
2
+
3
+ install:
4
+ - gem "public_code"
5
+ - PublicCode.generate
6
+ - bin/rails public_code:status
7
+
8
+ develop:
9
+ - has_public_code
10
+ - find_by_public_code!
11
+ - record.regenerate_public_code
12
+ - PublicCode::TestHelp
13
+ - assert_no_numbered_links
14
+ - PublicCode::TestHelp.numbered_links
15
+
16
+ sources:
17
+ - lib/public_code.rb
18
+ - lib/public_code/model.rb
19
+ - lib/public_code/test_help.rb
20
+ - lib/public_code/railtie.rb
21
+ - lib/tasks/public_code.rake
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: public_code
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
@@ -54,6 +54,10 @@ files:
54
54
  - lib/public_code/test_help.rb
55
55
  - lib/public_code/version.rb
56
56
  - lib/tasks/public_code.rake
57
+ - the_local/agents/public_code-develop.md
58
+ - the_local/agents/public_code-info.md
59
+ - the_local/agents/public_code-install.md
60
+ - the_local/interface.yml
57
61
  homepage: https://github.com/DYB-Development/public_code
58
62
  licenses:
59
63
  - MIT