-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathruby-rails.mdc
More file actions
124 lines (102 loc) · 8.29 KB
/
Copy pathruby-rails.mdc
File metadata and controls
124 lines (102 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
---
description: Rails conventions — service objects, strong parameters, scopes, background jobs.
globs: ""
alwaysApply: true
---
## File Structure and Organization
- All business logic must live in `app/services/` with class name `*Service` suffix. Controllers call exactly one service method.
- All database queries must live in `app/models/` as named scopes using `scope :name, -> { }` syntax. Never write `.where()` in controllers or services.
- All background jobs must live in `app/jobs/` with class name `*Job` suffix. Any operation taking >100ms must be a job.
- All validators must live in `app/validators/` with class name `*Validator` suffix and inherit from `ActiveModel::Validator`.
- All presenters must live in `app/presenters/` with class name `*Presenter` suffix. Controllers pass models to presenters, never format in views.
- Configuration must live in `config/settings/` as YAML files, loaded via `Settings` constant. Never use `ENV[]` directly in application code.
- All constants must live in `app/constants/` as separate files. Never define constants in models or services.
- Test files must mirror source structure: `spec/services/user_service_spec.rb` for `app/services/user_service.rb`.
## Naming Conventions
- Service classes: `UserCreationService`, `PaymentProcessingService`, `ReportGenerationService` — verb + noun + Service.
- Job classes: `SendWelcomeEmailJob`, `SyncInventoryJob`, `GenerateReportJob` — verb + noun + Job.
- Scope names: `active`, `recent`, `by_status`, `created_after` — snake_case, descriptive, chainable.
- Variable names in controllers: `@user`, `@users` — never `@u` or `@current_user_data`.
- Method names in services: `call`, `execute`, or specific verb like `create_user`, `process_payment` — never `do_thing` or `handle`.
- Boolean methods: `active?`, `valid_for_export?`, `requires_approval?` — always end with `?`.
- Private methods: prefix with `_` or use `private` keyword. Never call private methods from outside the class.
## Strong Parameters and Mass Assignment
- Every controller action that accepts params must call `permit_params` helper method defined in that controller.
- `permit_params` must explicitly whitelist every allowed parameter using `.permit(:field1, :field2)` syntax.
- Nested attributes must use `.permit(nested: [:field1, :field2])` syntax.
- Array parameters must use `.permit(tags: [])` syntax.
- Never use `.permit!` or `.permit(:all)`.
- Never pass `params` directly to `Model.create()` or `Model.update()`. Always call `permit_params` first.
## ActiveRecord Scopes and Queries
- All `.where()` calls must be extracted to named scopes in the model.
- All `.order()` calls must be extracted to named scopes in the model.
- All `.includes()` or `.joins()` must be extracted to named scopes in the model.
- Scopes must be chainable: `User.active.recent.by_role('admin')`.
- Complex queries with multiple conditions must use scope composition, never inline conditionals in controllers.
- Never use raw SQL strings. Use `where(column: value)` or `where("column > ?", value)` with bound parameters.
- Never use `find_by` in a loop. Use `where().map()` or extract to a scope.
- Pagination must use `kaminari` gem with `.page(params[:page]).per(20)` in a scope named `paginated`.
## Service Objects
- Every service must have exactly one public method: `call` or a specific verb like `execute`, `process`, `create`.
- Services must accept all dependencies in `initialize`. Never use global state or `current_user` directly.
- Services must return a result object with `.success?`, `.errors`, and `.data` attributes. Use `ServiceResult` class.
- Services must not call other services directly. Inject dependencies or use a coordinator service.
- Services must not query the database directly. Call model scopes or pass models as arguments.
- Services must not format output. Return raw data; let presenters handle formatting.
- Services must not raise exceptions for business logic failures. Return failed `ServiceResult` instead.
## Background Jobs
- Every job must inherit from `ApplicationJob`.
- Every job must have `sidekiq_options retry: 3, dead: true`.
- Jobs must accept only serializable arguments: strings, numbers, IDs — never pass model instances.
- Jobs must call a service in `perform` method. Never put business logic directly in jobs.
- Jobs must rescue `StandardError` and log with context: `Rails.logger.error("Job failed", error: e.message, job_id: job_id)`.
- Long-running jobs must update a status record every 30 seconds to prevent timeout assumptions.
- Jobs must not call other jobs. Use a coordinator service or schedule multiple jobs from controller.
## Error Handling
- All service methods must return `ServiceResult` with `.success?` boolean and `.errors` array.
- Controllers must check `result.success?` and render error response if false. Never assume success.
- Never rescue `StandardError` without re-raising or logging. Catch specific exceptions only.
- All database operations must rescue `ActiveRecord::RecordNotFound` and return 404 response.
- All external API calls must rescue `Timeout::Error`, `Net::OpenTimeout`, `Errno::ECONNREFUSED` and return failed result.
- Validation errors must be collected in `ServiceResult#errors` as array of hashes: `[{field: 'email', message: 'invalid'}]`.
- Never use `raise` for validation failures. Return failed result from service.
- All jobs must log failures with full context: `Rails.logger.error("Job failed", {job_id: jid, error: e.class, message: e.message})`.
## Security
- Never hardcode secrets, API keys, or credentials. Use `Settings` constant loaded from `config/settings/` or environment variables via `ENV.fetch('KEY')`.
- All user input must be validated at controller boundary using strong parameters and validators.
- All database queries must use bound parameters: `where("email = ?", user_email)` — never string interpolation.
- All external URLs must be validated before use: `URI.parse(url)` and check scheme is `http` or `https`.
- All file uploads must validate MIME type and size before processing.
- All JSON responses must use `render json: presenter.to_h` — never `render json: model`.
- All timestamps must be in UTC. Use `Time.current` never `Time.now`.
- All password operations must use `bcrypt` gem. Never store plaintext passwords.
## Testing
- Every service must have a spec file at `spec/services/service_name_spec.rb`.
- Every service spec must test the `call` method with success and failure cases.
- Every service spec must mock external dependencies: APIs, jobs, mailers.
- Every controller action must have a spec testing happy path and error cases.
- Every model scope must have a spec testing the query result.
- Every validator must have a spec testing valid and invalid inputs.
- Every job must have a spec testing `perform` with success and failure cases.
- Test files must use `describe`, `context`, `it` blocks. Never use `test` keyword.
- All specs must use `let` for setup, never `before` blocks with side effects.
- All specs must use `expect(result).to eq(expected)` syntax, never `assert_equal`.
- Mocking must use `allow(Object).to receive(:method)` syntax, never `stub`.
- All database tests must use `FactoryBot.create` for fixtures, never hardcoded data.
## Forbidden Patterns
- Never put business logic in controllers. Extract to services immediately.
- Never use `before_action` callbacks for business logic. Use them only for authentication/authorization.
- Never use `after_save` or `before_save` callbacks. Use services instead.
- Never use `scope` with complex logic. Keep scopes to single responsibility.
- Never use `delegate` to hide dependencies. Make them explicit.
- Never use `method_missing` or `send` dynamically. Write explicit methods.
- Never use global variables or class variables. Pass dependencies explicitly.
- Never use `eval` or `instance_eval` in production code.
- Never use `rescue` without specifying exception type. Never use bare `rescue`.
- Never use `puts` for logging. Use `Rails.logger.info`, `.warn`, `.error`.
- Never commit `.env` files or secrets to version control.
- Never use `find` without handling `RecordNotFound`. Always use `find_by` or rescue.
---
> Source: [Codelibrium](https://codelibrium.com) — the marketplace for AI behaviour files.
> Browse multiple rulesets at [codelibrium.com](https://codelibrium.com) or install via CLI:
> `npx codelibrium-cli install <ruleset-name>`