Skip to content

Conversation

@mroderick
Copy link
Collaborator

@mroderick mroderick commented Feb 11, 2026

Summary

Migrates codebar planner controllers to use Rails 8.0 params.expect for improved type safety and cleaner parameter handling syntax.

Motivation

Rails 8.0 introduces params.expect as a replacement for params.require().permit() with built-in type tampering protection. This provides more explicit parameter validation and clearer error handling.

Changes

Controllers Converted (13 total)

Core Controllers:

  • payments_controller - Migrated to params.expect with flattened Stripe token parameters
  • member/details_controller - Migrated to params.expect with nested array syntax
  • admin/member_search_controller - Conditional params.expect for optional parameters

Admin Controllers (10):

  • workshops_controller - Array params, helper methods
  • events_controller - Multiple nested sponsor arrays
  • sponsors_controller - Uses require().permit() (nested attributes incompatibility documented)
  • announcements_controller - Group IDs array
  • chapters_controller - Basic fields
  • groups_controller - Basic fields
  • meetings_controller - Multiple fields
  • bans_controller - Ban parameters
  • member_notes_controller - Note parameters

JavaScript Changes

payments.js - Refactored to wrap parameters in payment: hash with flattened Stripe fields:

  • Old: { amount: ..., name: ..., data: token }
  • New: { payment: { amount: ..., name: ..., stripe_email: token.email, stripe_token_id: token.id } }

Documentation

Added comprehensive "Parameter Handling" section to CLAUDE.md covering:

  • Basic params.expect syntax
  • Nested arrays and attributes
  • Conditional parameters
  • Type safety behavior (raises ActionController::ParameterMissing)
  • Testing guidelines
  • Exception for accepts_nested_attributes_for (requires require().permit())

Built On

This PR is built on top of #2475 (validation fix) which should be merged first.

Benefits

Security:

  • Explicit parameter validation with clear error handling
  • Consistent parameter validation across all controllers
  • Flattened payment parameters (clearer than nested data hash)

Code Quality:

  • Cleaner syntax: single method call vs chained methods
  • Self-documenting parameter structure
  • Easier to audit what parameters are accepted

Maintainability:

  • Documented patterns in CLAUDE.md with exception cases
  • Consistent approach across codebase
  • Clear examples for new controllers

Testing

  • ✅ All 42 controller tests passing
  • ✅ Zero regressions
  • ✅ 72.1% code coverage maintained

Syntax Examples

Before:

params.require(:workshop).permit(:name, :date, sponsor_ids: [])

After:

params.expect(workshop: [:name, :date, { sponsor_ids: [] }])

Payments (flattened):

# Before
params.permit(:amount, :name, data: [:email, :id])

# After
params.expect(payment: [:amount, :name, :stripe_email, :stripe_token_id])

Nested Attributes Exception:

# Uses require().permit() due to accepts_nested_attributes_for incompatibility
params.require(:sponsor).permit(
  :name, :website,
  address_attributes: [:id, :street, :city],
  contacts_attributes: [:id, :name, :_destroy]
)

Conditional:

search_params = if params.key?(:member_search)
                  params.expect(member_search: [:name])
                else
                  {}
                end

Notes

  • sponsors_controller uses require().permit() because Sponsor model uses accepts_nested_attributes_for (incompatible with params.expect)
  • Route parameters (:id, :chapter_id) continue using params.permit where appropriate
  • Non-admin controllers not converted (future work)

Problem:
- member_params was called 6+ times per request (inefficient)
- Validation was checking original params while attrs were modified
- Confusing control flow made code hard to maintain

Solution:
- Call member_params once and reuse the result
- Pass attrs to validation method instead of re-calling member_params
- Validate BEFORE modifying attributes for clearer logic flow

Performance: Reduced member_params calls from 6 to 1 per request

Test: Added test to verify member_params is called only once
- Replace raw params access with params.permit
- Add nested data parameter filtering for Stripe tokens
- Create test coverage for parameter filtering
- Add minimal view template for create action

Security improvement: prevents mass assignment of unpermitted fields
- Convert member_params to params.expect with nested array syntax
- Remove direct params[:member] access (was security issue)
- Refactor how_you_found_us validation to accept params hash
- Fix validation order: validate before clearing other_reason
- Call member_params only once (performance improvement)

Security fix: Previously mixed permitted params with raw params access,
allowing potential bypass of strong parameters. Now all access goes
through params.expect.

Bug fix: Validation now properly checks before modifying attributes.
- Convert index action to use conditional params.expect for optional member_search
- Convert results action to use params.expect with nested array syntax
- Add test coverage for results action
- Handles optional parameters gracefully with conditional check
@mroderick mroderick marked this pull request as draft February 11, 2026 09:09
Converted controllers:
- workshops_controller: array params, host_id helper method
- events_controller: multiple nested arrays for sponsor tiers
- sponsors_controller: nested attributes (address, contacts)
- announcements_controller: group_ids array
- chapters_controller: basic fields
- groups_controller: basic fields
- meetings_controller: multiple fields
- bans_controller: ban params
- member_notes_controller: note params

All controllers now use params.expect syntax instead of require().permit().
All existing tests pass with no regressions.
- Document Rails 8.0 params.expect usage
- Provide examples for basic, nested, and conditional patterns
- Explain type safety and 400 error behavior
- Document nested attributes syntax
- Add testing guidelines
- Update Rails version from 7.2 to 8.1

This helps future developers and AI assistants understand
the parameter handling pattern used throughout the codebase.
@mroderick mroderick marked this pull request as ready for review February 11, 2026 09:30
Copy link
Collaborator

@olleolleolle olleolleolle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat!


def create
@amount = params[:amount]
payment_params = params.permit(:amount, :name, data: [:email, :id])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are relying on :data to be present, perhaps we can tighten requirements here, and get it to use expect?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JavaScript (payments.js:12) sends root-level parameters: { amount: ..., name: ..., data: token } without a :payment wrapper. Because of this, we need to use params.permit (not params.expect) for root-level params.

While :data is required in practice, the code would crash with NoMethodError on line 12-14 if it's missing, making the nil case self-documenting. Adding explicit validation here wouldn't improve the error (still 500), so the current approach is appropriate for this legacy Stripe integration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated implementation in c681ebe - now wraps parameters in :payment hash with flattened Stripe fields (stripe_email, stripe_token_id) so payments_controller can use params.expect consistently with all other controllers.

## Project Overview

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.
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

- Explains why sponsors_controller uses require().permit() instead
  of params.expect (incompatible with nested attributes)
- Removes performance test referring to old implementation details
- Wrap AJAX parameters in `payment` hash for params.expect compatibility
- Flatten Stripe token fields (stripe_email, stripe_token_id) instead of
  nested `data` hash for clearer parameter names
- Update controller to use params.expect with flat structure
- Add type tampering test to verify 400 Bad Request behavior
- Remove root-level parameters exception from CLAUDE.md

Addresses review comment on codebar#2476 about payments_controller still using
params.permit. Now all controllers use params.expect consistently.
params.expect raises ActionController::ParameterMissing exception
instead of returning 400 status. Updated documentation to reflect
actual behavior.
@mroderick mroderick merged commit 3f873a1 into codebar:master Feb 11, 2026
8 checks passed
@mroderick mroderick deleted the params-expect branch February 11, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants