@cubis/foundry 0.3.71 → 0.3.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cli/core.js +4 -18
- package/dist/cli/core.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/core.ts +4 -18
- package/workflows/powers/accessibility/POWER.md +83 -94
- package/workflows/powers/accessibility/SKILL.md +82 -94
- package/workflows/powers/agent-design/POWER.md +201 -0
- package/workflows/powers/agent-design/SKILL.md +198 -0
- package/workflows/powers/agent-design/references/clarification-patterns.md +153 -0
- package/workflows/powers/agent-design/references/skill-testing.md +164 -0
- package/workflows/powers/agent-design/references/workflow-patterns.md +226 -0
- package/workflows/powers/agentic-eval/POWER.md +62 -0
- package/workflows/powers/agentic-eval/SKILL.md +59 -0
- package/workflows/powers/agentic-eval/references/rubric-and-regression-checklist.md +11 -0
- package/workflows/powers/api-designer/POWER.md +43 -71
- package/workflows/powers/api-designer/SKILL.md +43 -71
- package/workflows/powers/api-patterns/POWER.md +42 -56
- package/workflows/powers/api-patterns/SKILL.md +42 -57
- package/workflows/powers/architecture-designer/POWER.md +43 -60
- package/workflows/powers/architecture-designer/SKILL.md +43 -60
- package/workflows/powers/ask-questions-if-underspecified/POWER.md +51 -3
- package/workflows/powers/auth-architect/POWER.md +69 -0
- package/workflows/powers/auth-architect/SKILL.md +66 -0
- package/workflows/powers/auth-architect/references/session-token-policy-checklist.md +45 -0
- package/workflows/powers/behavioral-modes/POWER.md +100 -9
- package/workflows/powers/c-pro/POWER.md +105 -0
- package/workflows/powers/c-pro/SKILL.md +102 -0
- package/workflows/powers/c-pro/references/build-systems-and-toolchains.md +148 -0
- package/workflows/powers/c-pro/references/common-ub-and-portability.md +166 -0
- package/workflows/powers/c-pro/references/debugging-with-sanitizers.md +205 -0
- package/workflows/powers/c-pro/references/memory-safety-and-build-checklist.md +60 -0
- package/workflows/powers/c-pro/references/posix-and-platform-apis.md +244 -0
- package/workflows/powers/changelog-generator/POWER.md +127 -63
- package/workflows/powers/changelog-generator/SKILL.md +126 -63
- package/workflows/powers/ci-cd-pipelines/POWER.md +156 -0
- package/workflows/powers/ci-cd-pipelines/SKILL.md +153 -0
- package/workflows/powers/ci-cd-pipelines/references/github-actions-patterns.md +160 -0
- package/workflows/powers/ci-cd-pipelines/references/pipeline-security-checklist.md +57 -0
- package/workflows/powers/cli-developer/POWER.md +152 -95
- package/workflows/powers/cli-developer/SKILL.md +152 -95
- package/workflows/powers/cpp-pro/POWER.md +111 -0
- package/workflows/powers/cpp-pro/SKILL.md +108 -0
- package/workflows/powers/cpp-pro/references/concurrency-primitives.md +266 -0
- package/workflows/powers/cpp-pro/references/move-semantics-and-value-types.md +149 -0
- package/workflows/powers/cpp-pro/references/performance-and-profiling.md +191 -0
- package/workflows/powers/cpp-pro/references/raii-and-modern-cpp-checklist.md +87 -0
- package/workflows/powers/cpp-pro/references/template-and-concepts-patterns.md +205 -0
- package/workflows/powers/csharp-pro/POWER.md +47 -22
- package/workflows/powers/csharp-pro/SKILL.md +47 -22
- package/workflows/powers/dart-pro/POWER.md +68 -0
- package/workflows/powers/dart-pro/SKILL.md +65 -0
- package/workflows/powers/dart-pro/references/isolate-and-concurrency.md +180 -0
- package/workflows/powers/dart-pro/references/null-safety-and-async-patterns.md +133 -0
- package/workflows/powers/dart-pro/references/package-structure-and-linting.md +193 -0
- package/workflows/powers/dart-pro/references/sealed-records-patterns.md +173 -0
- package/workflows/powers/dart-pro/references/testing-and-mocking.md +235 -0
- package/workflows/powers/database-design/POWER.md +47 -33
- package/workflows/powers/database-design/SKILL.md +47 -33
- package/workflows/powers/database-optimizer/POWER.md +43 -64
- package/workflows/powers/database-optimizer/SKILL.md +43 -64
- package/workflows/powers/database-skills/POWER.md +59 -93
- package/workflows/powers/database-skills/SKILL.md +59 -93
- package/workflows/powers/debugging-strategies/POWER.md +69 -0
- package/workflows/powers/debugging-strategies/SKILL.md +66 -0
- package/workflows/powers/debugging-strategies/references/reproduce-isolate-verify-checklist.md +42 -0
- package/workflows/powers/deep-research/POWER.md +67 -0
- package/workflows/powers/deep-research/SKILL.md +64 -0
- package/workflows/powers/deep-research/references/multi-round-research-loop.md +80 -0
- package/workflows/powers/design-system-builder/POWER.md +130 -116
- package/workflows/powers/design-system-builder/SKILL.md +130 -116
- package/workflows/powers/devops-engineer/POWER.md +120 -57
- package/workflows/powers/devops-engineer/SKILL.md +120 -57
- package/workflows/powers/docker-kubernetes/POWER.md +94 -0
- package/workflows/powers/docker-kubernetes/SKILL.md +91 -0
- package/workflows/powers/docker-kubernetes/references/dockerfile-optimization-checklist.md +35 -0
- package/workflows/powers/docker-kubernetes/references/kubernetes-deployment-patterns.md +59 -0
- package/workflows/powers/documentation-templates/POWER.md +158 -127
- package/workflows/powers/documentation-templates/SKILL.md +158 -127
- package/workflows/powers/drizzle-expert/POWER.md +66 -0
- package/workflows/powers/drizzle-expert/SKILL.md +63 -0
- package/workflows/powers/drizzle-expert/references/runtime-pairing-matrix.md +16 -0
- package/workflows/powers/drizzle-expert/references/schema-and-migration-playbook.md +18 -0
- package/workflows/powers/error-ux-observability/POWER.md +144 -131
- package/workflows/powers/error-ux-observability/SKILL.md +143 -131
- package/workflows/powers/fastapi-expert/POWER.md +46 -60
- package/workflows/powers/fastapi-expert/SKILL.md +46 -60
- package/workflows/powers/firebase/POWER.md +65 -0
- package/workflows/powers/firebase/SKILL.md +62 -0
- package/workflows/powers/firebase/references/platform-routing.md +16 -0
- package/workflows/powers/firebase/references/rules-and-indexes-checklist.md +11 -0
- package/workflows/powers/flutter-design-system/POWER.md +63 -0
- package/workflows/powers/flutter-design-system/SKILL.md +60 -0
- package/workflows/powers/flutter-design-system/references/shared-widgets.md +29 -0
- package/workflows/powers/flutter-design-system/references/tokens-and-theme.md +34 -0
- package/workflows/powers/flutter-drift/POWER.md +65 -0
- package/workflows/powers/flutter-drift/SKILL.md +62 -0
- package/workflows/powers/flutter-drift/references/migrations.md +22 -0
- package/workflows/powers/flutter-drift/references/query-patterns.md +26 -0
- package/workflows/powers/flutter-feature/POWER.md +65 -0
- package/workflows/powers/flutter-feature/SKILL.md +62 -0
- package/workflows/powers/flutter-feature/references/architecture-rules.md +85 -0
- package/workflows/powers/flutter-feature/references/composite-provider.md +58 -0
- package/workflows/powers/flutter-feature/references/outbox-pattern.md +87 -0
- package/workflows/powers/flutter-feature/references/testing-patterns.md +218 -0
- package/workflows/powers/flutter-go-router/POWER.md +64 -0
- package/workflows/powers/flutter-go-router/SKILL.md +61 -0
- package/workflows/powers/flutter-go-router/references/guards-and-deeplinks.md +20 -0
- package/workflows/powers/flutter-go-router/references/typed-routes.md +27 -0
- package/workflows/powers/flutter-offline-sync/POWER.md +62 -0
- package/workflows/powers/flutter-offline-sync/SKILL.md +59 -0
- package/workflows/powers/flutter-offline-sync/references/outbox-full.md +44 -0
- package/workflows/powers/flutter-repository/POWER.md +64 -0
- package/workflows/powers/flutter-repository/SKILL.md +61 -0
- package/workflows/powers/flutter-repository/references/drift-patterns.md +21 -0
- package/workflows/powers/flutter-repository/references/retrofit-patterns.md +20 -0
- package/workflows/powers/flutter-riverpod/POWER.md +70 -0
- package/workflows/powers/flutter-riverpod/SKILL.md +67 -0
- package/workflows/powers/flutter-riverpod/references/async-and-mutations.md +19 -0
- package/workflows/powers/flutter-riverpod/references/async-lifecycle.md +19 -0
- package/workflows/powers/flutter-riverpod/references/provider-selection.md +20 -0
- package/workflows/powers/flutter-riverpod/references/testing.md +21 -0
- package/workflows/powers/flutter-riverpod/references/version-matrix.md +24 -0
- package/workflows/powers/flutter-state-machine/POWER.md +62 -0
- package/workflows/powers/flutter-state-machine/SKILL.md +59 -0
- package/workflows/powers/flutter-state-machine/references/app-state-contract.md +23 -0
- package/workflows/powers/flutter-state-machine/references/ui-rendering.md +14 -0
- package/workflows/powers/flutter-testing/POWER.md +64 -0
- package/workflows/powers/flutter-testing/SKILL.md +61 -0
- package/workflows/powers/flutter-testing/references/offline-sync-tests.md +16 -0
- package/workflows/powers/flutter-testing/references/test-layers.md +33 -0
- package/workflows/powers/frontend-code-review/POWER.md +137 -0
- package/workflows/powers/frontend-code-review/SKILL.md +134 -0
- package/workflows/powers/frontend-code-review/references/common-antipatterns.md +86 -0
- package/workflows/powers/frontend-code-review/references/performance-budgets.md +56 -0
- package/workflows/powers/frontend-code-review/references/review-checklists.md +47 -0
- package/workflows/powers/frontend-design/POWER.md +163 -362
- package/workflows/powers/frontend-design/SKILL.md +163 -362
- package/workflows/powers/game-development/POWER.md +57 -140
- package/workflows/powers/game-development/SKILL.md +57 -140
- package/workflows/powers/geo-fundamentals/POWER.md +64 -126
- package/workflows/powers/geo-fundamentals/SKILL.md +64 -127
- package/workflows/powers/git-workflow/POWER.md +135 -0
- package/workflows/powers/git-workflow/SKILL.md +132 -0
- package/workflows/powers/git-workflow/references/pr-review-checklist.md +63 -0
- package/workflows/powers/golang-pro/POWER.md +46 -35
- package/workflows/powers/golang-pro/SKILL.md +46 -35
- package/workflows/powers/graphql-architect/POWER.md +44 -62
- package/workflows/powers/graphql-architect/SKILL.md +44 -62
- package/workflows/powers/i18n-localization/POWER.md +118 -103
- package/workflows/powers/i18n-localization/SKILL.md +118 -103
- package/workflows/powers/java-pro/POWER.md +47 -22
- package/workflows/powers/java-pro/SKILL.md +47 -22
- package/workflows/powers/javascript-pro/POWER.md +47 -34
- package/workflows/powers/javascript-pro/SKILL.md +47 -34
- package/workflows/powers/kotlin-pro/POWER.md +46 -23
- package/workflows/powers/kotlin-pro/SKILL.md +46 -23
- package/workflows/powers/legacy-modernizer/POWER.md +43 -60
- package/workflows/powers/legacy-modernizer/SKILL.md +43 -60
- package/workflows/powers/mcp-builder/POWER.md +65 -0
- package/workflows/powers/mcp-builder/SKILL.md +62 -0
- package/workflows/powers/mcp-builder/references/testing-and-evals.md +17 -0
- package/workflows/powers/mcp-builder/references/transport-and-tool-design.md +17 -0
- package/workflows/powers/microservices-architect/POWER.md +43 -70
- package/workflows/powers/microservices-architect/SKILL.md +43 -70
- package/workflows/powers/mobile-design/POWER.md +110 -345
- package/workflows/powers/mobile-design/SKILL.md +110 -345
- package/workflows/powers/mongodb/POWER.md +67 -0
- package/workflows/powers/mongodb/SKILL.md +64 -0
- package/workflows/powers/mongodb/references/mongodb-checklist.md +20 -0
- package/workflows/powers/mysql/POWER.md +67 -0
- package/workflows/powers/mysql/SKILL.md +64 -0
- package/workflows/powers/mysql/references/mysql-checklist.md +20 -0
- package/workflows/powers/neki/POWER.md +67 -0
- package/workflows/powers/neki/SKILL.md +64 -0
- package/workflows/powers/neki/references/neki-checklist.md +18 -0
- package/workflows/powers/nestjs-expert/POWER.md +45 -91
- package/workflows/powers/nestjs-expert/SKILL.md +45 -91
- package/workflows/powers/nextjs-developer/POWER.md +51 -44
- package/workflows/powers/nextjs-developer/SKILL.md +51 -44
- package/workflows/powers/nodejs-best-practices/POWER.md +48 -29
- package/workflows/powers/nodejs-best-practices/SKILL.md +48 -29
- package/workflows/powers/observability/POWER.md +109 -0
- package/workflows/powers/observability/SKILL.md +106 -0
- package/workflows/powers/observability/references/alerting-and-slo-checklist.md +87 -0
- package/workflows/powers/observability/references/opentelemetry-setup-guide.md +121 -0
- package/workflows/powers/openai-docs/POWER.md +61 -0
- package/workflows/powers/openai-docs/SKILL.md +58 -0
- package/workflows/powers/openai-docs/references/official-source-playbook.md +10 -0
- package/workflows/powers/performance-profiling/POWER.md +61 -114
- package/workflows/powers/performance-profiling/SKILL.md +61 -114
- package/workflows/powers/php-pro/POWER.md +116 -0
- package/workflows/powers/php-pro/SKILL.md +113 -0
- package/workflows/powers/php-pro/references/architecture-and-di.md +239 -0
- package/workflows/powers/php-pro/references/modern-php-features.md +189 -0
- package/workflows/powers/php-pro/references/performance-and-deployment.md +197 -0
- package/workflows/powers/php-pro/references/php84-strict-typing-checklist.md +161 -0
- package/workflows/powers/php-pro/references/testing-and-static-analysis.md +235 -0
- package/workflows/powers/playwright-e2e/POWER.md +85 -0
- package/workflows/powers/playwright-e2e/SKILL.md +82 -0
- package/workflows/powers/playwright-e2e/references/locator-trace-flake-checklist.md +80 -0
- package/workflows/powers/postgres/POWER.md +67 -0
- package/workflows/powers/postgres/SKILL.md +64 -0
- package/workflows/powers/postgres/references/postgres-checklist.md +20 -0
- package/workflows/powers/prompt-engineer/POWER.md +47 -30
- package/workflows/powers/prompt-engineer/SKILL.md +47 -30
- package/workflows/powers/python-pro/POWER.md +47 -36
- package/workflows/powers/python-pro/SKILL.md +47 -36
- package/workflows/powers/react-best-practices/POWER.md +56 -33
- package/workflows/powers/react-best-practices/SKILL.md +56 -33
- package/workflows/powers/react-expert/POWER.md +47 -37
- package/workflows/powers/react-expert/SKILL.md +47 -37
- package/workflows/powers/redis/POWER.md +67 -0
- package/workflows/powers/redis/SKILL.md +64 -0
- package/workflows/powers/redis/references/redis-checklist.md +19 -0
- package/workflows/powers/ruby-pro/POWER.md +118 -0
- package/workflows/powers/ruby-pro/SKILL.md +115 -0
- package/workflows/powers/ruby-pro/references/modern-ruby-features.md +189 -0
- package/workflows/powers/ruby-pro/references/object-design-patterns.md +220 -0
- package/workflows/powers/ruby-pro/references/performance-and-profiling.md +224 -0
- package/workflows/powers/ruby-pro/references/ruby-concurrency-and-testing.md +190 -0
- package/workflows/powers/ruby-pro/references/testing-and-rspec.md +236 -0
- package/workflows/powers/rust-pro/POWER.md +45 -31
- package/workflows/powers/rust-pro/SKILL.md +45 -31
- package/workflows/powers/security-engineer/POWER.md +129 -0
- package/workflows/powers/security-engineer/SKILL.md +126 -0
- package/workflows/powers/seo-fundamentals/POWER.md +59 -102
- package/workflows/powers/seo-fundamentals/SKILL.md +59 -102
- package/workflows/powers/serverless-patterns/POWER.md +171 -0
- package/workflows/powers/serverless-patterns/SKILL.md +168 -0
- package/workflows/powers/skill-creator/POWER.md +90 -0
- package/workflows/powers/skill-creator/SKILL.md +87 -0
- package/workflows/powers/skill-creator/references/platform-formats.md +181 -0
- package/workflows/powers/skill-creator/references/schemas.md +430 -0
- package/workflows/powers/spec-miner/POWER.md +49 -57
- package/workflows/powers/spec-miner/SKILL.md +49 -57
- package/workflows/powers/sqlite/POWER.md +67 -0
- package/workflows/powers/sqlite/SKILL.md +64 -0
- package/workflows/powers/sqlite/references/sqlite-checklist.md +19 -0
- package/workflows/powers/sre-engineer/POWER.md +123 -64
- package/workflows/powers/sre-engineer/SKILL.md +123 -64
- package/workflows/powers/static-analysis/POWER.md +121 -77
- package/workflows/powers/static-analysis/SKILL.md +121 -77
- package/workflows/powers/stripe-best-practices/POWER.md +140 -17
- package/workflows/powers/stripe-best-practices/SKILL.md +139 -17
- package/workflows/powers/supabase/POWER.md +67 -0
- package/workflows/powers/supabase/SKILL.md +64 -0
- package/workflows/powers/supabase/references/supabase-checklist.md +19 -0
- package/workflows/powers/swift-pro/POWER.md +118 -0
- package/workflows/powers/swift-pro/SKILL.md +115 -0
- package/workflows/powers/swift-pro/references/concurrency-patterns.md +165 -0
- package/workflows/powers/swift-pro/references/protocol-and-generics.md +172 -0
- package/workflows/powers/swift-pro/references/sendable-and-isolation.md +116 -0
- package/workflows/powers/swift-pro/references/swift-concurrency-and-protocols.md +260 -0
- package/workflows/powers/swift-pro/references/testing-and-packages.md +192 -0
- package/workflows/powers/tailwind-patterns/POWER.md +71 -240
- package/workflows/powers/tailwind-patterns/SKILL.md +71 -240
- package/workflows/powers/testing-patterns/POWER.md +155 -10
- package/workflows/powers/testing-patterns/SKILL.md +155 -10
- package/workflows/powers/typescript-pro/POWER.md +47 -38
- package/workflows/powers/typescript-pro/SKILL.md +47 -38
- package/workflows/powers/vitess/POWER.md +67 -0
- package/workflows/powers/vitess/SKILL.md +64 -0
- package/workflows/powers/vitess/references/vitess-checklist.md +19 -0
- package/workflows/powers/vulnerability-scanner/POWER.md +146 -10
- package/workflows/powers/vulnerability-scanner/SKILL.md +146 -10
- package/workflows/powers/web-perf/POWER.md +43 -170
- package/workflows/powers/web-perf/SKILL.md +43 -170
- package/workflows/powers/webapp-testing/POWER.md +43 -164
- package/workflows/powers/webapp-testing/SKILL.md +43 -164
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Modern Ruby Features
|
|
2
|
+
|
|
3
|
+
## Pattern Matching (Ruby 3.0+)
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
# case/in pattern matching
|
|
7
|
+
case response
|
|
8
|
+
in { status: 200, body: { data: Array => items } }
|
|
9
|
+
process_items(items)
|
|
10
|
+
in { status: 200, body: { data: Hash => item } }
|
|
11
|
+
process_item(item)
|
|
12
|
+
in { status: 404 }
|
|
13
|
+
raise NotFoundError
|
|
14
|
+
in { status: (500..) }
|
|
15
|
+
raise ServerError, "Status: #{response[:status]}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Find pattern (Ruby 3.0) — match element in array
|
|
19
|
+
case users
|
|
20
|
+
in [*, { role: "admin", name: String => admin_name }, *]
|
|
21
|
+
puts "Found admin: #{admin_name}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Pin operator — match against existing variable
|
|
25
|
+
expected_status = 200
|
|
26
|
+
case response
|
|
27
|
+
in { status: ^expected_status }
|
|
28
|
+
puts "OK"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Rightward assignment with pattern
|
|
32
|
+
response => { status:, body: { data: } }
|
|
33
|
+
# status and data are now local variables
|
|
34
|
+
|
|
35
|
+
# Guard clauses
|
|
36
|
+
case user
|
|
37
|
+
in { age: (18..) => age } if age < 65
|
|
38
|
+
puts "Working age: #{age}"
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Data.define (Ruby 3.2+)
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
# Immutable value objects — replacement for Struct
|
|
46
|
+
Point = Data.define(:x, :y)
|
|
47
|
+
point = Point.new(x: 1, y: 2)
|
|
48
|
+
point.x # => 1
|
|
49
|
+
point.frozen? # => true
|
|
50
|
+
# point.x = 3 # NoMethodError — immutable
|
|
51
|
+
|
|
52
|
+
# With custom methods
|
|
53
|
+
Money = Data.define(:amount, :currency) do
|
|
54
|
+
def to_s = "#{format('%.2f', amount / 100.0)} #{currency}"
|
|
55
|
+
|
|
56
|
+
def +(other)
|
|
57
|
+
raise ArgumentError, "Currency mismatch" unless currency == other.currency
|
|
58
|
+
Money.new(amount: amount + other.amount, currency: currency)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
price = Money.new(amount: 1999, currency: "USD")
|
|
63
|
+
tax = Money.new(amount: 160, currency: "USD")
|
|
64
|
+
total = price + tax # Money(amount: 2159, currency: "USD")
|
|
65
|
+
|
|
66
|
+
# Pattern matching with Data
|
|
67
|
+
case point
|
|
68
|
+
in Point[x: 0, y: 0]
|
|
69
|
+
"origin"
|
|
70
|
+
in Point[x: 0, y:]
|
|
71
|
+
"on y-axis at #{y}"
|
|
72
|
+
in Point[x:, y: 0]
|
|
73
|
+
"on x-axis at #{x}"
|
|
74
|
+
in Point[x:, y:]
|
|
75
|
+
"(#{x}, #{y})"
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Numbered Block Parameters (Ruby 2.7+)
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# _1, _2, etc. for simple blocks
|
|
83
|
+
[1, 2, 3].map { _1 * 2 } # => [2, 4, 6]
|
|
84
|
+
{ a: 1, b: 2 }.map { "#{_1}=#{_2}" } # => ["a=1", "b=2"]
|
|
85
|
+
|
|
86
|
+
# Best for simple one-liners; use named params for complex blocks
|
|
87
|
+
users.select { _1.active? && _1.age >= 18 }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Endless Method Definition (Ruby 3.0+)
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# Single-expression methods without end
|
|
94
|
+
def full_name = "#{first_name} #{last_name}"
|
|
95
|
+
def adult? = age >= 18
|
|
96
|
+
def area = Math::PI * radius**2
|
|
97
|
+
|
|
98
|
+
# With arguments
|
|
99
|
+
def greet(name) = "Hello, #{name}!"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Hash Shorthand (Ruby 3.1+)
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
# Omit value when key and variable name match
|
|
106
|
+
name = "Alice"
|
|
107
|
+
age = 30
|
|
108
|
+
role = :admin
|
|
109
|
+
|
|
110
|
+
# Before
|
|
111
|
+
user = { name: name, age: age, role: role }
|
|
112
|
+
|
|
113
|
+
# After — shorthand
|
|
114
|
+
user = { name:, age:, role: }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Enumerator::Product and Composable Enumerators
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
# Enumerator.product (Ruby 3.2+)
|
|
121
|
+
colors = [:red, :blue]
|
|
122
|
+
sizes = [:s, :m, :l]
|
|
123
|
+
Enumerator::Product.new(colors, sizes).each do |color, size|
|
|
124
|
+
puts "#{color}-#{size}" # red-s, red-m, red-l, blue-s, blue-m, blue-l
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Lazy enumerators for large/infinite sequences
|
|
128
|
+
(1..).lazy
|
|
129
|
+
.select { _1.odd? }
|
|
130
|
+
.map { _1 ** 2 }
|
|
131
|
+
.first(5) # => [1, 9, 25, 49, 81]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Frozen String Literals
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# frozen_string_literal: true
|
|
138
|
+
|
|
139
|
+
# All string literals are frozen (immutable) — less GC pressure
|
|
140
|
+
greeting = "hello"
|
|
141
|
+
greeting << " world" # FrozenError raised
|
|
142
|
+
|
|
143
|
+
# Explicit mutable strings
|
|
144
|
+
mutable = String.new("hello")
|
|
145
|
+
mutable << " world" # OK
|
|
146
|
+
|
|
147
|
+
# Or duplicate
|
|
148
|
+
mutable = "hello".dup
|
|
149
|
+
mutable << " world" # OK
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Refinements
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
# Safe monkey-patching — scoped to file/module
|
|
156
|
+
module StringExtensions
|
|
157
|
+
refine String do
|
|
158
|
+
def to_slug
|
|
159
|
+
downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Only active where explicitly used
|
|
165
|
+
using StringExtensions
|
|
166
|
+
"Hello World!".to_slug # => "hello-world"
|
|
167
|
+
# Outside this file, String#to_slug doesn't exist
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Error Handling Improvements
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
# Exception#detailed_message (Ruby 3.2+)
|
|
174
|
+
class ApiError < StandardError
|
|
175
|
+
def initialize(status, body)
|
|
176
|
+
@status = status
|
|
177
|
+
@body = body
|
|
178
|
+
super("API error: #{status}")
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def detailed_message(highlight: false, **)
|
|
182
|
+
msg = "API Error #{@status}: #{@body.first(200)}"
|
|
183
|
+
highlight ? "\e[1;31m#{msg}\e[0m" : msg
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Syntax sugar for rescue in blocks
|
|
188
|
+
result = Integer("abc") rescue nil # inline rescue (use sparingly)
|
|
189
|
+
```
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Object Design Patterns
|
|
2
|
+
|
|
3
|
+
## Service Objects
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
# Single-purpose service with call interface
|
|
7
|
+
class CreateOrder
|
|
8
|
+
def initialize(order_repo:, pricing:, notifier:)
|
|
9
|
+
@order_repo = order_repo
|
|
10
|
+
@pricing = pricing
|
|
11
|
+
@notifier = notifier
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(customer:, items:)
|
|
15
|
+
total = @pricing.calculate(items)
|
|
16
|
+
order = Order.new(customer:, items:, total:)
|
|
17
|
+
|
|
18
|
+
@order_repo.save(order)
|
|
19
|
+
@notifier.order_created(order)
|
|
20
|
+
|
|
21
|
+
Result.success(order)
|
|
22
|
+
rescue Pricing::Error => e
|
|
23
|
+
Result.failure(e.message)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Usage
|
|
28
|
+
result = CreateOrder.new(
|
|
29
|
+
order_repo: OrderRepository.new,
|
|
30
|
+
pricing: PricingService.new,
|
|
31
|
+
notifier: EmailNotifier.new,
|
|
32
|
+
).call(customer: current_user, items: cart_items)
|
|
33
|
+
|
|
34
|
+
case result
|
|
35
|
+
in Result[success: true, value: order]
|
|
36
|
+
render json: order, status: :created
|
|
37
|
+
in Result[success: false, error: message]
|
|
38
|
+
render json: { error: message }, status: :unprocessable_entity
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Value Objects with Data.define
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
Email = Data.define(:address) do
|
|
46
|
+
def initialize(address:)
|
|
47
|
+
raise ArgumentError, "Invalid email" unless address.match?(/\A[^@]+@[^@]+\z/)
|
|
48
|
+
super(address: address.downcase.strip)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def domain = address.split("@").last
|
|
52
|
+
def to_s = address
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
DateRange = Data.define(:start_date, :end_date) do
|
|
56
|
+
def initialize(start_date:, end_date:)
|
|
57
|
+
raise ArgumentError, "Start must be before end" if start_date > end_date
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def days = (end_date - start_date).to_i
|
|
62
|
+
def include?(date) = (start_date..end_date).cover?(date)
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Result Monad
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
# Simple Result type for expected failures
|
|
70
|
+
class Result
|
|
71
|
+
attr_reader :value, :error
|
|
72
|
+
|
|
73
|
+
def initialize(success:, value: nil, error: nil)
|
|
74
|
+
@success = success
|
|
75
|
+
@value = value
|
|
76
|
+
@error = error
|
|
77
|
+
freeze
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.success(value) = new(success: true, value:)
|
|
81
|
+
def self.failure(error) = new(success: false, error:)
|
|
82
|
+
|
|
83
|
+
def success? = @success
|
|
84
|
+
def failure? = !@success
|
|
85
|
+
|
|
86
|
+
def map
|
|
87
|
+
return self if failure?
|
|
88
|
+
Result.success(yield(value))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def flat_map
|
|
92
|
+
return self if failure?
|
|
93
|
+
yield(value)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Pattern matching support
|
|
97
|
+
def deconstruct_keys(keys)
|
|
98
|
+
{ success: @success, value: @value, error: @error }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Chainable operations
|
|
103
|
+
result = validate_input(params)
|
|
104
|
+
.flat_map { |input| find_user(input[:user_id]) }
|
|
105
|
+
.flat_map { |user| authorize(user, :create_order) }
|
|
106
|
+
.flat_map { |user| create_order(user, params[:items]) }
|
|
107
|
+
.map { |order| OrderSerializer.new(order).as_json }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Query Objects
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# Encapsulate complex database queries
|
|
114
|
+
class ActiveOrdersQuery
|
|
115
|
+
def initialize(scope = Order.all)
|
|
116
|
+
@scope = scope
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def call(filters = {})
|
|
120
|
+
result = @scope.where(status: :active)
|
|
121
|
+
|
|
122
|
+
result = result.where(customer_id: filters[:customer_id]) if filters[:customer_id]
|
|
123
|
+
result = result.where("total >= ?", filters[:min_total]) if filters[:min_total]
|
|
124
|
+
result = result.where("created_at >= ?", filters[:since]) if filters[:since]
|
|
125
|
+
result = result.order(created_at: :desc)
|
|
126
|
+
|
|
127
|
+
result
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Usage
|
|
132
|
+
orders = ActiveOrdersQuery.new.call(
|
|
133
|
+
customer_id: 42,
|
|
134
|
+
min_total: 100,
|
|
135
|
+
since: 30.days.ago,
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Composition Over Inheritance
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
# BAD — deep inheritance
|
|
143
|
+
class AdminUser < PremiumUser < User < BaseModel
|
|
144
|
+
|
|
145
|
+
# GOOD — modules for shared behavior
|
|
146
|
+
module Authenticatable
|
|
147
|
+
def authenticate(password)
|
|
148
|
+
BCrypt::Password.new(password_digest) == password
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
module Auditable
|
|
153
|
+
def self.included(base)
|
|
154
|
+
base.before_save :record_audit_trail
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def record_audit_trail
|
|
160
|
+
AuditLog.record(self, changes)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class User
|
|
165
|
+
include Authenticatable
|
|
166
|
+
include Auditable
|
|
167
|
+
|
|
168
|
+
# User-specific behavior
|
|
169
|
+
end
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Decorator Pattern
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
# SimpleDelegator for transparent decoration
|
|
176
|
+
class LoggingOrderService < SimpleDelegator
|
|
177
|
+
def initialize(service, logger:)
|
|
178
|
+
super(service)
|
|
179
|
+
@logger = logger
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def create(params)
|
|
183
|
+
@logger.info("Creating order: #{params.inspect}")
|
|
184
|
+
result = super
|
|
185
|
+
@logger.info("Order created: #{result.value&.id}")
|
|
186
|
+
result
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Wrap the real service
|
|
191
|
+
service = LoggingOrderService.new(
|
|
192
|
+
CreateOrder.new(order_repo:, pricing:, notifier:),
|
|
193
|
+
logger: Rails.logger,
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Policy Objects
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
# Encapsulate authorization logic
|
|
201
|
+
class OrderPolicy
|
|
202
|
+
def initialize(user, order)
|
|
203
|
+
@user = user
|
|
204
|
+
@order = order
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def show? = owner? || admin?
|
|
208
|
+
def update? = owner? && @order.pending?
|
|
209
|
+
def cancel? = (owner? || admin?) && @order.cancellable?
|
|
210
|
+
def refund? = admin? && @order.paid?
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
def owner? = @user.id == @order.customer_id
|
|
215
|
+
def admin? = @user.admin?
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Usage
|
|
219
|
+
raise Forbidden unless OrderPolicy.new(current_user, order).cancel?
|
|
220
|
+
```
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Performance and Profiling
|
|
2
|
+
|
|
3
|
+
## Profiling Workflow
|
|
4
|
+
|
|
5
|
+
1. **Reproduce** — identify the slow path with real workload or representative benchmark.
|
|
6
|
+
2. **Profile** — CPU time, memory allocations, or GC pressure depending on the symptom.
|
|
7
|
+
3. **Identify hotspot** — focus on the top contributor, not scattered micro-optimizations.
|
|
8
|
+
4. **Change one thing** — benchmark before and after.
|
|
9
|
+
5. **Verify correctness** — run tests after every optimization.
|
|
10
|
+
|
|
11
|
+
## CPU Profiling with Stackprof
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Gemfile
|
|
15
|
+
gem "stackprof", group: [:development, :test]
|
|
16
|
+
|
|
17
|
+
# Sample CPU usage
|
|
18
|
+
StackProf.run(mode: :cpu, out: "tmp/stackprof-cpu.dump") do
|
|
19
|
+
perform_expensive_operation
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# View results
|
|
23
|
+
# stackprof tmp/stackprof-cpu.dump --text
|
|
24
|
+
# stackprof tmp/stackprof-cpu.dump --method 'OrderService#calculate'
|
|
25
|
+
# stackprof tmp/stackprof-cpu.dump --flamegraph > tmp/flamegraph.json
|
|
26
|
+
|
|
27
|
+
# Wall-clock profiling (includes I/O wait time)
|
|
28
|
+
StackProf.run(mode: :wall, interval: 1000) do
|
|
29
|
+
fetch_external_data
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Object allocation profiling
|
|
33
|
+
StackProf.run(mode: :object) do
|
|
34
|
+
process_batch(items)
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Memory Profiling
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# Gemfile
|
|
42
|
+
gem "memory_profiler", group: [:development, :test]
|
|
43
|
+
|
|
44
|
+
# Profile memory allocations
|
|
45
|
+
report = MemoryProfiler.report do
|
|
46
|
+
100.times { build_response(data) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
report.pretty_print
|
|
50
|
+
# Shows: allocated memory by gem, file, location, class
|
|
51
|
+
# Retained memory (memory that survived GC)
|
|
52
|
+
|
|
53
|
+
# Derailed benchmarks for Rails boot and request memory
|
|
54
|
+
# gem "derailed_benchmarks"
|
|
55
|
+
# bundle exec derailed bundle:mem # memory at boot
|
|
56
|
+
# bundle exec derailed exec perf:mem # memory per request
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Benchmarking
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
require "benchmark/ips"
|
|
63
|
+
|
|
64
|
+
Benchmark.ips do |x|
|
|
65
|
+
data = (1..1000).to_a
|
|
66
|
+
|
|
67
|
+
x.report("each + push") do
|
|
68
|
+
result = []
|
|
69
|
+
data.each { |i| result.push(i * 2) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
x.report("map") do
|
|
73
|
+
data.map { |i| i * 2 }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
x.report("map with _1") do
|
|
77
|
+
data.map { _1 * 2 }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
x.compare!
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## YJIT (Ruby 3.2+)
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Enable YJIT at startup — significant performance improvement
|
|
88
|
+
ruby --yjit app.rb
|
|
89
|
+
|
|
90
|
+
# Or via environment variable
|
|
91
|
+
RUBY_YJIT_ENABLE=1 bundle exec rails server
|
|
92
|
+
|
|
93
|
+
# YJIT stats (development)
|
|
94
|
+
ruby --yjit-stats app.rb
|
|
95
|
+
# Shows: compiled methods, inline cache hits, code size
|
|
96
|
+
|
|
97
|
+
# In Puma/Unicorn config
|
|
98
|
+
# YJIT is enabled by default in Ruby 3.3+ for processes with enough memory
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
# Check YJIT status at runtime
|
|
103
|
+
RubyVM::YJIT.enabled? # => true/false
|
|
104
|
+
RubyVM::YJIT.stats # hash of compilation stats
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## GC Tuning
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Environment variables for GC tuning
|
|
111
|
+
RUBY_GC_HEAP_INIT_SLOTS=600000 # initial heap size (reduce GC early in boot)
|
|
112
|
+
RUBY_GC_HEAP_FREE_SLOTS_MIN_RATIO=0.20
|
|
113
|
+
RUBY_GC_HEAP_FREE_SLOTS_MAX_RATIO=0.40
|
|
114
|
+
|
|
115
|
+
# Malloc-based GC tuning
|
|
116
|
+
RUBY_GC_MALLOC_LIMIT=128000000 # 128MB before major GC
|
|
117
|
+
RUBY_GC_OLDMALLOC_LIMIT=128000000
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
# Monitor GC in production
|
|
122
|
+
GC.stat
|
|
123
|
+
# => { count: 42, heap_allocated_pages: 150, total_allocated_objects: 5000000, ... }
|
|
124
|
+
|
|
125
|
+
# Disable GC during critical sections (use sparingly)
|
|
126
|
+
GC.disable
|
|
127
|
+
critical_operation
|
|
128
|
+
GC.enable
|
|
129
|
+
GC.start # trigger GC at convenient point
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## String Optimization
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# frozen_string_literal: true
|
|
136
|
+
|
|
137
|
+
# Frozen strings are deduplicated — one object for identical literals
|
|
138
|
+
greeting = "hello" # same object every time this line runs
|
|
139
|
+
|
|
140
|
+
# String interpolation creates new strings — cache when reused
|
|
141
|
+
# BAD — new string every iteration
|
|
142
|
+
items.each do |item|
|
|
143
|
+
cache_key = "item:#{item.id}:data" # new string each time
|
|
144
|
+
cache.get(cache_key)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# BETTER — use frozen format
|
|
148
|
+
FORMAT = "item:%d:data"
|
|
149
|
+
items.each do |item|
|
|
150
|
+
cache_key = format(FORMAT, item.id)
|
|
151
|
+
cache.get(cache_key)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Use symbols for hash keys — deduplicated by runtime
|
|
155
|
+
# GOOD: { name: "Alice" }
|
|
156
|
+
# BAD: { "name" => "Alice" } # string key allocated each time
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Enumerable Patterns
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# Prefer Enumerable methods over manual loops
|
|
163
|
+
|
|
164
|
+
# Lazy evaluation for large collections
|
|
165
|
+
File.open("huge.csv").each_line
|
|
166
|
+
.lazy
|
|
167
|
+
.reject { _1.start_with?("#") }
|
|
168
|
+
.map { _1.strip.split(",") }
|
|
169
|
+
.select { |cols| cols[2].to_i > 100 }
|
|
170
|
+
.first(10)
|
|
171
|
+
|
|
172
|
+
# each_with_object for building results
|
|
173
|
+
totals = orders.each_with_object(Hash.new(0)) do |order, sums|
|
|
174
|
+
sums[order.category] += order.total
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# chunk/slice for batch processing
|
|
178
|
+
large_dataset.each_slice(1000) do |batch|
|
|
179
|
+
Model.insert_all(batch.map(&:attributes))
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# tally for counting
|
|
183
|
+
["a", "b", "a", "c", "b", "a"].tally
|
|
184
|
+
# => {"a" => 3, "b" => 2, "c" => 1}
|
|
185
|
+
|
|
186
|
+
# filter_map — map + compact in one pass
|
|
187
|
+
users.filter_map { |u| u.email if u.active? }
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Caching
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
# Memoization — instance-level caching
|
|
194
|
+
def expensive_result
|
|
195
|
+
@expensive_result ||= compute_expensive_thing
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Memoize nil/false safely
|
|
199
|
+
def nullable_result
|
|
200
|
+
return @nullable_result if defined?(@nullable_result)
|
|
201
|
+
@nullable_result = potentially_nil_computation
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Application-level caching
|
|
205
|
+
Rails.cache.fetch("user:#{id}:profile", expires_in: 1.hour) do
|
|
206
|
+
UserProfile.new(user).compute
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Cache key fingerprinting
|
|
210
|
+
def cache_key
|
|
211
|
+
[self.class.name, id, updated_at.to_i].join("/")
|
|
212
|
+
end
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Common Anti-Patterns
|
|
216
|
+
|
|
217
|
+
| Anti-pattern | Impact | Fix |
|
|
218
|
+
| ------------------------------ | --------------------------- | ---------------------------------------- |
|
|
219
|
+
| N+1 queries | O(n) DB calls per list | `includes` / `preload` / `eager_load` |
|
|
220
|
+
| String concatenation in loop | O(n²) allocations | `StringIO` or `Array#join` |
|
|
221
|
+
| `OpenStruct` for data | 10x slower than Struct/Data | `Data.define` or `Struct` |
|
|
222
|
+
| `method_missing` without cache | Method lookup on every call | Define methods dynamically on first call |
|
|
223
|
+
| Loading entire table | Memory exhaustion | `find_each` / `in_batches` |
|
|
224
|
+
| No connection pooling | Exhausted connections | Configure pool size in database.yml |
|