Skip to content

Commit 3f873a1

Browse files
authored
Merge pull request #2476 from mroderick/params-expect
feat: migrate controllers to Rails 8.0 params.expect
2 parents 0da0035 + a55560f commit 3f873a1

21 files changed

+1258
-78
lines changed

CLAUDE.md

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,24 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
codebar planner is a Rails 7.2 application for managing [codebar.io](https://codebar.io) members and events. It handles workshop/event scheduling, member registration, invitations, RSVPs, and feedback collection for coding workshops organized by codebar chapters.
7+
codebar planner is a Rails 8.1 application for managing [codebar.io](https://codebar.io) members and events. It handles workshop/event scheduling, member registration, invitations, RSVPs, and feedback collection for coding workshops organized by codebar chapters.
88

99
## Development Setup
1010

11-
### Docker (Recommended)
12-
13-
- **Initial setup**: `bin/dup` - builds container, sets up database with seed data
14-
- **Start container**: `bin/dstart` - starts existing container
15-
- **Start Rails server**: `bin/dserver` - runs server on http://localhost:3000
16-
- **Run tests**: `bin/drspec [path]` - runs RSpec tests, optionally for specific file/line
17-
- **Rails console**: `bin/drails console`
18-
- **Run rake tasks**: `bin/drake [task]`
19-
- **Bash shell in container**: `bin/dexec`
20-
- **Grant admin access**: `bin/dadmin <email>` - gives admin role to user
21-
- **Stop container**: `bin/dstop`
22-
- **Destroy container**: `bin/ddown`
11+
**IMPORTANT**: Always use native installation with `bundle exec` commands. Never use Docker or `bin/d*` commands.
2312

2413
### Native Installation
2514

26-
If not using Docker:
27-
- Setup: `bundle && rake db:create db:migrate db:seed`
28-
- Server: `rails server`
29-
- Tests: `rake` or `rspec`
30-
- Linting: `rubocop`
15+
- **Setup**: `bundle && rake db:create db:migrate db:seed`
16+
- **Server**: `bundle exec rails server`
17+
- **Tests**: `bundle exec rspec [path]` - runs RSpec tests, optionally for specific file/line
18+
- **Rails console**: `bundle exec rails console`
19+
- **Run rake tasks**: `bundle exec rake [task]`
20+
- **Linting**: `bundle exec rubocop`
21+
22+
### Docker Setup (Not Used)
23+
24+
Docker setup exists in this repository (`bin/d*` commands) but is **not used** for development work with Claude Code.
3125

3226
### Environment Variables
3327

@@ -149,6 +143,95 @@ Key RuboCop exclusions:
149143
- `db/`, `spec/`, `config/`, `bin/` excluded from most cops
150144
- Documentation not required (`Style/Documentation: false`)
151145

146+
## Parameter Handling
147+
148+
This application uses Rails 8.0 `params.expect` for parameter filtering and validation.
149+
150+
### Basic Pattern
151+
152+
Use `params.expect` instead of `params.require().permit()`:
153+
154+
```ruby
155+
def resource_params
156+
params.expect(resource: [
157+
:field1, :field2, :field3
158+
])
159+
end
160+
```
161+
162+
### Type Safety
163+
164+
Controllers using `params.expect` raise `ActionController::ParameterMissing` when:
165+
- Parameter type is tampered (e.g., string sent instead of hash)
166+
- Required parameters are missing
167+
- Nested parameter structure is invalid
168+
169+
This makes parameter validation failures explicit and easier to handle at the application level.
170+
171+
### Nested Arrays
172+
173+
Use hash syntax for array parameters:
174+
175+
```ruby
176+
params.expect(workshop: [
177+
:name, :date,
178+
{ sponsor_ids: [] }
179+
])
180+
```
181+
182+
### Nested Attributes
183+
184+
**IMPORTANT:** `params.expect` does NOT work with `accepts_nested_attributes_for`.
185+
186+
Rails forms with nested attributes send hash-with-numeric-keys like `{'0' => {...}, '1' => {...}}`, which params.expect cannot handle. For controllers using `accepts_nested_attributes_for`, continue using the old syntax:
187+
188+
```ruby
189+
# Use require().permit() for nested attributes
190+
params.require(:sponsor).permit(
191+
:name, :website,
192+
address_attributes: [:id, :street, :city, :postal_code],
193+
contacts_attributes: [:id, :name, :email, :_destroy]
194+
)
195+
```
196+
197+
Note: `_destroy` is permitted like any other field for deletion.
198+
199+
### Conditional Parameters
200+
201+
When parameters are optional:
202+
203+
```ruby
204+
def index
205+
search_params = if params.key?(:member_search)
206+
params.expect(member_search: [:name, :callback_url])
207+
else
208+
{}
209+
end
210+
# use search_params
211+
end
212+
```
213+
214+
### Return Value
215+
216+
`params.expect(key: [...])` returns the **inner permitted parameters**, not wrapped:
217+
218+
```ruby
219+
result = params.expect(member: [:name, :email])
220+
# Access as: result[:name], NOT result[:member][:name]
221+
```
222+
223+
### Testing
224+
225+
Controller specs should verify parameter filtering works:
226+
227+
```ruby
228+
it 'filters unpermitted parameters' do
229+
post :create, params: { resource: valid_attrs, hacker_field: 'malicious' }
230+
expect(response).to be_successful # hacker_field is filtered out
231+
end
232+
```
233+
234+
152235
## Important Patterns
153236

154237
### Controllers

0 commit comments

Comments
 (0)