rails-hyperdrive-layered-rails 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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +30 -0
  3. data/README.md +89 -0
  4. data/UPSTREAM +4 -0
  5. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/SKILL.md.erb +315 -0
  6. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/authorization-to-policy.md +79 -0
  7. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/callbacks-to-service.md +72 -0
  8. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/complex-input-to-form-object.md +119 -0
  9. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/current-from-model.md +63 -0
  10. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/god-object-decomposition.md +81 -0
  11. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/implicit-to-explicit-state-machine.md +77 -0
  12. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/query-to-query-object.md +63 -0
  13. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/examples/view-logic-to-presenter.md +60 -0
  14. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/callbacks.md +114 -0
  15. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/concerns.md +92 -0
  16. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/helpers.md +66 -0
  17. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/jobs.md +71 -0
  18. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/layer-violations.md +176 -0
  19. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/service-objects.md +131 -0
  20. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/anti-patterns/testing.md +37 -0
  21. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/core/architecture-layers.md +204 -0
  22. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/core/extraction-signals.md +314 -0
  23. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/core/specification-test.md +229 -0
  24. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/action-policy.md +437 -0
  25. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/active-agent.md +275 -0
  26. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/active-delivery.md +221 -0
  27. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/active-job-performs.md +171 -0
  28. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/alba.md +257 -0
  29. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/anyway-config.md +212 -0
  30. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/rubanok.md +246 -0
  31. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/view-component.md +223 -0
  32. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/gems/workflow.md +305 -0
  33. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/collaborator-objects.md +209 -0
  34. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/concerns.md +381 -0
  35. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/filter-objects.md +222 -0
  36. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/form-objects.md +267 -0
  37. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/policy-objects.md +298 -0
  38. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/presenters.md +257 -0
  39. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/query-objects.md +208 -0
  40. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/repositories.md +382 -0
  41. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/serializers.md +256 -0
  42. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/service-objects.md +170 -0
  43. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/state-machines.md +368 -0
  44. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/patterns/value-objects.md +267 -0
  45. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/ai-integration.md +372 -0
  46. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/authorization.md +398 -0
  47. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/callbacks.md +336 -0
  48. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/configuration.md +383 -0
  49. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/current-attributes.md +307 -0
  50. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/instrumentation.md +386 -0
  51. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/notifications.md +373 -0
  52. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/references/topics/view-components.md +454 -0
  53. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/analyze-callbacks.md +255 -0
  54. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/analyze-gods.md +279 -0
  55. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/analyze-services.md +1444 -0
  56. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/analyze.md +376 -0
  57. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/plan.md +249 -0
  58. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/review.md +473 -0
  59. data/lib/rails-hyperdrive-layered-rails/hyperdrive/skills/layered-rails/workflows/spec-test.md +282 -0
  60. data/lib/rails-hyperdrive-layered-rails/version.rb +3 -0
  61. data/lib/rails-hyperdrive-layered-rails.rb +7 -0
  62. metadata +106 -0
@@ -0,0 +1,221 @@
1
+ # Active Delivery
2
+
3
+ Abstraction layer over delivery mechanisms (email, push, SMS, etc.).
4
+
5
+ **GitHub**: https://github.com/palkan/active_delivery
6
+ **Layer**: Application (delivery classes) / Infrastructure (notifiers)
7
+
8
+ ## Contents
9
+
10
+ - Installation
11
+ - Basic Usage
12
+ - Multiple Channels
13
+ - Conditional Delivery
14
+ - User Preferences
15
+ - Custom Delivery Lines
16
+ - Testing
17
+ - Inline Mode (Development)
18
+
19
+ ## Installation
20
+
21
+ ```ruby
22
+ # Gemfile
23
+ gem "active_delivery"
24
+
25
+ # Generate delivery
26
+ rails generate delivery Posts
27
+ ```
28
+
29
+ ## Basic Usage
30
+
31
+ ### Define Delivery
32
+
33
+ ```ruby
34
+ # app/deliveries/posts_delivery.rb
35
+ class PostsDelivery < ApplicationDelivery
36
+ deliver_by :mailer
37
+
38
+ def published(post)
39
+ params(post: post, author: post.author)
40
+ end
41
+
42
+ def commented(post, comment)
43
+ params(post: post, comment: comment, author: post.author)
44
+ end
45
+ end
46
+ ```
47
+
48
+ ### Define Mailer
49
+
50
+ ```ruby
51
+ # app/mailers/posts_mailer.rb
52
+ class PostsMailer < ApplicationMailer
53
+ def published
54
+ @post = params[:post]
55
+ @author = params[:author]
56
+
57
+ mail(to: @author.email, subject: "Your post is published!")
58
+ end
59
+
60
+ def commented
61
+ @post = params[:post]
62
+ @comment = params[:comment]
63
+
64
+ mail(to: params[:author].email, subject: "New comment on your post")
65
+ end
66
+ end
67
+ ```
68
+
69
+ ### Trigger Delivery
70
+
71
+ ```ruby
72
+ # Deliver later (async)
73
+ PostsDelivery.with(post: post).published.deliver_later
74
+
75
+ # Deliver now (sync)
76
+ PostsDelivery.with(post: post).published.deliver_now
77
+ ```
78
+
79
+ ## Multiple Channels
80
+
81
+ ```ruby
82
+ class PostsDelivery < ApplicationDelivery
83
+ deliver_by :mailer
84
+ deliver_by :push, to: PushNotifier
85
+ deliver_by :slack, to: SlackNotifier
86
+
87
+ def published(post)
88
+ params(post: post, author: post.author)
89
+ end
90
+ end
91
+
92
+ # Push notifier
93
+ class PushNotifier < AbstractNotifier::Base
94
+ self.driver = PushDriver.new
95
+
96
+ def published
97
+ notification(
98
+ user: params[:author],
99
+ title: "Post Published",
100
+ body: "Your post '#{params[:post].title}' is now live!"
101
+ )
102
+ end
103
+ end
104
+
105
+ # Slack notifier
106
+ class SlackNotifier < AbstractNotifier::Base
107
+ self.driver = SlackDriver.new
108
+
109
+ def published
110
+ notification(
111
+ channel: "#content",
112
+ text: "New post: #{params[:post].title}"
113
+ )
114
+ end
115
+ end
116
+ ```
117
+
118
+ ## Conditional Delivery
119
+
120
+ ```ruby
121
+ class PostsDelivery < ApplicationDelivery
122
+ deliver_by :mailer
123
+ deliver_by :push, if: :push_enabled?
124
+ deliver_by :slack, if: -> { params[:post].featured? }
125
+
126
+ private
127
+
128
+ def push_enabled?
129
+ params[:author]&.push_notifications_enabled?
130
+ end
131
+ end
132
+ ```
133
+
134
+ ## User Preferences
135
+
136
+ ```ruby
137
+ class ApplicationDelivery < ActiveDelivery::Base
138
+ deliver_by :mailer, if: :email_enabled?
139
+ deliver_by :push, if: :push_enabled?
140
+
141
+ private
142
+
143
+ def recipient
144
+ params[:user] || params[:recipient] || params[:author]
145
+ end
146
+
147
+ def email_enabled?
148
+ recipient&.email_notifications?
149
+ end
150
+
151
+ def push_enabled?
152
+ recipient&.push_notifications?
153
+ end
154
+ end
155
+ ```
156
+
157
+ ## Custom Delivery Lines
158
+
159
+ ```ruby
160
+ # Webhook delivery
161
+ class WebhookLine < ActiveDelivery::Lines::Base
162
+ def resolve_class(name)
163
+ "#{name}Webhook".safe_constantize
164
+ end
165
+
166
+ def notify(handler, mid, **options)
167
+ handler.public_send(mid)&.deliver
168
+ end
169
+ end
170
+
171
+ ActiveDelivery::Base.register_line :webhook, WebhookLine
172
+
173
+ class PostsDelivery < ApplicationDelivery
174
+ deliver_by :webhook
175
+ end
176
+
177
+ class PostsWebhook
178
+ def published
179
+ # Send webhook
180
+ end
181
+ end
182
+ ```
183
+
184
+ ## Testing
185
+
186
+ ```ruby
187
+ RSpec.describe PostsDelivery do
188
+ let(:post) { create(:post) }
189
+
190
+ describe "#published" do
191
+ it "delivers email" do
192
+ expect {
193
+ described_class.with(post: post).published.deliver_now
194
+ }.to have_delivered_to(PostsMailer, :published)
195
+ end
196
+
197
+ it "delivers push notification when enabled" do
198
+ post.author.update!(push_notifications: true)
199
+
200
+ expect {
201
+ described_class.with(post: post).published.deliver_now
202
+ }.to have_delivered_to(PushNotifier, :published)
203
+ end
204
+
205
+ it "skips push when disabled" do
206
+ post.author.update!(push_notifications: false)
207
+
208
+ expect {
209
+ described_class.with(post: post).published.deliver_now
210
+ }.not_to have_delivered_to(PushNotifier, :published)
211
+ end
212
+ end
213
+ end
214
+ ```
215
+
216
+ ## Inline Mode (Development)
217
+
218
+ ```ruby
219
+ # config/environments/development.rb
220
+ config.active_delivery.deliver_later_queue_adapter = :inline
221
+ ```
@@ -0,0 +1,171 @@
1
+ # active_job-performs
2
+
3
+ Eliminate boilerplate job classes by declaring background methods directly on models.
4
+
5
+ **Repository:** [kaspth/active_job-performs](https://github.com/kaspth/active_job-performs)
6
+
7
+ ## Contents
8
+
9
+ - When to Use
10
+ - The `performs` Pattern
11
+ - Detection Signals
12
+ - Configuration
13
+ - Application-Wide `performs` Patterns
14
+ - When NOT to Use
15
+ - Migration Path
16
+ - Layer Placement
17
+
18
+ ## When to Use
19
+
20
+ Use when you have **anemic jobs** - job classes that just call a single method on a model:
21
+
22
+ ```ruby
23
+ # BAD: Anemic job
24
+ class NotifyRecipientsJob < ApplicationJob
25
+ def perform(notifiable)
26
+ notifiable.notify_recipients
27
+ end
28
+ end
29
+
30
+ # BAD: Model method just to enqueue job
31
+ class Post < ApplicationRecord
32
+ def notify_recipients_later
33
+ NotifyRecipientsJob.perform_later(self)
34
+ end
35
+ end
36
+ ```
37
+
38
+ ## The `performs` Pattern
39
+
40
+ Replace anemic jobs with `performs`:
41
+
42
+ ```ruby
43
+ # GOOD: Short form (no job options needed)
44
+ class Post < ApplicationRecord
45
+ performs def notify_recipients
46
+ # Notification logic
47
+ end
48
+ end
49
+
50
+ # Usage
51
+ post.notify_recipients_later
52
+ ```
53
+
54
+ This generates:
55
+ - `Post::NotifyRecipientsJob` (automatically)
56
+ - `notify_recipients_later` instance method
57
+ - `notify_recipients_later_bulk` class method (Rails 7.1+)
58
+
59
+ ### When to Use Short vs Long Form
60
+
61
+ **Short form** - when no job configuration needed:
62
+ ```ruby
63
+ performs def sync_with_provider
64
+ # Simple async execution
65
+ end
66
+ ```
67
+
68
+ **Long form** - when job options are required:
69
+ ```ruby
70
+ performs :sync_with_provider, queue_as: :critical, discard_on: ApiError
71
+
72
+ def sync_with_provider
73
+ # ...
74
+ end
75
+ ```
76
+
77
+ ## Detection Signals
78
+
79
+ **Anemic job indicators:**
80
+ - Job's `perform` method is a single line calling a method on the argument
81
+ - Model has `*_later` method that just calls `SomeJob.perform_later(self)`
82
+ - Job class has no logic beyond delegation
83
+ - Job folder has many similar thin wrapper jobs
84
+
85
+ ## Configuration
86
+
87
+ ### Job Options
88
+
89
+ ```ruby
90
+ class Post < ApplicationRecord
91
+ performs :publish,
92
+ queue_as: :critical,
93
+ discard_on: ActiveRecord::RecordNotFound do
94
+ retry_on TimeoutError, wait: :polynomially_longer
95
+ end
96
+
97
+ def publish
98
+ # ...
99
+ end
100
+ end
101
+ ```
102
+
103
+ ### Delayed Execution
104
+
105
+ ```ruby
106
+ class Post < ApplicationRecord
107
+ performs :social_boost, wait: 5.minutes
108
+ performs :publish, wait_until: -> post { Date.tomorrow.noon }
109
+
110
+ def social_boost; end
111
+ def publish; end
112
+ end
113
+ ```
114
+
115
+ ### Private Methods
116
+
117
+ ```ruby
118
+ class Post < ApplicationRecord
119
+ performs :sync_to_warehouse
120
+
121
+ private
122
+
123
+ def sync_to_warehouse
124
+ # Only accessible via sync_to_warehouse_later
125
+ end
126
+ end
127
+ ```
128
+
129
+ ### Method Suffixes
130
+
131
+ ```ruby
132
+ class Post < ApplicationRecord
133
+ performs :publish! # Creates publish_later!
134
+ performs :dangerous? # Creates dangerous_later?
135
+ end
136
+ ```
137
+
138
+ ## Application-Wide `performs` Patterns
139
+
140
+ Define common async operations in `ApplicationRecord`:
141
+
142
+ ```ruby
143
+ class ApplicationRecord < ActiveRecord::Base
144
+ self.abstract_class = true
145
+
146
+ performs :destroy, queue_as: "active_record.destroy"
147
+ end
148
+
149
+ # Any model can now use:
150
+ post.destroy_later
151
+ ```
152
+
153
+ ## When NOT to Use
154
+
155
+ Keep separate job classes when:
156
+ - Job has complex logic beyond calling one method
157
+ - Job processes multiple records with custom batching
158
+ - Job needs to be triggered from multiple models
159
+ - Job has extensive retry/error handling configuration
160
+
161
+ ## Migration Path
162
+
163
+ 1. Identify anemic jobs (single method delegation)
164
+ 2. Add `performs :method_name` to the model
165
+ 3. Replace `SomeJob.perform_later(record)` with `record.method_name_later`
166
+ 4. Delete the job file
167
+ 5. Delete any `*_later` wrapper methods
168
+
169
+ ## Layer Placement
170
+
171
+ The `performs` declaration belongs in the **Domain Layer** (model), keeping the async execution detail close to the domain logic it executes.
@@ -0,0 +1,257 @@
1
+ # Alba
2
+
3
+ Fast JSON serialization with a clean DSL.
4
+
5
+ **GitHub**: https://github.com/okuramasafumi/alba
6
+ **Layer**: Presentation (API layer)
7
+
8
+ ## Contents
9
+
10
+ - Installation
11
+ - Basic Usage
12
+ - Associations
13
+ - Conditional Attributes
14
+ - Computed Attributes
15
+ - Root Key
16
+ - Base Serializer
17
+ - Key Transformation
18
+ - TypeScript Generation
19
+ - Performance Tips
20
+ - Testing
21
+
22
+ ## Installation
23
+
24
+ ```ruby
25
+ # Gemfile
26
+ gem "alba"
27
+ ```
28
+
29
+ ## Basic Usage
30
+
31
+ ### Define Serializer
32
+
33
+ ```ruby
34
+ class UserSerializer
35
+ include Alba::Resource
36
+
37
+ attributes :id, :email
38
+
39
+ attribute :full_name do |user|
40
+ "#{user.first_name} #{user.last_name}"
41
+ end
42
+ end
43
+ ```
44
+
45
+ ### Serialize
46
+
47
+ ```ruby
48
+ user = User.find(1)
49
+ UserSerializer.new(user).serialize
50
+ # => '{"id":1,"email":"user@example.com","full_name":"John Doe"}'
51
+
52
+ # Collection
53
+ users = User.all
54
+ UserSerializer.new(users).serialize
55
+ # => '[{"id":1,...},{"id":2,...}]'
56
+ ```
57
+
58
+ ### In Controller
59
+
60
+ ```ruby
61
+ class UsersController < ApplicationController
62
+ def show
63
+ user = User.find(params[:id])
64
+ render json: UserSerializer.new(user)
65
+ end
66
+
67
+ def index
68
+ users = User.all
69
+ render json: UserSerializer.new(users)
70
+ end
71
+ end
72
+ ```
73
+
74
+ ## Associations
75
+
76
+ ```ruby
77
+ class PostSerializer
78
+ include Alba::Resource
79
+
80
+ attributes :id, :title, :body
81
+
82
+ # Nested serializer
83
+ one :author, resource: UserSerializer
84
+
85
+ # Collection
86
+ many :comments, resource: CommentSerializer
87
+
88
+ # Inline definition
89
+ many :tags do
90
+ attributes :id, :name
91
+ end
92
+ end
93
+ ```
94
+
95
+ ## Conditional Attributes
96
+
97
+ ```ruby
98
+ class UserSerializer
99
+ include Alba::Resource
100
+
101
+ attributes :id, :name, :email
102
+
103
+ # Conditional attribute
104
+ attribute :admin_notes, if: proc { |user, context|
105
+ context[:current_user]&.admin?
106
+ }
107
+
108
+ # Method-based condition
109
+ attribute :phone, if: :show_phone?
110
+
111
+ def show_phone?(user)
112
+ user.phone_public?
113
+ end
114
+ end
115
+
116
+ # Pass context
117
+ UserSerializer.new(user, params: { current_user: current_user }).serialize
118
+ ```
119
+
120
+ ## Computed Attributes
121
+
122
+ ```ruby
123
+ class PostSerializer
124
+ include Alba::Resource
125
+
126
+ attributes :id, :title
127
+
128
+ attribute :is_published do |post|
129
+ post.published_at.present?
130
+ end
131
+
132
+ attribute :reading_time do |post|
133
+ (post.body.split.size / 200.0).ceil
134
+ end
135
+
136
+ attribute :url do |post|
137
+ Rails.application.routes.url_helpers.post_url(post)
138
+ end
139
+ end
140
+ ```
141
+
142
+ ## Root Key
143
+
144
+ ```ruby
145
+ class UserSerializer
146
+ include Alba::Resource
147
+
148
+ root_key :user, :users # singular, plural
149
+
150
+ attributes :id, :name
151
+ end
152
+
153
+ UserSerializer.new(user).serialize
154
+ # => '{"user":{"id":1,"name":"John"}}'
155
+
156
+ UserSerializer.new(users).serialize
157
+ # => '{"users":[{"id":1,"name":"John"},...]}'
158
+ ```
159
+
160
+ ## Base Serializer
161
+
162
+ ```ruby
163
+ class ApplicationSerializer
164
+ include Alba::Resource
165
+ include Rails.application.routes.url_helpers
166
+
167
+ # Common configuration
168
+ transform_keys :lower_camel
169
+
170
+ # Helper available to all serializers
171
+ def current_time
172
+ Time.current.iso8601
173
+ end
174
+ end
175
+
176
+ class UserSerializer < ApplicationSerializer
177
+ attributes :id, :name
178
+
179
+ attribute :serialized_at do
180
+ current_time
181
+ end
182
+ end
183
+ ```
184
+
185
+ ## Key Transformation
186
+
187
+ ```ruby
188
+ class UserSerializer
189
+ include Alba::Resource
190
+
191
+ # Transform all keys
192
+ transform_keys :lower_camel # firstName
193
+ transform_keys :dash # first-name
194
+
195
+ attributes :first_name, :last_name # becomes firstName, lastName
196
+ end
197
+ ```
198
+
199
+ ## TypeScript Generation
200
+
201
+ With typelizer:
202
+
203
+ ```ruby
204
+ # Gemfile
205
+ gem "typelizer"
206
+
207
+ class ApplicationSerializer
208
+ include Alba::Resource
209
+ include Typelizer::DSL
210
+ end
211
+
212
+ class UserSerializer < ApplicationSerializer
213
+ typelize_from User
214
+
215
+ attributes :id, :email
216
+ attribute :is_admin, &:admin?
217
+ end
218
+ ```
219
+
220
+ Generated TypeScript:
221
+
222
+ ```typescript
223
+ interface User {
224
+ id: number;
225
+ email: string;
226
+ is_admin: boolean;
227
+ }
228
+ ```
229
+
230
+ ## Performance Tips
231
+
232
+ ```ruby
233
+ # Use select to avoid loading unused columns
234
+ users = User.select(:id, :name, :email)
235
+ UserSerializer.new(users).serialize
236
+
237
+ # Preload associations
238
+ posts = Post.includes(:author, :comments)
239
+ PostSerializer.new(posts).serialize
240
+ ```
241
+
242
+ ## Testing
243
+
244
+ ```ruby
245
+ RSpec.describe UserSerializer do
246
+ let(:user) { create(:user, first_name: "John", last_name: "Doe") }
247
+ let(:serialized) { JSON.parse(described_class.new(user).serialize) }
248
+
249
+ it "includes expected attributes" do
250
+ expect(serialized).to include(
251
+ "id" => user.id,
252
+ "email" => user.email,
253
+ "full_name" => "John Doe"
254
+ )
255
+ end
256
+ end
257
+ ```