Skip to content

Commit e0af8d7

Browse files
committed
docs: add documentation for ResourceController patterns and permitted attributes
1 parent db15e3c commit e0af8d7

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
## ResourceController & FriendlyResourceController patterns
2+
3+
This document explains how `ResourceController` and `FriendlyResourceController` (which inherits from it) centralize common controller behavior for CRUD resources in the Better Together engine, including how permitted attributes are resolved.
4+
5+
Key points
6+
7+
- Resource controllers should set `resource_class` to the model they manage. The base `ResourceController` provides `resource_params` which calls `resource_class.permitted_attributes` to produce a strong-parameters list.
8+
- If your model defines `self.permitted_attributes` on the model (recommended), the base controller will automatically use that list when permitting params. This avoids duplication and centralizes permitted attribute definitions.
9+
- For ad-hoc controllers that do not inherit from `ResourceController` (for example, controller actions that are not full CRUD or need custom param handling), call `Model.permitted_attributes` directly when building permit lists.
10+
11+
Example: model-level permitted attributes composition
12+
13+
```ruby
14+
class BetterTogether::Message < ApplicationRecord
15+
def self.permitted_attributes
16+
%i[id sender_id content _destroy]
17+
end
18+
end
19+
20+
class BetterTogether::Conversation < ApplicationRecord
21+
def self.permitted_attributes
22+
[ :title, { participant_ids: [] }, { messages_attributes: BetterTogether::Message.permitted_attributes } ]
23+
end
24+
end
25+
```
26+
27+
Flow diagram
28+
29+
```mermaid
30+
flowchart TD
31+
A[HTTP request params] --> B[Controller resource_params]
32+
B --> C{Does controller inherit ResourceController?}
33+
C -- Yes --> D[resource_class.permitted_attributes]
34+
C -- No --> E[Call Model.permitted_attributes manually]
35+
D & E --> F[params.require(...).permit(...)]
36+
F --> G[Model.new/Model.update]
37+
G --> H[Model validations & save]
38+
```
39+
40+
When to add `self.permitted_attributes` to a model
41+
42+
- New models or models used in forms that accept nested attributes should expose a `self.permitted_attributes` class method.
43+
- Prefer composing nested attributes using other models' permitted attributes rather than repeating nested keys inline.
44+
45+
Testing note
46+
47+
- Controllers inheriting from `ResourceController` do not need to implement `*_params` methods unless special handling is required. For request/controller/feature specs you can rely on model-level permitted attributes to validate parameter handling.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## ResourceController & model-level permitted_attributes
2+
3+
This document explains how the `ResourceController` and `FriendlyResourceController` cooperate with model-level `permitted_attributes` to centralize strong parameter definitions.
4+
5+
Key points
6+
- `ResourceController#permitted_attributes` returns `resource_class.permitted_attributes`.
7+
- Controllers inheriting from `ResourceController` or `FriendlyResourceController` use `resource_params` which delegates to `permitted_attributes` so you normally don't need to implement `*_params` methods in those controllers.
8+
- For controllers that do not inherit from `ResourceController`, prefer calling `Model.permitted_attributes` directly, for example:
9+
10+
```ruby
11+
def conversation_params
12+
params.require(:conversation).permit(*BetterTogether::Conversation.permitted_attributes)
13+
end
14+
```
15+
16+
Composing nested permitted attributes
17+
- Instead of hard-coding nested permit lists, compose them by referencing other models' `permitted_attributes`. Example:
18+
19+
```ruby
20+
class BetterTogether::Message < ApplicationRecord
21+
def self.permitted_attributes
22+
%i[id sender_id content _destroy]
23+
end
24+
end
25+
26+
class BetterTogether::Conversation < ApplicationRecord
27+
def self.permitted_attributes
28+
[:title, { participant_ids: [] }, { messages_attributes: BetterTogether::Message.permitted_attributes }]
29+
end
30+
end
31+
```
32+
33+
Flow diagram
34+
35+
```mermaid
36+
flowchart TD
37+
A[Request arrives at Controller] --> B{Controller type}
38+
B -->|Inherits ResourceController| C[resource_params -> resource_class.permitted_attributes]
39+
B -->|Custom Controller| D[Call Model.permitted_attributes directly]
40+
C --> E[Strong parameters applied]
41+
D --> E
42+
E --> F[Save/Update model]
43+
```
44+
45+
Why this pattern
46+
- Single source of truth for strong parameters.
47+
- Easier to compose nested attributes.
48+
- Reduces duplication and accidental permission mismatches.
49+
50+
Where to update
51+
- If you add nested attributes, update the child model with `self.permitted_attributes` and reference it from the parent model.
52+
- When adding controllers, prefer inheriting `ResourceController` if the resource fits that pattern; otherwise call `Model.permitted_attributes` explicitly.

0 commit comments

Comments
 (0)