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.
Files changed (111) hide show
  1. package/README.md +128 -0
  2. package/bin/claude-framework +3 -0
  3. package/framework/agents/design-lead.md +240 -0
  4. package/framework/agents/product-owner.md +179 -0
  5. package/framework/agents/tech-lead.md +226 -0
  6. package/framework/commands/ayuda.md +127 -0
  7. package/framework/commands/a/303/261adir.md +98 -0
  8. package/framework/commands/backup.md +397 -0
  9. package/framework/commands/cambiar.md +110 -0
  10. package/framework/commands/cloud.md +457 -0
  11. package/framework/commands/code.md +142 -0
  12. package/framework/commands/debug.md +334 -0
  13. package/framework/commands/deploy.md +383 -0
  14. package/framework/commands/deshacer.md +120 -0
  15. package/framework/commands/estado.md +218 -0
  16. package/framework/commands/explica.md +227 -0
  17. package/framework/commands/feature.md +120 -0
  18. package/framework/commands/git.md +427 -0
  19. package/framework/commands/historial.md +202 -0
  20. package/framework/commands/learn.md +408 -0
  21. package/framework/commands/movil.md +245 -0
  22. package/framework/commands/nuevo.md +118 -0
  23. package/framework/commands/plan.md +134 -0
  24. package/framework/commands/prd.md +113 -0
  25. package/framework/commands/probar.md +148 -0
  26. package/framework/commands/revisar.md +208 -0
  27. package/framework/commands/seeds.md +230 -0
  28. package/framework/commands/seguridad.md +226 -0
  29. package/framework/commands/tasks.md +157 -0
  30. package/framework/skills/architecture/algorithms.md +970 -0
  31. package/framework/skills/architecture/clean-code.md +1080 -0
  32. package/framework/skills/architecture/design-patterns.md +1984 -0
  33. package/framework/skills/architecture/functional-programming.md +972 -0
  34. package/framework/skills/architecture/solid.md +991 -0
  35. package/framework/skills/cloud/cloud-aws.md +848 -0
  36. package/framework/skills/cloud/cloud-azure.md +931 -0
  37. package/framework/skills/cloud/cloud-gcp.md +848 -0
  38. package/framework/skills/cloud/message-queues.md +1229 -0
  39. package/framework/skills/core/accessibility.md +401 -0
  40. package/framework/skills/core/api.md +474 -0
  41. package/framework/skills/core/authentication.md +306 -0
  42. package/framework/skills/core/authorization.md +388 -0
  43. package/framework/skills/core/background-jobs.md +341 -0
  44. package/framework/skills/core/caching.md +473 -0
  45. package/framework/skills/core/code-review.md +341 -0
  46. package/framework/skills/core/controllers.md +290 -0
  47. package/framework/skills/core/cua.md +285 -0
  48. package/framework/skills/core/documentation.md +472 -0
  49. package/framework/skills/core/file-uploads.md +351 -0
  50. package/framework/skills/core/hotwire-native.md +296 -0
  51. package/framework/skills/core/hotwire.md +278 -0
  52. package/framework/skills/core/i18n.md +334 -0
  53. package/framework/skills/core/imports-exports.md +750 -0
  54. package/framework/skills/core/infrastructure.md +337 -0
  55. package/framework/skills/core/models.md +228 -0
  56. package/framework/skills/core/notifications.md +672 -0
  57. package/framework/skills/core/payments.md +581 -0
  58. package/framework/skills/core/performance.md +361 -0
  59. package/framework/skills/core/rails-scaffold.md +131 -0
  60. package/framework/skills/core/search.md +518 -0
  61. package/framework/skills/core/security.md +565 -0
  62. package/framework/skills/core/seeds.md +307 -0
  63. package/framework/skills/core/seo.md +542 -0
  64. package/framework/skills/core/testing.md +393 -0
  65. package/framework/skills/core/views.md +260 -0
  66. package/framework/skills/core/websockets.md +564 -0
  67. package/framework/skills/data/advanced-sql.md +1204 -0
  68. package/framework/skills/data/nosql.md +1141 -0
  69. package/framework/skills/devops/containers-advanced.md +1237 -0
  70. package/framework/skills/devops/debugging.md +834 -0
  71. package/framework/skills/devops/git-workflow.md +752 -0
  72. package/framework/skills/devops/networking.md +932 -0
  73. package/framework/skills/devops/shell-scripting.md +1132 -0
  74. package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
  75. package/framework/sub-agents/cloud-agent.md +677 -0
  76. package/framework/sub-agents/data.md +504 -0
  77. package/framework/sub-agents/debugging-agent.md +554 -0
  78. package/framework/sub-agents/devops.md +483 -0
  79. package/framework/sub-agents/docs.md +176 -0
  80. package/framework/sub-agents/frontend-dev.md +349 -0
  81. package/framework/sub-agents/git-workflow-agent.md +697 -0
  82. package/framework/sub-agents/integrations.md +630 -0
  83. package/framework/sub-agents/native-dev.md +434 -0
  84. package/framework/sub-agents/qa.md +138 -0
  85. package/framework/sub-agents/rails-dev.md +375 -0
  86. package/framework/sub-agents/security.md +526 -0
  87. package/framework/sub-agents/ui.md +437 -0
  88. package/framework/sub-agents/ux.md +284 -0
  89. package/framework/templates/api-spec.md +500 -0
  90. package/framework/templates/component-spec.md +248 -0
  91. package/framework/templates/feature.json +13 -0
  92. package/framework/templates/model-spec.md +318 -0
  93. package/framework/templates/prd-template.md +80 -0
  94. package/framework/templates/task-plan.md +122 -0
  95. package/framework/templates/task-user-story.md +52 -0
  96. package/framework/templates/technical-spec.md +260 -0
  97. package/framework/templates/user-story.md +95 -0
  98. package/package.json +42 -0
  99. package/project-templates/CLAUDE.md +42 -0
  100. package/project-templates/contexts/architecture.md +25 -0
  101. package/project-templates/contexts/conventions.md +46 -0
  102. package/project-templates/contexts/design-system.md +47 -0
  103. package/project-templates/contexts/requirements.md +38 -0
  104. package/project-templates/contexts/stack.md +30 -0
  105. package/project-templates/history/active/models.md +11 -0
  106. package/project-templates/history/changelog.md +15 -0
  107. package/project-templates/workspace/.gitkeep +0 -0
  108. package/src/cli.js +52 -0
  109. package/src/init.js +104 -0
  110. package/src/status.js +75 -0
  111. package/src/update.js +88 -0
@@ -0,0 +1,1984 @@
1
+ # Skill: Design Patterns (GoF)
2
+
3
+ ## Purpose
4
+ Implement Gang of Four design patterns in Ruby and Rails to solve common software design problems with proven, reusable solutions.
5
+
6
+ ---
7
+
8
+ ## Creational Patterns
9
+
10
+ ### Factory Method
11
+ **Problem:** Need to create objects without specifying their exact class.
12
+ **When to use:** When a class cannot anticipate the type of objects it needs to create.
13
+
14
+ ```ruby
15
+ # Base creator
16
+ class NotificationFactory
17
+ def self.create(type, recipient, message)
18
+ case type
19
+ when :email
20
+ EmailNotification.new(recipient, message)
21
+ when :sms
22
+ SmsNotification.new(recipient, message)
23
+ when :push
24
+ PushNotification.new(recipient, message)
25
+ else
26
+ raise ArgumentError, "Unknown notification type: #{type}"
27
+ end
28
+ end
29
+ end
30
+
31
+ # Product classes
32
+ class EmailNotification
33
+ def initialize(recipient, message)
34
+ @recipient = recipient
35
+ @message = message
36
+ end
37
+
38
+ def deliver
39
+ # Send email
40
+ end
41
+ end
42
+
43
+ class SmsNotification
44
+ def initialize(recipient, message)
45
+ @recipient = recipient
46
+ @message = message
47
+ end
48
+
49
+ def deliver
50
+ # Send SMS
51
+ end
52
+ end
53
+
54
+ # Usage
55
+ notification = NotificationFactory.create(:email, user.email, "Welcome!")
56
+ notification.deliver
57
+ ```
58
+
59
+ **Rails Example:**
60
+ ```ruby
61
+ # app/services/payment_processor_factory.rb
62
+ class PaymentProcessorFactory
63
+ PROCESSORS = {
64
+ stripe: StripeProcessor,
65
+ paypal: PaypalProcessor,
66
+ braintree: BraintreeProcessor
67
+ }.freeze
68
+
69
+ def self.for(gateway)
70
+ PROCESSORS.fetch(gateway) do
71
+ raise ArgumentError, "Unknown payment gateway: #{gateway}"
72
+ end.new
73
+ end
74
+ end
75
+
76
+ # Usage in controller
77
+ class PaymentsController < ApplicationController
78
+ def create
79
+ processor = PaymentProcessorFactory.for(params[:gateway].to_sym)
80
+ result = processor.charge(current_user, amount)
81
+ end
82
+ end
83
+ ```
84
+
85
+ ---
86
+
87
+ ### Abstract Factory
88
+ **Problem:** Need to create families of related objects without specifying their concrete classes.
89
+ **When to use:** When your system needs to work with multiple families of products.
90
+
91
+ ```ruby
92
+ # Abstract factory
93
+ class UIFactory
94
+ def create_button
95
+ raise NotImplementedError
96
+ end
97
+
98
+ def create_input
99
+ raise NotImplementedError
100
+ end
101
+ end
102
+
103
+ # Concrete factories
104
+ class MaterialUIFactory < UIFactory
105
+ def create_button
106
+ MaterialButton.new
107
+ end
108
+
109
+ def create_input
110
+ MaterialInput.new
111
+ end
112
+ end
113
+
114
+ class BootstrapUIFactory < UIFactory
115
+ def create_button
116
+ BootstrapButton.new
117
+ end
118
+
119
+ def create_input
120
+ BootstrapInput.new
121
+ end
122
+ end
123
+
124
+ # Usage
125
+ factory = Rails.application.config.ui_framework == :material ?
126
+ MaterialUIFactory.new :
127
+ BootstrapUIFactory.new
128
+
129
+ button = factory.create_button
130
+ input = factory.create_input
131
+ ```
132
+
133
+ **Rails Example:**
134
+ ```ruby
135
+ # app/services/export_factory.rb
136
+ class ExportFactory
137
+ def self.for(format)
138
+ case format
139
+ when :pdf
140
+ PdfExportFactory.new
141
+ when :excel
142
+ ExcelExportFactory.new
143
+ when :csv
144
+ CsvExportFactory.new
145
+ end
146
+ end
147
+ end
148
+
149
+ class PdfExportFactory
150
+ def create_document
151
+ PdfDocument.new
152
+ end
153
+
154
+ def create_formatter
155
+ PdfFormatter.new
156
+ end
157
+
158
+ def create_writer
159
+ PdfWriter.new
160
+ end
161
+ end
162
+
163
+ # Usage
164
+ factory = ExportFactory.for(:pdf)
165
+ doc = factory.create_document
166
+ formatter = factory.create_formatter
167
+ writer = factory.create_writer
168
+ ```
169
+
170
+ ---
171
+
172
+ ### Builder
173
+ **Problem:** Need to construct complex objects step by step.
174
+ **When to use:** When an object requires many steps to be created or has many optional parameters.
175
+
176
+ ```ruby
177
+ class QueryBuilder
178
+ def initialize
179
+ @select = []
180
+ @where = []
181
+ @order = nil
182
+ @limit = nil
183
+ end
184
+
185
+ def select(*columns)
186
+ @select.concat(columns)
187
+ self
188
+ end
189
+
190
+ def where(condition)
191
+ @where << condition
192
+ self
193
+ end
194
+
195
+ def order(column, direction = :asc)
196
+ @order = { column: column, direction: direction }
197
+ self
198
+ end
199
+
200
+ def limit(count)
201
+ @limit = count
202
+ self
203
+ end
204
+
205
+ def build
206
+ query = "SELECT #{@select.join(', ')} FROM users"
207
+ query += " WHERE #{@where.join(' AND ')}" if @where.any?
208
+ query += " ORDER BY #{@order[:column]} #{@order[:direction]}" if @order
209
+ query += " LIMIT #{@limit}" if @limit
210
+ query
211
+ end
212
+ end
213
+
214
+ # Usage with method chaining
215
+ query = QueryBuilder.new
216
+ .select(:id, :name, :email)
217
+ .where("active = true")
218
+ .where("created_at > '2024-01-01'")
219
+ .order(:created_at, :desc)
220
+ .limit(10)
221
+ .build
222
+ ```
223
+
224
+ **Rails Example:**
225
+ ```ruby
226
+ # app/builders/user_builder.rb
227
+ class UserBuilder
228
+ def initialize
229
+ @user = User.new
230
+ end
231
+
232
+ def with_email(email)
233
+ @user.email = email
234
+ self
235
+ end
236
+
237
+ def with_name(first, last)
238
+ @user.first_name = first
239
+ @user.last_name = last
240
+ self
241
+ end
242
+
243
+ def with_role(role)
244
+ @user.role = role
245
+ self
246
+ end
247
+
248
+ def with_profile(attributes = {})
249
+ @user.build_profile(attributes)
250
+ self
251
+ end
252
+
253
+ def with_preferences(prefs)
254
+ @user.preferences = prefs
255
+ self
256
+ end
257
+
258
+ def build
259
+ @user
260
+ end
261
+
262
+ def create!
263
+ @user.save!
264
+ @user
265
+ end
266
+ end
267
+
268
+ # Usage
269
+ user = UserBuilder.new
270
+ .with_email("john@example.com")
271
+ .with_name("John", "Doe")
272
+ .with_role(:admin)
273
+ .with_profile(bio: "Developer")
274
+ .create!
275
+ ```
276
+
277
+ ---
278
+
279
+ ### Singleton
280
+ **Problem:** Ensure a class has only one instance with global access.
281
+ **When to use:** When exactly one instance is needed (configurations, connection pools, caches).
282
+
283
+ ```ruby
284
+ # Ruby's built-in Singleton module
285
+ require 'singleton'
286
+
287
+ class Configuration
288
+ include Singleton
289
+
290
+ attr_accessor :api_key, :debug_mode, :timeout
291
+
292
+ def initialize
293
+ @api_key = nil
294
+ @debug_mode = false
295
+ @timeout = 30
296
+ end
297
+ end
298
+
299
+ # Usage
300
+ config = Configuration.instance
301
+ config.api_key = "secret123"
302
+ config.debug_mode = true
303
+
304
+ # Anywhere else in the app
305
+ Configuration.instance.api_key # => "secret123"
306
+ ```
307
+
308
+ **Rails Example:**
309
+ ```ruby
310
+ # Rails already provides singletons via configuration
311
+ # config/application.rb
312
+ module MyApp
313
+ class Application < Rails::Application
314
+ config.my_setting = "value"
315
+ end
316
+ end
317
+
318
+ # Access anywhere
319
+ Rails.application.config.my_setting
320
+
321
+ # Custom singleton service
322
+ class FeatureFlags
323
+ include Singleton
324
+
325
+ def initialize
326
+ @flags = {}
327
+ load_flags
328
+ end
329
+
330
+ def enabled?(feature)
331
+ @flags[feature.to_sym] == true
332
+ end
333
+
334
+ def enable(feature)
335
+ @flags[feature.to_sym] = true
336
+ end
337
+
338
+ private
339
+
340
+ def load_flags
341
+ @flags = Rails.cache.fetch("feature_flags", expires_in: 1.hour) do
342
+ Feature.pluck(:name, :enabled).to_h.symbolize_keys
343
+ end
344
+ end
345
+ end
346
+
347
+ # Usage
348
+ FeatureFlags.instance.enabled?(:new_checkout)
349
+ ```
350
+
351
+ ---
352
+
353
+ ### Prototype
354
+ **Problem:** Create new objects by copying existing ones.
355
+ **When to use:** When creating an object is expensive and you can clone existing ones.
356
+
357
+ ```ruby
358
+ class Document
359
+ attr_accessor :title, :content, :styles, :metadata
360
+
361
+ def initialize
362
+ @styles = {}
363
+ @metadata = {}
364
+ end
365
+
366
+ def clone
367
+ copy = super
368
+ copy.styles = @styles.dup
369
+ copy.metadata = @metadata.dup
370
+ copy
371
+ end
372
+ end
373
+
374
+ # Usage
375
+ template = Document.new
376
+ template.title = "Template"
377
+ template.styles = { font: "Arial", size: 12 }
378
+ template.metadata = { author: "System", version: 1 }
379
+
380
+ # Create copies
381
+ doc1 = template.clone
382
+ doc1.title = "Report Q1"
383
+
384
+ doc2 = template.clone
385
+ doc2.title = "Report Q2"
386
+ ```
387
+
388
+ **Rails Example:**
389
+ ```ruby
390
+ # Using ActiveRecord's dup
391
+ class Order < ApplicationRecord
392
+ has_many :order_items
393
+
394
+ def duplicate
395
+ new_order = dup
396
+ new_order.status = :draft
397
+ new_order.created_at = nil
398
+
399
+ order_items.each do |item|
400
+ new_order.order_items << item.dup
401
+ end
402
+
403
+ new_order
404
+ end
405
+ end
406
+
407
+ # Usage
408
+ original_order = Order.find(1)
409
+ new_order = original_order.duplicate
410
+ new_order.save!
411
+ ```
412
+
413
+ ---
414
+
415
+ ## Structural Patterns
416
+
417
+ ### Adapter
418
+ **Problem:** Make incompatible interfaces work together.
419
+ **When to use:** When you need to use an existing class with an incompatible interface.
420
+
421
+ ```ruby
422
+ # Target interface expected by client
423
+ class PaymentGateway
424
+ def charge(amount, card)
425
+ raise NotImplementedError
426
+ end
427
+ end
428
+
429
+ # Adaptee - legacy system with different interface
430
+ class LegacyPaymentSystem
431
+ def process_payment(cents, card_number, expiry, cvv)
432
+ # Legacy implementation
433
+ end
434
+ end
435
+
436
+ # Adapter
437
+ class LegacyPaymentAdapter < PaymentGateway
438
+ def initialize
439
+ @legacy = LegacyPaymentSystem.new
440
+ end
441
+
442
+ def charge(amount, card)
443
+ cents = (amount * 100).to_i
444
+ @legacy.process_payment(
445
+ cents,
446
+ card[:number],
447
+ card[:expiry],
448
+ card[:cvv]
449
+ )
450
+ end
451
+ end
452
+
453
+ # Usage
454
+ gateway = LegacyPaymentAdapter.new
455
+ gateway.charge(99.99, { number: "4111...", expiry: "12/25", cvv: "123" })
456
+ ```
457
+
458
+ **Rails Example:**
459
+ ```ruby
460
+ # app/adapters/external_user_adapter.rb
461
+ class ExternalUserAdapter
462
+ def initialize(external_data)
463
+ @data = external_data
464
+ end
465
+
466
+ def to_user_params
467
+ {
468
+ email: @data["email_address"],
469
+ first_name: @data["given_name"],
470
+ last_name: @data["family_name"],
471
+ phone: format_phone(@data["phone_number"]),
472
+ external_id: @data["id"]
473
+ }
474
+ end
475
+
476
+ private
477
+
478
+ def format_phone(phone)
479
+ phone&.gsub(/\D/, "")
480
+ end
481
+ end
482
+
483
+ # Usage
484
+ external_data = ExternalApi.fetch_user(123)
485
+ adapter = ExternalUserAdapter.new(external_data)
486
+ User.create!(adapter.to_user_params)
487
+ ```
488
+
489
+ ---
490
+
491
+ ### Bridge
492
+ **Problem:** Separate abstraction from implementation so both can vary independently.
493
+ **When to use:** When you want to avoid a permanent binding between abstraction and implementation.
494
+
495
+ ```ruby
496
+ # Implementation interface
497
+ class Renderer
498
+ def render_title(text)
499
+ raise NotImplementedError
500
+ end
501
+
502
+ def render_body(text)
503
+ raise NotImplementedError
504
+ end
505
+ end
506
+
507
+ # Concrete implementations
508
+ class HtmlRenderer < Renderer
509
+ def render_title(text)
510
+ "<h1>#{text}</h1>"
511
+ end
512
+
513
+ def render_body(text)
514
+ "<p>#{text}</p>"
515
+ end
516
+ end
517
+
518
+ class MarkdownRenderer < Renderer
519
+ def render_title(text)
520
+ "# #{text}\n"
521
+ end
522
+
523
+ def render_body(text)
524
+ "#{text}\n"
525
+ end
526
+ end
527
+
528
+ # Abstraction
529
+ class Document
530
+ def initialize(renderer)
531
+ @renderer = renderer
532
+ end
533
+
534
+ def render
535
+ raise NotImplementedError
536
+ end
537
+ end
538
+
539
+ # Refined abstraction
540
+ class Article < Document
541
+ attr_accessor :title, :body
542
+
543
+ def render
544
+ @renderer.render_title(@title) + @renderer.render_body(@body)
545
+ end
546
+ end
547
+
548
+ # Usage
549
+ html_article = Article.new(HtmlRenderer.new)
550
+ html_article.title = "Hello"
551
+ html_article.body = "World"
552
+ html_article.render # => "<h1>Hello</h1><p>World</p>"
553
+
554
+ md_article = Article.new(MarkdownRenderer.new)
555
+ md_article.title = "Hello"
556
+ md_article.body = "World"
557
+ md_article.render # => "# Hello\nWorld\n"
558
+ ```
559
+
560
+ ---
561
+
562
+ ### Composite
563
+ **Problem:** Compose objects into tree structures and treat individual objects and compositions uniformly.
564
+ **When to use:** When you need to represent part-whole hierarchies.
565
+
566
+ ```ruby
567
+ # Component
568
+ class MenuComponent
569
+ def add(component)
570
+ raise NotImplementedError
571
+ end
572
+
573
+ def render
574
+ raise NotImplementedError
575
+ end
576
+ end
577
+
578
+ # Leaf
579
+ class MenuItem < MenuComponent
580
+ attr_reader :name, :url
581
+
582
+ def initialize(name, url)
583
+ @name = name
584
+ @url = url
585
+ end
586
+
587
+ def render
588
+ "<a href='#{@url}'>#{@name}</a>"
589
+ end
590
+ end
591
+
592
+ # Composite
593
+ class Menu < MenuComponent
594
+ attr_reader :name
595
+
596
+ def initialize(name)
597
+ @name = name
598
+ @children = []
599
+ end
600
+
601
+ def add(component)
602
+ @children << component
603
+ end
604
+
605
+ def render
606
+ html = "<ul class='menu'><li>#{@name}"
607
+ html += "<ul>"
608
+ @children.each { |child| html += "<li>#{child.render}</li>" }
609
+ html += "</ul></li></ul>"
610
+ html
611
+ end
612
+ end
613
+
614
+ # Usage
615
+ main_menu = Menu.new("Main")
616
+ main_menu.add(MenuItem.new("Home", "/"))
617
+ main_menu.add(MenuItem.new("About", "/about"))
618
+
619
+ products_menu = Menu.new("Products")
620
+ products_menu.add(MenuItem.new("Software", "/products/software"))
621
+ products_menu.add(MenuItem.new("Hardware", "/products/hardware"))
622
+
623
+ main_menu.add(products_menu)
624
+ main_menu.render
625
+ ```
626
+
627
+ **Rails Example:**
628
+ ```ruby
629
+ # app/models/category.rb - Tree structure with acts_as_tree or closure_tree
630
+ class Category < ApplicationRecord
631
+ has_many :children, class_name: "Category", foreign_key: "parent_id"
632
+ belongs_to :parent, class_name: "Category", optional: true
633
+ has_many :products
634
+
635
+ def all_products
636
+ Product.where(category_id: subtree_ids)
637
+ end
638
+
639
+ def subtree_ids
640
+ [id] + children.flat_map(&:subtree_ids)
641
+ end
642
+
643
+ def render_tree(level = 0)
644
+ indent = " " * level
645
+ output = "#{indent}- #{name}\n"
646
+ children.each { |child| output += child.render_tree(level + 1) }
647
+ output
648
+ end
649
+ end
650
+ ```
651
+
652
+ ---
653
+
654
+ ### Decorator
655
+ **Problem:** Add responsibilities to objects dynamically without altering their structure.
656
+ **When to use:** When you need to add behavior to objects without subclassing.
657
+
658
+ ```ruby
659
+ # Component interface
660
+ class Coffee
661
+ def cost
662
+ raise NotImplementedError
663
+ end
664
+
665
+ def description
666
+ raise NotImplementedError
667
+ end
668
+ end
669
+
670
+ # Concrete component
671
+ class SimpleCoffee < Coffee
672
+ def cost
673
+ 2.0
674
+ end
675
+
676
+ def description
677
+ "Simple coffee"
678
+ end
679
+ end
680
+
681
+ # Decorator base
682
+ class CoffeeDecorator < Coffee
683
+ def initialize(coffee)
684
+ @coffee = coffee
685
+ end
686
+
687
+ def cost
688
+ @coffee.cost
689
+ end
690
+
691
+ def description
692
+ @coffee.description
693
+ end
694
+ end
695
+
696
+ # Concrete decorators
697
+ class Milk < CoffeeDecorator
698
+ def cost
699
+ @coffee.cost + 0.5
700
+ end
701
+
702
+ def description
703
+ "#{@coffee.description}, milk"
704
+ end
705
+ end
706
+
707
+ class Sugar < CoffeeDecorator
708
+ def cost
709
+ @coffee.cost + 0.2
710
+ end
711
+
712
+ def description
713
+ "#{@coffee.description}, sugar"
714
+ end
715
+ end
716
+
717
+ # Usage
718
+ coffee = SimpleCoffee.new
719
+ coffee = Milk.new(coffee)
720
+ coffee = Sugar.new(coffee)
721
+ coffee.cost # => 2.7
722
+ coffee.description # => "Simple coffee, milk, sugar"
723
+ ```
724
+
725
+ **Rails Example:**
726
+ ```ruby
727
+ # Using SimpleDelegator
728
+ class UserPresenter < SimpleDelegator
729
+ def full_name
730
+ "#{first_name} #{last_name}"
731
+ end
732
+
733
+ def formatted_created_at
734
+ created_at.strftime("%B %d, %Y")
735
+ end
736
+
737
+ def avatar_url
738
+ avatar.attached? ? avatar : "default_avatar.png"
739
+ end
740
+ end
741
+
742
+ # Usage in view
743
+ presenter = UserPresenter.new(@user)
744
+ presenter.email # delegates to user
745
+ presenter.full_name # decorator method
746
+ ```
747
+
748
+ ---
749
+
750
+ ### Facade
751
+ **Problem:** Provide a simplified interface to a complex subsystem.
752
+ **When to use:** When you need a simple interface to a complex set of classes.
753
+
754
+ ```ruby
755
+ # Complex subsystem classes
756
+ class Inventory
757
+ def check_stock(product_id)
758
+ # Check inventory
759
+ end
760
+
761
+ def reserve(product_id, quantity)
762
+ # Reserve stock
763
+ end
764
+ end
765
+
766
+ class PaymentProcessor
767
+ def authorize(amount, card)
768
+ # Authorize payment
769
+ end
770
+
771
+ def capture(authorization_id)
772
+ # Capture payment
773
+ end
774
+ end
775
+
776
+ class ShippingService
777
+ def calculate_cost(address, weight)
778
+ # Calculate shipping
779
+ end
780
+
781
+ def create_shipment(order, address)
782
+ # Create shipment
783
+ end
784
+ end
785
+
786
+ class NotificationService
787
+ def send_confirmation(user, order)
788
+ # Send email
789
+ end
790
+ end
791
+
792
+ # Facade
793
+ class OrderFacade
794
+ def initialize
795
+ @inventory = Inventory.new
796
+ @payment = PaymentProcessor.new
797
+ @shipping = ShippingService.new
798
+ @notification = NotificationService.new
799
+ end
800
+
801
+ def place_order(user, cart, address, payment_info)
802
+ # Check stock for all items
803
+ cart.items.each do |item|
804
+ unless @inventory.check_stock(item.product_id)
805
+ raise OutOfStockError, item.name
806
+ end
807
+ end
808
+
809
+ # Reserve inventory
810
+ cart.items.each { |item| @inventory.reserve(item.product_id, item.quantity) }
811
+
812
+ # Process payment
813
+ auth = @payment.authorize(cart.total, payment_info)
814
+ @payment.capture(auth.id)
815
+
816
+ # Create shipment
817
+ @shipping.create_shipment(order, address)
818
+
819
+ # Notify user
820
+ @notification.send_confirmation(user, order)
821
+
822
+ order
823
+ end
824
+ end
825
+
826
+ # Usage - simple interface to complex process
827
+ facade = OrderFacade.new
828
+ facade.place_order(current_user, cart, address, payment_info)
829
+ ```
830
+
831
+ ---
832
+
833
+ ### Proxy
834
+ **Problem:** Provide a surrogate or placeholder for another object.
835
+ **When to use:** For lazy loading, access control, logging, or caching.
836
+
837
+ ```ruby
838
+ # Subject interface
839
+ class Image
840
+ def display
841
+ raise NotImplementedError
842
+ end
843
+ end
844
+
845
+ # Real subject
846
+ class RealImage < Image
847
+ def initialize(filename)
848
+ @filename = filename
849
+ load_from_disk
850
+ end
851
+
852
+ def display
853
+ puts "Displaying #{@filename}"
854
+ end
855
+
856
+ private
857
+
858
+ def load_from_disk
859
+ puts "Loading #{@filename} from disk..."
860
+ sleep(2) # Expensive operation
861
+ end
862
+ end
863
+
864
+ # Proxy - lazy loading
865
+ class ImageProxy < Image
866
+ def initialize(filename)
867
+ @filename = filename
868
+ @real_image = nil
869
+ end
870
+
871
+ def display
872
+ @real_image ||= RealImage.new(@filename)
873
+ @real_image.display
874
+ end
875
+ end
876
+
877
+ # Usage
878
+ image = ImageProxy.new("large_photo.jpg")
879
+ # Image not loaded yet
880
+ image.display # Loads and displays
881
+ image.display # Just displays (cached)
882
+ ```
883
+
884
+ **Rails Example:**
885
+ ```ruby
886
+ # Caching proxy
887
+ class CachedProductRepository
888
+ def initialize(repository = ProductRepository.new)
889
+ @repository = repository
890
+ @cache = Rails.cache
891
+ end
892
+
893
+ def find(id)
894
+ @cache.fetch("product:#{id}", expires_in: 1.hour) do
895
+ @repository.find(id)
896
+ end
897
+ end
898
+
899
+ def all
900
+ @cache.fetch("products:all", expires_in: 15.minutes) do
901
+ @repository.all
902
+ end
903
+ end
904
+
905
+ def invalidate(id)
906
+ @cache.delete("product:#{id}")
907
+ @cache.delete("products:all")
908
+ end
909
+ end
910
+ ```
911
+
912
+ ---
913
+
914
+ ## Behavioral Patterns
915
+
916
+ ### Chain of Responsibility
917
+ **Problem:** Pass requests along a chain of handlers.
918
+ **When to use:** When multiple objects may handle a request and the handler isn't known beforehand.
919
+
920
+ ```ruby
921
+ class Handler
922
+ attr_accessor :next_handler
923
+
924
+ def handle(request)
925
+ if can_handle?(request)
926
+ process(request)
927
+ elsif next_handler
928
+ next_handler.handle(request)
929
+ else
930
+ default_handle(request)
931
+ end
932
+ end
933
+
934
+ def can_handle?(request)
935
+ raise NotImplementedError
936
+ end
937
+
938
+ def process(request)
939
+ raise NotImplementedError
940
+ end
941
+
942
+ def default_handle(request)
943
+ raise "No handler for request: #{request}"
944
+ end
945
+ end
946
+
947
+ class SmallOrderHandler < Handler
948
+ def can_handle?(request)
949
+ request[:amount] < 100
950
+ end
951
+
952
+ def process(request)
953
+ puts "Processing small order: $#{request[:amount]}"
954
+ :approved
955
+ end
956
+ end
957
+
958
+ class MediumOrderHandler < Handler
959
+ def can_handle?(request)
960
+ request[:amount] < 1000
961
+ end
962
+
963
+ def process(request)
964
+ puts "Processing medium order: $#{request[:amount]} - needs review"
965
+ :pending_review
966
+ end
967
+ end
968
+
969
+ class LargeOrderHandler < Handler
970
+ def can_handle?(request)
971
+ request[:amount] >= 1000
972
+ end
973
+
974
+ def process(request)
975
+ puts "Processing large order: $#{request[:amount]} - needs approval"
976
+ :pending_approval
977
+ end
978
+ end
979
+
980
+ # Setup chain
981
+ small = SmallOrderHandler.new
982
+ medium = MediumOrderHandler.new
983
+ large = LargeOrderHandler.new
984
+
985
+ small.next_handler = medium
986
+ medium.next_handler = large
987
+
988
+ # Usage
989
+ small.handle({ amount: 50 }) # Small handler
990
+ small.handle({ amount: 500 }) # Medium handler
991
+ small.handle({ amount: 5000 }) # Large handler
992
+ ```
993
+
994
+ **Rails Example:**
995
+ ```ruby
996
+ # Middleware-style chain
997
+ class AuthenticationMiddleware
998
+ def initialize(app)
999
+ @app = app
1000
+ end
1001
+
1002
+ def call(env)
1003
+ if authenticated?(env)
1004
+ @app.call(env)
1005
+ else
1006
+ [401, {}, ["Unauthorized"]]
1007
+ end
1008
+ end
1009
+ end
1010
+
1011
+ # Or as service objects
1012
+ class ValidationChain
1013
+ def self.build
1014
+ chain = RequiredFieldsValidator.new
1015
+ chain.next_handler = EmailFormatValidator.new
1016
+ chain.next_handler.next_handler = UniqueEmailValidator.new
1017
+ chain
1018
+ end
1019
+ end
1020
+ ```
1021
+
1022
+ ---
1023
+
1024
+ ### Command
1025
+ **Problem:** Encapsulate a request as an object.
1026
+ **When to use:** For undo/redo, queuing operations, or logging requests.
1027
+
1028
+ ```ruby
1029
+ # Command interface
1030
+ class Command
1031
+ def execute
1032
+ raise NotImplementedError
1033
+ end
1034
+
1035
+ def undo
1036
+ raise NotImplementedError
1037
+ end
1038
+ end
1039
+
1040
+ # Concrete commands
1041
+ class AddItemCommand < Command
1042
+ def initialize(cart, item)
1043
+ @cart = cart
1044
+ @item = item
1045
+ end
1046
+
1047
+ def execute
1048
+ @cart.add(@item)
1049
+ end
1050
+
1051
+ def undo
1052
+ @cart.remove(@item)
1053
+ end
1054
+ end
1055
+
1056
+ class RemoveItemCommand < Command
1057
+ def initialize(cart, item)
1058
+ @cart = cart
1059
+ @item = item
1060
+ end
1061
+
1062
+ def execute
1063
+ @cart.remove(@item)
1064
+ end
1065
+
1066
+ def undo
1067
+ @cart.add(@item)
1068
+ end
1069
+ end
1070
+
1071
+ # Invoker with history
1072
+ class CommandInvoker
1073
+ def initialize
1074
+ @history = []
1075
+ end
1076
+
1077
+ def execute(command)
1078
+ command.execute
1079
+ @history << command
1080
+ end
1081
+
1082
+ def undo
1083
+ return if @history.empty?
1084
+ command = @history.pop
1085
+ command.undo
1086
+ end
1087
+ end
1088
+
1089
+ # Usage
1090
+ invoker = CommandInvoker.new
1091
+ invoker.execute(AddItemCommand.new(cart, product))
1092
+ invoker.execute(AddItemCommand.new(cart, another_product))
1093
+ invoker.undo # Removes another_product
1094
+ ```
1095
+
1096
+ **Rails Example:**
1097
+ ```ruby
1098
+ # app/commands/create_order_command.rb
1099
+ class CreateOrderCommand
1100
+ def initialize(user:, cart:, address:)
1101
+ @user = user
1102
+ @cart = cart
1103
+ @address = address
1104
+ end
1105
+
1106
+ def execute
1107
+ ActiveRecord::Base.transaction do
1108
+ @order = Order.create!(
1109
+ user: @user,
1110
+ address: @address,
1111
+ total: @cart.total
1112
+ )
1113
+
1114
+ @cart.items.each do |item|
1115
+ @order.order_items.create!(
1116
+ product: item.product,
1117
+ quantity: item.quantity,
1118
+ price: item.price
1119
+ )
1120
+ end
1121
+
1122
+ @cart.clear
1123
+ OrderMailer.confirmation(@order).deliver_later
1124
+ end
1125
+
1126
+ @order
1127
+ end
1128
+
1129
+ def undo
1130
+ return unless @order
1131
+ @order.cancel!
1132
+ end
1133
+ end
1134
+ ```
1135
+
1136
+ ---
1137
+
1138
+ ### Iterator
1139
+ **Problem:** Access elements of a collection sequentially without exposing underlying representation.
1140
+ **When to use:** When you need to traverse a collection in different ways.
1141
+
1142
+ ```ruby
1143
+ # Ruby's Enumerable module provides iterator pattern
1144
+ class Library
1145
+ include Enumerable
1146
+
1147
+ def initialize
1148
+ @books = []
1149
+ end
1150
+
1151
+ def add(book)
1152
+ @books << book
1153
+ end
1154
+
1155
+ def each(&block)
1156
+ @books.each(&block)
1157
+ end
1158
+
1159
+ # Custom iterators
1160
+ def each_by_author(author, &block)
1161
+ @books.select { |b| b.author == author }.each(&block)
1162
+ end
1163
+
1164
+ def each_published_after(year, &block)
1165
+ @books.select { |b| b.year > year }.each(&block)
1166
+ end
1167
+ end
1168
+
1169
+ # Usage
1170
+ library = Library.new
1171
+ library.add(Book.new("1984", "Orwell", 1949))
1172
+ library.add(Book.new("Brave New World", "Huxley", 1932))
1173
+
1174
+ library.each { |book| puts book.title }
1175
+ library.map(&:title)
1176
+ library.select { |b| b.year > 1940 }
1177
+ library.each_by_author("Orwell") { |b| puts b.title }
1178
+ ```
1179
+
1180
+ **Rails Example:**
1181
+ ```ruby
1182
+ # ActiveRecord already implements Enumerable
1183
+ User.all.each { |user| process(user) }
1184
+ User.find_each(batch_size: 1000) { |user| process(user) } # Memory efficient
1185
+
1186
+ # Custom iterator for API pagination
1187
+ class PaginatedIterator
1188
+ include Enumerable
1189
+
1190
+ def initialize(client, resource)
1191
+ @client = client
1192
+ @resource = resource
1193
+ end
1194
+
1195
+ def each
1196
+ page = 1
1197
+ loop do
1198
+ response = @client.get(@resource, page: page)
1199
+ break if response.data.empty?
1200
+
1201
+ response.data.each { |item| yield item }
1202
+ page += 1
1203
+ end
1204
+ end
1205
+ end
1206
+ ```
1207
+
1208
+ ---
1209
+
1210
+ ### Mediator
1211
+ **Problem:** Reduce chaotic dependencies between objects by having them communicate through a mediator.
1212
+ **When to use:** When objects have complex many-to-many relationships.
1213
+
1214
+ ```ruby
1215
+ class ChatRoom
1216
+ def initialize
1217
+ @users = []
1218
+ end
1219
+
1220
+ def register(user)
1221
+ @users << user
1222
+ user.chat_room = self
1223
+ end
1224
+
1225
+ def send_message(sender, message, recipient = nil)
1226
+ if recipient
1227
+ # Private message
1228
+ recipient.receive(message, sender)
1229
+ else
1230
+ # Broadcast
1231
+ @users.each do |user|
1232
+ user.receive(message, sender) unless user == sender
1233
+ end
1234
+ end
1235
+ end
1236
+ end
1237
+
1238
+ class User
1239
+ attr_reader :name
1240
+ attr_accessor :chat_room
1241
+
1242
+ def initialize(name)
1243
+ @name = name
1244
+ end
1245
+
1246
+ def send(message, recipient = nil)
1247
+ @chat_room.send_message(self, message, recipient)
1248
+ end
1249
+
1250
+ def receive(message, sender)
1251
+ puts "#{@name} received from #{sender.name}: #{message}"
1252
+ end
1253
+ end
1254
+
1255
+ # Usage
1256
+ room = ChatRoom.new
1257
+ alice = User.new("Alice")
1258
+ bob = User.new("Bob")
1259
+
1260
+ room.register(alice)
1261
+ room.register(bob)
1262
+
1263
+ alice.send("Hello everyone!") # Broadcast
1264
+ alice.send("Hi Bob!", bob) # Private
1265
+ ```
1266
+
1267
+ **Rails Example:**
1268
+ ```ruby
1269
+ # Event-driven mediator using ActiveSupport::Notifications
1270
+ class OrderMediator
1271
+ def self.setup
1272
+ ActiveSupport::Notifications.subscribe("order.placed") do |*args|
1273
+ event = ActiveSupport::Notifications::Event.new(*args)
1274
+ order = event.payload[:order]
1275
+
1276
+ InventoryService.reserve(order)
1277
+ PaymentService.capture(order)
1278
+ NotificationService.confirm(order)
1279
+ end
1280
+ end
1281
+ end
1282
+
1283
+ # In order creation
1284
+ ActiveSupport::Notifications.instrument("order.placed", order: @order)
1285
+ ```
1286
+
1287
+ ---
1288
+
1289
+ ### Memento
1290
+ **Problem:** Capture and restore an object's internal state.
1291
+ **When to use:** For undo/redo, snapshots, or checkpoints.
1292
+
1293
+ ```ruby
1294
+ # Memento
1295
+ class EditorMemento
1296
+ attr_reader :content, :cursor_position
1297
+
1298
+ def initialize(content, cursor_position)
1299
+ @content = content.dup
1300
+ @cursor_position = cursor_position
1301
+ end
1302
+ end
1303
+
1304
+ # Originator
1305
+ class TextEditor
1306
+ attr_accessor :content, :cursor_position
1307
+
1308
+ def initialize
1309
+ @content = ""
1310
+ @cursor_position = 0
1311
+ end
1312
+
1313
+ def type(text)
1314
+ @content.insert(@cursor_position, text)
1315
+ @cursor_position += text.length
1316
+ end
1317
+
1318
+ def save
1319
+ EditorMemento.new(@content, @cursor_position)
1320
+ end
1321
+
1322
+ def restore(memento)
1323
+ @content = memento.content
1324
+ @cursor_position = memento.cursor_position
1325
+ end
1326
+ end
1327
+
1328
+ # Caretaker
1329
+ class EditorHistory
1330
+ def initialize
1331
+ @mementos = []
1332
+ end
1333
+
1334
+ def push(memento)
1335
+ @mementos << memento
1336
+ end
1337
+
1338
+ def pop
1339
+ @mementos.pop
1340
+ end
1341
+ end
1342
+
1343
+ # Usage
1344
+ editor = TextEditor.new
1345
+ history = EditorHistory.new
1346
+
1347
+ editor.type("Hello")
1348
+ history.push(editor.save)
1349
+
1350
+ editor.type(" World")
1351
+ history.push(editor.save)
1352
+
1353
+ editor.type("!!!")
1354
+ puts editor.content # => "Hello World!!!"
1355
+
1356
+ editor.restore(history.pop)
1357
+ puts editor.content # => "Hello World"
1358
+ ```
1359
+
1360
+ **Rails Example:**
1361
+ ```ruby
1362
+ # Using paper_trail gem or custom versioning
1363
+ class Document < ApplicationRecord
1364
+ has_paper_trail
1365
+
1366
+ def restore_version(version_id)
1367
+ version = versions.find(version_id)
1368
+ version.reify.save!
1369
+ end
1370
+ end
1371
+
1372
+ # Or manual memento
1373
+ class DocumentMemento < ApplicationRecord
1374
+ belongs_to :document
1375
+ serialize :state, coder: JSON
1376
+ end
1377
+
1378
+ class Document < ApplicationRecord
1379
+ has_many :mementos, class_name: "DocumentMemento"
1380
+
1381
+ def create_snapshot
1382
+ mementos.create!(state: attributes)
1383
+ end
1384
+
1385
+ def restore_snapshot(memento)
1386
+ update!(memento.state.except("id", "created_at", "updated_at"))
1387
+ end
1388
+ end
1389
+ ```
1390
+
1391
+ ---
1392
+
1393
+ ### Observer
1394
+ **Problem:** Define a one-to-many dependency so that when one object changes state, all dependents are notified.
1395
+ **When to use:** For event handling, notifications, or decoupled updates.
1396
+
1397
+ ```ruby
1398
+ # Using Ruby's Observable module
1399
+ require 'observer'
1400
+
1401
+ class Stock
1402
+ include Observable
1403
+
1404
+ attr_reader :name, :price
1405
+
1406
+ def initialize(name, price)
1407
+ @name = name
1408
+ @price = price
1409
+ end
1410
+
1411
+ def price=(new_price)
1412
+ @price = new_price
1413
+ changed
1414
+ notify_observers(self)
1415
+ end
1416
+ end
1417
+
1418
+ class StockAlert
1419
+ def update(stock)
1420
+ puts "Alert: #{stock.name} is now $#{stock.price}"
1421
+ end
1422
+ end
1423
+
1424
+ class StockLogger
1425
+ def update(stock)
1426
+ puts "[LOG] #{Time.now}: #{stock.name} changed to $#{stock.price}"
1427
+ end
1428
+ end
1429
+
1430
+ # Usage
1431
+ stock = Stock.new("AAPL", 150)
1432
+ stock.add_observer(StockAlert.new)
1433
+ stock.add_observer(StockLogger.new)
1434
+
1435
+ stock.price = 155 # Both observers notified
1436
+ ```
1437
+
1438
+ **Rails Example:**
1439
+ ```ruby
1440
+ # ActiveRecord callbacks as observers
1441
+ class Order < ApplicationRecord
1442
+ after_create :notify_observers
1443
+
1444
+ private
1445
+
1446
+ def notify_observers
1447
+ OrderCreatedJob.perform_later(id)
1448
+ SlackNotifier.order_placed(self)
1449
+ AnalyticsService.track("order_created", order_id: id)
1450
+ end
1451
+ end
1452
+
1453
+ # Using ActiveSupport::Notifications
1454
+ class Order < ApplicationRecord
1455
+ after_create do
1456
+ ActiveSupport::Notifications.instrument("order.created", order: self)
1457
+ end
1458
+ end
1459
+
1460
+ # Subscribers
1461
+ ActiveSupport::Notifications.subscribe("order.created") do |*args|
1462
+ event = ActiveSupport::Notifications::Event.new(*args)
1463
+ OrderMailer.confirmation(event.payload[:order]).deliver_later
1464
+ end
1465
+ ```
1466
+
1467
+ ---
1468
+
1469
+ ### State
1470
+ **Problem:** Allow an object to alter its behavior when its internal state changes.
1471
+ **When to use:** When an object's behavior depends on its state and must change at runtime.
1472
+
1473
+ ```ruby
1474
+ # State interface
1475
+ class OrderState
1476
+ def process(order)
1477
+ raise NotImplementedError
1478
+ end
1479
+
1480
+ def ship(order)
1481
+ raise NotImplementedError
1482
+ end
1483
+
1484
+ def cancel(order)
1485
+ raise NotImplementedError
1486
+ end
1487
+ end
1488
+
1489
+ # Concrete states
1490
+ class PendingState < OrderState
1491
+ def process(order)
1492
+ puts "Processing payment..."
1493
+ order.state = PaidState.new
1494
+ end
1495
+
1496
+ def ship(order)
1497
+ puts "Cannot ship - payment pending"
1498
+ end
1499
+
1500
+ def cancel(order)
1501
+ puts "Order cancelled"
1502
+ order.state = CancelledState.new
1503
+ end
1504
+ end
1505
+
1506
+ class PaidState < OrderState
1507
+ def process(order)
1508
+ puts "Already paid"
1509
+ end
1510
+
1511
+ def ship(order)
1512
+ puts "Shipping order..."
1513
+ order.state = ShippedState.new
1514
+ end
1515
+
1516
+ def cancel(order)
1517
+ puts "Refunding and cancelling..."
1518
+ order.state = CancelledState.new
1519
+ end
1520
+ end
1521
+
1522
+ class ShippedState < OrderState
1523
+ def process(order)
1524
+ puts "Already processed and shipped"
1525
+ end
1526
+
1527
+ def ship(order)
1528
+ puts "Already shipped"
1529
+ end
1530
+
1531
+ def cancel(order)
1532
+ puts "Cannot cancel - already shipped"
1533
+ end
1534
+ end
1535
+
1536
+ # Context
1537
+ class Order
1538
+ attr_accessor :state
1539
+
1540
+ def initialize
1541
+ @state = PendingState.new
1542
+ end
1543
+
1544
+ def process
1545
+ @state.process(self)
1546
+ end
1547
+
1548
+ def ship
1549
+ @state.ship(self)
1550
+ end
1551
+
1552
+ def cancel
1553
+ @state.cancel(self)
1554
+ end
1555
+ end
1556
+
1557
+ # Usage
1558
+ order = Order.new
1559
+ order.process # => "Processing payment..."
1560
+ order.ship # => "Shipping order..."
1561
+ order.cancel # => "Cannot cancel - already shipped"
1562
+ ```
1563
+
1564
+ **Rails Example:**
1565
+ ```ruby
1566
+ # Using AASM gem
1567
+ class Order < ApplicationRecord
1568
+ include AASM
1569
+
1570
+ aasm column: :status do
1571
+ state :pending, initial: true
1572
+ state :paid, :shipped, :delivered, :cancelled
1573
+
1574
+ event :pay do
1575
+ transitions from: :pending, to: :paid
1576
+ after { OrderMailer.payment_received(self).deliver_later }
1577
+ end
1578
+
1579
+ event :ship do
1580
+ transitions from: :paid, to: :shipped
1581
+ after { ShippingService.create_label(self) }
1582
+ end
1583
+
1584
+ event :deliver do
1585
+ transitions from: :shipped, to: :delivered
1586
+ end
1587
+
1588
+ event :cancel do
1589
+ transitions from: [:pending, :paid], to: :cancelled
1590
+ after { RefundService.process(self) if paid? }
1591
+ end
1592
+ end
1593
+ end
1594
+ ```
1595
+
1596
+ ---
1597
+
1598
+ ### Strategy
1599
+ **Problem:** Define a family of algorithms and make them interchangeable.
1600
+ **When to use:** When you have multiple ways to do something and want to switch at runtime.
1601
+
1602
+ ```ruby
1603
+ # Strategy interface
1604
+ class ShippingStrategy
1605
+ def calculate(package)
1606
+ raise NotImplementedError
1607
+ end
1608
+ end
1609
+
1610
+ # Concrete strategies
1611
+ class GroundShipping < ShippingStrategy
1612
+ def calculate(package)
1613
+ package.weight * 1.5
1614
+ end
1615
+ end
1616
+
1617
+ class AirShipping < ShippingStrategy
1618
+ def calculate(package)
1619
+ package.weight * 3.0
1620
+ end
1621
+ end
1622
+
1623
+ class ExpressShipping < ShippingStrategy
1624
+ def calculate(package)
1625
+ package.weight * 5.0 + 10.0
1626
+ end
1627
+ end
1628
+
1629
+ # Context
1630
+ class ShippingCalculator
1631
+ attr_accessor :strategy
1632
+
1633
+ def initialize(strategy)
1634
+ @strategy = strategy
1635
+ end
1636
+
1637
+ def calculate(package)
1638
+ @strategy.calculate(package)
1639
+ end
1640
+ end
1641
+
1642
+ # Usage
1643
+ package = OpenStruct.new(weight: 10)
1644
+ calculator = ShippingCalculator.new(GroundShipping.new)
1645
+ puts calculator.calculate(package) # => 15.0
1646
+
1647
+ calculator.strategy = ExpressShipping.new
1648
+ puts calculator.calculate(package) # => 60.0
1649
+ ```
1650
+
1651
+ **Rails Example:**
1652
+ ```ruby
1653
+ # app/strategies/pricing_strategy.rb
1654
+ class PricingStrategy
1655
+ def self.for(user)
1656
+ case user.membership
1657
+ when "premium"
1658
+ PremiumPricing.new
1659
+ when "wholesale"
1660
+ WholesalePricing.new
1661
+ else
1662
+ StandardPricing.new
1663
+ end
1664
+ end
1665
+ end
1666
+
1667
+ class StandardPricing
1668
+ def calculate(product)
1669
+ product.base_price
1670
+ end
1671
+ end
1672
+
1673
+ class PremiumPricing
1674
+ def calculate(product)
1675
+ product.base_price * 0.9 # 10% discount
1676
+ end
1677
+ end
1678
+
1679
+ class WholesalePricing
1680
+ def calculate(product)
1681
+ product.base_price * 0.7 # 30% discount
1682
+ end
1683
+ end
1684
+
1685
+ # Usage in service
1686
+ class CartService
1687
+ def total(user, cart)
1688
+ strategy = PricingStrategy.for(user)
1689
+ cart.items.sum { |item| strategy.calculate(item.product) * item.quantity }
1690
+ end
1691
+ end
1692
+ ```
1693
+
1694
+ ---
1695
+
1696
+ ### Template Method
1697
+ **Problem:** Define the skeleton of an algorithm and let subclasses override specific steps.
1698
+ **When to use:** When you have a common algorithm with varying implementations.
1699
+
1700
+ ```ruby
1701
+ # Abstract class with template method
1702
+ class DataExporter
1703
+ # Template method
1704
+ def export(data)
1705
+ validate(data)
1706
+ formatted = format(data)
1707
+ output = generate_output(formatted)
1708
+ save(output)
1709
+ notify
1710
+ end
1711
+
1712
+ private
1713
+
1714
+ def validate(data)
1715
+ raise ArgumentError, "Data cannot be empty" if data.empty?
1716
+ end
1717
+
1718
+ def format(data)
1719
+ raise NotImplementedError
1720
+ end
1721
+
1722
+ def generate_output(formatted)
1723
+ raise NotImplementedError
1724
+ end
1725
+
1726
+ def save(output)
1727
+ File.write(filename, output)
1728
+ end
1729
+
1730
+ def notify
1731
+ puts "Export completed: #{filename}"
1732
+ end
1733
+
1734
+ def filename
1735
+ raise NotImplementedError
1736
+ end
1737
+ end
1738
+
1739
+ # Concrete implementations
1740
+ class CsvExporter < DataExporter
1741
+ def format(data)
1742
+ data.map { |row| row.values }
1743
+ end
1744
+
1745
+ def generate_output(formatted)
1746
+ formatted.map { |row| row.join(",") }.join("\n")
1747
+ end
1748
+
1749
+ def filename
1750
+ "export.csv"
1751
+ end
1752
+ end
1753
+
1754
+ class JsonExporter < DataExporter
1755
+ def format(data)
1756
+ data
1757
+ end
1758
+
1759
+ def generate_output(formatted)
1760
+ JSON.pretty_generate(formatted)
1761
+ end
1762
+
1763
+ def filename
1764
+ "export.json"
1765
+ end
1766
+ end
1767
+
1768
+ # Usage
1769
+ data = [{ name: "Alice", age: 30 }, { name: "Bob", age: 25 }]
1770
+ CsvExporter.new.export(data)
1771
+ JsonExporter.new.export(data)
1772
+ ```
1773
+
1774
+ **Rails Example:**
1775
+ ```ruby
1776
+ # app/services/base_importer.rb
1777
+ class BaseImporter
1778
+ def import(file)
1779
+ records = parse(file)
1780
+ validate_records(records)
1781
+
1782
+ imported = 0
1783
+ records.each do |record|
1784
+ next unless valid?(record)
1785
+ create_record(record)
1786
+ imported += 1
1787
+ end
1788
+
1789
+ finalize(imported)
1790
+ end
1791
+
1792
+ private
1793
+
1794
+ def parse(file)
1795
+ raise NotImplementedError
1796
+ end
1797
+
1798
+ def validate_records(records)
1799
+ raise ImportError, "No records found" if records.empty?
1800
+ end
1801
+
1802
+ def valid?(record)
1803
+ true # Override in subclass
1804
+ end
1805
+
1806
+ def create_record(record)
1807
+ raise NotImplementedError
1808
+ end
1809
+
1810
+ def finalize(count)
1811
+ Rails.logger.info "Imported #{count} records"
1812
+ end
1813
+ end
1814
+
1815
+ class UserImporter < BaseImporter
1816
+ private
1817
+
1818
+ def parse(file)
1819
+ CSV.read(file, headers: true).map(&:to_h)
1820
+ end
1821
+
1822
+ def valid?(record)
1823
+ record["email"].present?
1824
+ end
1825
+
1826
+ def create_record(record)
1827
+ User.create!(
1828
+ email: record["email"],
1829
+ name: record["name"]
1830
+ )
1831
+ end
1832
+ end
1833
+ ```
1834
+
1835
+ ---
1836
+
1837
+ ### Visitor
1838
+ **Problem:** Add new operations to existing object structures without modifying them.
1839
+ **When to use:** When you need to perform many unrelated operations on objects.
1840
+
1841
+ ```ruby
1842
+ # Visitor interface
1843
+ class ShapeVisitor
1844
+ def visit_circle(circle)
1845
+ raise NotImplementedError
1846
+ end
1847
+
1848
+ def visit_rectangle(rectangle)
1849
+ raise NotImplementedError
1850
+ end
1851
+ end
1852
+
1853
+ # Concrete visitors
1854
+ class AreaCalculator < ShapeVisitor
1855
+ def visit_circle(circle)
1856
+ Math::PI * circle.radius ** 2
1857
+ end
1858
+
1859
+ def visit_rectangle(rectangle)
1860
+ rectangle.width * rectangle.height
1861
+ end
1862
+ end
1863
+
1864
+ class DrawingVisitor < ShapeVisitor
1865
+ def visit_circle(circle)
1866
+ puts "Drawing circle with radius #{circle.radius}"
1867
+ end
1868
+
1869
+ def visit_rectangle(rectangle)
1870
+ puts "Drawing rectangle #{rectangle.width}x#{rectangle.height}"
1871
+ end
1872
+ end
1873
+
1874
+ # Elements
1875
+ class Circle
1876
+ attr_reader :radius
1877
+
1878
+ def initialize(radius)
1879
+ @radius = radius
1880
+ end
1881
+
1882
+ def accept(visitor)
1883
+ visitor.visit_circle(self)
1884
+ end
1885
+ end
1886
+
1887
+ class Rectangle
1888
+ attr_reader :width, :height
1889
+
1890
+ def initialize(width, height)
1891
+ @width = width
1892
+ @height = height
1893
+ end
1894
+
1895
+ def accept(visitor)
1896
+ visitor.visit_rectangle(self)
1897
+ end
1898
+ end
1899
+
1900
+ # Usage
1901
+ shapes = [Circle.new(5), Rectangle.new(4, 6), Circle.new(3)]
1902
+
1903
+ area_calc = AreaCalculator.new
1904
+ total_area = shapes.sum { |shape| shape.accept(area_calc) }
1905
+ puts "Total area: #{total_area}"
1906
+
1907
+ drawer = DrawingVisitor.new
1908
+ shapes.each { |shape| shape.accept(drawer) }
1909
+ ```
1910
+
1911
+ **Rails Example:**
1912
+ ```ruby
1913
+ # Reporting visitor for different model types
1914
+ class ReportVisitor
1915
+ def visit_order(order)
1916
+ {
1917
+ type: "order",
1918
+ total: order.total,
1919
+ items: order.items.count
1920
+ }
1921
+ end
1922
+
1923
+ def visit_refund(refund)
1924
+ {
1925
+ type: "refund",
1926
+ amount: refund.amount,
1927
+ reason: refund.reason
1928
+ }
1929
+ end
1930
+
1931
+ def visit_subscription(subscription)
1932
+ {
1933
+ type: "subscription",
1934
+ plan: subscription.plan.name,
1935
+ mrr: subscription.monthly_amount
1936
+ }
1937
+ end
1938
+ end
1939
+
1940
+ # Models include visitable concern
1941
+ module Visitable
1942
+ def accept(visitor)
1943
+ method_name = "visit_#{self.class.name.underscore}"
1944
+ visitor.send(method_name, self)
1945
+ end
1946
+ end
1947
+
1948
+ class Order < ApplicationRecord
1949
+ include Visitable
1950
+ end
1951
+
1952
+ # Generate report
1953
+ visitor = ReportVisitor.new
1954
+ transactions = Order.all + Refund.all + Subscription.all
1955
+ report = transactions.map { |t| t.accept(visitor) }
1956
+ ```
1957
+
1958
+ ---
1959
+
1960
+ ## Pattern Selection Guide
1961
+
1962
+ | Problem | Pattern |
1963
+ |---------|---------|
1964
+ | Create objects without specifying class | Factory Method |
1965
+ | Create families of related objects | Abstract Factory |
1966
+ | Construct complex objects step by step | Builder |
1967
+ | Single instance with global access | Singleton |
1968
+ | Clone existing objects | Prototype |
1969
+ | Make incompatible interfaces work together | Adapter |
1970
+ | Separate abstraction from implementation | Bridge |
1971
+ | Tree structures with uniform interface | Composite |
1972
+ | Add behavior dynamically | Decorator |
1973
+ | Simplify complex subsystem | Facade |
1974
+ | Control access, lazy loading, caching | Proxy |
1975
+ | Pass request through handler chain | Chain of Responsibility |
1976
+ | Encapsulate request as object | Command |
1977
+ | Traverse collections uniformly | Iterator |
1978
+ | Reduce dependencies between objects | Mediator |
1979
+ | Capture and restore state | Memento |
1980
+ | Notify dependents of changes | Observer |
1981
+ | Behavior depends on state | State |
1982
+ | Interchangeable algorithms | Strategy |
1983
+ | Algorithm skeleton with customizable steps | Template Method |
1984
+ | Add operations without modifying objects | Visitor |