pdf 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/execution/00-overview.md +121 -0
- data/.claude/execution/01-core.md +324 -0
- data/.claude/execution/02-renderer.md +237 -0
- data/.claude/execution/03-components.md +551 -0
- data/.claude/execution/04-builders.md +322 -0
- data/.claude/execution/05-view-layout.md +362 -0
- data/.claude/execution/06-evaluators.md +494 -0
- data/.claude/execution/07-entry-extensibility.md +435 -0
- data/.claude/execution/08-integration-tests.md +978 -0
- data/Rakefile +7 -3
- data/examples/01_basic_invoice.rb +139 -0
- data/examples/02_report_with_layout.rb +266 -0
- data/examples/03_inherited_views.rb +318 -0
- data/examples/04_conditional_content.rb +421 -0
- data/examples/05_custom_components.rb +442 -0
- data/examples/README.md +123 -0
- data/lib/pdf/blueprint.rb +50 -0
- data/lib/pdf/builders/content_builder.rb +96 -0
- data/lib/pdf/builders/footer_builder.rb +24 -0
- data/lib/pdf/builders/header_builder.rb +31 -0
- data/lib/pdf/component.rb +43 -0
- data/lib/pdf/components/alert.rb +42 -0
- data/lib/pdf/components/context.rb +16 -0
- data/lib/pdf/components/date.rb +28 -0
- data/lib/pdf/components/heading.rb +12 -0
- data/lib/pdf/components/hr.rb +12 -0
- data/lib/pdf/components/logo.rb +15 -0
- data/lib/pdf/components/paragraph.rb +12 -0
- data/lib/pdf/components/qr_code.rb +38 -0
- data/lib/pdf/components/spacer.rb +11 -0
- data/lib/pdf/components/span.rb +12 -0
- data/lib/pdf/components/subtitle.rb +12 -0
- data/lib/pdf/components/table.rb +48 -0
- data/lib/pdf/components/title.rb +12 -0
- data/lib/pdf/content_evaluator.rb +218 -0
- data/lib/pdf/dynamic_components.rb +17 -0
- data/lib/pdf/footer_evaluator.rb +66 -0
- data/lib/pdf/header_evaluator.rb +56 -0
- data/lib/pdf/layout.rb +61 -0
- data/lib/pdf/renderer.rb +153 -0
- data/lib/pdf/resolver.rb +36 -0
- data/lib/pdf/version.rb +1 -1
- data/lib/pdf/view.rb +113 -0
- data/lib/pdf.rb +74 -1
- metadata +127 -2
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example 03: View Inheritance
|
|
5
|
+
# Run: ruby examples/03_inherited_views.rb
|
|
6
|
+
#
|
|
7
|
+
# Demonstrates:
|
|
8
|
+
# - Base view with shared elements
|
|
9
|
+
# - Child views that extend parent
|
|
10
|
+
# - Multi-level inheritance
|
|
11
|
+
# - Layout inheritance
|
|
12
|
+
# - Method overriding in child views
|
|
13
|
+
# - Shared and specialized sections
|
|
14
|
+
|
|
15
|
+
require_relative "../lib/pdf"
|
|
16
|
+
|
|
17
|
+
# --- Shared Layout ---
|
|
18
|
+
|
|
19
|
+
class CorporateLayout < Pdf::Layout
|
|
20
|
+
header do
|
|
21
|
+
context :document_header, label: "DOCUMENT"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
footer do
|
|
25
|
+
text :company_footer, size: 7
|
|
26
|
+
page_number position: :right
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
margins top: 85, bottom: 65
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# --- Base Document (Level 1) ---
|
|
33
|
+
|
|
34
|
+
class BaseDocument < Pdf::View
|
|
35
|
+
layout CorporateLayout
|
|
36
|
+
|
|
37
|
+
# All documents start with this
|
|
38
|
+
title :document_title, size: 20
|
|
39
|
+
date :document_date
|
|
40
|
+
hr
|
|
41
|
+
spacer amount: 10
|
|
42
|
+
|
|
43
|
+
# All documents end with this
|
|
44
|
+
def document_header
|
|
45
|
+
[
|
|
46
|
+
"Acme Corporation",
|
|
47
|
+
"Document: #{data[:doc_type] || 'General'}",
|
|
48
|
+
"ID: #{data[:doc_id] || 'N/A'}"
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def company_footer
|
|
53
|
+
"Acme Corporation | Confidential | #{Date.today.year}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def document_title
|
|
57
|
+
data[:title] || "Untitled Document"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def document_date
|
|
61
|
+
data[:date] || Date.today
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# --- Report Base (Level 2) - Extends BaseDocument ---
|
|
66
|
+
|
|
67
|
+
class BaseReport < BaseDocument
|
|
68
|
+
# Inherits: title, date, hr, spacer from BaseDocument
|
|
69
|
+
|
|
70
|
+
# Reports add an executive summary
|
|
71
|
+
alert :summary_alert
|
|
72
|
+
|
|
73
|
+
section "Overview" do
|
|
74
|
+
paragraph :overview_text
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def summary_alert
|
|
78
|
+
{
|
|
79
|
+
title: "Summary",
|
|
80
|
+
description: data[:summary] || "No summary provided.",
|
|
81
|
+
color: "blue"
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def overview_text
|
|
86
|
+
data[:overview] || "Overview not available."
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# --- Sales Report (Level 3) - Extends BaseReport ---
|
|
91
|
+
|
|
92
|
+
class SalesReport < BaseReport
|
|
93
|
+
# Inherits: title, date, hr, spacer, summary_alert, overview section
|
|
94
|
+
|
|
95
|
+
section "Sales Performance" do
|
|
96
|
+
table :sales_data, columns: [
|
|
97
|
+
{ key: :product, label: "Product" },
|
|
98
|
+
{ key: :units, label: "Units Sold" },
|
|
99
|
+
{ key: :revenue, label: "Revenue" },
|
|
100
|
+
{ key: :margin, label: "Margin %" }
|
|
101
|
+
]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
section "Top Performers" do
|
|
105
|
+
each(:top_sellers) do |seller|
|
|
106
|
+
heading seller[:name], size: 11
|
|
107
|
+
span "Region: #{seller[:region]} | Sales: #{seller[:sales]}"
|
|
108
|
+
spacer amount: 5
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
section "Recommendations" do
|
|
113
|
+
each(:recommendations) do |rec|
|
|
114
|
+
paragraph "- #{rec}"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def sales_data
|
|
119
|
+
data[:sales] || []
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def top_sellers
|
|
123
|
+
data[:top_sellers] || []
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def recommendations
|
|
127
|
+
data[:recommendations] || []
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# --- HR Report (Level 3) - Also Extends BaseReport ---
|
|
132
|
+
|
|
133
|
+
class HRReport < BaseReport
|
|
134
|
+
# Inherits: title, date, hr, spacer, summary_alert, overview section
|
|
135
|
+
|
|
136
|
+
section "Headcount Summary" do
|
|
137
|
+
table :headcount_data, columns: [:department, :count, :change, :openings]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
section "Recent Hires" do
|
|
141
|
+
each(:recent_hires) do |hire|
|
|
142
|
+
paragraph "#{hire[:name]} - #{hire[:role]} (#{hire[:department]}) - Started #{hire[:start_date]}"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
section "Turnover Analysis" do
|
|
147
|
+
alert :turnover_alert
|
|
148
|
+
paragraph :turnover_analysis
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def headcount_data
|
|
152
|
+
data[:headcount] || []
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def recent_hires
|
|
156
|
+
data[:recent_hires] || []
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def turnover_alert
|
|
160
|
+
rate = data[:turnover_rate] || 0
|
|
161
|
+
color = rate < 10 ? "green" : rate < 20 ? "orange" : "red"
|
|
162
|
+
{
|
|
163
|
+
title: "Turnover Rate: #{rate}%",
|
|
164
|
+
description: rate < 10 ? "Healthy retention" : "Attention needed",
|
|
165
|
+
color: color
|
|
166
|
+
}
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def turnover_analysis
|
|
170
|
+
data[:turnover_analysis] || "No analysis available."
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# --- Finance Report (Level 3) - Also Extends BaseReport ---
|
|
175
|
+
|
|
176
|
+
class FinanceReport < BaseReport
|
|
177
|
+
# Inherits: title, date, hr, spacer, summary_alert, overview section
|
|
178
|
+
|
|
179
|
+
section "Financial Summary" do
|
|
180
|
+
table :financial_data, columns: [:metric, :current, :previous, :change]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
section "Budget Status" do
|
|
184
|
+
each(:budget_items) do |item|
|
|
185
|
+
color = item[:status] == "On Track" ? "green" : item[:status] == "At Risk" ? "orange" : "red"
|
|
186
|
+
alert title: item[:category], description: "#{item[:spent]} of #{item[:budget]} (#{item[:status]})", color: color
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
section "Cash Flow" do
|
|
191
|
+
paragraph :cash_flow_summary
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def financial_data
|
|
195
|
+
data[:financials] || []
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def budget_items
|
|
199
|
+
data[:budget] || []
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def cash_flow_summary
|
|
203
|
+
data[:cash_flow] || "Cash flow data not available."
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# --- Generate All Three Report Types ---
|
|
208
|
+
|
|
209
|
+
puts "Generating inherited view examples...\n\n"
|
|
210
|
+
|
|
211
|
+
# 1. Sales Report
|
|
212
|
+
sales_data = {
|
|
213
|
+
doc_type: "Sales Report",
|
|
214
|
+
doc_id: "SR-2024-Q4-001",
|
|
215
|
+
title: "Q4 2024 Sales Report",
|
|
216
|
+
date: Date.new(2024, 12, 31),
|
|
217
|
+
summary: "Strong Q4 performance with 22% growth over Q3. Enterprise segment exceeded targets.",
|
|
218
|
+
overview: "This report covers sales performance for Q4 2024, highlighting key metrics, top performers, and strategic recommendations for Q1 2025.",
|
|
219
|
+
|
|
220
|
+
sales: [
|
|
221
|
+
{ product: "Enterprise Suite", units: 145, revenue: "$2.1M", margin: "68%" },
|
|
222
|
+
{ product: "Professional", units: 892, revenue: "$1.3M", margin: "72%" },
|
|
223
|
+
{ product: "Starter", units: 2340, revenue: "$468K", margin: "81%" },
|
|
224
|
+
{ product: "Add-ons", units: 1567, revenue: "$235K", margin: "89%" }
|
|
225
|
+
],
|
|
226
|
+
|
|
227
|
+
top_sellers: [
|
|
228
|
+
{ name: "Sarah Johnson", region: "West Coast", sales: "$890K" },
|
|
229
|
+
{ name: "Michael Chen", region: "Northeast", sales: "$756K" },
|
|
230
|
+
{ name: "Emily Rodriguez", region: "Southwest", sales: "$623K" }
|
|
231
|
+
],
|
|
232
|
+
|
|
233
|
+
recommendations: [
|
|
234
|
+
"Increase Enterprise Suite marketing budget by 15%",
|
|
235
|
+
"Launch referral program for Professional tier",
|
|
236
|
+
"Expand West Coast team based on performance",
|
|
237
|
+
"Develop upsell path from Starter to Professional"
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
sales_path = "/tmp/sales_report_inherited.pdf"
|
|
242
|
+
SalesReport.new(sales_data).to_file(sales_path)
|
|
243
|
+
puts "Generated Sales Report: #{sales_path}"
|
|
244
|
+
|
|
245
|
+
# 2. HR Report
|
|
246
|
+
hr_data = {
|
|
247
|
+
doc_type: "HR Report",
|
|
248
|
+
doc_id: "HR-2024-Q4-001",
|
|
249
|
+
title: "Q4 2024 HR Report",
|
|
250
|
+
date: Date.new(2024, 12, 31),
|
|
251
|
+
summary: "Successful hiring quarter with 28 new employees. Retention improved by 3%.",
|
|
252
|
+
overview: "Quarterly human resources report covering headcount, hiring, and employee retention metrics.",
|
|
253
|
+
|
|
254
|
+
headcount: [
|
|
255
|
+
{ department: "Engineering", count: 45, change: "+8", openings: 5 },
|
|
256
|
+
{ department: "Sales", count: 23, change: "+4", openings: 2 },
|
|
257
|
+
{ department: "Marketing", count: 12, change: "+2", openings: 1 },
|
|
258
|
+
{ department: "Operations", count: 18, change: "+3", openings: 0 },
|
|
259
|
+
{ department: "Finance", count: 8, change: "+1", openings: 1 }
|
|
260
|
+
],
|
|
261
|
+
|
|
262
|
+
recent_hires: [
|
|
263
|
+
{ name: "Alex Thompson", role: "Senior Engineer", department: "Engineering", start_date: "Dec 1" },
|
|
264
|
+
{ name: "Jessica Lee", role: "Account Executive", department: "Sales", start_date: "Nov 15" },
|
|
265
|
+
{ name: "David Park", role: "Product Designer", department: "Engineering", start_date: "Nov 1" },
|
|
266
|
+
{ name: "Maria Santos", role: "Marketing Manager", department: "Marketing", start_date: "Oct 15" }
|
|
267
|
+
],
|
|
268
|
+
|
|
269
|
+
turnover_rate: 8,
|
|
270
|
+
turnover_analysis: "Q4 turnover decreased from 11% to 8%, primarily due to improved compensation " \
|
|
271
|
+
"packages and the new remote work policy. Engineering retention improved significantly " \
|
|
272
|
+
"following the technical career ladder implementation."
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
hr_path = "/tmp/hr_report_inherited.pdf"
|
|
276
|
+
HRReport.new(hr_data).to_file(hr_path)
|
|
277
|
+
puts "Generated HR Report: #{hr_path}"
|
|
278
|
+
|
|
279
|
+
# 3. Finance Report
|
|
280
|
+
finance_data = {
|
|
281
|
+
doc_type: "Finance Report",
|
|
282
|
+
doc_id: "FIN-2024-Q4-001",
|
|
283
|
+
title: "Q4 2024 Finance Report",
|
|
284
|
+
date: Date.new(2024, 12, 31),
|
|
285
|
+
summary: "Revenue exceeded forecast by 8%. Operating margin improved to 24%.",
|
|
286
|
+
overview: "Quarterly financial report with key metrics, budget analysis, and cash flow summary.",
|
|
287
|
+
|
|
288
|
+
financials: [
|
|
289
|
+
{ metric: "Revenue", current: "$4.1M", previous: "$3.6M", change: "+13.9%" },
|
|
290
|
+
{ metric: "Gross Profit", current: "$2.9M", previous: "$2.5M", change: "+16.0%" },
|
|
291
|
+
{ metric: "Operating Expenses", current: "$1.9M", previous: "$1.8M", change: "+5.6%" },
|
|
292
|
+
{ metric: "Net Income", current: "$980K", previous: "$720K", change: "+36.1%" },
|
|
293
|
+
{ metric: "EBITDA", current: "$1.2M", previous: "$950K", change: "+26.3%" }
|
|
294
|
+
],
|
|
295
|
+
|
|
296
|
+
budget: [
|
|
297
|
+
{ category: "Engineering", budget: "$1.2M", spent: "$1.15M", status: "On Track" },
|
|
298
|
+
{ category: "Sales & Marketing", budget: "$800K", spent: "$820K", status: "At Risk" },
|
|
299
|
+
{ category: "Operations", budget: "$400K", spent: "$380K", status: "On Track" },
|
|
300
|
+
{ category: "R&D", budget: "$600K", spent: "$720K", status: "Over Budget" }
|
|
301
|
+
],
|
|
302
|
+
|
|
303
|
+
cash_flow: "Operating cash flow remained strong at $1.1M for the quarter. Major capital expenditure " \
|
|
304
|
+
"of $250K for infrastructure upgrades was completed. Cash reserves stand at $3.2M, " \
|
|
305
|
+
"providing 8 months of runway at current burn rate. Accounts receivable aging improved " \
|
|
306
|
+
"with DSO reduced from 45 to 38 days."
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
finance_path = "/tmp/finance_report_inherited.pdf"
|
|
310
|
+
FinanceReport.new(finance_data).to_file(finance_path)
|
|
311
|
+
puts "Generated Finance Report: #{finance_path}"
|
|
312
|
+
|
|
313
|
+
puts "\nOpening all three reports..."
|
|
314
|
+
system("open", sales_path)
|
|
315
|
+
sleep 0.5
|
|
316
|
+
system("open", hr_path)
|
|
317
|
+
sleep 0.5
|
|
318
|
+
system("open", finance_path)
|