-
Notifications
You must be signed in to change notification settings - Fork 0
Add form objects and specs #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7fa9d80
725869c
48be322
093f194
9374ddb
5dd7c1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Copilot Instructions for structured_params | ||
|
|
||
| ## Coding Style | ||
|
|
||
| ### RBS Inline | ||
|
|
||
| This project uses `rbs-inline` for type annotations. Use the **method_type_signature** style: | ||
|
|
||
| ```ruby | ||
| # Good: method_type_signature style | ||
| class Example | ||
| #: () -> String | ||
| def method_name | ||
| "result" | ||
| end | ||
|
|
||
| #: (String) -> Integer | ||
| def method_with_param(value) | ||
| value.length | ||
| end | ||
|
|
||
| #: (String value, ?Integer default) -> String | ||
| def method_with_optional(value, default: 0) | ||
| "#{value}: #{default}" | ||
| end | ||
| end | ||
| ``` | ||
|
|
||
| **Exception: Instance variables** must use doc style (`# @rbs`): | ||
|
|
||
| ```ruby | ||
| # Good: instance variable type definition | ||
| class Example | ||
| # @rbs @name: String? | ||
|
|
||
| class << self | ||
| # @rbs @cache: Hash[Symbol, String]? | ||
| end | ||
|
|
||
| #: (String) -> void | ||
| def initialize(name) | ||
| @name = name | ||
| end | ||
| end | ||
| ``` | ||
|
|
||
| **DO NOT** use the doc style for method signatures: | ||
|
|
||
| ```ruby | ||
| # Bad: doc style for methods (do not use) | ||
| # @rbs return: String | ||
| def method_name | ||
| "result" | ||
| end | ||
|
|
||
| # @rbs param: String | ||
| # @rbs return: Integer | ||
| def method_with_param(value) | ||
| value.length | ||
| end | ||
| ``` | ||
|
|
||
| ### Configuration | ||
|
|
||
| The RuboCop configuration enforces this style: | ||
|
|
||
| ```yaml | ||
| Style/RbsInline/MissingTypeAnnotation: | ||
| EnforcedStyle: method_type_signature | ||
| ``` | ||
|
|
||
| ### RBS Signature Generation | ||
|
|
||
| **DO NOT** manually edit files in `sig/` directory. These files are auto-generated from inline annotations. | ||
|
|
||
| To generate RBS signature files: | ||
|
|
||
| ```bash | ||
| # Run this command to generate sig files from inline annotations | ||
| lefthook run prepare-commit-msg | ||
| ``` | ||
|
|
||
| This command will: | ||
| 1. Extract type annotations from Ruby files using `rbs-inline` | ||
| 2. Generate corresponding `.rbs` files in `sig/` directory | ||
| 3. Ensure type signatures are in sync with the code | ||
|
|
||
| **Note:** The `sig/` directory is automatically updated by the git hook, but you can manually run it when needed. | ||
|
|
||
| ## Project-Specific Guidelines | ||
|
|
||
| ### Strong Parameters | ||
|
|
||
| - For API usage: Use simple `UserParams.new(params)` | ||
| - For Form Objects: Use `UserForm.new(UserForm.permit(params))` | ||
| - `permit` method is available but not required for API usage | ||
|
|
||
| ### Form Objects | ||
|
|
||
| This gem supports both Strong Parameters validation and Form Object pattern: | ||
|
|
||
| - Form Objects should use `permit(params)` to handle `require` + `permit` | ||
| - `model_name` automatically removes "Parameters", "Parameter", or "Form" suffix | ||
| - Provides `persisted?`, `to_key`, `to_model` for Rails form helpers integration | ||
|
|
||
| ### Testing | ||
|
|
||
| - Use RSpec for testing | ||
| - Group tests by context (e.g., "API context", "Form Object context") | ||
| - Test files are in `spec/` directory | ||
| - Support files (test helper classes) are in `spec/support/` |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -3,13 +3,25 @@ | |||||
| module StructuredParams | ||||||
| # Parameter model that supports structured objects and arrays | ||||||
| # | ||||||
| # Usage example: | ||||||
| # class UserParameter < StructuredParams::Params | ||||||
| # This class can be used in two ways: | ||||||
| # 1. Strong Parameters validation (params validation in controllers) | ||||||
| # 2. Form objects (using with form_with/form_for in views) | ||||||
| # | ||||||
| # Strong Parameters example: | ||||||
| # class UserParameters < StructuredParams::Params | ||||||
| # attribute :name, :string | ||||||
| # attribute :address, :object, value_class: AddressParameter | ||||||
| # attribute :hobbies, :array, value_class: HobbyParameter | ||||||
| # attribute :address, :object, value_class: AddressParameters | ||||||
| # attribute :hobbies, :array, value_class: HobbyParameters | ||||||
| # attribute :tags, :array, value_type: :string | ||||||
| # end | ||||||
| # | ||||||
| # Form object example: | ||||||
| # class UserRegistrationForm < StructuredParams::Params | ||||||
| # attribute :name, :string | ||||||
| # attribute :email, :string | ||||||
| # validates :name, presence: true | ||||||
| # validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } | ||||||
| # end | ||||||
| class Params | ||||||
| include ActiveModel::Model | ||||||
|
|
||||||
|
|
@@ -19,10 +31,39 @@ module StructuredParams | |||||
|
|
||||||
| self.@structured_attributes: Hash[Symbol, singleton(::StructuredParams::Params)]? | ||||||
|
|
||||||
| self.@_model_name: ::ActiveModel::Name? | ||||||
|
||||||
| self.@_model_name: ::ActiveModel::Name? | |
| self.@model_name: ::ActiveModel::Name? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The controller usage example in the class-level documentation comment shows
@form = UserRegistrationForm.new(params[:user]), but forUserRegistrationFormthe param_key isuser_registration(the "Form" suffix is stripped, leavingUserRegistration). This meansparams[:user]would beniland the form would receive no data. The example should useparams[:user_registration], or better yet, use thepermitmethod as documented in the copilot instructions (e.g.,UserRegistrationForm.new(UserRegistrationForm.permit(params))).This is misleading documentation that could cause developers using the form object pattern to construct the object with
nilor empty params.