claude-agent-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -0
- package/bin/claude-framework +3 -0
- package/framework/agents/design-lead.md +240 -0
- package/framework/agents/product-owner.md +179 -0
- package/framework/agents/tech-lead.md +226 -0
- package/framework/commands/ayuda.md +127 -0
- package/framework/commands/a/303/261adir.md +98 -0
- package/framework/commands/backup.md +397 -0
- package/framework/commands/cambiar.md +110 -0
- package/framework/commands/cloud.md +457 -0
- package/framework/commands/code.md +142 -0
- package/framework/commands/debug.md +334 -0
- package/framework/commands/deploy.md +383 -0
- package/framework/commands/deshacer.md +120 -0
- package/framework/commands/estado.md +218 -0
- package/framework/commands/explica.md +227 -0
- package/framework/commands/feature.md +120 -0
- package/framework/commands/git.md +427 -0
- package/framework/commands/historial.md +202 -0
- package/framework/commands/learn.md +408 -0
- package/framework/commands/movil.md +245 -0
- package/framework/commands/nuevo.md +118 -0
- package/framework/commands/plan.md +134 -0
- package/framework/commands/prd.md +113 -0
- package/framework/commands/probar.md +148 -0
- package/framework/commands/revisar.md +208 -0
- package/framework/commands/seeds.md +230 -0
- package/framework/commands/seguridad.md +226 -0
- package/framework/commands/tasks.md +157 -0
- package/framework/skills/architecture/algorithms.md +970 -0
- package/framework/skills/architecture/clean-code.md +1080 -0
- package/framework/skills/architecture/design-patterns.md +1984 -0
- package/framework/skills/architecture/functional-programming.md +972 -0
- package/framework/skills/architecture/solid.md +991 -0
- package/framework/skills/cloud/cloud-aws.md +848 -0
- package/framework/skills/cloud/cloud-azure.md +931 -0
- package/framework/skills/cloud/cloud-gcp.md +848 -0
- package/framework/skills/cloud/message-queues.md +1229 -0
- package/framework/skills/core/accessibility.md +401 -0
- package/framework/skills/core/api.md +474 -0
- package/framework/skills/core/authentication.md +306 -0
- package/framework/skills/core/authorization.md +388 -0
- package/framework/skills/core/background-jobs.md +341 -0
- package/framework/skills/core/caching.md +473 -0
- package/framework/skills/core/code-review.md +341 -0
- package/framework/skills/core/controllers.md +290 -0
- package/framework/skills/core/cua.md +285 -0
- package/framework/skills/core/documentation.md +472 -0
- package/framework/skills/core/file-uploads.md +351 -0
- package/framework/skills/core/hotwire-native.md +296 -0
- package/framework/skills/core/hotwire.md +278 -0
- package/framework/skills/core/i18n.md +334 -0
- package/framework/skills/core/imports-exports.md +750 -0
- package/framework/skills/core/infrastructure.md +337 -0
- package/framework/skills/core/models.md +228 -0
- package/framework/skills/core/notifications.md +672 -0
- package/framework/skills/core/payments.md +581 -0
- package/framework/skills/core/performance.md +361 -0
- package/framework/skills/core/rails-scaffold.md +131 -0
- package/framework/skills/core/search.md +518 -0
- package/framework/skills/core/security.md +565 -0
- package/framework/skills/core/seeds.md +307 -0
- package/framework/skills/core/seo.md +542 -0
- package/framework/skills/core/testing.md +393 -0
- package/framework/skills/core/views.md +260 -0
- package/framework/skills/core/websockets.md +564 -0
- package/framework/skills/data/advanced-sql.md +1204 -0
- package/framework/skills/data/nosql.md +1141 -0
- package/framework/skills/devops/containers-advanced.md +1237 -0
- package/framework/skills/devops/debugging.md +834 -0
- package/framework/skills/devops/git-workflow.md +752 -0
- package/framework/skills/devops/networking.md +932 -0
- package/framework/skills/devops/shell-scripting.md +1132 -0
- package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
- package/framework/sub-agents/cloud-agent.md +677 -0
- package/framework/sub-agents/data.md +504 -0
- package/framework/sub-agents/debugging-agent.md +554 -0
- package/framework/sub-agents/devops.md +483 -0
- package/framework/sub-agents/docs.md +176 -0
- package/framework/sub-agents/frontend-dev.md +349 -0
- package/framework/sub-agents/git-workflow-agent.md +697 -0
- package/framework/sub-agents/integrations.md +630 -0
- package/framework/sub-agents/native-dev.md +434 -0
- package/framework/sub-agents/qa.md +138 -0
- package/framework/sub-agents/rails-dev.md +375 -0
- package/framework/sub-agents/security.md +526 -0
- package/framework/sub-agents/ui.md +437 -0
- package/framework/sub-agents/ux.md +284 -0
- package/framework/templates/api-spec.md +500 -0
- package/framework/templates/component-spec.md +248 -0
- package/framework/templates/feature.json +13 -0
- package/framework/templates/model-spec.md +318 -0
- package/framework/templates/prd-template.md +80 -0
- package/framework/templates/task-plan.md +122 -0
- package/framework/templates/task-user-story.md +52 -0
- package/framework/templates/technical-spec.md +260 -0
- package/framework/templates/user-story.md +95 -0
- package/package.json +42 -0
- package/project-templates/CLAUDE.md +42 -0
- package/project-templates/contexts/architecture.md +25 -0
- package/project-templates/contexts/conventions.md +46 -0
- package/project-templates/contexts/design-system.md +47 -0
- package/project-templates/contexts/requirements.md +38 -0
- package/project-templates/contexts/stack.md +30 -0
- package/project-templates/history/active/models.md +11 -0
- package/project-templates/history/changelog.md +15 -0
- package/project-templates/workspace/.gitkeep +0 -0
- package/src/cli.js +52 -0
- package/src/init.js +104 -0
- package/src/status.js +75 -0
- package/src/update.js +88 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Skill: Infrastructure
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Set up and manage deployment infrastructure for Rails applications.
|
|
5
|
+
|
|
6
|
+
## Development Environment
|
|
7
|
+
|
|
8
|
+
### Prerequisites
|
|
9
|
+
```bash
|
|
10
|
+
# Ruby (via rbenv)
|
|
11
|
+
brew install rbenv ruby-build
|
|
12
|
+
rbenv install 3.3.0
|
|
13
|
+
rbenv global 3.3.0
|
|
14
|
+
|
|
15
|
+
# Node.js (for asset compilation)
|
|
16
|
+
brew install node
|
|
17
|
+
|
|
18
|
+
# SQLite3
|
|
19
|
+
brew install sqlite3
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Project Setup
|
|
23
|
+
```bash
|
|
24
|
+
# Create new Rails app
|
|
25
|
+
rails new myapp --database=sqlite3 --css=tailwind --skip-jbuilder
|
|
26
|
+
|
|
27
|
+
# Or with existing project
|
|
28
|
+
cd myapp
|
|
29
|
+
bundle install
|
|
30
|
+
rails db:setup
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Development Server
|
|
34
|
+
```bash
|
|
35
|
+
# Start server with all processes
|
|
36
|
+
bin/dev
|
|
37
|
+
|
|
38
|
+
# Or manually
|
|
39
|
+
rails server
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Production Configuration
|
|
43
|
+
|
|
44
|
+
### Database (SQLite3 for Production)
|
|
45
|
+
```yaml
|
|
46
|
+
# config/database.yml
|
|
47
|
+
production:
|
|
48
|
+
adapter: sqlite3
|
|
49
|
+
database: storage/production.sqlite3
|
|
50
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
51
|
+
timeout: 5000
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Environment Variables
|
|
55
|
+
```bash
|
|
56
|
+
# .env.production (not committed)
|
|
57
|
+
RAILS_ENV=production
|
|
58
|
+
SECRET_KEY_BASE=your-secret-key-here
|
|
59
|
+
RAILS_MASTER_KEY=your-master-key
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Production Settings
|
|
63
|
+
```ruby
|
|
64
|
+
# config/environments/production.rb
|
|
65
|
+
Rails.application.configure do
|
|
66
|
+
config.cache_classes = true
|
|
67
|
+
config.eager_load = true
|
|
68
|
+
config.consider_all_requests_local = false
|
|
69
|
+
config.action_controller.perform_caching = true
|
|
70
|
+
|
|
71
|
+
# Force SSL
|
|
72
|
+
config.force_ssl = true
|
|
73
|
+
|
|
74
|
+
# Logging
|
|
75
|
+
config.log_level = :info
|
|
76
|
+
config.log_tags = [:request_id]
|
|
77
|
+
|
|
78
|
+
# Asset serving
|
|
79
|
+
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
|
|
80
|
+
|
|
81
|
+
# Active Storage
|
|
82
|
+
config.active_storage.service = :local
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Deployment Options
|
|
87
|
+
|
|
88
|
+
### Option 1: Render.com (Recommended Free Option)
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
# render.yaml
|
|
92
|
+
services:
|
|
93
|
+
- type: web
|
|
94
|
+
name: myapp
|
|
95
|
+
env: ruby
|
|
96
|
+
buildCommand: bundle install && bundle exec rails assets:precompile
|
|
97
|
+
startCommand: bundle exec puma -C config/puma.rb
|
|
98
|
+
envVars:
|
|
99
|
+
- key: RAILS_MASTER_KEY
|
|
100
|
+
sync: false
|
|
101
|
+
- key: SECRET_KEY_BASE
|
|
102
|
+
generateValue: true
|
|
103
|
+
disk:
|
|
104
|
+
name: data
|
|
105
|
+
mountPath: /app/storage
|
|
106
|
+
sizeGB: 1
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Option 2: Fly.io
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
# fly.toml
|
|
113
|
+
app = "myapp"
|
|
114
|
+
primary_region = "iad"
|
|
115
|
+
|
|
116
|
+
[build]
|
|
117
|
+
builder = "heroku/buildpacks:22"
|
|
118
|
+
|
|
119
|
+
[env]
|
|
120
|
+
RAILS_ENV = "production"
|
|
121
|
+
RAILS_LOG_TO_STDOUT = "true"
|
|
122
|
+
|
|
123
|
+
[http_service]
|
|
124
|
+
internal_port = 3000
|
|
125
|
+
force_https = true
|
|
126
|
+
auto_stop_machines = true
|
|
127
|
+
auto_start_machines = true
|
|
128
|
+
|
|
129
|
+
[mounts]
|
|
130
|
+
source = "data"
|
|
131
|
+
destination = "/app/storage"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Deploy commands
|
|
136
|
+
fly launch
|
|
137
|
+
fly secrets set RAILS_MASTER_KEY=xxx
|
|
138
|
+
fly deploy
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Option 3: Railway
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
// railway.json
|
|
145
|
+
{
|
|
146
|
+
"build": {
|
|
147
|
+
"builder": "nixpacks"
|
|
148
|
+
},
|
|
149
|
+
"deploy": {
|
|
150
|
+
"startCommand": "bundle exec rails server -b 0.0.0.0 -p $PORT",
|
|
151
|
+
"healthcheckPath": "/up",
|
|
152
|
+
"healthcheckTimeout": 100
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Option 4: VPS (DigitalOcean, Linode, etc.)
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Server setup script
|
|
161
|
+
#!/bin/bash
|
|
162
|
+
|
|
163
|
+
# Install dependencies
|
|
164
|
+
apt update && apt upgrade -y
|
|
165
|
+
apt install -y curl git build-essential libssl-dev zlib1g-dev
|
|
166
|
+
|
|
167
|
+
# Install Ruby via rbenv
|
|
168
|
+
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
|
|
169
|
+
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
|
|
170
|
+
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
|
|
171
|
+
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
|
172
|
+
source ~/.bashrc
|
|
173
|
+
|
|
174
|
+
rbenv install 3.3.0
|
|
175
|
+
rbenv global 3.3.0
|
|
176
|
+
|
|
177
|
+
# Install bundler
|
|
178
|
+
gem install bundler
|
|
179
|
+
|
|
180
|
+
# Clone and setup app
|
|
181
|
+
git clone your-repo /var/www/myapp
|
|
182
|
+
cd /var/www/myapp
|
|
183
|
+
bundle install --deployment
|
|
184
|
+
RAILS_ENV=production rails db:migrate
|
|
185
|
+
RAILS_ENV=production rails assets:precompile
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Puma Configuration
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
# config/puma.rb
|
|
192
|
+
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
|
|
193
|
+
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
|
|
194
|
+
threads min_threads_count, max_threads_count
|
|
195
|
+
|
|
196
|
+
port ENV.fetch("PORT") { 3000 }
|
|
197
|
+
|
|
198
|
+
environment ENV.fetch("RAILS_ENV") { "development" }
|
|
199
|
+
|
|
200
|
+
# Workers for production
|
|
201
|
+
workers ENV.fetch("WEB_CONCURRENCY") { 2 } if ENV["RAILS_ENV"] == "production"
|
|
202
|
+
|
|
203
|
+
preload_app!
|
|
204
|
+
|
|
205
|
+
# Solid Queue integration
|
|
206
|
+
plugin :solid_queue if ENV["RAILS_ENV"] == "production"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Background Jobs in Production
|
|
210
|
+
|
|
211
|
+
### Solid Queue Setup
|
|
212
|
+
```bash
|
|
213
|
+
# Procfile
|
|
214
|
+
web: bundle exec puma -C config/puma.rb
|
|
215
|
+
worker: bundle exec rake solid_queue:start
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Or with Puma Plugin
|
|
219
|
+
```ruby
|
|
220
|
+
# config/puma.rb
|
|
221
|
+
plugin :solid_queue
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Health Checks
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# config/routes.rb
|
|
228
|
+
Rails.application.routes.draw do
|
|
229
|
+
get "up" => "rails/health#show", as: :rails_health_check
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## SSL/HTTPS
|
|
234
|
+
|
|
235
|
+
### Force SSL in Production
|
|
236
|
+
```ruby
|
|
237
|
+
# config/environments/production.rb
|
|
238
|
+
config.force_ssl = true
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Let's Encrypt (VPS)
|
|
242
|
+
```bash
|
|
243
|
+
# Install certbot
|
|
244
|
+
apt install certbot python3-certbot-nginx
|
|
245
|
+
|
|
246
|
+
# Get certificate
|
|
247
|
+
certbot --nginx -d yourdomain.com
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Monitoring
|
|
251
|
+
|
|
252
|
+
### Basic Logging
|
|
253
|
+
```ruby
|
|
254
|
+
# config/environments/production.rb
|
|
255
|
+
config.log_level = :info
|
|
256
|
+
config.log_formatter = ::Logger::Formatter.new
|
|
257
|
+
|
|
258
|
+
# Log to STDOUT for container environments
|
|
259
|
+
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
|
260
|
+
logger = ActiveSupport::Logger.new(STDOUT)
|
|
261
|
+
logger.formatter = config.log_formatter
|
|
262
|
+
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
|
263
|
+
end
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Error Tracking (Optional)
|
|
267
|
+
```ruby
|
|
268
|
+
# Gemfile
|
|
269
|
+
gem "sentry-ruby"
|
|
270
|
+
gem "sentry-rails"
|
|
271
|
+
|
|
272
|
+
# config/initializers/sentry.rb
|
|
273
|
+
Sentry.init do |config|
|
|
274
|
+
config.dsn = ENV["SENTRY_DSN"]
|
|
275
|
+
config.environment = Rails.env
|
|
276
|
+
end
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Backup Strategy
|
|
280
|
+
|
|
281
|
+
### Database Backup Script
|
|
282
|
+
```bash
|
|
283
|
+
#!/bin/bash
|
|
284
|
+
# backup.sh
|
|
285
|
+
|
|
286
|
+
BACKUP_DIR="/backups"
|
|
287
|
+
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
288
|
+
DB_FILE="/app/storage/production.sqlite3"
|
|
289
|
+
|
|
290
|
+
# Create backup
|
|
291
|
+
cp $DB_FILE "$BACKUP_DIR/production_$TIMESTAMP.sqlite3"
|
|
292
|
+
|
|
293
|
+
# Keep only last 7 days
|
|
294
|
+
find $BACKUP_DIR -name "*.sqlite3" -mtime +7 -delete
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Cron Job
|
|
298
|
+
```bash
|
|
299
|
+
# crontab -e
|
|
300
|
+
0 2 * * * /app/backup.sh
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Deployment Checklist
|
|
304
|
+
|
|
305
|
+
### Pre-deployment
|
|
306
|
+
- [ ] All tests pass
|
|
307
|
+
- [ ] No security vulnerabilities (brakeman)
|
|
308
|
+
- [ ] Environment variables set
|
|
309
|
+
- [ ] Database migrations ready
|
|
310
|
+
- [ ] Assets precompiled
|
|
311
|
+
|
|
312
|
+
### Deployment
|
|
313
|
+
- [ ] Database backup taken
|
|
314
|
+
- [ ] Deploy code
|
|
315
|
+
- [ ] Run migrations
|
|
316
|
+
- [ ] Restart application
|
|
317
|
+
- [ ] Verify health check
|
|
318
|
+
|
|
319
|
+
### Post-deployment
|
|
320
|
+
- [ ] Verify application loads
|
|
321
|
+
- [ ] Check error logs
|
|
322
|
+
- [ ] Test critical flows
|
|
323
|
+
- [ ] Monitor performance
|
|
324
|
+
|
|
325
|
+
## Rollback Plan
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# If using Git-based deployment
|
|
329
|
+
git revert HEAD
|
|
330
|
+
git push origin main
|
|
331
|
+
|
|
332
|
+
# Restore database if needed
|
|
333
|
+
cp /backups/latest.sqlite3 /app/storage/production.sqlite3
|
|
334
|
+
|
|
335
|
+
# Restart application
|
|
336
|
+
systemctl restart myapp
|
|
337
|
+
```
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Skill: Models
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Create and manage Active Record models with validations, associations, scopes, and callbacks.
|
|
5
|
+
|
|
6
|
+
## Model Structure
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
class ModelName < ApplicationRecord
|
|
10
|
+
# 1. Constants
|
|
11
|
+
STATUSES = %w[draft published archived].freeze
|
|
12
|
+
|
|
13
|
+
# 2. Associations
|
|
14
|
+
belongs_to :user
|
|
15
|
+
has_many :comments, dependent: :destroy
|
|
16
|
+
has_one :profile, dependent: :destroy
|
|
17
|
+
has_many :tags, through: :taggings
|
|
18
|
+
has_one_attached :avatar
|
|
19
|
+
has_many_attached :images
|
|
20
|
+
|
|
21
|
+
# 3. Validations
|
|
22
|
+
validates :title, presence: true, length: { maximum: 255 }
|
|
23
|
+
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
|
|
24
|
+
validates :status, inclusion: { in: STATUSES }
|
|
25
|
+
|
|
26
|
+
# 4. Scopes
|
|
27
|
+
scope :published, -> { where(published: true) }
|
|
28
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
29
|
+
scope :by_user, ->(user) { where(user: user) }
|
|
30
|
+
|
|
31
|
+
# 5. Callbacks
|
|
32
|
+
before_validation :normalize_data
|
|
33
|
+
after_create :send_notification
|
|
34
|
+
|
|
35
|
+
# 6. Class methods
|
|
36
|
+
def self.search(query)
|
|
37
|
+
where("title LIKE ?", "%#{query}%")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# 7. Instance methods
|
|
41
|
+
def full_name
|
|
42
|
+
"#{first_name} #{last_name}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def normalize_data
|
|
48
|
+
self.email = email&.downcase&.strip
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Associations
|
|
54
|
+
|
|
55
|
+
### Basic Associations
|
|
56
|
+
```ruby
|
|
57
|
+
# One-to-many
|
|
58
|
+
class User < ApplicationRecord
|
|
59
|
+
has_many :posts, dependent: :destroy
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class Post < ApplicationRecord
|
|
63
|
+
belongs_to :user
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Many-to-many
|
|
67
|
+
class Article < ApplicationRecord
|
|
68
|
+
has_many :taggings, dependent: :destroy
|
|
69
|
+
has_many :tags, through: :taggings
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class Tag < ApplicationRecord
|
|
73
|
+
has_many :taggings, dependent: :destroy
|
|
74
|
+
has_many :articles, through: :taggings
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class Tagging < ApplicationRecord
|
|
78
|
+
belongs_to :article
|
|
79
|
+
belongs_to :tag
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Association Options
|
|
84
|
+
```ruby
|
|
85
|
+
has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
|
|
86
|
+
belongs_to :author, class_name: "User", foreign_key: "user_id"
|
|
87
|
+
has_one :profile, dependent: :destroy, inverse_of: :user
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Validations
|
|
91
|
+
|
|
92
|
+
### Common Validations
|
|
93
|
+
```ruby
|
|
94
|
+
# Presence
|
|
95
|
+
validates :name, presence: true
|
|
96
|
+
|
|
97
|
+
# Length
|
|
98
|
+
validates :title, length: { minimum: 5, maximum: 255 }
|
|
99
|
+
validates :password, length: { in: 8..72 }
|
|
100
|
+
|
|
101
|
+
# Format
|
|
102
|
+
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
|
|
103
|
+
validates :slug, format: { with: /\A[a-z0-9-]+\z/ }
|
|
104
|
+
|
|
105
|
+
# Uniqueness
|
|
106
|
+
validates :email, uniqueness: { case_sensitive: false }
|
|
107
|
+
validates :slug, uniqueness: { scope: :user_id }
|
|
108
|
+
|
|
109
|
+
# Numericality
|
|
110
|
+
validates :price, numericality: { greater_than: 0 }
|
|
111
|
+
validates :quantity, numericality: { only_integer: true }
|
|
112
|
+
|
|
113
|
+
# Inclusion
|
|
114
|
+
validates :status, inclusion: { in: %w[draft published] }
|
|
115
|
+
|
|
116
|
+
# Custom
|
|
117
|
+
validate :end_date_after_start_date
|
|
118
|
+
|
|
119
|
+
def end_date_after_start_date
|
|
120
|
+
return if end_date.blank? || start_date.blank?
|
|
121
|
+
if end_date < start_date
|
|
122
|
+
errors.add(:end_date, "must be after start date")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Scopes
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# Basic scopes
|
|
131
|
+
scope :active, -> { where(active: true) }
|
|
132
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
133
|
+
scope :limit_recent, ->(n) { recent.limit(n) }
|
|
134
|
+
|
|
135
|
+
# Composed scopes
|
|
136
|
+
scope :published_recently, -> { published.where("created_at > ?", 1.week.ago) }
|
|
137
|
+
|
|
138
|
+
# With joins
|
|
139
|
+
scope :with_comments, -> { joins(:comments).distinct }
|
|
140
|
+
|
|
141
|
+
# Default scope (use sparingly)
|
|
142
|
+
default_scope { order(created_at: :desc) }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Callbacks
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
# Available callbacks (in order)
|
|
149
|
+
before_validation
|
|
150
|
+
after_validation
|
|
151
|
+
before_save
|
|
152
|
+
around_save
|
|
153
|
+
after_save
|
|
154
|
+
before_create / before_update
|
|
155
|
+
around_create / around_update
|
|
156
|
+
after_create / after_update
|
|
157
|
+
after_commit / after_rollback
|
|
158
|
+
|
|
159
|
+
# Example usage
|
|
160
|
+
before_save :generate_slug
|
|
161
|
+
after_create :send_welcome_email
|
|
162
|
+
after_commit :update_search_index, on: [:create, :update]
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
def generate_slug
|
|
167
|
+
self.slug = title.parameterize if slug.blank?
|
|
168
|
+
end
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Migrations
|
|
172
|
+
|
|
173
|
+
### Create Table
|
|
174
|
+
```ruby
|
|
175
|
+
class CreateArticles < ActiveRecord::Migration[8.0]
|
|
176
|
+
def change
|
|
177
|
+
create_table :articles do |t|
|
|
178
|
+
t.string :title, null: false
|
|
179
|
+
t.text :body
|
|
180
|
+
t.references :user, null: false, foreign_key: true
|
|
181
|
+
t.boolean :published, default: false
|
|
182
|
+
t.timestamps
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
add_index :articles, :title
|
|
186
|
+
add_index :articles, [:user_id, :created_at]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Modify Table
|
|
192
|
+
```ruby
|
|
193
|
+
class AddCategoryToArticles < ActiveRecord::Migration[8.0]
|
|
194
|
+
def change
|
|
195
|
+
add_column :articles, :category, :string
|
|
196
|
+
add_index :articles, :category
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Query Interface
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
# Find
|
|
205
|
+
User.find(1)
|
|
206
|
+
User.find_by(email: "test@example.com")
|
|
207
|
+
User.find_or_create_by(email: "new@example.com")
|
|
208
|
+
|
|
209
|
+
# Where
|
|
210
|
+
User.where(active: true)
|
|
211
|
+
User.where("created_at > ?", 1.week.ago)
|
|
212
|
+
User.where(role: ["admin", "moderator"])
|
|
213
|
+
|
|
214
|
+
# Ordering
|
|
215
|
+
User.order(created_at: :desc)
|
|
216
|
+
User.order(:last_name, :first_name)
|
|
217
|
+
|
|
218
|
+
# Eager loading (N+1 prevention)
|
|
219
|
+
User.includes(:posts).where(posts: { published: true })
|
|
220
|
+
User.preload(:comments)
|
|
221
|
+
User.eager_load(:profile)
|
|
222
|
+
|
|
223
|
+
# Aggregations
|
|
224
|
+
User.count
|
|
225
|
+
User.average(:age)
|
|
226
|
+
User.sum(:balance)
|
|
227
|
+
User.group(:status).count
|
|
228
|
+
```
|