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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/public_code/version.rb +1 -1
- data/the_local/agents/public_code-develop.md +137 -0
- data/the_local/agents/public_code-info.md +66 -0
- data/the_local/agents/public_code-install.md +102 -0
- data/the_local/interface.yml +21 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fbf9421a17e6726374e177c5cc9580777fc403a08a07e85b3950c67d507be428
|
|
4
|
+
data.tar.gz: 90ac5dfcc08bdd8939bc80f5b56f22aaf498a6740ac6869e89f8ed4c6daa988d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec35ac1138505263d7debde048327e95786067fbb03f2310e220c3eac355e55a3ce7c2dfd919eb11fa8651c55c36e819c58b8a3da0388c008357c89bfa4155e8
|
|
7
|
+
data.tar.gz: 4a358f1a573c7976112006f38210015c4265ee6b252103cb0939525c0f511aff1d0a4ff282d1e676a3f8ed32057a26d3e16173d7a0f226db3452475acba7abe8
|
data/CHANGELOG.md
CHANGED
data/lib/public_code/version.rb
CHANGED
|
@@ -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.
|
|
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
|