rails_claude_skills 0.1.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.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
- data/.github/ISSUE_TEMPLATE/config.yml +11 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
- data/.github/ISSUE_TEMPLATE/question.yml +90 -0
- data/.github/dependabot.yml +19 -0
- data/.github/workflows/ci.yml +77 -0
- data/.github/workflows/release.yml +66 -0
- data/.rubocop.yml +52 -0
- data/CHANGELOG.md +94 -0
- data/CLAUDE.md +332 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +580 -0
- data/LICENSE.txt +21 -0
- data/README.md +544 -0
- data/Rakefile +8 -0
- data/lib/generators/claude/agent/agent_generator.rb +71 -0
- data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
- data/lib/generators/claude/command/command_generator.rb +50 -0
- data/lib/generators/claude/command/templates/command.md.tt +28 -0
- data/lib/generators/claude/commands_library/create-pr.md +27 -0
- data/lib/generators/claude/commands_library/dbchange.md +19 -0
- data/lib/generators/claude/commands_library/quality.md +20 -0
- data/lib/generators/claude/commands_library/stimulus.md +19 -0
- data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
- data/lib/generators/claude/install/install_generator.rb +211 -0
- data/lib/generators/claude/install/templates/README.md.tt +59 -0
- data/lib/generators/claude/install/templates/USAGE +28 -0
- data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
- data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
- data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
- data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
- data/lib/generators/claude/rule/rule_generator.rb +175 -0
- data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
- data/lib/generators/claude/rules_library/code-style.md +37 -0
- data/lib/generators/claude/rules_library/database.md +47 -0
- data/lib/generators/claude/rules_library/hotwire.md +56 -0
- data/lib/generators/claude/rules_library/security.md +54 -0
- data/lib/generators/claude/rules_library/testing.md +47 -0
- data/lib/generators/claude/skill/skill_generator.rb +196 -0
- data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
- data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
- data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
- data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
- data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
- data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
- data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
- data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
- data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
- data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
- data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
- data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
- data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
- data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
- data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
- data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
- data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
- data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
- data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
- data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
- data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
- data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
- data/lib/generators/claude/views/views_generator.rb +113 -0
- data/lib/rails_claude_skills/railtie.rb +16 -0
- data/lib/rails_claude_skills/version.rb +5 -0
- data/lib/rails_claude_skills.rb +27 -0
- data/sig/rails_claude_skills.rbs +4 -0
- metadata +199 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-models
|
|
3
|
+
description: ActiveRecord patterns, migrations, validations, callbacks, associations
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
rails_version: ">= 7.0"
|
|
6
|
+
tags:
|
|
7
|
+
- activerecord
|
|
8
|
+
- models
|
|
9
|
+
- database
|
|
10
|
+
- orm
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Rails Models (ActiveRecord)
|
|
14
|
+
|
|
15
|
+
## Quick Reference
|
|
16
|
+
|
|
17
|
+
| Pattern | Example |
|
|
18
|
+
|---------|---------|
|
|
19
|
+
| **Model Generation** | `rails g model User name:string email:string` |
|
|
20
|
+
| **Migration** | `rails g migration AddAgeToUsers age:integer` |
|
|
21
|
+
| **Validation** | `validates :email, presence: true, uniqueness: true` |
|
|
22
|
+
| **Association** | `has_many :posts, dependent: :destroy` |
|
|
23
|
+
| **Callback** | `before_save :normalize_email` |
|
|
24
|
+
| **Scope** | `scope :active, -> { where(active: true) }` |
|
|
25
|
+
| **Query** | `User.where(active: true).order(created_at: :desc)` |
|
|
26
|
+
|
|
27
|
+
## Model Definition
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
class User < ApplicationRecord
|
|
31
|
+
# Constants
|
|
32
|
+
ROLES = %w[admin user guest].freeze
|
|
33
|
+
|
|
34
|
+
# Associations
|
|
35
|
+
has_many :posts, dependent: :destroy
|
|
36
|
+
has_many :comments
|
|
37
|
+
belongs_to :organization, optional: true
|
|
38
|
+
|
|
39
|
+
# Validations
|
|
40
|
+
validates :email, presence: true, uniqueness: true
|
|
41
|
+
validates :name, presence: true, length: { minimum: 2 }
|
|
42
|
+
validates :role, inclusion: { in: ROLES }
|
|
43
|
+
|
|
44
|
+
# Callbacks
|
|
45
|
+
before_save :normalize_email
|
|
46
|
+
after_create :send_welcome_email
|
|
47
|
+
|
|
48
|
+
# Scopes
|
|
49
|
+
scope :active, -> { where(active: true) }
|
|
50
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
51
|
+
|
|
52
|
+
# Class methods
|
|
53
|
+
def self.search(query)
|
|
54
|
+
where("name ILIKE ?", "%#{query}%")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Instance methods
|
|
58
|
+
def full_name
|
|
59
|
+
"#{first_name} #{last_name}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def normalize_email
|
|
65
|
+
self.email = email.downcase.strip
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Migrations
|
|
71
|
+
|
|
72
|
+
### Creating Tables
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
class CreateUsers < ActiveRecord::Migration[7.0]
|
|
76
|
+
def change
|
|
77
|
+
create_table :users do |t|
|
|
78
|
+
t.string :name, null: false
|
|
79
|
+
t.string :email, null: false
|
|
80
|
+
t.boolean :active, default: true
|
|
81
|
+
t.integer :role, default: 0
|
|
82
|
+
t.references :organization, foreign_key: true
|
|
83
|
+
|
|
84
|
+
t.timestamps
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
add_index :users, :email, unique: true
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Modifying Tables
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
class AddFieldsToUsers < ActiveRecord::Migration[7.0]
|
|
96
|
+
def change
|
|
97
|
+
add_column :users, :bio, :text
|
|
98
|
+
add_column :users, :avatar_url, :string
|
|
99
|
+
add_reference :users, :manager, foreign_key: { to_table: :users }
|
|
100
|
+
|
|
101
|
+
change_column_null :users, :email, false
|
|
102
|
+
change_column_default :users, :active, from: nil, to: true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Validations
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
class User < ApplicationRecord
|
|
111
|
+
# Presence
|
|
112
|
+
validates :email, presence: true
|
|
113
|
+
|
|
114
|
+
# Uniqueness
|
|
115
|
+
validates :email, uniqueness: { case_sensitive: false }
|
|
116
|
+
validates :username, uniqueness: { scope: :organization_id }
|
|
117
|
+
|
|
118
|
+
# Format
|
|
119
|
+
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
|
|
120
|
+
validates :phone, format: { with: /\A\d{10}\z/ }
|
|
121
|
+
|
|
122
|
+
# Length
|
|
123
|
+
validates :name, length: { minimum: 2, maximum: 50 }
|
|
124
|
+
validates :bio, length: { maximum: 500 }
|
|
125
|
+
|
|
126
|
+
# Numericality
|
|
127
|
+
validates :age, numericality: { greater_than: 0, less_than: 150 }
|
|
128
|
+
|
|
129
|
+
# Inclusion/Exclusion
|
|
130
|
+
validates :role, inclusion: { in: ROLES }
|
|
131
|
+
validates :username, exclusion: { in: %w[admin root] }
|
|
132
|
+
|
|
133
|
+
# Custom validation
|
|
134
|
+
validate :email_domain_allowed
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def email_domain_allowed
|
|
139
|
+
return if email.blank?
|
|
140
|
+
domain = email.split('@').last
|
|
141
|
+
unless %w[example.com company.com].include?(domain)
|
|
142
|
+
errors.add(:email, "must be from an allowed domain")
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Associations
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# One-to-Many
|
|
152
|
+
class Author < ApplicationRecord
|
|
153
|
+
has_many :books, dependent: :destroy
|
|
154
|
+
has_many :published_books, -> { where(published: true) }, class_name: 'Book'
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
class Book < ApplicationRecord
|
|
158
|
+
belongs_to :author
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Many-to-Many (has_and_belongs_to_many)
|
|
162
|
+
class Student < ApplicationRecord
|
|
163
|
+
has_and_belongs_to_many :courses
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class Course < ApplicationRecord
|
|
167
|
+
has_and_belongs_to_many :students
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Many-to-Many (has_many :through)
|
|
171
|
+
class Student < ApplicationRecord
|
|
172
|
+
has_many :enrollments
|
|
173
|
+
has_many :courses, through: :enrollments
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
class Enrollment < ApplicationRecord
|
|
177
|
+
belongs_to :student
|
|
178
|
+
belongs_to :course
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
class Course < ApplicationRecord
|
|
182
|
+
has_many :enrollments
|
|
183
|
+
has_many :students, through: :enrollments
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# One-to-One
|
|
187
|
+
class User < ApplicationRecord
|
|
188
|
+
has_one :profile, dependent: :destroy
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
class Profile < ApplicationRecord
|
|
192
|
+
belongs_to :user
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Polymorphic
|
|
196
|
+
class Comment < ApplicationRecord
|
|
197
|
+
belongs_to :commentable, polymorphic: true
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class Post < ApplicationRecord
|
|
201
|
+
has_many :comments, as: :commentable
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
class Photo < ApplicationRecord
|
|
205
|
+
has_many :comments, as: :commentable
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Callbacks
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class User < ApplicationRecord
|
|
213
|
+
# Order of execution:
|
|
214
|
+
before_validation :normalize_data
|
|
215
|
+
after_validation :log_validation_errors
|
|
216
|
+
|
|
217
|
+
before_save :encrypt_password
|
|
218
|
+
around_save :log_save_time
|
|
219
|
+
after_save :clear_cache
|
|
220
|
+
|
|
221
|
+
before_create :set_default_role
|
|
222
|
+
after_create :send_welcome_email
|
|
223
|
+
|
|
224
|
+
before_update :check_changes
|
|
225
|
+
after_update :notify_changes
|
|
226
|
+
|
|
227
|
+
before_destroy :check_dependencies
|
|
228
|
+
after_destroy :cleanup_files
|
|
229
|
+
|
|
230
|
+
private
|
|
231
|
+
|
|
232
|
+
def normalize_data
|
|
233
|
+
self.email = email.downcase if email.present?
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def around_save
|
|
237
|
+
start_time = Time.current
|
|
238
|
+
yield
|
|
239
|
+
Rails.logger.info "Save took #{Time.current - start_time}s"
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Scopes and Queries
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
class Post < ApplicationRecord
|
|
248
|
+
# Scopes
|
|
249
|
+
scope :published, -> { where(published: true) }
|
|
250
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
251
|
+
scope :by_author, ->(author_id) { where(author_id: author_id) }
|
|
252
|
+
scope :created_between, ->(start_date, end_date) {
|
|
253
|
+
where(created_at: start_date..end_date)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
# Chaining scopes
|
|
257
|
+
# Post.published.recent.limit(10)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Query methods
|
|
261
|
+
Post.where(published: true)
|
|
262
|
+
Post.where("views > ?", 100)
|
|
263
|
+
Post.where(author_id: [1, 2, 3])
|
|
264
|
+
Post.where.not(category: 'draft')
|
|
265
|
+
|
|
266
|
+
# Ordering
|
|
267
|
+
Post.order(created_at: :desc)
|
|
268
|
+
Post.order(views: :desc, created_at: :asc)
|
|
269
|
+
|
|
270
|
+
# Limiting
|
|
271
|
+
Post.limit(10)
|
|
272
|
+
Post.offset(20).limit(10)
|
|
273
|
+
|
|
274
|
+
# Joins
|
|
275
|
+
Post.joins(:author)
|
|
276
|
+
Post.joins(:author, :comments)
|
|
277
|
+
Post.left_joins(:comments)
|
|
278
|
+
|
|
279
|
+
# Includes (eager loading)
|
|
280
|
+
Post.includes(:author, :comments)
|
|
281
|
+
Post.includes(author: :profile)
|
|
282
|
+
|
|
283
|
+
# Selecting specific fields
|
|
284
|
+
Post.select(:id, :title, :created_at)
|
|
285
|
+
Post.pluck(:title)
|
|
286
|
+
Post.pluck(:id, :title)
|
|
287
|
+
|
|
288
|
+
# Aggregations
|
|
289
|
+
Post.count
|
|
290
|
+
Post.average(:views)
|
|
291
|
+
Post.maximum(:views)
|
|
292
|
+
Post.minimum(:views)
|
|
293
|
+
Post.sum(:views)
|
|
294
|
+
|
|
295
|
+
# Group
|
|
296
|
+
Post.group(:category).count
|
|
297
|
+
Post.group(:author_id).average(:views)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Enums
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
class Post < ApplicationRecord
|
|
304
|
+
enum status: {
|
|
305
|
+
draft: 0,
|
|
306
|
+
published: 1,
|
|
307
|
+
archived: 2
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# Or with prefix/suffix
|
|
311
|
+
enum visibility: {
|
|
312
|
+
public: 0,
|
|
313
|
+
private: 1
|
|
314
|
+
}, _prefix: :visibility
|
|
315
|
+
|
|
316
|
+
# Usage:
|
|
317
|
+
# post.draft!
|
|
318
|
+
# post.published?
|
|
319
|
+
# Post.published
|
|
320
|
+
# post.visibility_public!
|
|
321
|
+
end
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Model Concerns
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
# app/models/concerns/taggable.rb
|
|
328
|
+
module Taggable
|
|
329
|
+
extend ActiveSupport::Concern
|
|
330
|
+
|
|
331
|
+
included do
|
|
332
|
+
has_many :taggings, as: :taggable
|
|
333
|
+
has_many :tags, through: :taggings
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
class_methods do
|
|
337
|
+
def tagged_with(tag_name)
|
|
338
|
+
joins(:tags).where(tags: { name: tag_name })
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def tag_list
|
|
343
|
+
tags.pluck(:name).join(', ')
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Usage in model
|
|
348
|
+
class Post < ApplicationRecord
|
|
349
|
+
include Taggable
|
|
350
|
+
end
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Best Practices
|
|
354
|
+
|
|
355
|
+
1. **Fat models, skinny controllers** - Business logic belongs in models
|
|
356
|
+
2. **Use scopes** for common queries
|
|
357
|
+
3. **Validate at database level** with constraints when possible
|
|
358
|
+
4. **Use indexes** for frequently queried columns
|
|
359
|
+
5. **Eager load associations** to avoid N+1 queries
|
|
360
|
+
6. **Use concerns** to share behavior across models
|
|
361
|
+
7. **Keep callbacks simple** - avoid complex logic
|
|
362
|
+
8. **Use transactions** for multi-step operations
|
|
363
|
+
9. **Avoid callbacks for cross-cutting concerns** - use service objects instead
|
|
364
|
+
|
|
365
|
+
## Common Pitfalls
|
|
366
|
+
|
|
367
|
+
- **N+1 queries**: Use `includes`, `preload`, or `eager_load`
|
|
368
|
+
- **Callback hell**: Keep callbacks simple, use service objects for complex logic
|
|
369
|
+
- **Mass assignment vulnerabilities**: Use strong parameters in controllers
|
|
370
|
+
- **Missing indexes**: Add indexes for foreign keys and frequently queried columns
|
|
371
|
+
- **Ignoring database constraints**: Add NOT NULL, unique constraints in migrations
|
|
372
|
+
|
|
373
|
+
## References
|
|
374
|
+
|
|
375
|
+
- [Rails Guides - Active Record](https://guides.rubyonrails.org/active_record_basics.html)
|
|
376
|
+
- [Rails Guides - Validations](https://guides.rubyonrails.org/active_record_validations.html)
|
|
377
|
+
- [Rails Guides - Associations](https://guides.rubyonrails.org/association_basics.html)
|
|
378
|
+
- [Rails Guides - Migrations](https://guides.rubyonrails.org/active_record_migrations.html)
|
|
379
|
+
- [Rails Guides - Queries](https://guides.rubyonrails.org/active_record_querying.html)
|