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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
  5. data/.github/ISSUE_TEMPLATE/question.yml +90 -0
  6. data/.github/dependabot.yml +19 -0
  7. data/.github/workflows/ci.yml +77 -0
  8. data/.github/workflows/release.yml +66 -0
  9. data/.rubocop.yml +52 -0
  10. data/CHANGELOG.md +94 -0
  11. data/CLAUDE.md +332 -0
  12. data/CODE_OF_CONDUCT.md +134 -0
  13. data/CONTRIBUTING.md +580 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +544 -0
  16. data/Rakefile +8 -0
  17. data/lib/generators/claude/agent/agent_generator.rb +71 -0
  18. data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
  19. data/lib/generators/claude/command/command_generator.rb +50 -0
  20. data/lib/generators/claude/command/templates/command.md.tt +28 -0
  21. data/lib/generators/claude/commands_library/create-pr.md +27 -0
  22. data/lib/generators/claude/commands_library/dbchange.md +19 -0
  23. data/lib/generators/claude/commands_library/quality.md +20 -0
  24. data/lib/generators/claude/commands_library/stimulus.md +19 -0
  25. data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
  26. data/lib/generators/claude/install/install_generator.rb +211 -0
  27. data/lib/generators/claude/install/templates/README.md.tt +59 -0
  28. data/lib/generators/claude/install/templates/USAGE +28 -0
  29. data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
  30. data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
  31. data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
  32. data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
  33. data/lib/generators/claude/rule/rule_generator.rb +175 -0
  34. data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
  35. data/lib/generators/claude/rules_library/code-style.md +37 -0
  36. data/lib/generators/claude/rules_library/database.md +47 -0
  37. data/lib/generators/claude/rules_library/hotwire.md +56 -0
  38. data/lib/generators/claude/rules_library/security.md +54 -0
  39. data/lib/generators/claude/rules_library/testing.md +47 -0
  40. data/lib/generators/claude/skill/skill_generator.rb +196 -0
  41. data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
  42. data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
  43. data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
  44. data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
  45. data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
  46. data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
  47. data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
  48. data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
  49. data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
  50. data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
  51. data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
  52. data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
  53. data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
  54. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
  55. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
  56. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
  57. data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
  58. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
  59. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
  60. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
  61. data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
  62. data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
  63. data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
  64. data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
  65. data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
  66. data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
  67. data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
  68. data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
  69. data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
  70. data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
  71. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
  72. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
  73. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
  74. data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
  75. data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
  76. data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
  77. data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
  78. data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
  79. data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
  80. data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
  81. data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
  82. data/lib/generators/claude/views/views_generator.rb +113 -0
  83. data/lib/rails_claude_skills/railtie.rb +16 -0
  84. data/lib/rails_claude_skills/version.rb +5 -0
  85. data/lib/rails_claude_skills.rb +27 -0
  86. data/sig/rails_claude_skills.rbs +4 -0
  87. metadata +199 -0
@@ -0,0 +1,331 @@
1
+ # Advanced Devise Patterns
2
+
3
+ ## Table of Contents
4
+ - [Multiple User Models](#multiple-user-models)
5
+ - [Email Confirmation](#email-confirmation)
6
+ - [Account Locking](#account-locking)
7
+ - [Password Complexity](#password-complexity)
8
+ - [Custom Authentication Logic](#custom-authentication-logic)
9
+ - [Soft Delete Users](#soft-delete-users)
10
+ - [Background Email Delivery](#background-email-delivery)
11
+ - [I18n Configuration](#i18n-configuration)
12
+
13
+ ## Multiple User Models
14
+
15
+ ### Setup Admin Alongside User
16
+
17
+ ```bash
18
+ rails generate devise Admin
19
+ rails db:migrate
20
+ ```
21
+
22
+ Each model gets separate routes and helpers:
23
+ ```ruby
24
+ # Routes
25
+ devise_for :users
26
+ devise_for :admins
27
+
28
+ # Controllers
29
+ before_action :authenticate_user! # for users
30
+ before_action :authenticate_admin! # for admins
31
+
32
+ # Helpers
33
+ user_signed_in? / current_user / user_session
34
+ admin_signed_in? / current_admin / admin_session
35
+ ```
36
+
37
+ ### Scoped Views
38
+
39
+ Enable in initializer:
40
+ ```ruby
41
+ # config/initializers/devise.rb
42
+ config.scoped_views = true
43
+ ```
44
+
45
+ Generate scoped views:
46
+ ```bash
47
+ rails generate devise:views users
48
+ rails generate devise:views admins
49
+ ```
50
+
51
+ Views will be in `app/views/users/` and `app/views/admins/`.
52
+
53
+ ## Email Confirmation
54
+
55
+ ### Enable Confirmable
56
+
57
+ 1. Add to model:
58
+ ```ruby
59
+ devise :database_authenticatable, :registerable, :confirmable
60
+ ```
61
+
62
+ 2. Add migration columns:
63
+ ```bash
64
+ rails g migration AddConfirmableToUsers confirmation_token:string confirmed_at:datetime confirmation_sent_at:datetime unconfirmed_email:string
65
+ ```
66
+
67
+ ```ruby
68
+ class AddConfirmableToUsers < ActiveRecord::Migration[7.0]
69
+ def change
70
+ add_column :users, :confirmation_token, :string
71
+ add_column :users, :confirmed_at, :datetime
72
+ add_column :users, :confirmation_sent_at, :datetime
73
+ add_column :users, :unconfirmed_email, :string
74
+ add_index :users, :confirmation_token, unique: true
75
+
76
+ # Confirm existing users
77
+ User.update_all(confirmed_at: Time.current)
78
+ end
79
+ end
80
+ ```
81
+
82
+ ### Customize Confirmation
83
+
84
+ In `config/initializers/devise.rb`:
85
+ ```ruby
86
+ config.confirm_within = 3.days # Token valid for 3 days
87
+ config.reconfirmable = true # Require confirmation on email change
88
+ config.allow_unconfirmed_access_for = 2.days # Grace period
89
+ ```
90
+
91
+ ### Skip Confirmation (Admin-created users)
92
+
93
+ ```ruby
94
+ user = User.new(email: 'test@example.com', password: 'password')
95
+ user.skip_confirmation!
96
+ user.save!
97
+ ```
98
+
99
+ ## Account Locking
100
+
101
+ ### Enable Lockable
102
+
103
+ 1. Add to model:
104
+ ```ruby
105
+ devise :database_authenticatable, :registerable, :lockable
106
+ ```
107
+
108
+ 2. Add migration:
109
+ ```bash
110
+ rails g migration AddLockableToUsers failed_attempts:integer unlock_token:string locked_at:datetime
111
+ ```
112
+
113
+ ```ruby
114
+ class AddLockableToUsers < ActiveRecord::Migration[7.0]
115
+ def change
116
+ add_column :users, :failed_attempts, :integer, default: 0, null: false
117
+ add_column :users, :unlock_token, :string
118
+ add_column :users, :locked_at, :datetime
119
+ add_index :users, :unlock_token, unique: true
120
+ end
121
+ end
122
+ ```
123
+
124
+ ### Configuration
125
+
126
+ ```ruby
127
+ # config/initializers/devise.rb
128
+ config.lock_strategy = :failed_attempts
129
+ config.unlock_keys = [:email]
130
+ config.unlock_strategy = :both # :email, :time, or :both
131
+ config.maximum_attempts = 5
132
+ config.unlock_in = 1.hour
133
+ ```
134
+
135
+ ## Password Complexity
136
+
137
+ ### Basic Validation
138
+
139
+ ```ruby
140
+ # app/models/user.rb
141
+ validate :password_complexity
142
+
143
+ private
144
+
145
+ def password_complexity
146
+ return if password.blank?
147
+
148
+ unless password.match?(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
149
+ errors.add :password, 'must include at least one lowercase letter, one uppercase letter, and one digit'
150
+ end
151
+ end
152
+ ```
153
+
154
+ ### Using devise-security Gem
155
+
156
+ ```ruby
157
+ # Gemfile
158
+ gem 'devise-security'
159
+
160
+ # app/models/user.rb
161
+ devise :password_archivable, :password_expirable, :secure_validatable
162
+ ```
163
+
164
+ ## Custom Authentication Logic
165
+
166
+ ### Sign in with Username OR Email
167
+
168
+ ```ruby
169
+ # config/initializers/devise.rb
170
+ config.authentication_keys = [:login]
171
+
172
+ # app/models/user.rb
173
+ attr_accessor :login
174
+
175
+ def self.find_for_database_authentication(warden_conditions)
176
+ conditions = warden_conditions.dup
177
+ if (login = conditions.delete(:login))
178
+ where(conditions.to_h).where(
179
+ ['lower(username) = :value OR lower(email) = :value', { value: login.downcase }]
180
+ ).first
181
+ elsif conditions.key?(:username) || conditions.key?(:email)
182
+ where(conditions.to_h).first
183
+ end
184
+ end
185
+ ```
186
+
187
+ Update views to use `:login` instead of `:email`.
188
+
189
+ ### Custom Account Validation
190
+
191
+ ```ruby
192
+ # app/models/user.rb
193
+ def active_for_authentication?
194
+ super && approved? && !banned?
195
+ end
196
+
197
+ def inactive_message
198
+ if !approved?
199
+ :not_approved
200
+ elsif banned?
201
+ :banned
202
+ else
203
+ super
204
+ end
205
+ end
206
+ ```
207
+
208
+ Add to locale:
209
+ ```yaml
210
+ # config/locales/devise.en.yml
211
+ en:
212
+ devise:
213
+ failure:
214
+ not_approved: "Your account has not been approved yet."
215
+ banned: "Your account has been banned."
216
+ ```
217
+
218
+ ## Soft Delete Users
219
+
220
+ Using `discard` or `paranoia` gem:
221
+
222
+ ```ruby
223
+ # app/models/user.rb
224
+ include Discard::Model
225
+
226
+ def active_for_authentication?
227
+ super && !discarded?
228
+ end
229
+
230
+ def inactive_message
231
+ discarded? ? :account_deleted : super
232
+ end
233
+
234
+ # Override destroy in registrations controller
235
+ # app/controllers/users/registrations_controller.rb
236
+ class Users::RegistrationsController < Devise::RegistrationsController
237
+ def destroy
238
+ resource.discard
239
+ Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
240
+ set_flash_message! :notice, :destroyed
241
+ yield resource if block_given?
242
+ respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
243
+ end
244
+ end
245
+ ```
246
+
247
+ ## Background Email Delivery
248
+
249
+ ### Using Active Job
250
+
251
+ ```ruby
252
+ # app/models/user.rb
253
+ def send_devise_notification(notification, *args)
254
+ devise_mailer.send(notification, self, *args).deliver_later
255
+ end
256
+ ```
257
+
258
+ ### With Queue Priority
259
+
260
+ ```ruby
261
+ def send_devise_notification(notification, *args)
262
+ devise_mailer.send(notification, self, *args).deliver_later(queue: :high_priority)
263
+ end
264
+ ```
265
+
266
+ ## I18n Configuration
267
+
268
+ ### Flash Messages
269
+
270
+ ```yaml
271
+ # config/locales/devise.en.yml
272
+ en:
273
+ devise:
274
+ sessions:
275
+ signed_in: "Welcome back!"
276
+ signed_out: "Goodbye!"
277
+ already_signed_out: "You're already signed out."
278
+ registrations:
279
+ signed_up: "Welcome! You have signed up successfully."
280
+ signed_up_but_unconfirmed: "Please check your email to confirm your account."
281
+ updated: "Your account has been updated successfully."
282
+ destroyed: "Your account has been deleted. We're sorry to see you go!"
283
+ passwords:
284
+ send_instructions: "You will receive an email with instructions shortly."
285
+ updated: "Your password has been changed successfully."
286
+ confirmations:
287
+ confirmed: "Your email has been confirmed."
288
+ send_instructions: "Confirmation instructions sent."
289
+ unlocks:
290
+ send_instructions: "Unlock instructions sent."
291
+ unlocked: "Your account has been unlocked."
292
+ mailer:
293
+ confirmation_instructions:
294
+ subject: "Confirm your email"
295
+ reset_password_instructions:
296
+ subject: "Reset your password"
297
+ unlock_instructions:
298
+ subject: "Unlock your account"
299
+ ```
300
+
301
+ ### Per-Resource Messages
302
+
303
+ ```yaml
304
+ en:
305
+ devise:
306
+ sessions:
307
+ user:
308
+ signed_in: "Welcome, user!"
309
+ admin:
310
+ signed_in: "Welcome back, administrator!"
311
+ ```
312
+
313
+ ## Customizing Mailer
314
+
315
+ ```ruby
316
+ # app/mailers/custom_devise_mailer.rb
317
+ class CustomDeviseMailer < Devise::Mailer
318
+ helper :application
319
+ include Devise::Controllers::UrlHelpers
320
+ default template_path: 'devise/mailer'
321
+ layout 'mailer'
322
+
323
+ def confirmation_instructions(record, token, opts = {})
324
+ opts[:subject] = "Welcome to MyApp - Please confirm your email"
325
+ super
326
+ end
327
+ end
328
+
329
+ # config/initializers/devise.rb
330
+ config.mailer = 'CustomDeviseMailer'
331
+ ```
@@ -0,0 +1,266 @@
1
+ # API Authentication with Devise
2
+
3
+ ## Table of Contents
4
+ - [Rails API Mode Setup](#rails-api-mode-setup)
5
+ - [HTTP Basic Authentication](#http-basic-authentication)
6
+ - [Token Authentication](#token-authentication)
7
+ - [JWT Authentication](#jwt-authentication)
8
+ - [Testing API Authentication](#testing-api-authentication)
9
+
10
+ ## Rails API Mode Setup
11
+
12
+ For `rails new myapp --api` applications:
13
+
14
+ ### Enable Required Middleware
15
+
16
+ In `config/application.rb`:
17
+ ```ruby
18
+ config.middleware.use ActionDispatch::Cookies
19
+ config.middleware.use ActionDispatch::Session::CookieStore
20
+ ```
21
+
22
+ Or for testing only in `config/environments/test.rb`:
23
+ ```ruby
24
+ Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Cookies
25
+ Rails.application.config.middleware.insert_before Warden::Manager, ActionDispatch::Session::CookieStore
26
+ ```
27
+
28
+ ### Disable Views
29
+
30
+ In `config/initializers/devise.rb`:
31
+ ```ruby
32
+ config.navigational_formats = []
33
+ ```
34
+
35
+ ## HTTP Basic Authentication
36
+
37
+ Enable in initializer:
38
+ ```ruby
39
+ # config/initializers/devise.rb
40
+ config.http_authenticatable = [:database]
41
+ ```
42
+
43
+ Usage:
44
+ ```bash
45
+ curl -u user@example.com:password http://localhost:3000/api/resource
46
+ ```
47
+
48
+ ## Token Authentication
49
+
50
+ ### Simple Token Setup
51
+
52
+ 1. Add token column:
53
+ ```bash
54
+ rails g migration AddAuthenticationTokenToUsers authentication_token:string:index
55
+ rails db:migrate
56
+ ```
57
+
58
+ 2. Configure User model:
59
+ ```ruby
60
+ class User < ApplicationRecord
61
+ devise :database_authenticatable, :registerable,
62
+ :recoverable, :rememberable, :validatable
63
+
64
+ before_save :ensure_authentication_token
65
+
66
+ def ensure_authentication_token
67
+ self.authentication_token ||= generate_authentication_token
68
+ end
69
+
70
+ def regenerate_authentication_token!
71
+ self.authentication_token = generate_authentication_token
72
+ save!
73
+ end
74
+
75
+ private
76
+
77
+ def generate_authentication_token
78
+ loop do
79
+ token = Devise.friendly_token
80
+ break token unless User.exists?(authentication_token: token)
81
+ end
82
+ end
83
+ end
84
+ ```
85
+
86
+ 3. Create token authentication concern:
87
+ ```ruby
88
+ # app/controllers/concerns/api_authenticatable.rb
89
+ module ApiAuthenticatable
90
+ extend ActiveSupport::Concern
91
+
92
+ included do
93
+ before_action :authenticate_with_token!
94
+ end
95
+
96
+ private
97
+
98
+ def authenticate_with_token!
99
+ authenticate_or_request_with_http_token do |token, _options|
100
+ @current_user = User.find_by(authentication_token: token)
101
+ end
102
+ end
103
+
104
+ def current_user
105
+ @current_user
106
+ end
107
+ end
108
+ ```
109
+
110
+ 4. Use in API controller:
111
+ ```ruby
112
+ class Api::V1::BaseController < ActionController::API
113
+ include ApiAuthenticatable
114
+ end
115
+ ```
116
+
117
+ Usage:
118
+ ```bash
119
+ curl -H "Authorization: Token token=YOUR_TOKEN" http://localhost:3000/api/v1/resource
120
+ ```
121
+
122
+ ## JWT Authentication
123
+
124
+ Use `devise-jwt` gem for JWT-based authentication:
125
+
126
+ 1. Add gems:
127
+ ```ruby
128
+ gem 'devise-jwt'
129
+ ```
130
+
131
+ 2. Configure secret:
132
+ ```ruby
133
+ # config/initializers/devise.rb
134
+ Devise.setup do |config|
135
+ config.jwt do |jwt|
136
+ jwt.secret = Rails.application.credentials.devise_jwt_secret_key!
137
+ jwt.dispatch_requests = [
138
+ ['POST', %r{^/login$}]
139
+ ]
140
+ jwt.revocation_requests = [
141
+ ['DELETE', %r{^/logout$}]
142
+ ]
143
+ jwt.expiration_time = 1.day.to_i
144
+ end
145
+ end
146
+ ```
147
+
148
+ 3. Set up revocation strategy:
149
+ ```ruby
150
+ # app/models/user.rb
151
+ class User < ApplicationRecord
152
+ include Devise::JWT::RevocationStrategies::JTIMatcher
153
+
154
+ devise :database_authenticatable, :registerable,
155
+ :jwt_authenticatable, jwt_revocation_strategy: self
156
+ end
157
+ ```
158
+
159
+ 4. Add JTI column:
160
+ ```bash
161
+ rails g migration AddJtiToUsers jti:string:index:unique
162
+ rails db:migrate
163
+ ```
164
+
165
+ Update migration to add NOT NULL and default:
166
+ ```ruby
167
+ add_column :users, :jti, :string, null: false, default: ""
168
+ add_index :users, :jti, unique: true
169
+ ```
170
+
171
+ 5. Create sessions controller:
172
+ ```ruby
173
+ # app/controllers/api/v1/sessions_controller.rb
174
+ class Api::V1::SessionsController < Devise::SessionsController
175
+ respond_to :json
176
+
177
+ private
178
+
179
+ def respond_with(resource, _opts = {})
180
+ render json: { user: resource, token: request.env['warden-jwt_auth.token'] }
181
+ end
182
+
183
+ def respond_to_on_destroy
184
+ head :no_content
185
+ end
186
+ end
187
+ ```
188
+
189
+ 6. Routes:
190
+ ```ruby
191
+ devise_for :users, path: '', path_names: {
192
+ sign_in: 'login',
193
+ sign_out: 'logout',
194
+ registration: 'signup'
195
+ }, controllers: {
196
+ sessions: 'api/v1/sessions',
197
+ registrations: 'api/v1/registrations'
198
+ }
199
+ ```
200
+
201
+ Usage:
202
+ ```bash
203
+ # Login
204
+ curl -X POST http://localhost:3000/login \
205
+ -H "Content-Type: application/json" \
206
+ -d '{"user":{"email":"user@example.com","password":"password"}}'
207
+
208
+ # Use token
209
+ curl http://localhost:3000/api/v1/resource \
210
+ -H "Authorization: Bearer YOUR_JWT_TOKEN"
211
+ ```
212
+
213
+ ## API Response Format
214
+
215
+ Create consistent JSON responses:
216
+
217
+ ```ruby
218
+ # app/controllers/api/v1/registrations_controller.rb
219
+ class Api::V1::RegistrationsController < Devise::RegistrationsController
220
+ respond_to :json
221
+
222
+ private
223
+
224
+ def respond_with(resource, _opts = {})
225
+ if resource.persisted?
226
+ render json: {
227
+ status: { code: 200, message: 'Signed up successfully.' },
228
+ data: UserSerializer.new(resource).serializable_hash[:data][:attributes]
229
+ }
230
+ else
231
+ render json: {
232
+ status: { code: 422, message: "User couldn't be created." },
233
+ errors: resource.errors.full_messages
234
+ }, status: :unprocessable_entity
235
+ end
236
+ end
237
+ end
238
+ ```
239
+
240
+ ## Testing API Authentication
241
+
242
+ ```ruby
243
+ # spec/support/api_helpers.rb
244
+ module ApiHelpers
245
+ def auth_headers(user)
246
+ token = Warden::JWTAuth::UserEncoder.new.call(user, :user, nil).first
247
+ { 'Authorization' => "Bearer #{token}" }
248
+ end
249
+ end
250
+
251
+ RSpec.configure do |config|
252
+ config.include ApiHelpers, type: :request
253
+ end
254
+ ```
255
+
256
+ Usage in tests:
257
+ ```ruby
258
+ describe 'GET /api/v1/profile' do
259
+ let(:user) { create(:user) }
260
+
261
+ it 'returns user profile' do
262
+ get '/api/v1/profile', headers: auth_headers(user)
263
+ expect(response).to have_http_status(:ok)
264
+ end
265
+ end
266
+ ```